Mercurial > hg > udpmsg3
diff unrealircdlink/unrealircdlink.php @ 0:dd81c38b513a
Initial commit
author | Ivo Smits <Ivo@UCIS.nl> |
---|---|
date | Mon, 28 Feb 2011 00:49:07 +0100 |
parents | |
children | 7f01316130e8 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/unrealircdlink/unrealircdlink.php Mon Feb 28 00:49:07 2011 +0100 @@ -0,0 +1,413 @@ +<?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.*/ + +if (defined('APP_LOADED')) return; +define('APP_LOADED', TRUE); +if (!defined('CONFIGFILE')) define('CONFIGFILE', './config.php'); + +print("UCIS IRC Relay bot (c) 2010 Ivo Smits <Ivo@UCIS.nl>\n"); +print("More information: http://wiki.qontrol.nl/IRCRelay\n"); +print("\n"); + +class UUser { + public $iname = NULL; + public $unet = NULL; + public $uname = NULL; + public $channels = array(); + public $seen = 0; + public $explicit = FALSE; +} +class IUser { + public $iname = NULL; + public $channels = array(); +} +class Channel { + public $name = NULL; + public $iname = NULL; + public $uname = NULL; +} + +$iusers = array(); +$uusers = array(); +$ichannels = array(); +$uchannels = array(); + +$ircd_socket = NULL; +$ircd_buffer = ''; +$ircd_name = ''; + +$udpmsg_socket = NULL; +$udpmsg_buffer = ''; +$udpmsg_config = array(); + +$bot_nick = NULL; + +srand(); + +print("Loading configuration...\n"); +if (!isset($config)) require constant('CONFIGFILE'); + +$ircd_socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); +socket_connect($ircd_socket, $config['ircd']['host'], $config['ircd']['port']); +$ircd_name = $config['ircd']['name']; +$bot_nick = $config['ircd']['bot_nick']; +if (!strlen($bot_nick)) $bot_nick = NULL; +ircd_send('PROTOCTL'); +ircd_send('PASS :'.$config['ircd']['password']); +ircd_send('SERVER '.$ircd_name.' 0 0 :'.$config['ircd']['desc']); +ircd_send('AO 0 0 0 0 - 0 :Kwaaknet.org'); +if ($bot_nick) ircd_send('NICK '.$bot_nick.' 0 0 gateway '.$ircd_name.' '.$ircd_name.' 0 :'.$config['ircd']['desc']); +ircd_send('ES'); +$udpmsg_socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); +socket_connect($udpmsg_socket, $config['udpmsg']['host'], $config['udpmsg']['port']); +foreach ($config['channels'] as $iname => $uname) { + $ch = new Channel(); + $ch->iname = $iname; + $ch->uname = $uname; + $ch->name = $iname; + $uchannels[$ch->uname] = $ch; + $ichannels[strtoupper($ch->iname)] = $ch; + if ($bot_nick) ircd_send(':'.$bot_nick.' JOIN :'.$ch->iname); +} +$iusers[strtoupper($bot_nick)] = new IUser(); +$iusers[strtoupper($bot_nick)]->iname = $bot_nick; + +function reconfigure() { + global $config; + include constant('CONFIGFILE'); +} + +$ntime = 0; +$atime = 0; +print("Running\n"); +while (TRUE) { + $selread = array($udpmsg_socket, $ircd_socket); + $selwrite = NULL; + $selerror = array($udpmsg_socket, $ircd_socket); + socket_select(&$selread, &$selwrite, &$selerror, 60); + if (in_array($udpmsg_socket, $selerror)) die("UDPMSG: Error: select error.\n"); + if (in_array($ircd_socket, $selerror)) die("IRCd: Error: select error.\n"); + if (in_array($udpmsg_socket, $selread) && !udpmsg_read()) die("UDPMSG: Error: closing connection.\n"); + if (in_array($ircd_socket, $selread) && !ircd_read()) die("IRCd: read error.\n"); + $ctime = time(); + if ($ntime < $ctime) { + foreach ($uusers as $usr) { + if ($usr->seen > $ctime - $config['udpmsg'][$usr->explicit ? 'timeout_explicit' : 'timeout_implicit']) continue; + unset($uusers[$usr->uname]); + unset($iusers[strtoupper($usr->iname)]); + ircd_send(':'.$usr->iname.' QUIT :Inactivity'); + } + $ntime = $ctime + 120; //min($config['udpmsg']['timeout_explicit'], $config['udpmsg']['timeout_implicit']); + } + if ($atime < $ctime) { + foreach ($iusers as $usr) if (is_a($usr, 'IUser')) ircd_userchannels($usr, array('CMD' => 'ALIVE')); + $atime = $ctime + (isset($config['udpmsg']['send_alive']) ? $config['udpmsg']['send_alive'] : 1800); + } +} + +function udpmsg_read() { + global $udpmsg_socket, $udpmsg_buffer; + $msg = socket_read($udpmsg_socket, 1024); + if (!strlen($msg)) { + fprintf(STDERR, "UDPMSG: End of file\n"); + 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) { + fprintf(STDERR, "UDPMSG: Error: protocol error\n"); + 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]; + udpmsg_process($ret); + } + return TRUE; +} + +function udpmsg_send($arr) { + global $udpmsg_socket, $config; + $tmp = array(); + $arr['DUMMY'] = rand(0, 999999); + $arr['NET'] = $config['udpmsg']['netname']; + foreach ($arr as $key => $value) { $tmp[] = $key; $tmp[] = $value; } + $tmp[] = ''; + $msg = implode("\0", $tmp); + $len = strlen($msg); + if ($len > 1024) { + fprintf(STDERR, "UDPMSG: Error: message too long!\n"); + return; + } + $lens = chr(floor($len / 256)).chr($len % 256); + socket_write($udpmsg_socket, $lens.$msg); +} + +function ircd_read() { + global $ircd_socket, $ircd_buffer; + $newdata = socket_read($ircd_socket, 1024); + if ($newdata === FALSE || !strlen($newdata)) return FALSE; + $ircd_buffer .= $newdata; + $offset = 0; + $len = strlen($ircd_buffer); + while ($offset < $len) { + $posa = strpos($ircd_buffer, "\n", $offset); + $posb = strpos($ircd_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($ircd_buffer, $offset, $pos - $offset); + if (strlen($line) && !ircd_process($line)) { + fprintf(STDERR, "IRCd: process error\n"); + return FALSE; + } + $offset = $pos + 1; + } + $ircd_buffer = ($offset == $len) ? '' : substr($ircd_buffer, $offset); + return TRUE; +} + +function ircd_send($line) { + global $ircd_socket; + print('IW: '.$line."\n"); + $line .= "\r\n"; + socket_send($ircd_socket, $line, strlen($line), 0); +} + +function udpmsg_process($ret) { + global $uchannels; + if (!isset($ret['CHN']) || !isset($ret['CMD']) || !isset($ret['USR'])) return; + if (!array_key_exists($ret['CHN'], $uchannels)) return; + $ch = $uchannels[$ret['CHN']]; + $net = isset($ret['NET']) ? $ret['NET'] : NULL; + switch ($ret['CMD']) { + case 'MSG': + if (!isset($ret['MSG'])) break; + $usr = udpmsg_join($ret['USR'], $ch, $net); + ircd_send(':'.$usr->iname.' PRIVMSG '.$ch->iname.' :'.preg_replace('/[\x00\x10-\x13]/', '', $ret['MSG'])); + break; + case 'ALIVE': + case 'JOIN': + udpmsg_join($ret['USR'], $ch, $net, TRUE); + break; + case 'PART': + udpmsg_part($ret['USR'], $ch); + break; + case 'NICK': + udpmsg_part($ret['USR'], $ch); + if (!isset($ret['NEWNICK'])) break; + udpmsg_join($ret['NEWNICK'], $ch, $net, TRUE); + break; + } +} + +function udpmsg_join($name, $ch, $net = NULL, $explicit = FALSE) { + $usr = udpmsg_getuser($name, TRUE, $net, $explicit); + if (array_key_exists($ch->name, $usr->channels)) return $usr; + $usr->channels[$ch->name] = $ch; + ircd_send(':'.$usr->iname.' JOIN :'.$ch->iname); + return $usr; +} + +function udpmsg_part($name, $ch) { + global $uusers, $iusers; + $usr = udpmsg_getuser($name, FALSE); + if ($usr === NULL || !array_key_exists($ch->name, $usr->channels)) return; + unset($usr->channels[$ch->name]); + if (count($usr->channels)) { + ircd_send(':'.$usr->iname.' PART :'.$ch->iname); + } else { + unset($uusers[$usr->uname]); + unset($iusers[strtoupper($usr->iname)]); + ircd_send(':'.$usr->iname.' QUIT :No more channels'); + } +} + +function udpmsg_getuser($nick, $create = FALSE, $net = NULL, $explicit = FALSE) { + global $uusers, $iusers, $ircd_name; + if (array_key_exists($nick, $uusers)) { + $usr = $uusers[$nick]; + } else { + if (!$create) return NULL; + $usr = new UUser(); + $usr->uname = $nick; + $usr->unet = $net; + $usr->explicit = $explicit; + $usr->iname = udpmsg_getnick($usr, $nick); + $ident = preg_replace('/[^a-zA-Z0-9\-_]/', '', $usr->uname); + if (!strlen($ident)) $ident = 'unknown'; + $net = preg_replace('/[^a-zA-Z0-9\-_]/', '', $net); + if (!strlen($net)) $net = 'unknown'; + ircd_send('NICK '.$usr->iname.' 0 0 '.$ident.' '.$net.'.udpmsg3 '.$ircd_name.' 0 :UDPMSG3 user'); + $uusers[$nick] = $iusers[strtoupper($usr->iname)] = $usr; + } + $usr->seen = time(); + return $usr; +} + +function udpmsg_getnick($usr, $req) { + global $iusers, $config; + $nick = preg_replace('/[^a-zA-Z0-9\-_]/', '', $req); + if (!strlen($nick)) $nick = 'NoNick'; + switch ($config['ircd']['nick_format']) { + case 'plain': break; + case 'prefix': $nick = $config['ircd']['nick_prefix'].$nick; break; + case 'suffix': $nick = $nick.$config['ircd']['nick_suffix']; break; + } + if (isset($iusers[strtoupper($nick)])) { + switch ($config['ircd']['nick_in_use']) { + case 'prefix': $nick = $config['ircd']['nick_prefix'].$nick; break; + case 'suffix': $nick = $nick.$config['ircd']['nick_suffix']; break; + } + $bnick = $nick; + while (isset($iusers[strtoupper($nick)])) $nick = $bnick.rand(0, 999); + } + return $nick; +} + +function ircd_process($line) { + global $ichannels, $iusers, $ircd_name, $bot_nick; + print('IR: '.$line."\n"); + $partsa = explode(' :', $line, 2); + $parts = explode(' ', $partsa[0]); + $sender = ($parts[0][0] == ':') ? substr(array_shift(&$parts), 1) : NULL; + $command = array_shift(&$parts); + if (count($partsa) > 1) array_push(&$parts, $partsa[1]); + $partsa = explode('!', $sender); + $sendernick = $partsa[0]; + switch (strtoupper($command)) { + case 'REHASH': + if (strtoupper($parts[0]) != strtoupper($ircd_name)) break; + reconfigure(); + ircd_send('NOTICE '.$sendernick.' :Reloaded configuration.'); + break; + case 'PING': + ircd_send('PONG'.($sender?' '.$sender:'').' :'.$parts[0]); + break; + case 'SVSNICK': + $sendernick = array_shift($parts); + case 'NICK': + if ($sender === NULL) { + ircd_getuser($parts[0], TRUE); + } else { + $usr = ircd_getuser($sendernick, FALSE); + if ($usr === NULL) break; + if (is_a($usr, 'IUser')) ircd_userchannels($usr, array('CMD' => 'NICK', 'NEWNICK' => $parts[0])); + unset($iusers[strtoupper($usr->iname)]); + $usr->iname = $parts[0]; + $iusers[strtoupper($usr->iname)] = $usr; + } + break; + case 'PRIVMSG': case 'NOTICE': + if ($parts[1] == '--print-internals' && $parts[0][0] == '#') { + ircd_send(':'.$bot_nick.' PRIVMSG '.$parts[0].' :We have '.count($GLOBALS['iusers']).' IRC users of which '.count($GLOBALS['uusers']).' UDPMSG users, and '.count($GLOBALS['ichannels']).' channels.'); + break; + } + $ch = ircd_getchannel($parts[0]); + $usr = ircd_getuser($sendernick, FALSE); + if ($ch === NULL || $usr === NULL) break; + udpmsg_send(array('CMD' => 'MSG', 'CHN' => $ch->uname, 'MSG' => $parts[1], 'USR' => $usr->iname)); + break; + case 'SAJOIN': + case 'SVSJOIN': + $sendernick = array_shift($parts); + case 'JOIN': + $usr = ircd_getuser($sendernick, FALSE); + if ($usr === NULL) break; + $chs = explode(',', $parts[0]); + foreach ($chs as $chn) { + $ch = ircd_getchannel($chn); + if ($ch === NULL) break; + $usr->channels[$ch->name] = $ch; + if (is_a($usr, 'IUser')) udpmsg_send(array('CMD' => 'JOIN', 'CHN' => $ch->uname, 'USR' => $usr->iname)); + } + break; + case 'SAPART': + case 'SVSPART': + $sendernick = array_shift($parts); + case 'PART': + $usr = ircd_getuser($sendernick, FALSE); + if ($usr === NULL) break; + $chs = explode(',', $parts[0]); + foreach ($chs as $chn) ircd_part($usr, $chn); + break; + case 'KICK': + ircd_part(ircd_getuser($parts[1], FALSE), $parts[0]); + break; + case 'QUIT': + ircd_quit($sendernick); + break; + case 'KILL': + case 'SVSKILL': + ircd_quit($parts[0]); + break; + } + return TRUE; +} + +function ircd_quit($nick) { + global $iusers, $uusers; + $usr = ircd_getuser($nick, FALSE); + if ($usr === NULL) return; + if (is_a($usr, 'IUser')) ircd_userchannels($usr, array('CMD' => 'PART')); + if (is_a($usr, 'UUser')) unset($uusers[$usr->uname]); + unset($iusers[strtoupper($usr->iname)]); +} + +function ircd_getchannel($chn) { + global $ichannels; + $chn = strtoupper($chn); + if (!array_key_exists($chn, $ichannels)) return NULL; + return $ichannels[$chn]; +} +function ircd_getuser($nick, $create = TRUE) { + global $iusers; + $snick = strtoupper($nick); + if (array_key_exists($snick, $iusers)) return $iusers[$snick]; + if (!$create) return NULL; + $usr = new IUser(); + $usr->iname = $nick; + $iusers[$snick] = $usr; + return $usr; +} +function ircd_part($usr, $chn) { + $ch = ircd_getchannel($chn); + if ($ch === NULL || $usr === NULL) return; + unset($usr->channels[$ch->name]); + if (is_a($usr, 'IUser')) udpmsg_send(array('CMD' => 'PART', 'CHN' => $ch->uname, 'USR' => $usr->iname)); +} +function ircd_userchannels($usr, $msg) { + $msg['USR'] = $usr->iname; + foreach ($usr->channels as $ch) { + $msg['CHN'] = $ch->uname; + udpmsg_send($msg); + } +}