Browse Source

Initial commit.

qwnu 11 years ago
parent
commit
77c0170564
2 changed files with 510 additions and 0 deletions
  1. 242 0
      qwnet.php
  2. 268 0
      ssh.php

+ 242 - 0
qwnet.php

@@ -0,0 +1,242 @@
+<?
+##############################################################################################################
+#
+# QWnet
+#
+# Developer(s): swiNg
+#
+# This script is running as a daemon on the server, parsing messages from QWnet through SSH.
+#
+##############################################################################################################
+
+##############################################################################################################
+# INCLUDES
+##############################################################################################################
+
+require "../includes/database.php";
+require "../includes/ssh.php";
+
+##############################################################################################################
+# PROCEDURES
+##############################################################################################################
+
+#*************************************************************************************************************
+# FUNCTION: SaveMessage() // Save QWnet message to database.
+#*************************************************************************************************************
+function SaveMessage($timestamp, $ident, $network, $frequency, $user_id, $nick, $source, $message) {
+
+	// open database connection
+
+	$qwnu_dbconn = gOpenDB();
+
+	// check if message already exist in the database (posted from website)
+	// in that case update it with the fetched ident
+
+	$id = GetMessageID("", $network, $frequency, $nick, $source, $message);
+
+	// construct sql query
+
+	if ($id == 0) {
+		$sql = "
+			INSERT INTO qwnu_qwnet (create_time, ident, network, frequency, user_id, nick, source, message, counter) VALUES (
+				'".mysql_real_escape_string($timestamp)."',
+				'".mysql_real_escape_string($ident)."',
+				'".mysql_real_escape_string($network)."',
+				'".mysql_real_escape_string($frequency)."',
+				'".mysql_real_escape_string($user_id)."',
+				'".mysql_real_escape_string($nick)."',
+				'".mysql_real_escape_string($source)."',
+				'".mysql_real_escape_string($message)."',
+				'0'
+			)";
+	}
+	else {
+		$sql = "UPDATE qwnu_qwnet SET ident = '".mysql_real_escape_string($ident)."' WHERE id = ".$id;
+	}
+
+	// execute sql query
+
+	if (!mysql_query($sql)) {
+		die(mysql_error());
+	}
+
+	// close database connection
+
+	gCloseDB($qwnu_dbconn);
+
+}
+
+#*************************************************************************************************************
+# FUNCTION: GetMessageID() // Get ID of already existing message.
+#*************************************************************************************************************
+function GetMessageID($ident, $network, $frequency, $nick, $source, $message) {
+
+	// query sql for previous approved posts
+
+	$sql = "
+		SELECT id
+		FROM qwnu_qwnet WHERE
+			ident = '".mysql_real_escape_string($ident)."' AND
+			network = '".mysql_real_escape_string($network)."' AND
+			frequency = '".mysql_real_escape_string($frequency)."' AND
+			nick = '".mysql_real_escape_string($nick)."' AND
+			source = '".mysql_real_escape_string($source)."' AND
+			message = '".mysql_real_escape_string($message)."'
+		ORDER BY id DESC
+		LIMIT 0, 1";
+
+	$recordset = mysql_query($sql);
+	$row = mysql_fetch_assoc($recordset);
+
+	// return result
+
+	return (mysql_num_rows($recordset) > 0 ? $row["id"] : 0);
+}
+
+#*************************************************************************************************************
+# FUNCTION: MainLoop() // Parses the stream and saves messages.
+#*************************************************************************************************************
+function MainLoop(&$ssh) {
+
+	// set variables
+
+	$timeout = 300;
+	$time_last_activity = time();
+
+	// loop
+
+	while (true) {
+		$data = "";
+
+		// read stream
+
+		if ($ssh->authed) {
+
+			// fetch data
+
+			while ($buf = $ssh->ReadStream()) {
+				$data .= $buf;
+			}
+
+			// parse data
+
+			if ($data) {
+
+				$time_last_activity = time();
+				$data_array = explode("\r\n", $data);
+
+				// echo any data to console
+
+				foreach ($data_array as $data) {
+					if ($data != "") {
+
+						// get timestamp and action
+
+						$array = explode(" ", substr($data, 27));
+						$timestamp = time(); #strtotime(substr($data, 0, 19)); // use local timestamp time()
+		 				$action = $array[0];
+
+						// get action and ident
+
+						if ($action == "BC") {
+
+							// get ident
+
+							$ident = (array_key_exists(1, $array) ? $array[1] : "");
+
+							// get network, frequency and source
+
+							$i = 0;
+							$array = explode(",", substr($data, strpos($data, $ident) + strlen($ident) + 1));
+							$network = $array[0];
+							$frequency = (array_key_exists(1, $array) ? $array[1] : "");
+							$source = (array_key_exists(2, $array) ? $array[2] : "");
+							$message = "";
+
+							// get nick and message
+
+							$array = explode("','", substr($data, strpos($data, $source) + strlen($source) + 2));
+
+							foreach($array as $item) {
+								if ($i == 0) {
+									$nick = $item;
+								}
+								else {
+									$message .= $item;
+								}
+
+								$i++;
+							}
+
+							if (strlen($message) > 0) {
+								$message = substr($message, 0, strrpos($message, "'"));
+							}
+
+							// save message to database
+
+							if ($network == "QWalt" && $frequency == "-qw-") {
+								SaveMessage($timestamp, $ident, $network, $frequency, 0, $nick, $source, $message);
+							}
+						}
+
+						// print anything but ping messages
+
+						if (substr($data, 27) != "PING alive?") {
+							echo $data."\n";
+						}
+					}
+				}
+			}
+		}
+
+		// break on exceeded time limit or sleep 2 seconds before itteration
+
+		if ($timeout >= 0 && time() - $time_last_activity >= $timeout) {
+			echo "Connection timeout. Reconnecting...\n";
+
+			$ssh->Connect(QWNET_HOST, QWNET_PORT, QWNET_FP, QWNET_USR, "", QWNET_KEY_PATH_PUB, QWNET_KEY_PATH_PRIV);
+
+			echo $ssh->Shell("vt102", 1);
+
+			$time_last_activity = time();
+		}
+		else {
+			sleep(3);
+		}
+	}
+}
+
+##############################################################################################################
+# INLINE CODE
+##############################################################################################################
+
+// disable script timeout
+
+set_time_limit(0);
+
+// ssh settings
+
+define("QWNET_HOST", "b4r.org");
+define("QWNET_PORT", "22");
+define("QWNET_FP", "90C300C5853F3679C87240DAAF5F32A1");
+define("QWNET_USR", "stomp");
+define("QWNET_KEY_PATH_PUB", "");
+define("QWNET_KEY_PATH_PRIV", "");
+
+// open ssh connection and shell, then jump into main loop
+
+$ssh = new SSH;
+
+try {
+	echo "Connecting to QWnet central...\n";
+
+	$ssh->Connect(QWNET_HOST, QWNET_PORT, QWNET_FP, QWNET_USR, "", QWNET_KEY_PATH_PUB, QWNET_KEY_PATH_PRIV);
+	$ssh->cmd_exit = "PART";
+
+	echo $ssh->Shell("vt102", 1);
+
+	MainLoop($ssh);
+}
+catch (Exception $e) {
+	die("SSH error: ".$e->getMessage());
+}

+ 268 - 0
ssh.php

@@ -0,0 +1,268 @@
+<?
+##############################################################################################################
+#
+# SSH
+#
+# Developer(s): swiNg
+#
+# Handles communication through SSH.
+#
+# I recommended that you pass a fingerprint hash when connecting to host. That will verify the host before
+# sending your credentials to it. Get fingerprint from the fingerprint property when connected if you don't
+# already have it.
+#
+# If a shell is opened, the stream is kept alive until disconnected. Commands executed while shell is open
+# is written to the existing stream rather than trying to execute the command directly.
+#
+# The destructor takes care of disconnecting if you don't run disconnect yourself.
+#
+# Disconnecting will try to make a clean exit before closing the connection by running the command stored in
+# the cmd_exit property.
+#
+##############################################################################################################
+
+##############################################################################################################
+# CLASS: SSH
+##############################################################################################################
+class SSH {
+
+	// set variables
+
+	public $authed = false;				// is set to true if authed
+	public $fingerprint;					// server hostkey hash
+	public $cmd_delay = 1;				// default delay before reading stream after executing a command
+	public $cmd_exit = "exit";		// command to execute on disconnect
+	public $cmd_exit_echo = true;	// echo response after exit command
+
+	private $connection;					// variable that holds ssh connection
+	private $stream;							// variable that holds opened shell stream
+
+	#*************************************************************************************************************
+	# FUNCTION: __destruct() //
+	#*************************************************************************************************************
+	public function __destruct() {
+
+		// always disconnect
+
+		$this->Disconnect();
+	}
+
+	#*************************************************************************************************************
+	# FUNCTION: Connect() // Open SSH connection.
+	#*************************************************************************************************************
+	public function Connect($host, $port, $fingerprint = "", $username = "", $password = "", $public_key_path = "", $private_key_path = "") {
+
+		$this->Authed = false;
+
+		// establish connection
+
+		if (!($this->connection = @ssh2_connect($host, $port))) {
+			throw new Exception("Could not connect to server.");
+		}
+
+		// get/verify fingerprint
+
+		$this->fingerprint = ssh2_fingerprint($this->connection, SSH2_FINGERPRINT_MD5 | SSH2_FINGERPRINT_HEX);
+
+		if ($fingerprint) {
+			if (strcmp($this->fingerprint, $fingerprint) !== 0) {
+				throw new Exception("Unable to verify server identity.");
+			}
+		}
+
+		// authenticate by key or password if credentials are given
+
+		if ($username && $public_key_path && $private_key_path) {
+			$this->Authed = $this->AuthByKey($username, $public_key_path, $private_key_path);
+		}
+		elseif ($username && $password) {
+			$this->Authed = $this->AuthByPassword($username, $password);
+		}
+	}
+
+	#*************************************************************************************************************
+	# FUNCTION: Disconnect() // Close SSH connection.
+	#*************************************************************************************************************
+	public function Disconnect($run_exit_cmd = true) {
+
+		// set variables
+
+		$response = "";
+
+		// execute exit command if defined and close shell stream
+
+		if ($this->stream) {
+			if ($run_exit_cmd && $this->cmd_exit) {
+				$response = $this->Exec($this->cmd_exit, 1);
+			}
+			fclose($this->stream);
+		}
+		elseif ($run_exit_cmd && $this->authed) {
+			$response = $this->Exec($this->cmd_exit);
+		}
+
+		// echo response
+
+		if ($this->cmd_exit_echo) {
+			echo $response;
+		}
+
+		// clean up
+
+		$this->authed = false;
+		$this->stream = null;
+		$this->connection = null;
+	}
+
+	#*************************************************************************************************************
+	# FUNCTION: AuthByPassword() // Authenticate using password.
+	#*************************************************************************************************************
+	public function AuthByPassword($username, $password) {
+
+		if ($this->connection) {
+			if(!$this->authed = @ssh2_auth_password($this->connection, $username, $password)) {
+				throw new Exception("Autentication rejected by server.");
+			}
+		}
+	}
+
+	#*************************************************************************************************************
+	# FUNCTION: AuthByKey() // Authenticate using key files.
+	#*************************************************************************************************************
+	public function AuthByKey($username, $public_key_path, $private_key_path) {
+
+		// password protected public keys are not supported becase of a bug in the libssh2 build
+
+		if ($this->connection) {
+			if (!file_exists($public_key_path)) {
+				throw new Exception("Failed to find public key.");
+			}
+			elseif (!file_exists($private_key_path)) {
+				throw new Exception("Failed to find private key.");
+			}
+			elseif (!is_readable($public_key_path)) {
+				throw new Exception("Failed to read public key.");
+			}
+			elseif (!is_readable($private_key_path)) {
+				throw new Exception("Failed to read private key.");
+			}
+			elseif (!$this->authed = @ssh2_auth_pubkey_file($this->connection, $username, $public_key_path, $private_key_path)) {
+				throw new Exception("Autentication rejected by server.");
+			}
+		}
+	}
+
+	#*************************************************************************************************************
+	# FUNCTION: ReadStream() // 
+	#*************************************************************************************************************
+	public function ReadStream($buffer_size = 4096) {
+
+		// read stream
+
+		if ($this->stream) {
+			$data = fread($this->stream, $buffer_size);
+		}
+		else {
+			$data = false;
+		}
+
+		// return response
+
+		return $data;
+	}
+
+	#*************************************************************************************************************
+	# FUNCTION: Exec() // Execute a command and retrieve the response.
+	#*************************************************************************************************************
+	public function Exec($cmd, $delay = -1) {
+
+		// set variables
+
+		$data = "";
+
+ 		// use existing shell stream if aviable
+
+		if ($this->stream) {
+
+			// write to stream
+
+	    fwrite($this->stream, $cmd.PHP_EOL);
+
+			// issue delay
+
+			if ($delay != 0) {
+				sleep(($delay == -1 ? $this->cmd_delay : $delay));
+			}
+
+			// read stream
+
+			while ($buf = fread($this->stream, 4096)) {
+				$data .= $buf;
+			}
+		}
+		else {
+			if (!($stream = @ssh2_exec($this->connection, $cmd))) {
+				throw new exception("Command failed.");
+			}
+
+			// fetch streams
+
+			$err_stream = ssh2_fetch_stream($stream, SSH2_STREAM_STDERR);
+			$io_stream = ssh2_fetch_stream($stream, SSH2_STREAM_STDIO);
+
+			stream_set_blocking($err_stream, true);
+			stream_set_blocking($io_stream, true);
+
+			// read stream (stderr)
+
+			while ($buf = fread($err_stream, 4096)) {
+				$data .= $buf;
+			}
+
+			// read stream (dio)
+
+			while ($buf = fread($io_stream, 4096)) {
+				$data .= $buf;
+			}
+
+			fclose($stream);
+		}
+
+		// return response
+
+		return $data;
+	}
+
+	#*************************************************************************************************************
+	# FUNCTION: Shell() // Request shell and keep stream open.
+	#*************************************************************************************************************
+	public function Shell($term_type = "vt102", $delay = -1) {
+
+		// set variables
+
+		$data = "";
+
+		// request shell
+
+		if (!($this->stream = @ssh2_shell($this->connection, $term_type))) {
+			throw new exception("Shell failed.");
+		}
+
+		// issue delay
+
+		if ($delay != 0) {
+			sleep(($delay == -1 ? $this->cmd_delay : $delay));
+		}
+
+		// read stream
+
+		while ($buf = fread($this->stream, 4096)) {
+			$data .= $buf;
+		}
+
+		// return response
+
+		return $data;
+	}
+}
+?>