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 }