Mercurial > hg > udpmsg3
comparison singlerelay/singlerelay.php @ 0:dd81c38b513a
Initial commit
author | Ivo Smits <Ivo@UCIS.nl> |
---|---|
date | Mon, 28 Feb 2011 00:49:07 +0100 |
parents | |
children |
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 | |
30 print("UCIS UDPMSG3 Single server IRC relay (c) 2010 Ivo Smits <Ivo@UCIS.nl>\n"); | |
31 print("More information: http://wiki.ucis.nl/UDPMSG3\n"); | |
32 print("\n"); | |
33 | |
34 $irc_socket = NULL; | |
35 $irc_nick = NULL; | |
36 $irc_channels = array(); | |
37 $irc_buffer = ''; | |
38 $irc_connected = FALSE; | |
39 $irc_users = array(); | |
40 | |
41 $udpmsg_channels = array(); | |
42 $udpmsg_buffer = ''; | |
43 | |
44 class Channel { | |
45 public $uname = NULL; | |
46 public $iname = NULL; | |
47 } | |
48 class User { | |
49 public $iname = NULL; | |
50 public $channels = array(); | |
51 } | |
52 | |
53 srand(); | |
54 | |
55 print("Loading configuration...\n"); | |
56 if (!isset($config)) require './config.php'; | |
57 $udpmsg_socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); | |
58 socket_connect($udpmsg_socket, $config['udpmsg']['host'], $config['udpmsg']['port']); | |
59 | |
60 $irc_socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); | |
61 socket_connect($irc_socket, $config['irc']['host'], $config['irc']['port']); | |
62 irc_send('USER '.$config['irc']['ident'].' host server :'.$config['irc']['realname']); | |
63 $irc_nick = $config['irc']['nick']; | |
64 irc_send('NICK '.$irc_nick); | |
65 foreach ($config['channels'] as $iname => $uname) { | |
66 $ch = new Channel(); | |
67 $ch->iname = $iname; | |
68 $ch->uname = $uname; | |
69 $irc_channels[strtoupper($ch->iname)] = $ch; | |
70 $udpmsg_channels[$ch->uname] = $ch; | |
71 } | |
72 | |
73 print("Starting IRC Relay...\n"); | |
74 print("Running\n"); | |
75 while (TRUE) { | |
76 $selread = array($udpmsg_socket, $irc_socket); | |
77 $selwrite = NULL; | |
78 $selerror = array($udpmsg_socket, $irc_socket); | |
79 socket_select(&$selread, &$selwrite, &$selerror, 120); | |
80 if (in_array($udpmsg_socket, $selerror)) die("UDPMSG: Error: select error.\n"); | |
81 if (in_array($irc_socket, $selerror)) die("IRC: Error: select error.\n"); | |
82 if (in_array($udpmsg_socket, $selread) && !udpmsg_read()) die("UDPMSG: Error: closing connection.\n"); | |
83 if (in_array($irc_socket, $selread) && !irc_read()) die("IRC: read error.\n"); | |
84 } | |
85 | |
86 function udpmsg_read() { | |
87 global $udpmsg_socket, $udpmsg_buffer; | |
88 $msg = socket_read($udpmsg_socket, 1024); | |
89 if (!strlen($msg)) { | |
90 fprintf(STDERR, "UDPMSG: End of file\n"); | |
91 return FALSE; | |
92 } | |
93 $udpmsg_buffer .= $msg; | |
94 while (strlen($udpmsg_buffer) > 2) { | |
95 $len = ord($udpmsg_buffer[0]) * 256 + ord($udpmsg_buffer[1]); | |
96 if ($len <= 0 || $len > 1024) { | |
97 fprintf(STDERR, "UDPMSG: Error: protocol error\n"); | |
98 return FALSE; | |
99 } | |
100 if (strlen($udpmsg_buffer) < 2 + $len) break; | |
101 $msg = substr($udpmsg_buffer, 2, $len); | |
102 $udpmsg_buffer = substr($udpmsg_buffer, 2 + $len); | |
103 $parts = explode("\0", $msg); | |
104 $ret = array(); | |
105 for ($i = 0; $i < count($parts)-1; $i += 2) $ret[$parts[$i]] = $parts[$i+1]; | |
106 udpmsg_process($ret); | |
107 } | |
108 return TRUE; | |
109 } | |
110 | |
111 function udpmsg_send($arr) { | |
112 global $udpmsg_socket, $config; | |
113 $tmp = array(); | |
114 $arr['DUMMY'] = rand(0, 999999); | |
115 $arr['NET'] = $config['udpmsg']['netname']; | |
116 foreach ($arr as $key => $value) { $tmp[] = $key; $tmp[] = $value; } | |
117 $tmp[] = ''; | |
118 $msg = implode("\0", $tmp); | |
119 $len = strlen($msg); | |
120 if ($len > 1024) { | |
121 fprintf(STDERR, "UDPMSG: Error: message too long!\n"); | |
122 return; | |
123 } | |
124 $lens = chr(floor($len / 256)).chr($len % 256); | |
125 socket_write($udpmsg_socket, $lens.$msg); | |
126 } | |
127 | |
128 function irc_read() { | |
129 global $irc_socket, $irc_buffer; | |
130 $newdata = socket_read($irc_socket, 1024); | |
131 if ($newdata === FALSE || !strlen($newdata)) return FALSE; | |
132 $irc_buffer .= $newdata; | |
133 $offset = 0; | |
134 $len = strlen($irc_buffer); | |
135 while ($offset < $len) { | |
136 $posa = strpos($irc_buffer, "\n", $offset); | |
137 $posb = strpos($irc_buffer, "\r", $offset); | |
138 if ($posa !== FALSE && $posb !== FALSE) $pos = min($posa, $posb); | |
139 else if ($posa !== FALSE) $pos = $posa; | |
140 else if ($posb !== FALSE) $pos = $posb; | |
141 else break; | |
142 $line = substr($irc_buffer, $offset, $pos - $offset); | |
143 if (strlen($line) && !irc_process($line)) return FALSE; | |
144 $offset = $pos + 1; | |
145 } | |
146 $irc_buffer = ($offset == $len) ? '' : substr($irc_buffer, $offset); | |
147 return TRUE; | |
148 } | |
149 | |
150 function irc_send($line) { | |
151 global $irc_socket; | |
152 print('IW: '.$line."\n"); | |
153 $line .= "\r\n"; | |
154 socket_send($irc_socket, $line, strlen($line), 0); | |
155 } | |
156 | |
157 function udpmsg_process($ret) { | |
158 global $udpmsg_channels; | |
159 if (!isset($ret['CHN']) || !isset($ret['CMD']) || !isset($ret['USR'])) return; | |
160 if (!array_key_exists($ret['CHN'], $udpmsg_channels)) return; | |
161 $ch = $udpmsg_channels[$ret['CHN']]; | |
162 $net = isset($ret['NET']) ? preg_replace('/[\x00\x10-\x13]/', '', $ret['NET']) : NULL; | |
163 $usr = preg_replace('/[\x00\x10-\x13]/', '', $ret['USR']); | |
164 switch ($ret['CMD']) { | |
165 case 'MSG': | |
166 if (!isset($ret['MSG'])) break; | |
167 $msg = preg_replace('/[\x00\x10-\x13]/', '', $ret['MSG']); | |
168 $pfx = '['.$usr.($net?' @ '.$net:'').'] '; | |
169 if (ord($msg[0]) == 1) { | |
170 if (substr($message, 1, 6) != 'ACTION') break; | |
171 $msg = chr(1).'ACTION '.$pfx.substr($msg, 8, -1).chr(1); | |
172 } else { | |
173 $msg = $pfx.$msg; | |
174 } | |
175 irc_channel_send($ch, $msg); | |
176 break; | |
177 case 'JOIN': | |
178 irc_channel_send($ch, '* '.$usr.' has joined'.($net?' on network '.$net:'')); | |
179 break; | |
180 case 'PART': | |
181 irc_channel_send($ch, '* '.$usr.' has left'.($net?' on network '.$net:'')); | |
182 break; | |
183 case 'NICK': | |
184 if (!isset($ret['NEWNICK'])) break; | |
185 $newnick = preg_replace('/[\x00\x10-\x13]/', '', $ret['NEWNICK']); | |
186 irc_channel_send($ch, '* '.$usr.' has changed their nickname to '.$newnick.' '.($net?' on network '.$net:'')); | |
187 break; | |
188 } | |
189 } | |
190 | |
191 function irc_process($line) { | |
192 global $irc_nick, $irc_connected, $irc_channels, $config, $irc_users; | |
193 print('IR: '.$line."\n"); | |
194 $partsa = explode(' :', $line, 2); | |
195 $parts = explode(' ', $partsa[0]); | |
196 $sender = ($parts[0][0] == ':') ? substr(array_shift(&$parts), 1) : NULL; | |
197 $command = array_shift(&$parts); | |
198 if (count($partsa) > 1) array_push(&$parts, $partsa[1]); | |
199 $partsa = explode('!', $sender); | |
200 $sendernick = $partsa[0]; | |
201 switch (strtoupper($command)) { | |
202 case '001': //Welcome | |
203 $irc_nick = $parts[0]; | |
204 $irc_connected = TRUE; | |
205 foreach ($irc_channels as $ch) irc_send("JOIN :".$ch->iname); | |
206 break; | |
207 case '433': //Nickname in use | |
208 irc_send("NICK ".$config['irc']['nick'].rand(100,999)); | |
209 break; | |
210 case 'PING': | |
211 irc_send('PONG :'.$parts[0]); | |
212 break; | |
213 case 'NICK': //Nickname change | |
214 if (strcasecmp($sendernick, $irc_nick)) { | |
215 $irc_nick = $parts[0]; | |
216 } else { | |
217 $usr = irc_getuser($sendernick, FALSE); | |
218 if ($usr === NULL) break; | |
219 irc_userchannels($usr, array('CMD' => 'NICK', 'NEWNICK' => $parts[0])); | |
220 unset($irc_users[strtoupper($usr->iname)]); | |
221 $usr->iname = $parts[0]; | |
222 $irc_users[strtoupper($usr->iname)] = $usr; | |
223 } | |
224 break; | |
225 case 'JOIN': | |
226 if (!strcasecmp($sendernick, $irc_nick)) break; | |
227 $chs = explode(',', $parts[0]); | |
228 $usr = NULL; | |
229 foreach ($chs as $chn) { | |
230 $ch = irc_getchannel($chn); | |
231 if ($ch === NULL) continue; | |
232 if ($usr === NULL) $usr = irc_getuser($sendernick, TRUE); | |
233 $usr->channels[$ch->iname] = $ch; | |
234 udpmsg_send(array('CMD' => 'JOIN', 'CHN' => $ch->uname, 'USR' => $usr->iname)); | |
235 } | |
236 break; | |
237 case '353': //:server 353 me = channel :nickname nickname nickname | |
238 $ch = irc_getchannel($parts[2]); | |
239 if ($ch === NULL) break; | |
240 $partsa = explode(' ', $parts[3]); | |
241 foreach ($partsa as $un) { | |
242 if (!strlen($un)) continue; | |
243 $usr = irc_getuser(ltrim($un, '~&@%+'), TRUE); | |
244 $usr->channels[$ch->iname] = $ch; | |
245 udpmsg_send(array('CMD' => 'JOIN', 'CHN' => $ch->uname, 'USR' => $usr->iname)); | |
246 } | |
247 break; | |
248 case 'PART': | |
249 $usr = irc_getuser($sendernick, FALSE); | |
250 if ($usr === NULL) break; | |
251 $chs = explode(',', $parts[0]); | |
252 foreach ($chs as $chn) irc_part($usr, $chn); | |
253 break; | |
254 case 'KICK': | |
255 irc_part(irc_getuser($parts[1], FALSE), $parts[0]); | |
256 break; | |
257 case 'QUIT': | |
258 $usr = irc_getuser($sendernick, FALSE); | |
259 if ($usr === NULL) return; | |
260 irc_userchannels($usr, array('CMD' => 'PART')); | |
261 unset($irc_users[strtoupper($usr->iname)]); | |
262 break; | |
263 case 'PRIVMSG': | |
264 case 'NOTICE': | |
265 $ch = irc_getchannel($parts[0]); | |
266 if ($ch === NULL) break; | |
267 udpmsg_send(array('CMD' => 'MSG', 'CHN' => $ch->uname, 'MSG' => $parts[1], 'USR' => $sendernick)); | |
268 break; | |
269 } | |
270 return TRUE; | |
271 } | |
272 | |
273 function irc_getchannel($chn) { | |
274 global $irc_channels; | |
275 $chn = strtoupper($chn); | |
276 if (!array_key_exists($chn, $irc_channels)) return NULL; | |
277 return $irc_channels[$chn]; | |
278 } | |
279 function irc_getuser($nick, $create = TRUE) { | |
280 global $irc_users; | |
281 $snick = strtoupper($nick); | |
282 if (array_key_exists($snick, $irc_users)) return $irc_users[$snick]; | |
283 if (!$create) return NULL; | |
284 $usr = new User(); | |
285 $usr->iname = $nick; | |
286 $irc_users[$snick] = $usr; | |
287 return $usr; | |
288 } | |
289 function irc_part($usr, $chn) { | |
290 $ch = irc_getchannel($chn); | |
291 if ($ch === NULL || $usr === NULL) return; | |
292 unset($usr->channels[$ch->name]); | |
293 udpmsg_send(array('CMD' => 'PART', 'CHN' => $ch->uname, 'USR' => $usr->iname)); | |
294 } | |
295 function irc_userchannels($usr, $msg) { | |
296 $msg['USR'] = $usr->iname; | |
297 foreach ($usr->channels as $ch) { | |
298 $msg['CHN'] = $ch->uname; | |
299 udpmsg_send($msg); | |
300 } | |
301 } | |
302 function irc_channel_send($ch, $msg) { | |
303 global $irc_connected; | |
304 if (!$irc_connected) return; | |
305 irc_send('PRIVMSG '.$ch->iname.' :'.$msg); | |
306 } |