0
|
1 #!/usr/bin/php |
|
2 <?php |
|
3 /* Copyright 2010 Ivo Smits <Ivo@UCIS.nl>. All rights reserved. |
|
4 Redistribution and use in source and binary forms, with or without modification, are |
|
5 permitted provided that the following conditions are met: |
|
6 |
|
7 1. Redistributions of source code must retain the above copyright notice, this list of |
|
8 conditions and the following disclaimer. |
|
9 |
|
10 2. Redistributions in binary form must reproduce the above copyright notice, this list |
|
11 of conditions and the following disclaimer in the documentation and/or other materials |
|
12 provided with the distribution. |
|
13 |
|
14 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED |
|
15 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
|
16 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR |
|
17 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
18 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
|
19 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
|
20 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|
21 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
|
22 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
23 |
|
24 The views and conclusions contained in the software and documentation are those of the |
|
25 authors and should not be interpreted as representing official policies, either expressed |
|
26 or implied, of Ivo Smits.*/ |
|
27 |
|
28 srand(); |
|
29 |
|
30 $listchannels = array(); |
|
31 $channels = array(); |
|
32 |
|
33 $udpmsg = NULL; |
|
34 $udpmsg_buffer = ''; |
|
35 |
|
36 $client_name = NULL; |
|
37 $client_buffer = ''; |
|
38 |
|
39 srand(); |
|
40 |
|
41 require 'config.php'; |
|
42 |
|
43 client_send(':server NOTICE AUTH :*** Not looking up your hostname, we do not care.'); |
|
44 client_send(':server NOTICE AUTH :*** Not checking ident, are you serious?'); |
|
45 |
|
46 while ($client_name === NULL) { |
|
47 $line = client_read_line(); |
|
48 $partsa = explode(' :', $line, 2); |
|
49 $parts = explode(' ', $partsa[0]); |
|
50 $command = array_shift(&$parts); |
|
51 if (count($partsa) > 1) array_push(&$parts, $partsa[1]); |
|
52 switch (strtoupper($command)) { |
|
53 case 'NICK': //Nickname change: newnick = $parts[0] |
|
54 if (!preg_match('/^[a-zA-Z0-9\-_]+$/', $parts[0])) { |
|
55 client_sendnumeric(432, $parts[0].' :Erroneous nickname'); |
|
56 } else { |
|
57 $client_name = $parts[0]; |
|
58 client_sendnumeric(001, ':Welcome to the Internet Relay Chat Network'); |
|
59 client_sendnumeric(002, ':Your host is UDPMSG3-IRCD'); |
|
60 client_sendnumeric(003, ':This server was created some time ago'); |
|
61 client_sendnumeric(004, 'irc.udpmsg3.local 1.0 + +'); |
|
62 client_sendnumeric(005, 'NICKLEN=16 CHANNELLEN=16 CHANTYPES=# NETWORK=UDPMSG3 :are supported by this server'); |
|
63 client_sendnumeric(251, ':There are 1 users and some invisible on a bunch of servers'); |
|
64 client_sendnumeric(252, '0 :operator(s) online'); |
|
65 client_sendnumeric(254, count($config['channels']).' :channels formed'); |
|
66 client_sendnumeric(255, ':I have a bunch of clients and a bunch of servers'); |
|
67 client_sendnumeric(265, ':Current Local Users: undisclosed'); |
|
68 client_sendnumeric(266, ':Current Global Users: unknown'); |
|
69 client_sendnumeric(375, ':- server Message of the Day - '); |
|
70 client_sendnumeric(372, ':- No message, sorry.'); |
|
71 client_sendnumeric(376, ':End of /MOTD command.'); |
|
72 } |
|
73 case 'USER': |
|
74 case 'PASS': |
|
75 break; |
|
76 default: |
|
77 client_sendnumeric(421, $command.' :Unknown command'); |
|
78 } |
|
79 } |
|
80 |
|
81 $udpmsg = stream_socket_client('tcp://'.$config['udpmsg']['host'].':'.$config['udpmsg']['port']); |
|
82 if (!$udpmsg) { |
|
83 client_send('ERROR :Could not connect to exchange.'); |
|
84 die(); |
|
85 } |
|
86 |
|
87 while (TRUE) { |
|
88 $selwrite = NULL; |
|
89 $selread = array($udpmsg, STDIN); |
|
90 $selerror = array($udpmsg, STDIN); |
|
91 stream_select(&$selread, &$selwrite, &$selerror, 10); |
|
92 if (in_array($udpmsg, $selerror)) { |
|
93 fclose($udpmsg); |
|
94 client_send('ERROR :Lost connection to exchange.'); |
|
95 die(); |
|
96 } else { |
|
97 if (in_array($udpmsg, $selread)) { |
|
98 if (!udpmsg_read($udpmsg)) { |
|
99 fclose($udpmsg); |
|
100 client_send('ERROR :Error in exchange protocol.'); |
|
101 die(); |
|
102 } |
|
103 } |
|
104 } |
|
105 if (in_array(STDIN, $selerror)) { |
|
106 fclose($udpmsg); |
|
107 client_send('ERROR :Error while reading from client.'); |
|
108 die(); |
|
109 } else if (in_array(STDIN, $selread)) { |
|
110 if (!client_read()) { |
|
111 fclose($udpmsg); |
|
112 client_send('ERROR :End of file.'); |
|
113 die(); |
|
114 } |
|
115 } |
|
116 } |
|
117 |
|
118 function udpmsg_read() { |
|
119 global $channels, $udpmsg, $udpmsg_buffer, $config; |
|
120 $msg = fread($udpmsg, 1024); |
|
121 if (!strlen($msg)) return FALSE; |
|
122 $udpmsg_buffer .= $msg; |
|
123 while (strlen($udpmsg_buffer) > 2) { |
|
124 $len = ord($udpmsg_buffer[0]) * 256 + ord($udpmsg_buffer[1]); |
|
125 if ($len <= 0 || $len > 1024) return FALSE; |
|
126 if (strlen($udpmsg_buffer) < 2 + $len) break; |
|
127 $msg = substr($udpmsg_buffer, 2, $len); |
|
128 $udpmsg_buffer = substr($udpmsg_buffer, 2 + $len); |
|
129 $parts = explode("\0", $msg); |
|
130 $ret = array(); |
|
131 for ($i = 0; $i < count($parts)-1; $i += 2) $ret[$parts[$i]] = $parts[$i+1]; |
|
132 |
|
133 if (!isset($ret['CHN']) || !isset($ret['CMD']) || !isset($ret['USR'])) continue; |
|
134 if (!substr($ret['CHN'], 0, strlen($config['udpmsg']['chprefix']))) continue; |
|
135 $chn = '#'.substr($ret['CHN'], strlen($config['udpmsg']['chprefix'])); |
|
136 if (!in_array($chn, $channels)) continue; |
|
137 if ($ret['CMD'] == 'MSG') { |
|
138 if (!isset($ret['MSG'])) continue; |
|
139 $from = preg_replace('/[^a-zA-Z0-9\-_]/', '', $ret['USR']); |
|
140 if ($config['udpmsg']['nicknetwork'] && isset($ret['NET'])) $from = preg_replace('/[^a-zA-Z0-9\-_]/', '', $ret['NET']).$config['udpmsg']['nickseparator'].$from; |
|
141 $from = $config['udpmsg']['nickprefix'].$from; |
|
142 $message = preg_replace('/[\x00\x10-\x13]/', '', $ret['MSG']); |
|
143 client_send(':'.$from.' PRIVMSG '.$chn.' :'.$message); |
|
144 } |
|
145 } |
|
146 return TRUE; |
|
147 } |
|
148 |
|
149 function udpmsg_send($arr) { |
|
150 global $udpmsg; |
|
151 $tmp = array(); |
|
152 foreach ($arr as $key => $value) { $tmp[] = $key; $tmp[] = $value; } |
|
153 $tmp[] = ''; |
|
154 $msg = implode("\0", $tmp); |
|
155 $len = strlen($msg); |
|
156 if ($len > 1024) return; |
|
157 $lens = chr(floor($len / 256)).chr($len % 256); |
|
158 fwrite($udpmsg, $lens.$msg); |
|
159 } |
|
160 |
|
161 function array_remove(&$arr, $item) { |
|
162 foreach ($arr as $key => $value) if ($value === $item) unset($arr[$key]); |
|
163 } |
|
164 |
|
165 function client_read() { |
|
166 global $client_buffer; |
|
167 $newdata = fread(STDIN, 1024); |
|
168 if ($newdata === FALSE || !strlen($newdata)) return FALSE; |
|
169 $client_buffer .= $newdata; |
|
170 $offset = 0; |
|
171 $len = strlen($client_buffer); |
|
172 while ($offset < $len) { |
|
173 $posa = strpos($client_buffer, "\n", $offset); |
|
174 $posb = strpos($client_buffer, "\r", $offset); |
|
175 if ($posa !== FALSE && $posb !== FALSE) { |
|
176 $pos = min($posa, $posb); |
|
177 } else if ($posa !== FALSE) { |
|
178 $pos = $posa; |
|
179 } else if ($posb !== FALSE) { |
|
180 $pos = $posb; |
|
181 } else { |
|
182 break; |
|
183 } |
|
184 $line = substr($client_buffer, $offset, $pos - $offset); |
|
185 if (strlen($line)) { |
|
186 if (!client_process($line)) return FALSE; |
|
187 } |
|
188 $offset = $pos + 1; |
|
189 } |
|
190 if ($offset == $len) { |
|
191 $client_buffer = ''; |
|
192 } else if ($offset != 0) { |
|
193 $client_buffer = substr($client_buffer, $offset); |
|
194 } |
|
195 return TRUE; |
|
196 } |
|
197 |
|
198 function client_process($line) { |
|
199 global $client_name, $config; |
|
200 $partsa = explode(' :', $line, 2); |
|
201 $parts = explode(' ', $partsa[0]); |
|
202 $command = array_shift(&$parts); |
|
203 if (count($partsa) > 1) array_push(&$parts, $partsa[1]); |
|
204 switch (strtoupper($command)) { |
|
205 case 'NICK': |
|
206 if (!preg_match('/^[a-zA-Z0-9\-_]+$/', $parts[0])) { |
|
207 client_sendnumeric(432, $parts[0].' :Erroneous nickname'); |
|
208 } else { |
|
209 $client_name = $parts[0]; |
|
210 } |
|
211 break; |
|
212 case 'PING': |
|
213 client_send('PONG :'.$parts[0]); |
|
214 break; |
|
215 case 'PRIVMSG': |
|
216 case 'NOTICE': |
|
217 $channels = explode(',', $parts[0]); |
|
218 foreach ($channels as $chn) { |
|
219 if ($chn[0] == '#') { |
|
220 if (!in_array($chn, $GLOBALS['channels'])) { |
|
221 client_sendnumeric(404, $chn.' :Can not send to channel'); |
|
222 } else { |
|
223 $umsg = array('CMD' => 'MSG', 'CHN' => $config['udpmsg']['chprefix'].substr($chn, 1), 'MSG' => $parts[1], 'USR' => $client_name, 'DUMMY' => rand(10000, 99999)); |
|
224 if ($config['udpmsg']['netname']) $umsg['NET'] = $config['udpmsg']['netname']; |
|
225 udpmsg_send($umsg); |
|
226 } |
|
227 } else { |
|
228 client_sendnumeric(401, $chn.' :No such nickname'); |
|
229 } |
|
230 } |
|
231 break; |
|
232 case 'JOIN': |
|
233 $channels = explode(',', $parts[0]); |
|
234 foreach ($channels as $chn) { |
|
235 if (!preg_match('/^#[a-zA-Z0-9\-_]+$/', $chn)) { |
|
236 client_sendnumeric($client, 403, $chn.' :No such channel'); |
|
237 continue; |
|
238 } |
|
239 if (!in_array($chn, $GLOBALS['channels'])) { |
|
240 client_send(':'.$client_name.'!user@host JOIN :'.$chn); |
|
241 $GLOBALS['channels'][] = $chn; |
|
242 } |
|
243 client_sendnumeric(353, '= '.$chn.' :'.$client_name); |
|
244 client_sendnumeric(366, $chn.' :End of /NAMES list.'); |
|
245 } |
|
246 break; |
|
247 case 'PART': |
|
248 client_send(':'.$client_name.'!user@host PART :'.$chn); |
|
249 array_remove($GLOBALS['channels'], $chn); |
|
250 break; |
|
251 case 'QUIT': |
|
252 return FALSE; |
|
253 case 'NAMES': |
|
254 client_sendnumeric(353, '= '.$parts[0].' :'.$client_name); |
|
255 client_sendnumeric(366, $parts[0].' :End of /NAMES list.'); |
|
256 break; |
|
257 case 'LIST': |
|
258 client_sendnumeric($client, 321, 'Channel :Users Name'); |
|
259 foreach ($GLOBALS['listchannels'] as $chn) { |
|
260 client_sendnumeric(322, $chn.' 0 :'); |
|
261 } |
|
262 client_sendnumeric(323, 'End of /LIST'); |
|
263 break; |
|
264 case 'MODE': |
|
265 case 'USER': |
|
266 case 'USERHOST': |
|
267 break; |
|
268 default: |
|
269 client_sendnumeric(421, $command.' :Unknown command'); |
|
270 } |
|
271 return TRUE; |
|
272 } |
|
273 |
|
274 function client_sendnumeric($num, $line) { |
|
275 global $client_name; |
|
276 client_send(':server '.str_pad($num, 3, '0', STR_PAD_LEFT).' '.$client_name.' '.$line); |
|
277 } |
|
278 |
|
279 function client_send($line) { |
|
280 print($line."\r\n"); |
|
281 } |
|
282 |
|
283 function client_read_line() { |
|
284 $line = fgets(STDIN); |
|
285 if (!strlen($line)) { |
|
286 client_send('ERROR :End of file.'); |
|
287 die(); |
|
288 } |
|
289 return rtrim($line, "\r\n\x0B"); |
|
290 } |