diff simpleircd/simpleircd.php @ 0:dd81c38b513a

Initial commit
author Ivo Smits <Ivo@UCIS.nl>
date Mon, 28 Feb 2011 00:49:07 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/simpleircd/simpleircd.php	Mon Feb 28 00:49:07 2011 +0100
@@ -0,0 +1,290 @@
+#!/usr/bin/php
+<?php
+/* Copyright 2010 Ivo Smits <Ivo@UCIS.nl>. All rights reserved.
+   Redistribution and use in source and binary forms, with or without modification, are
+   permitted provided that the following conditions are met:
+
+   1. Redistributions of source code must retain the above copyright notice, this list of
+      conditions and the following disclaimer.
+
+   2. Redistributions in binary form must reproduce the above copyright notice, this list
+      of conditions and the following disclaimer in the documentation and/or other materials
+      provided with the distribution.
+
+   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+   FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR
+   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+   ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+   ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   The views and conclusions contained in the software and documentation are those of the
+   authors and should not be interpreted as representing official policies, either expressed
+   or implied, of Ivo Smits.*/
+
+srand();
+
+$listchannels = array();
+$channels = array();
+
+$udpmsg = NULL;
+$udpmsg_buffer = '';
+
+$client_name = NULL;
+$client_buffer = '';
+
+srand();
+
+require 'config.php';
+
+client_send(':server NOTICE AUTH :*** Not looking up your hostname, we do not care.');
+client_send(':server NOTICE AUTH :*** Not checking ident, are you serious?');
+
+while ($client_name === NULL) {
+	$line = client_read_line();
+	$partsa = explode(' :', $line, 2);
+	$parts = explode(' ', $partsa[0]);
+	$command = array_shift(&$parts);
+	if (count($partsa) > 1) array_push(&$parts, $partsa[1]);
+	switch (strtoupper($command)) {
+		case 'NICK': //Nickname change: newnick = $parts[0]
+			if (!preg_match('/^[a-zA-Z0-9\-_]+$/', $parts[0])) {
+				client_sendnumeric(432, $parts[0].' :Erroneous nickname');
+			} else {
+				$client_name = $parts[0];
+				client_sendnumeric(001, ':Welcome to the Internet Relay Chat Network');
+				client_sendnumeric(002, ':Your host is UDPMSG3-IRCD');
+				client_sendnumeric(003, ':This server was created some time ago');
+				client_sendnumeric(004, 'irc.udpmsg3.local 1.0 + +');
+				client_sendnumeric(005, 'NICKLEN=16 CHANNELLEN=16 CHANTYPES=# NETWORK=UDPMSG3 :are supported by this server');
+				client_sendnumeric(251, ':There are 1 users and some invisible on a bunch of servers');
+				client_sendnumeric(252, '0 :operator(s) online');
+				client_sendnumeric(254, count($config['channels']).' :channels formed');
+				client_sendnumeric(255, ':I have a bunch of clients and a bunch of servers');
+				client_sendnumeric(265, ':Current Local Users: undisclosed');
+				client_sendnumeric(266, ':Current Global Users: unknown');
+				client_sendnumeric(375, ':- server Message of the Day - ');
+				client_sendnumeric(372, ':- No message, sorry.');
+				client_sendnumeric(376, ':End of /MOTD command.');
+			}
+		case 'USER':
+		case 'PASS':
+			break;
+		default:
+			client_sendnumeric(421, $command.' :Unknown command');
+	}
+}
+
+$udpmsg = stream_socket_client('tcp://'.$config['udpmsg']['host'].':'.$config['udpmsg']['port']);
+if (!$udpmsg) {
+	client_send('ERROR :Could not connect to exchange.');
+	die();
+}
+
+while (TRUE) {
+	$selwrite = NULL;
+	$selread = array($udpmsg, STDIN);
+	$selerror = array($udpmsg, STDIN);
+	stream_select(&$selread, &$selwrite, &$selerror, 10);
+	if (in_array($udpmsg, $selerror)) {
+		fclose($udpmsg);
+		client_send('ERROR :Lost connection to exchange.');
+		die();
+	} else {
+		if (in_array($udpmsg, $selread)) {
+			if (!udpmsg_read($udpmsg)) {
+				fclose($udpmsg);
+				client_send('ERROR :Error in exchange protocol.');
+				die();
+			}
+		}
+	}
+	if (in_array(STDIN, $selerror)) {
+		fclose($udpmsg);
+		client_send('ERROR :Error while reading from client.');
+		die();
+	} else if (in_array(STDIN, $selread)) {
+		if (!client_read()) {
+		fclose($udpmsg);
+			client_send('ERROR :End of file.');
+			die();
+		}
+	}
+}
+
+function udpmsg_read() {
+	global $channels, $udpmsg, $udpmsg_buffer, $config;
+	$msg = fread($udpmsg, 1024);
+	if (!strlen($msg)) return FALSE;
+	$udpmsg_buffer .= $msg;
+	while (strlen($udpmsg_buffer) > 2) {
+		$len = ord($udpmsg_buffer[0]) * 256 + ord($udpmsg_buffer[1]);
+		if ($len <= 0 || $len > 1024) return FALSE;
+		if (strlen($udpmsg_buffer) < 2 + $len) break;
+		$msg = substr($udpmsg_buffer, 2, $len);
+		$udpmsg_buffer = substr($udpmsg_buffer, 2 + $len);
+		$parts = explode("\0", $msg);
+		$ret = array();
+		for ($i = 0; $i < count($parts)-1; $i += 2) $ret[$parts[$i]] = $parts[$i+1];
+
+		if (!isset($ret['CHN']) || !isset($ret['CMD']) || !isset($ret['USR'])) continue;
+		if (!substr($ret['CHN'], 0, strlen($config['udpmsg']['chprefix']))) continue;
+		$chn = '#'.substr($ret['CHN'], strlen($config['udpmsg']['chprefix']));
+		if (!in_array($chn, $channels)) continue;
+		if ($ret['CMD'] == 'MSG') {
+			if (!isset($ret['MSG'])) continue;
+			$from = preg_replace('/[^a-zA-Z0-9\-_]/', '', $ret['USR']);
+			if ($config['udpmsg']['nicknetwork'] && isset($ret['NET'])) $from = preg_replace('/[^a-zA-Z0-9\-_]/', '', $ret['NET']).$config['udpmsg']['nickseparator'].$from;
+			$from = $config['udpmsg']['nickprefix'].$from;
+			$message = preg_replace('/[\x00\x10-\x13]/', '', $ret['MSG']);
+			client_send(':'.$from.' PRIVMSG '.$chn.' :'.$message);
+		}
+	}
+	return TRUE;
+}
+
+function udpmsg_send($arr) {
+	global $udpmsg;
+	$tmp = array();
+	foreach ($arr as $key => $value) { $tmp[] = $key; $tmp[] = $value; }
+	$tmp[] = '';
+	$msg = implode("\0", $tmp);
+	$len = strlen($msg);
+	if ($len > 1024) return;
+	$lens = chr(floor($len / 256)).chr($len % 256);
+	fwrite($udpmsg, $lens.$msg);
+}
+
+function array_remove(&$arr, $item) {
+	foreach ($arr as $key => $value) if ($value === $item) unset($arr[$key]);
+}
+
+function client_read() {
+	global $client_buffer;
+	$newdata = fread(STDIN, 1024);
+	if ($newdata === FALSE || !strlen($newdata)) return FALSE;
+	$client_buffer .= $newdata;
+	$offset = 0;
+	$len = strlen($client_buffer);
+	while ($offset < $len) {
+		$posa = strpos($client_buffer, "\n", $offset);
+		$posb = strpos($client_buffer, "\r", $offset);
+		if ($posa !== FALSE && $posb !== FALSE) {
+			$pos = min($posa, $posb);
+		} else if ($posa !== FALSE) {
+			$pos = $posa;
+		} else if ($posb !== FALSE) {
+			$pos = $posb;
+		} else {
+			break;
+		}
+		$line = substr($client_buffer, $offset, $pos - $offset);
+		if (strlen($line)) {
+			if (!client_process($line)) return FALSE;
+		}
+		$offset = $pos + 1;
+	}
+	if ($offset == $len) {
+		$client_buffer = '';
+	} else if ($offset != 0) {
+		$client_buffer = substr($client_buffer, $offset);
+	}
+	return TRUE;
+}
+
+function client_process($line) {
+	global $client_name, $config;
+	$partsa = explode(' :', $line, 2);
+	$parts = explode(' ', $partsa[0]);
+	$command = array_shift(&$parts);
+	if (count($partsa) > 1) array_push(&$parts, $partsa[1]);
+	switch (strtoupper($command)) {
+		case 'NICK':
+			if (!preg_match('/^[a-zA-Z0-9\-_]+$/', $parts[0])) {
+				client_sendnumeric(432, $parts[0].' :Erroneous nickname');
+			} else {
+				$client_name = $parts[0];
+			}
+			break;
+		case 'PING':
+			client_send('PONG :'.$parts[0]);
+			break;
+		case 'PRIVMSG':
+		case 'NOTICE':
+			$channels = explode(',', $parts[0]);
+			foreach ($channels as $chn) {
+				if ($chn[0] == '#') {
+					if (!in_array($chn, $GLOBALS['channels']))  {
+						client_sendnumeric(404, $chn.' :Can not send to channel');
+					} else {
+						$umsg = array('CMD' => 'MSG', 'CHN' => $config['udpmsg']['chprefix'].substr($chn, 1), 'MSG' => $parts[1], 'USR' => $client_name, 'DUMMY' => rand(10000, 99999));
+						if ($config['udpmsg']['netname']) $umsg['NET'] = $config['udpmsg']['netname'];
+						udpmsg_send($umsg);
+					}
+				} else {
+					client_sendnumeric(401, $chn.' :No such nickname');
+				}
+			}
+			break;
+		case 'JOIN':
+			$channels = explode(',', $parts[0]);
+			foreach ($channels as $chn) {
+				if (!preg_match('/^#[a-zA-Z0-9\-_]+$/', $chn)) {
+					client_sendnumeric($client, 403, $chn.' :No such channel');
+					continue;
+				}
+				if (!in_array($chn, $GLOBALS['channels'])) {
+					client_send(':'.$client_name.'!user@host JOIN :'.$chn);
+					$GLOBALS['channels'][] = $chn;
+				}
+				client_sendnumeric(353, '= '.$chn.' :'.$client_name);
+				client_sendnumeric(366, $chn.' :End of /NAMES list.');
+			}
+			break;
+		case 'PART':
+			client_send(':'.$client_name.'!user@host PART :'.$chn);
+			array_remove($GLOBALS['channels'], $chn);
+			break;
+		case 'QUIT':
+			return FALSE;
+		case 'NAMES':
+			client_sendnumeric(353, '= '.$parts[0].' :'.$client_name);
+			client_sendnumeric(366, $parts[0].' :End of /NAMES list.');
+			break;
+		case 'LIST':
+			client_sendnumeric($client, 321, 'Channel :Users  Name');
+			foreach ($GLOBALS['listchannels'] as $chn) {
+				client_sendnumeric(322, $chn.' 0 :');
+			}
+			client_sendnumeric(323, 'End of /LIST');
+			break;
+		case 'MODE':
+		case 'USER':
+		case 'USERHOST':
+			break;
+		default:
+			client_sendnumeric(421, $command.' :Unknown command');
+	}
+	return TRUE;
+}
+
+function client_sendnumeric($num, $line) {
+	global $client_name;
+	client_send(':server '.str_pad($num, 3, '0', STR_PAD_LEFT).' '.$client_name.' '.$line);
+}
+
+function client_send($line) {
+	print($line."\r\n");
+}
+
+function client_read_line() {
+	$line = fgets(STDIN);
+	if (!strlen($line)) {
+		client_send('ERROR :End of file.');
+		die();
+	}
+	return rtrim($line, "\r\n\x0B");
+}