<?php
/*
This PHP script is licensed under the GPL
Author: Terence Yim
E-mail: chtyim@gmail.com
Homepage: http://web-news.sourceforge.net
*/
require("webnews/util.php");
require("webnews/MIME_Message.php");
define("NNTP_PORT", 119);
// Define the return status code
define("SERVER_READY", 200);
define("SERVER_READY_NO_POST", 201);
define("GROUP_SELECTED", 211);
define("INFORMATION_FOLLOWS", 215);
define("ARTICLE_HEAD_BODY", 220);
define("ARTICLE_HEAD", 221);
define("ARTICLE_BODY", 222);
define("ARTICLE_OVERVIEW", 224);
define("ARTICLE_POST_OK", 240);
define("ARTICLE_POST_READY", 340);
define("AUTH_ACCEPT", 281);
define("MORE_AUTH_INFO", 381);
define("AUTH_REQUIRED", 480);
define("AUTH_REJECTED", 482);
define("NO_PERMISSION", 502);
class NNTP {
public $nntp; // var
public $server;
public $user;
public $pass;
public $proxy_server;
public $proxy_port;
public $proxy_user;
public $proxy_pass;
public $use_proxy;
public $error_number;
public $error_message;
//function NNTP($server, $user = "", $pass = "", $proxy_server = "", $proxy_port = "", $proxy_user = "", $proxy_pass = "") {
public function __construct($server, $user = "", $pass = "", $proxy_server = "", $proxy_port = "", $proxy_user = "", $proxy_pass = "") {
$this->server = $server;
$this->user = $user;
$this->pass = $pass;
$this->proxy_server = $proxy_server;
$this->proxy_port = $proxy_port;
$this->proxy_user = $proxy_user;
$this->proxy_pass = $proxy_pass;
if ((strcmp($this->proxy_server, "") != 0) && (strcmp($this->proxy_port, "") != 0)) {
$this->use_proxy = TRUE;
} else {
$this->use_proxy = FALSE;
}
}
/* Open a TCP connection to the specific server
Return: TRUE - open succeeded
FALSE - open failed
*/
function connect() {
if ($this->nntp) { // We won't try to re-connect an already opened connection
return TRUE;
}
if ($this->use_proxy) {
$this->nntp = fsockopen($this->proxy_server, $this->proxy_port, $this->error_number, $this->error_message);
} else {
$this->nntp = fsockopen($this->server, NNTP_PORT, $this->error_number, $this->error_message);
}
if ($this->nntp) {
if ($this->use_proxy) {
$response = "CONNECT ".$this->server.":".NNTP_PORT." HTTP/1.0\r\n";
if ((strcmp($this->proxy_user, "") != 0) && (strcmp($this->proxy_pass, "") != 0)) {
$response .= "Proxy-Authorization: Basic "; // Only support Basic authentication type
$response .= base64_encode($this->proxy_user.":".$this->proxy_pass);
$response .= "\r\n";
}
$response = $this->send_request($response);
if (strstr($response, "200 Connection established")) {
fgets($this->nntp, 4096); // Skip an empty line
$response = $this->parse_response(fgets($this->nntp, 4096));
} else {
$response["status"] = NO_PERMISSION; // Assign it to something dummy
$response["message"] = "No permission";
}
} else {
$response = $this->parse_response(fgets($this->nntp, 4096));
}
if (($response["status"] == SERVER_READY) || ($response["status"] == SERVER_READY_NO_POST)) {
$this->send_request("mode reader");
if (strcmp($this->user, "") != 0) {
$response = $this->parse_response($this->send_request("authinfo user ".$this->user));
if ($response["status"] == MORE_AUTH_INFO) {
$response = $this->parse_response($this->send_request("authinfo pass ".$this->pass));
if ($response["status"] == AUTH_ACCEPT) {
return TRUE;
}
}
} else {
return TRUE;
}
}
$this->error_number = $response["status"];
$this->error_message = $response["message"];
}
return FALSE;
}
/* Close the TCP Connection
*/
function quit() {
if ($this->nntp) {
$this->send_request("quit");
fclose($this->nntp);
$this->nntp = NULL;
}
}
function parse_response($response) {
$status = substr($response, 0, 3);
$message = str_replace("\r\n", "", substr($response, 4));
return array("status" => intval($status),
"message" => $message);
}
function send_request($request) {
if ($this->nntp) {
fputs($this->nntp, $request."\r\n");
fflush($this->nntp);
return fgets($this->nntp, 4096);
}
}
function read_response_body() {
if ($this->nntp) {
$result = "";
$buf = fgets($this->nntp, 4096);
while (!preg_match("/^\.\s*$/", $buf)) {
$result .= $buf;
$buf = fgets($this->nntp, 4096);
}
return $result;
}
}
function join_group($group) {
if ($this->nntp) {
$buf = $this->send_request("group ".$group);
$response = $this->parse_response($buf);
if ($response["status"] == GROUP_SELECTED) {
$result = preg_split("/\s/", $response["message"]);
return array("count" => $result[0],
"start_id" => $result[1],
"end_id" => $result[2],
"group" => $result[3]);
}
}
$this->error_number = $response["status"];
$this->error_message = $response["message"];
return NULL;
}
function get_article_list($group) {
if ($this->nntp) {
$buf = $this->send_request("listgroup ".$group);
$response = $this->parse_response($buf);
if ($response["status"] == GROUP_SELECTED) {
$body = $this->read_response_body();
return explode("\r\n", substr($body, 0, strlen($body) - 2)); // Cut the last \r\n
}
}
$this->error_number = $response["status"];
$this->error_message = $response["message"];
return false;
}
function get_group_list($group_pattern) {
$response = $this->parse_response($this->send_request("list active ".$group_pattern));
if ($response["status"] == INFORMATION_FOLLOWS) {
$result = array();
$buf = fgets($this->nntp, 4096);
while (!preg_match("/^\.\s*$/", $buf)) {
list($group, $last, $first, $post) = preg_split("/\s+/", $buf, 4);
$result[] = $group;
$buf = fgets($this->nntp, 4096);
}
return $result;
}
$this->error_number = $response["status"];
$this->error_message = $response["message"];
return FALSE;
}
// The $group can have wildcard like comp.lang.*
function get_groups_description($groups) {
$response = $this->parse_response($this->send_request("list newsgroups ".$groups));
if ($response["status"] == INFORMATION_FOLLOWS) {
$result = array();
$buf = fgets($this->nntp, 4096);
while (!preg_match("/^\.\s*$/", $buf)) {
list($key, $value) = preg_split("/\s+/", $buf, 2);
$result[$key] = trim($value);
$buf = fgets($this->nntp, 4096);
}
return $result;
}
$this->error_number = $response["status"];
$this->error_message = $response["message"];
return FALSE;
}
// Get a message summary tree. The subject and sender will be matched with the reg_pat using regular expression
function get_message_summary($start_id, $end_id, $reg_pat="//", $flat_tree=FALSE) {
$buf = $this->send_request("xover ".$start_id."-".$end_id);
$response = $this->parse_response($buf);
$message_tree_root = new MessageTreeNode(NULL);
$message_tree_root->set_show_children(TRUE);
$ref_list = array();
if ($response["status"] == ARTICLE_OVERVIEW) {
$buf = fgets($this->nntp, 4096);
while (!preg_match("/^\.\s*$/", $buf)) {
$elements = preg_split("/\t/", $buf);
$elements[1] = decode_MIME_header($elements[1]); // Decode subject
$elements[2] = decode_MIME_header($elements[2]); // Decode from
if (preg_match($reg_pat, $elements[1]) || preg_match($reg_pat, $elements[2])) {
$message_info = new MessageInfo();
$message_info->nntp_message_id = $elements[0];
if (!mb_check_encoding($elements[1], 'UTF-8')) {
//$elements[1] = mb_convert_encoding($elements[1],'HTML-ENTITIES','UTF-8');
//$elements[1]= imap_utf8(utf8_decode($elements[1]));
$elements[1] = iconv('Windows-1252', 'UTF-8//IGNORE', $elements[1]);
}
$message_info->subject = $elements[1];
if (!mb_check_encoding($elements[2], 'UTF-8')) {
//$elements[2] = mb_convert_encoding($elements[2],'HTML-ENTITIES','UTF-8');
//$elements[2]= imap_utf8(utf8_decode($elements[2]));
$elements[2] = iconv('Windows-1252', 'UTF-8//IGNORE', $elements[2]);
}
$message_info->from = decode_sender($elements[2]);
$elements[3] = preg_replace('/\s*\(.*\)$/', '', $elements[3]);
$message_info->date = strtotime($elements[3]);
if ($message_info->date == -1) {
$message_info->date = $elements[3];
}
$message_info->message_id = $elements[4];
if (strlen($elements[5]) != 0) {
$message_info->references = preg_split("/\s+/", trim($elements[5]));
} else {
$message_info->references = array();
}
$message_info->byte_count = $elements[6];
$message_info->line_count = $elements[7];
$message_tree_root->insert_message_info($message_info, $flat_tree);
$ref_list[$message_info->nntp_message_id] = array($message_info->message_id, $message_info->references);
}
$buf = fgets($this->nntp, 4096);
}
return array($message_tree_root, $ref_list);
}
$this->error_number = $response["status"];
$this->error_message = $response["message"];
return NULL;
}
// Similar to the get_message_summary function, except that the processing is much
// lightweight with the return is just an array of message summaries instead of
// a tree plus a reference list.
function get_summary($start_id, $end_id) {
$buf = $this->send_request("xover ".$start_id."-".$end_id);
$response = $this->parse_response($buf);
if ($response["status"] == ARTICLE_OVERVIEW) {
$buf = fgets($this->nntp, 4096);
$result = array();
while (!preg_match("/^\.\s*$/", $buf)) {
$elements = preg_split("/\t/", $buf);
$nntp_id = $elements[0];
$result[$nntp_id]["subject"] = decode_MIME_header($elements[1]);
$from = decode_sender(decode_MIME_header($elements[2]));
$result[$nntp_id]["from_name"] = $from["name"];
$result[$nntp_id]["from_email"] = $from["email"];
$elements[3] = preg_replace('/\s*\(.*\)$/', '', $elements[3]);
$result[$nntp_id]["date"] = strtotime($elements[3]);
if ($result[$nntp_id]["date"] == -1) {
$result[$nntp_id]["date"] = $elements[3];
}
$result[$nntp_id]["message_id"] = $elements[4];
$result[$nntp_id]["references"] = trim($elements[5]);
$result[$nntp_id]["byte_count"] = $elements[6];
$result[$nntp_id]["line_count"] = $elements[7];
$buf = fgets($this->nntp, 4096);
}
return $result;
}
$this->error_number = $response["status"];
$this->error_message = $response["message"];
return NULL;
}
// Get a tree structure with the $message_id is one of the children
// $message_id here is the Message-ID field
function get_message_thread($start_id, $end_id, $root_ref) {
if (!$root_ref) {
return NULL;
}
$buf = $this->send_request("xover ".$start_id."-".$end_id);
$response = $this->parse_response($buf);
$thread_root = new MessageTreeNode(NULL);
$thread_root->set_show_children(TRUE);
if ($response["status"] == ARTICLE_OVERVIEW) {
$buf = fgets($this->nntp, 4096);
while (!preg_match("/^\.\s*$/", $buf)) {
$elements = preg_split("/\t/", $buf);
$elements[1] = decode_MIME_header($elements[1]); // Decode subject
$elements[2] = decode_MIME_header($elements[2]); // Decode from
if ((strpos($elements[4], $root_ref) !== FALSE) || (strpos($elements[5], $root_ref) !== FALSE)) {
$message_info = new MessageInfo();
$message_info->nntp_message_id = $elements[0];
if (!mb_check_encoding($elements[1], 'UTF-8')) {
//$elements[1]= imap_utf8(utf8_decode($elements[1]));
$elements[1] = iconv('Windows-1252', 'UTF-8//IGNORE', $elements[1]);
//var_dump(mb_detect_encoding($elements[1], ['UTF-8', 'Windows-1252', 'ISO-8859-1'], true));
//$elements[1] = mb_convert_encoding($elements[1],'HTML-ENTITIES','UTF-8');
//$elements[1]=htmlspecialchars($elements[1]);
}
$message_info->subject = $elements[1];
if (!mb_check_encoding($elements[2], 'UTF-8')) {
//$elements[2] = mb_convert_encoding($elements[2],'HTML-ENTITIES','UTF-8');
//$elements[2]= imap_utf8(utf8_decode($elements[2]));
$elements[2] = iconv('Windows-1252', 'UTF-8//IGNORE', $elements[2]);
}
$message_info->from = decode_sender($elements[2]);
$elements[3] = preg_replace('/\s*\(.*\)$/', '', $elements[3]);
$message_info->date = strtotime($elements[3]);
if ($message_info->date == -1) {
$message_info->date = $elements[3];
}
$message_info->message_id = $elements[4];
if (strlen($elements[5]) != 0) {
$message_info->references = preg_split("/\s+/", trim($elements[5]));
} else {
$message_info->references = array();
}
$message_info->byte_count = $elements[6];
$message_info->line_count = $elements[7];
$thread_root->insert_message_info($message_info, $flat_tree);
}
$buf = fgets($this->nntp, 4096);
}
return $thread_root;
}
$this->error_number = $response["status"];
$this->error_message = $response["message"];
return NULL;
}
function get_header($message_id) {
$response = $this->parse_response($this->send_request("head ".$message_id));
if (($response["status"] == ARTICLE_HEAD) || ($response["status"] == ARTICLE_HEAD_BODY)) {
$header = "";
$buf = fgets($this->nntp, 4096);
while (!preg_match("/^\.\s*$/", $buf)) {
$header .= $buf;
$buf = fgets($this->nntp, 4096);
}
return new MIME_message($header);
}
$this->error_number = $response["status"];
$this->error_message = $response["message"];
return NULL;
}
function get_article($message_id) {
$response = $this->parse_response($this->send_request("article ".$message_id));
if (($response["status"] == ARTICLE_BODY) || ($response["status"] == ARTICLE_HEAD_BODY)) {
$message = "";
$buf = fgets($this->nntp, 4096);
while (!preg_match("/^\.\s*$/", $buf)) {
$message .= $buf;
$buf = fgets($this->nntp, 4096);
}
if (!mb_check_encoding($message, 'UTF-8')) {
//$message = mb_convert_encoding($message,'HTML-ENTITIES','UTF-8');
$message = iconv('Windows-1252', 'UTF-8//IGNORE', $message);
}
return new MIME_Message($message);
}
$this->error_number = $response["status"];
$this->error_message = $response["message"];
return NULL;
}
function post_article($subject, $name, $email, $newsgroups, $references, $message, $files) {
global $messages_ini;
$from = encode_MIME_header($name)." <".$email.">";
$groups = "";
foreach ($newsgroups as $news) {
$groups = $groups.",".$news;
}
$groups = substr($groups, 1);
$current_time = date("D, d M Y H:i:s O", time());
if (strlen($groups) != 0) {
$response = $this->parse_response($this->send_request("post"));
if ($response["status"] == ARTICLE_POST_READY) {
$send_message = "";
// Send the header
$send_message .= "Subject: ".encode_MIME_header($subject)."\r\n";
$send_message .= "From: ".$from."\r\n";
$send_message .= "Newsgroups: ".$groups."\r\n";
$send_message .= "Date: ".$current_time."\r\n";
$send_message .= "X-Newsreader: CatWalker 0126\r\n";
$send_message .= "Mime-Version: 1.0\r\n";
if (sizeof($files) != 0) { // Handling uploaded files
srand();
$boundary = "----------".rand().time();
$send_message .= "Content-Type: multipart/mixed; boundary=\"".$boundary."\"\r\n";
$boundary = "--".$boundary;
} else {
$boundary = "";
$send_message .= "Content-Type: text/plain; charset=\"UTF-8\";\r\n";
}
if ($references && (strlen($references) != 0)) {
$send_message .= "References: ".$references."\r\n";
}
$send_message .= "\r\n"; // Header body separator
$send_message .= create_message_body($message, $files, $boundary);
// Send the body
fputs($this->nntp, $send_message);
$response = $this->parse_response($this->send_request("\r\n."));
if ($response["status"] == ARTICLE_POST_OK) {
// Return the message sent with all the attachments stripped
if (sizeof($files) != 0) { // There is attachment, strip it
$len = strpos($send_message, $boundary, strpos($send_message, $boundary) + strlen($boundary));
$send_message = substr($send_message, 0, $len);
$send_message .= "\r\n";
$send_message .= sizeof($files);
$send_message .= $messages_ini["text"]["post_attachments"];
$send_message .= "\r\n".$boundary."--";
}
return new MIME_Message($send_message);
}
}
}
return NULL;
}
function get_error_number() {
return $this->error_number;
}
function get_error_message() {
return $this->error_message;
}
}
class MessageInfo {
public $nntp_message_id; //var
public $subject;
public $from;
public $date;
public $message_id;
public $references;
public $byte_count;
public $line_count;
}
class MessageTreeNode {
public $message_info;
public $children;
public $show_children;
//function MessageTreeNode($message_info) {
public function __construct($message_info) {
$this->message_info = $message_info;
$this->children = array();
$this->show_children = FALSE;
}
function set_show_children($show) {
$this->show_children = $show;
}
function set_show_all_children($show) {
$this->set_show_children($show);
$keys = $this->get_children_keys();
foreach ($keys as $key) {
$child =$this->get_child($key);
//$child->set_show_all_children($show);
if (is_object($child)) {
$child->set_show_all_children($show);
}
}
}
function is_show_children() {
return $this->show_children;
}
function set_message_info($message_info) {
$this->message_info = $message_info;
}
function get_message_info() {
return $this->message_info;
}
function set_child($key, &$child) {
$this->children[$key] = $child;
}
function &get_child($key) {
if (isset($this->children[$key])) {
return $this->children[$key];
} else {
return NULL;
}
}
function count_children() {
//return sizeof($this->children);
return sizeof(isset($this->children) ? $this->children : []);
}
function get_children_keys() {
//return array_keys($this->children);
return is_array($this->children) ? array_keys($this->children) : [];
}
function get_children($start = 0, $length = -1) {
if ($length == -1) {
return array_slice($this->children, $start);
} else {
return array_slice($this->children, $start, $length);
}
}
function insert_message_info($message_info, $flat_tree) {
$node =& $this;
if (!$flat_tree) {
foreach ($message_info->references as $ref_no) {
$tmpnode =& $node->get_child($ref_no);
if ($tmpnode != NULL) {
$node =& $tmpnode;
} else {
$tmp_info = new MessageInfo();
$tmp_info->nntp_message_id = -1;
$tmp_info->message_id = $ref_no;
$tmp_info->date = 0;
$newnode = new MessageTreeNode($tmp_info);
$node->set_child($ref_no, $newnode);
$node =& $node->get_child($ref_no);
}
}
}
$child =& $node->get_child($message_info->message_id);
if ($child == NULL) {
$child = new MessageTreeNode($message_info);
} else {
$child->set_message_info($message_info);
}
$node->set_child($message_info->message_id, $child);
}
function merge_tree($root_node) {
// If 2 children have the same key, the new one will replace the current one
$keys = $root_node->get_children_keys();
foreach ($keys as $key) {
$child =& $root_node->get_child($key);
$message_info = $child->get_message_info();
$ref_list = $message_info->references;
$node =& $this;
if (sizeof($ref_list) != 0) {
foreach ($ref_list as $ref) {
$tmp =& $node->get_child($ref);
if ($tmp != NULL) {
$node =& $tmp;
}
}
}
$node->set_child($key, $child);
}
}
function compact_tree() {
$children_keys = $this->get_children_keys();
foreach ($children_keys as $child_key) {
$child =& $this->get_child($child_key);
$child->compact_tree();
$info = $child->get_message_info();
if ($info->nntp_message_id == -1) {
// Need to remove this child and promote it's children
$keys = $child->get_children_keys();
foreach ($keys as $key) {
$tmp_node =& $child->get_child($key);
$this->set_child($key, $tmp_node);
}
unset($this->children[$child_key]);
}
}
}
function sort_message($field, $asc) {
$function_name = "compare_by_".$field;
if ($asc) {
$function_name .= "_asc";
} else {
$function_name .= "_desc";
}
if (method_exists($this, $function_name)) {
/*if (sizeof($this->children) != 0) {
uasort($this->children, array($this, $function_name));
}*/
if (!empty($this->children)) {
uasort($this->children, [$this, $function_name]);
}
}
}
function deep_sort_message($field, $asc) {
$this->sort_message($field, $asc);
//if (sizeof($this->children) != 0) {
if (sizeof(isset($this->children) ? $this->children : []) != 0) {
$keys = $this->get_children_keys();
foreach ($keys as $key) {
$child =& $this->get_child($key);
$child->deep_sort_message($field, $asc);
}
}
}
function compare_by_subject_asc($node_1, $node_2) {
$subject_1 = $node_1->get_message_info();
$subject_2 = $node_2->get_message_info();
$subject_1 = $subject_1->subject;
$subject_2 = $subject_2->subject;
return strcasecmp($subject_1, $subject_2);
}
function compare_by_subject_desc($node_1, $node_2) {
$subject_1 = $node_1->get_message_info();
$subject_2 = $node_2->get_message_info();
$subject_1 = $subject_1->subject;
$subject_2 = $subject_2->subject;
return strcasecmp($subject_2, $subject_1);
}
function compare_by_from_asc($node_1, $node_2) {
$from_1 = $node_1->get_message_info();
$from_2 = $node_2->get_message_info();
$from_1 = $from_1->from["name"];
$from_2 = $from_2->from["name"];
return strcasecmp($from_1, $from_2);
}
function compare_by_from_desc($node_1, $node_2) {
$from_1 = $node_1->get_message_info();
$from_2 = $node_2->get_message_info();
$from_1 = $from_1->from["name"];
$from_2 = $from_2->from["name"];
return strcasecmp($from_2, $from_1);
}
function compare_by_date_asc($node_1, $node_2) {
$date_1 = $node_1->get_message_info();
$date_2 = $node_2->get_message_info();
$date_1 = $date_1->date;
$date_2 = $date_2->date;
if ($date_1 < $date_2) {
return -1;
} else if ($date_1 > $date_2) {
return 1;
} else {
return 0;
}
}
function compare_by_date_desc($node_1, $node_2) {
$date_1 = $node_1->get_message_info();
$date_2 = $node_2->get_message_info();
$date_1 = $date_1->date;
$date_2 = $date_2->date;
if ($date_1 > $date_2) {
return -1;
} else if ($date_1 < $date_2) {
return 1;
} else {
return 0;
}
}
}
?>
Show this record as plain/text
nopaste service by: Roberto Corrado