Mercurial > hg > udpmsg3
comparison 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 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:dd81c38b513a |
---|---|
1 <?php | |
2 /* Copyright 2010 Ivo Smits <Ivo@UCIS.nl>. All rights reserved. | |
3 Redistribution and use in source and binary forms, with or without modification, are | |
4 permitted provided that the following conditions are met: | |
5 | |
6 1. Redistributions of source code must retain the above copyright notice, this list of | |
7 conditions and the following disclaimer. | |
8 | |
9 2. Redistributions in binary form must reproduce the above copyright notice, this list | |
10 of conditions and the following disclaimer in the documentation and/or other materials | |
11 provided with the distribution. | |
12 | |
13 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
14 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND | |
15 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR | |
16 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
17 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
18 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | |
19 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
20 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |
21 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
22 | |
23 The views and conclusions contained in the software and documentation are those of the | |
24 authors and should not be interpreted as representing official policies, either expressed | |
25 or implied, of Ivo Smits.*/ | |
26 | |
27 if (defined('APP_LOADED')) return; | |
28 define('APP_LOADED', TRUE); | |
29 if (!defined('CONFIGFILE')) define('CONFIGFILE', './config.php'); | |
30 | |
31 print("UCIS IRC Relay bot (c) 2010 Ivo Smits <Ivo@UCIS.nl>\n"); | |
32 print("More information: http://wiki.qontrol.nl/IRCRelay\n"); | |
33 print("\n"); | |
34 | |
35 class UUser { | |
36 public $iname = NULL; | |
37 public $unet = NULL; | |
38 public $uname = NULL; | |
39 public $channels = array(); | |
40 public $seen = 0; | |
41 public $explicit = FALSE; | |
42 } | |
43 class IUser { | |
44 public $iname = NULL; | |
45 public $channels = array(); | |
46 } | |
47 class Channel { | |
48 public $name = NULL; | |
49 public $iname = NULL; | |
50 public $uname = NULL; | |
51 } | |
52 | |
53 $iusers = array(); | |
54 $uusers = array(); | |
55 $ichannels = array(); | |
56 $uchannels = array(); | |
57 | |
58 $ircd_socket = NULL; | |
59 $ircd_buffer = ''; | |
60 $ircd_name = ''; | |
61 | |
62 $udpmsg_socket = NULL; | |
63 $udpmsg_buffer = ''; | |
64 $udpmsg_config = array(); | |
65 | |
66 $bot_nick = NULL; | |
67 | |
68 srand(); | |
69 | |
70 print("Loading configuration...\n"); | |
71 if (!isset($config)) require constant('CONFIGFILE'); | |
72 | |
73 $ircd_socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); | |
74 socket_connect($ircd_socket, $config['ircd']['host'], $config['ircd']['port']); | |
75 $ircd_name = $config['ircd']['name']; | |
76 $bot_nick = $config['ircd']['bot_nick']; | |
77 if (!strlen($bot_nick)) $bot_nick = NULL; | |
78 ircd_send('PROTOCTL'); | |
79 ircd_send('PASS :'.$config['ircd']['password']); | |
80 ircd_send('SERVER '.$ircd_name.' 0 0 :'.$config['ircd']['desc']); | |
81 ircd_send('AO 0 0 0 0 - 0 :Kwaaknet.org'); | |
82 if ($bot_nick) ircd_send('NICK '.$bot_nick.' 0 0 gateway '.$ircd_name.' '.$ircd_name.' 0 :'.$config['ircd']['desc']); | |
83 ircd_send('ES'); | |
84 $udpmsg_socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); | |
85 socket_connect($udpmsg_socket, $config['udpmsg']['host'], $config['udpmsg']['port']); | |
86 foreach ($config['channels'] as $iname => $uname) { | |
87 $ch = new Channel(); | |
88 $ch->iname = $iname; | |
89 $ch->uname = $uname; | |
90 $ch->name = $iname; | |
91 $uchannels[$ch->uname] = $ch; | |
92 $ichannels[strtoupper($ch->iname)] = $ch; | |
93 if ($bot_nick) ircd_send(':'.$bot_nick.' JOIN :'.$ch->iname); | |
94 } | |
95 $iusers[strtoupper($bot_nick)] = new IUser(); | |
96 $iusers[strtoupper($bot_nick)]->iname = $bot_nick; | |
97 | |
98 function reconfigure() { | |
99 global $config; | |
100 include constant('CONFIGFILE'); | |
101 } | |
102 | |
103 $ntime = 0; | |
104 $atime = 0; | |
105 print("Running\n"); | |
106 while (TRUE) { | |
107 $selread = array($udpmsg_socket, $ircd_socket); | |
108 $selwrite = NULL; | |
109 $selerror = array($udpmsg_socket, $ircd_socket); | |
110 socket_select(&$selread, &$selwrite, &$selerror, 60); | |
111 if (in_array($udpmsg_socket, $selerror)) die("UDPMSG: Error: select error.\n"); | |
112 if (in_array($ircd_socket, $selerror)) die("IRCd: Error: select error.\n"); | |
113 if (in_array($udpmsg_socket, $selread) && !udpmsg_read()) die("UDPMSG: Error: closing connection.\n"); | |
114 if (in_array($ircd_socket, $selread) && !ircd_read()) die("IRCd: read error.\n"); | |
115 $ctime = time(); | |
116 if ($ntime < $ctime) { | |
117 foreach ($uusers as $usr) { | |
118 if ($usr->seen > $ctime - $config['udpmsg'][$usr->explicit ? 'timeout_explicit' : 'timeout_implicit']) continue; | |
119 unset($uusers[$usr->uname]); | |
120 unset($iusers[strtoupper($usr->iname)]); | |
121 ircd_send(':'.$usr->iname.' QUIT :Inactivity'); | |
122 } | |
123 $ntime = $ctime + 120; //min($config['udpmsg']['timeout_explicit'], $config['udpmsg']['timeout_implicit']); | |
124 } | |
125 if ($atime < $ctime) { | |
126 foreach ($iusers as $usr) if (is_a($usr, 'IUser')) ircd_userchannels($usr, array('CMD' => 'ALIVE')); | |
127 $atime = $ctime + (isset($config['udpmsg']['send_alive']) ? $config['udpmsg']['send_alive'] : 1800); | |
128 } | |
129 } | |
130 | |
131 function udpmsg_read() { | |
132 global $udpmsg_socket, $udpmsg_buffer; | |
133 $msg = socket_read($udpmsg_socket, 1024); | |
134 if (!strlen($msg)) { | |
135 fprintf(STDERR, "UDPMSG: End of file\n"); | |
136 return FALSE; | |
137 } | |
138 $udpmsg_buffer .= $msg; | |
139 while (strlen($udpmsg_buffer) > 2) { | |
140 $len = ord($udpmsg_buffer[0]) * 256 + ord($udpmsg_buffer[1]); | |
141 if ($len <= 0 || $len > 1024) { | |
142 fprintf(STDERR, "UDPMSG: Error: protocol error\n"); | |
143 return FALSE; | |
144 } | |
145 if (strlen($udpmsg_buffer) < 2 + $len) break; | |
146 $msg = substr($udpmsg_buffer, 2, $len); | |
147 $udpmsg_buffer = substr($udpmsg_buffer, 2 + $len); | |
148 $parts = explode("\0", $msg); | |
149 $ret = array(); | |
150 for ($i = 0; $i < count($parts)-1; $i += 2) $ret[$parts[$i]] = $parts[$i+1]; | |
151 udpmsg_process($ret); | |
152 } | |
153 return TRUE; | |
154 } | |
155 | |
156 function udpmsg_send($arr) { | |
157 global $udpmsg_socket, $config; | |
158 $tmp = array(); | |
159 $arr['DUMMY'] = rand(0, 999999); | |
160 $arr['NET'] = $config['udpmsg']['netname']; | |
161 foreach ($arr as $key => $value) { $tmp[] = $key; $tmp[] = $value; } | |
162 $tmp[] = ''; | |
163 $msg = implode("\0", $tmp); | |
164 $len = strlen($msg); | |
165 if ($len > 1024) { | |
166 fprintf(STDERR, "UDPMSG: Error: message too long!\n"); | |
167 return; | |
168 } | |
169 $lens = chr(floor($len / 256)).chr($len % 256); | |
170 socket_write($udpmsg_socket, $lens.$msg); | |
171 } | |
172 | |
173 function ircd_read() { | |
174 global $ircd_socket, $ircd_buffer; | |
175 $newdata = socket_read($ircd_socket, 1024); | |
176 if ($newdata === FALSE || !strlen($newdata)) return FALSE; | |
177 $ircd_buffer .= $newdata; | |
178 $offset = 0; | |
179 $len = strlen($ircd_buffer); | |
180 while ($offset < $len) { | |
181 $posa = strpos($ircd_buffer, "\n", $offset); | |
182 $posb = strpos($ircd_buffer, "\r", $offset); | |
183 if ($posa !== FALSE && $posb !== FALSE) $pos = min($posa, $posb); | |
184 else if ($posa !== FALSE) $pos = $posa; | |
185 else if ($posb !== FALSE) $pos = $posb; | |
186 else break; | |
187 $line = substr($ircd_buffer, $offset, $pos - $offset); | |
188 if (strlen($line) && !ircd_process($line)) { | |
189 fprintf(STDERR, "IRCd: process error\n"); | |
190 return FALSE; | |
191 } | |
192 $offset = $pos + 1; | |
193 } | |
194 $ircd_buffer = ($offset == $len) ? '' : substr($ircd_buffer, $offset); | |
195 return TRUE; | |
196 } | |
197 | |
198 function ircd_send($line) { | |
199 global $ircd_socket; | |
200 print('IW: '.$line."\n"); | |
201 $line .= "\r\n"; | |
202 socket_send($ircd_socket, $line, strlen($line), 0); | |
203 } | |
204 | |
205 function udpmsg_process($ret) { | |
206 global $uchannels; | |
207 if (!isset($ret['CHN']) || !isset($ret['CMD']) || !isset($ret['USR'])) return; | |
208 if (!array_key_exists($ret['CHN'], $uchannels)) return; | |
209 $ch = $uchannels[$ret['CHN']]; | |
210 $net = isset($ret['NET']) ? $ret['NET'] : NULL; | |
211 switch ($ret['CMD']) { | |
212 case 'MSG': | |
213 if (!isset($ret['MSG'])) break; | |
214 $usr = udpmsg_join($ret['USR'], $ch, $net); | |
215 ircd_send(':'.$usr->iname.' PRIVMSG '.$ch->iname.' :'.preg_replace('/[\x00\x10-\x13]/', '', $ret['MSG'])); | |
216 break; | |
217 case 'ALIVE': | |
218 case 'JOIN': | |
219 udpmsg_join($ret['USR'], $ch, $net, TRUE); | |
220 break; | |
221 case 'PART': | |
222 udpmsg_part($ret['USR'], $ch); | |
223 break; | |
224 case 'NICK': | |
225 udpmsg_part($ret['USR'], $ch); | |
226 if (!isset($ret['NEWNICK'])) break; | |
227 udpmsg_join($ret['NEWNICK'], $ch, $net, TRUE); | |
228 break; | |
229 } | |
230 } | |
231 | |
232 function udpmsg_join($name, $ch, $net = NULL, $explicit = FALSE) { | |
233 $usr = udpmsg_getuser($name, TRUE, $net, $explicit); | |
234 if (array_key_exists($ch->name, $usr->channels)) return $usr; | |
235 $usr->channels[$ch->name] = $ch; | |
236 ircd_send(':'.$usr->iname.' JOIN :'.$ch->iname); | |
237 return $usr; | |
238 } | |
239 | |
240 function udpmsg_part($name, $ch) { | |
241 global $uusers, $iusers; | |
242 $usr = udpmsg_getuser($name, FALSE); | |
243 if ($usr === NULL || !array_key_exists($ch->name, $usr->channels)) return; | |
244 unset($usr->channels[$ch->name]); | |
245 if (count($usr->channels)) { | |
246 ircd_send(':'.$usr->iname.' PART :'.$ch->iname); | |
247 } else { | |
248 unset($uusers[$usr->uname]); | |
249 unset($iusers[strtoupper($usr->iname)]); | |
250 ircd_send(':'.$usr->iname.' QUIT :No more channels'); | |
251 } | |
252 } | |
253 | |
254 function udpmsg_getuser($nick, $create = FALSE, $net = NULL, $explicit = FALSE) { | |
255 global $uusers, $iusers, $ircd_name; | |
256 if (array_key_exists($nick, $uusers)) { | |
257 $usr = $uusers[$nick]; | |
258 } else { | |
259 if (!$create) return NULL; | |
260 $usr = new UUser(); | |
261 $usr->uname = $nick; | |
262 $usr->unet = $net; | |
263 $usr->explicit = $explicit; | |
264 $usr->iname = udpmsg_getnick($usr, $nick); | |
265 $ident = preg_replace('/[^a-zA-Z0-9\-_]/', '', $usr->uname); | |
266 if (!strlen($ident)) $ident = 'unknown'; | |
267 $net = preg_replace('/[^a-zA-Z0-9\-_]/', '', $net); | |
268 if (!strlen($net)) $net = 'unknown'; | |
269 ircd_send('NICK '.$usr->iname.' 0 0 '.$ident.' '.$net.'.udpmsg3 '.$ircd_name.' 0 :UDPMSG3 user'); | |
270 $uusers[$nick] = $iusers[strtoupper($usr->iname)] = $usr; | |
271 } | |
272 $usr->seen = time(); | |
273 return $usr; | |
274 } | |
275 | |
276 function udpmsg_getnick($usr, $req) { | |
277 global $iusers, $config; | |
278 $nick = preg_replace('/[^a-zA-Z0-9\-_]/', '', $req); | |
279 if (!strlen($nick)) $nick = 'NoNick'; | |
280 switch ($config['ircd']['nick_format']) { | |
281 case 'plain': break; | |
282 case 'prefix': $nick = $config['ircd']['nick_prefix'].$nick; break; | |
283 case 'suffix': $nick = $nick.$config['ircd']['nick_suffix']; break; | |
284 } | |
285 if (isset($iusers[strtoupper($nick)])) { | |
286 switch ($config['ircd']['nick_in_use']) { | |
287 case 'prefix': $nick = $config['ircd']['nick_prefix'].$nick; break; | |
288 case 'suffix': $nick = $nick.$config['ircd']['nick_suffix']; break; | |
289 } | |
290 $bnick = $nick; | |
291 while (isset($iusers[strtoupper($nick)])) $nick = $bnick.rand(0, 999); | |
292 } | |
293 return $nick; | |
294 } | |
295 | |
296 function ircd_process($line) { | |
297 global $ichannels, $iusers, $ircd_name, $bot_nick; | |
298 print('IR: '.$line."\n"); | |
299 $partsa = explode(' :', $line, 2); | |
300 $parts = explode(' ', $partsa[0]); | |
301 $sender = ($parts[0][0] == ':') ? substr(array_shift(&$parts), 1) : NULL; | |
302 $command = array_shift(&$parts); | |
303 if (count($partsa) > 1) array_push(&$parts, $partsa[1]); | |
304 $partsa = explode('!', $sender); | |
305 $sendernick = $partsa[0]; | |
306 switch (strtoupper($command)) { | |
307 case 'REHASH': | |
308 if (strtoupper($parts[0]) != strtoupper($ircd_name)) break; | |
309 reconfigure(); | |
310 ircd_send('NOTICE '.$sendernick.' :Reloaded configuration.'); | |
311 break; | |
312 case 'PING': | |
313 ircd_send('PONG'.($sender?' '.$sender:'').' :'.$parts[0]); | |
314 break; | |
315 case 'SVSNICK': | |
316 $sendernick = array_shift($parts); | |
317 case 'NICK': | |
318 if ($sender === NULL) { | |
319 ircd_getuser($parts[0], TRUE); | |
320 } else { | |
321 $usr = ircd_getuser($sendernick, FALSE); | |
322 if ($usr === NULL) break; | |
323 if (is_a($usr, 'IUser')) ircd_userchannels($usr, array('CMD' => 'NICK', 'NEWNICK' => $parts[0])); | |
324 unset($iusers[strtoupper($usr->iname)]); | |
325 $usr->iname = $parts[0]; | |
326 $iusers[strtoupper($usr->iname)] = $usr; | |
327 } | |
328 break; | |
329 case 'PRIVMSG': case 'NOTICE': | |
330 if ($parts[1] == '--print-internals' && $parts[0][0] == '#') { | |
331 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.'); | |
332 break; | |
333 } | |
334 $ch = ircd_getchannel($parts[0]); | |
335 $usr = ircd_getuser($sendernick, FALSE); | |
336 if ($ch === NULL || $usr === NULL) break; | |
337 udpmsg_send(array('CMD' => 'MSG', 'CHN' => $ch->uname, 'MSG' => $parts[1], 'USR' => $usr->iname)); | |
338 break; | |
339 case 'SAJOIN': | |
340 case 'SVSJOIN': | |
341 $sendernick = array_shift($parts); | |
342 case 'JOIN': | |
343 $usr = ircd_getuser($sendernick, FALSE); | |
344 if ($usr === NULL) break; | |
345 $chs = explode(',', $parts[0]); | |
346 foreach ($chs as $chn) { | |
347 $ch = ircd_getchannel($chn); | |
348 if ($ch === NULL) break; | |
349 $usr->channels[$ch->name] = $ch; | |
350 if (is_a($usr, 'IUser')) udpmsg_send(array('CMD' => 'JOIN', 'CHN' => $ch->uname, 'USR' => $usr->iname)); | |
351 } | |
352 break; | |
353 case 'SAPART': | |
354 case 'SVSPART': | |
355 $sendernick = array_shift($parts); | |
356 case 'PART': | |
357 $usr = ircd_getuser($sendernick, FALSE); | |
358 if ($usr === NULL) break; | |
359 $chs = explode(',', $parts[0]); | |
360 foreach ($chs as $chn) ircd_part($usr, $chn); | |
361 break; | |
362 case 'KICK': | |
363 ircd_part(ircd_getuser($parts[1], FALSE), $parts[0]); | |
364 break; | |
365 case 'QUIT': | |
366 ircd_quit($sendernick); | |
367 break; | |
368 case 'KILL': | |
369 case 'SVSKILL': | |
370 ircd_quit($parts[0]); | |
371 break; | |
372 } | |
373 return TRUE; | |
374 } | |
375 | |
376 function ircd_quit($nick) { | |
377 global $iusers, $uusers; | |
378 $usr = ircd_getuser($nick, FALSE); | |
379 if ($usr === NULL) return; | |
380 if (is_a($usr, 'IUser')) ircd_userchannels($usr, array('CMD' => 'PART')); | |
381 if (is_a($usr, 'UUser')) unset($uusers[$usr->uname]); | |
382 unset($iusers[strtoupper($usr->iname)]); | |
383 } | |
384 | |
385 function ircd_getchannel($chn) { | |
386 global $ichannels; | |
387 $chn = strtoupper($chn); | |
388 if (!array_key_exists($chn, $ichannels)) return NULL; | |
389 return $ichannels[$chn]; | |
390 } | |
391 function ircd_getuser($nick, $create = TRUE) { | |
392 global $iusers; | |
393 $snick = strtoupper($nick); | |
394 if (array_key_exists($snick, $iusers)) return $iusers[$snick]; | |
395 if (!$create) return NULL; | |
396 $usr = new IUser(); | |
397 $usr->iname = $nick; | |
398 $iusers[$snick] = $usr; | |
399 return $usr; | |
400 } | |
401 function ircd_part($usr, $chn) { | |
402 $ch = ircd_getchannel($chn); | |
403 if ($ch === NULL || $usr === NULL) return; | |
404 unset($usr->channels[$ch->name]); | |
405 if (is_a($usr, 'IUser')) udpmsg_send(array('CMD' => 'PART', 'CHN' => $ch->uname, 'USR' => $usr->iname)); | |
406 } | |
407 function ircd_userchannels($usr, $msg) { | |
408 $msg['USR'] = $usr->iname; | |
409 foreach ($usr->channels as $ch) { | |
410 $msg['CHN'] = $ch->uname; | |
411 udpmsg_send($msg); | |
412 } | |
413 } |