Commit | Line | Data |
---|---|---|
54f4a22e D |
1 | <?php\r |
2 | \r | |
3 | class SocketStream {\r | |
4 | var $stream;\r | |
5 | function __construct($host = null, $port = null, $blocking = true) {\r | |
6 | if(!$host && !$port)\r | |
7 | return;\r | |
8 | $scheme = 'tcp';\r | |
9 | if($host{0}=='!')\r | |
10 | {\r | |
11 | $scheme = 'ssl';\r | |
12 | $port = 6697;\r | |
13 | $host = substr($host, 1);\r | |
14 | } \r | |
15 | $this->stream = @stream_socket_client("{$scheme}://$host:$port", $errno, $errstr, 4);\r | |
16 | if(!$this->stream)\r | |
17 | {\r | |
18 | debug("Error creating socket: $host $errstr\n");\r | |
19 | return;\r | |
20 | }\r | |
21 | // socket_set_timeout($this->stream, 30);\r | |
22 | stream_set_blocking($this->stream, $blocking);\r | |
23 | }\r | |
24 | function __destruct()\r | |
25 | {\r | |
26 | if($this->stream)\r | |
27 | {\r | |
28 | fclose($this->stream);\r | |
29 | $this->stream = null;\r | |
30 | }\r | |
31 | }\r | |
32 | function SetBlocking($blocking = true)\r | |
33 | {\r | |
34 | if(!$this->stream)\r | |
35 | return false;\r | |
36 | stream_set_blocking($this->stream, $blocking);\r | |
37 | }\r | |
38 | function SetTimeout($timeout, $ms=0)\r | |
39 | {\r | |
40 | if(!$this->stream)\r | |
41 | return false;\r | |
42 | stream_set_timeout($this->stream, $timeout, $ms);\r | |
43 | }\r | |
44 | function Eof()\r | |
45 | {\r | |
46 | if(!$this->stream)\r | |
47 | return true;\r | |
48 | $x = stream_get_meta_data($this->stream);\r | |
49 | if($x['eof'] && $x['unread_bytes']==0 && feof($this->stream))\r | |
50 | return true;\r | |
51 | return false;\r | |
52 | }\r | |
53 | function Read($length = 512, $type = PHP_BINARY_READ)\r | |
54 | {\r | |
55 | if(!$this->stream) return false;\r | |
56 | if($type == PHP_NORMAL_READ){\r | |
57 | $ret = fgets($this->stream, $length);\r | |
58 | } elseif($type == PHP_BINARY_READ) {\r | |
59 | $ret = fread($this->stream, $length);\r | |
60 | }\r | |
61 | //file_put_contents('/home/wok/public_html/kiwi/rawlog', '> '.$ret, FILE_APPEND);\r | |
62 | return $ret;\r | |
63 | }\r | |
64 | function Write($data)\r | |
65 | {\r | |
66 | global $totalup;\r | |
67 | if(!$this->stream) return false;\r | |
68 | \r | |
69 | //file_put_contents('/home/wok/public_html/kiwi/rawlog', '< '.$data, FILE_APPEND);\r | |
70 | $x = @fwrite($this->stream, $data);\r | |
71 | return $x;\r | |
72 | }\r | |
73 | function GetInfo()\r | |
74 | {\r | |
75 | if(!$this->stream)\r | |
76 | return false;\r | |
77 | return array('local'=>stream_socket_get_name($this->stream,false),'remote'=>stream_socket_get_name($this->stream, true));\r | |
78 | }\r | |
79 | }\r | |
80 | class SocketStreamSSL extends SocketStream\r | |
81 | {\r | |
82 | function __construct($host = null, $port = null, $blocking = true)\r | |
83 | {\r | |
84 | if(!$host && !$port)\r | |
85 | return;\r | |
86 | $this->stream = @stream_socket_client("ssl://$host:$port", $errno, $errstr, 4);\r | |
87 | if(!$this->stream)\r | |
88 | {\r | |
89 | debug("Error creating socket: $host $errstr\n");\r | |
90 | return;\r | |
91 | }\r | |
92 | // socket_set_timeout($this->stream, 30);\r | |
93 | stream_set_blocking($this->stream, $blocking);\r | |
94 | }\r | |
95 | }\r | |
96 | class IRC\r | |
97 | {\r | |
98 | var $stream;\r | |
99 | var $url, $urlinfo;\r | |
100 | var $nick;\r | |
101 | protected $msgqueue = array();\r | |
102 | protected $dccs = array();\r | |
103 | private $reconnect=0;\r | |
104 | private $nextping=0;\r | |
105 | public $modes;\r | |
106 | public $chanlist;\r | |
107 | public $connected;\r | |
108 | function __construct($url, $kiwi=null, $opts=array()){ \r | |
109 | $this->url = $url;\r | |
110 | $urlinfo = parse_url($url);\r | |
111 | $this->urlinfo = $urlinfo;\r | |
112 | if(!ereg("^ircs?$", $urlinfo['scheme']))\r | |
113 | return false;\r | |
114 | $ssl = ($urlinfo['scheme']=='ircs');\r | |
115 | $host = $urlinfo['host'];\r | |
116 | $port = isset($urlinfo['port'])?$urlinfo['port']:6667;\r | |
117 | $this->nick = $nick = isset($urlinfo['user'])?$urlinfo['user']:'kiwi_user|'.(string)rand(0,9999);\r | |
118 | //$ident = isset($urlinfo['pass'])?$urlinfo['pass']:$nick;\r | |
119 | $ident = (isset($opts['ident'])) ? "{$opts['ident']}_kiwi" : "{$nick}_kiwi";\r | |
120 | $chans = false;\r | |
121 | if(isset($urlinfo['path'])){\r | |
122 | $path = trim($urlinfo['path'], "/");\r | |
123 | $chans = explode(",", $path); //"#".str_replace(array(":", ","), array(" ", ",#"), $path);\r | |
124 | }\r | |
125 | $this->connected = false;\r | |
126 | if($ssl)\r | |
127 | $this->stream = new SocketStreamSSL($host, $port, false);\r | |
128 | else\r | |
129 | $this->stream = new SocketStream($host, $port, false);\r | |
130 | if(!$this->stream || !$this->stream->stream){\r | |
131 | return false;\r | |
132 | }\r | |
133 | \r | |
134 | // Send the login data\r | |
135 | if($kiwi != null) $this->stream->Write($kiwi."\r\n");\r | |
136 | if(isset($urlinfo['fragment'])) $this->stream->Write("PASS {$urlinfo['fragment']}\r\n");\r | |
137 | \r | |
138 | $this->stream->Write("NICK $nick\r\n");\r | |
139 | $this->stream->Write("USER $ident 0 0 :$nick\r\n");\r | |
140 | //if($chans){\r | |
141 | // foreach($chans as $chan)\r | |
142 | // $this->stream->Write("JOIN ".str_replace(":", " ", $chan)."\r\n");\r | |
143 | //} else {\r | |
144 | $chans = array();\r | |
145 | //}\r | |
146 | $this->chans = $chans;\r | |
147 | $this->connected = true;\r | |
148 | return true;\r | |
149 | }\r | |
150 | function __destruct(){\r | |
151 | if($this->stream){\r | |
152 | $this->stream->Write("QUIT :kiwi\r\n"); \r | |
153 | }\r | |
154 | }\r | |
155 | function Process(){\r | |
156 | if((!$this->stream || !$this->stream->stream)){\r | |
157 | if(time() > $this->reconnect) {\r | |
158 | $this->__construct($this->url);\r | |
159 | $this->reconnect = time() + 10;\r | |
160 | }\r | |
161 | usleep(50000);\r | |
162 | return;\r | |
163 | }\r | |
164 | if($this->stream->Eof()){\r | |
165 | $this->stream = null;\r | |
166 | return;\r | |
167 | }\r | |
168 | $r=array($this->stream->stream);\r | |
169 | $w = $e = array();\r | |
170 | if(($num=@stream_select($r, $w,$e,0,50000))===false){\r | |
171 | \r | |
172 | } elseif($num>0){\r | |
173 | $data=$this->stream->Read(512, PHP_NORMAL_READ);\r | |
174 | //deb($data);\r | |
175 | if(preg_match("/^(?::(?:([a-z0-9\x5B-\x60\x7B-\x7D\.\-]+)|([a-z0-9\x5B-\x60\x7B-\x7D\.\-]+)!([a-z0-9~\.\-_|]+)@([a-z0-9\.\-:]+)) )?([a-z0-9]+)(?:(?: ([^:]+))?(?: :(.+))?)$/i", $data, $regs))\r | |
176 | {\r | |
177 | unset($prefix, $nick, $ident, $hostname, $command, $params, $trailing, $flarp);\r | |
178 | $prefix = $regs[1];\r | |
179 | $nick = $regs[2];\r | |
180 | $ident = $regs[3];\r | |
181 | $hostname = $regs[4];\r | |
182 | $command = $regs[5];\r | |
183 | $params = isset($regs[6]) ? $regs[6] : '';\r | |
184 | $trailing = trim(isset($regs[7]) ? $regs[7] : '');\r | |
185 | $flarp = compact('prefix', 'nick', 'ident', 'hostname', 'command', 'params', 'trailing');\r | |
186 | $this->ProcessMessage($flarp);\r | |
187 | }\r | |
188 | }\r | |
189 | else\r | |
190 | {\r | |
191 | if(sizeof($this->msgqueue) && (microtime(true)>($this->lastqueuewrite+0.1)) && ($msg=array_shift($this->msgqueue)))\r | |
192 | { $this->lastqueuewrite = microtime(true); $this->stream->Write($msg); }\r | |
193 | foreach($this->dccs as $k=>&$dcc)\r | |
194 | {\r | |
195 | if(!$dcc->Process())\r | |
196 | {\r | |
197 | if($dcc->errorinfo)\r | |
198 | $this->SendNotice($dcc->errorinfo['nick'], $dcc->errorinfo['text']);\r | |
199 | unset($this->dccs[$k]);\r | |
200 | }\r | |
201 | }\r | |
202 | if(($now=microtime(1))>$this->nextping)\r | |
203 | {\r | |
204 | $this->stream->Write("PONG x\r\n");\r | |
205 | $this->nextping = $now + 150;\r | |
206 | }\r | |
207 | }\r | |
208 | }\r | |
209 | function ProcessMessage($message)\r | |
210 | {\r | |
211 | //echo $message['command']."\n";\r | |
212 | switch($message['command'])\r | |
213 | {\r | |
214 | case "PING":\r | |
215 | $this->stream->Write("PONG {$message['trailing']}\r\n");\r | |
216 | break;\r | |
217 | case "PRIVMSG":\r | |
218 | $this->ProcessPrivMsg($message);\r | |
219 | break;\r | |
220 | case "001":\r | |
221 | if($this->chans)\r | |
222 | foreach($this->chans as $chan)\r | |
223 | $this->stream->Write("JOIN ".str_replace(":", " ", $chan)."\r\n");\r | |
224 | break;\r | |
225 | case "443":\r | |
226 | $newnick = 'kiwi_user|'.rand(0,99999);\r | |
227 | $this->SendCmd("NICK ".$newnick);\r | |
228 | $this->nick = $newnick;\r | |
229 | break;\r | |
230 | case "MODE":\r | |
231 | $this->OnMODE($message);\r | |
232 | break;\r | |
233 | case "353":\r | |
234 | $this->On353($message);\r | |
235 | break;\r | |
236 | case "QUIT":\r | |
237 | $chan = $message['params'];\r | |
238 | foreach($this->chanlist as &$chan){\r | |
239 | if(isset($chan['userlist'][$message['nick']]))\r | |
240 | unset($chan['userlist'][$message['nick']]);\r | |
241 | }\r | |
242 | break;\r | |
243 | case "PART":\r | |
244 | $chan = $message['params'];\r | |
245 | //debug("Parting {$message['nick']} from $chan");\r | |
246 | if(isset($this->chanlist[ltrim($chan, "#")])){\r | |
247 | unset($this->chanlist[ltrim($chan, "#")]['userlist'][$message['nick']]);\r | |
248 | }\r | |
249 | break;\r | |
250 | case "JOIN":\r | |
251 | $chan = $message['trailing'];\r | |
252 | $nick = array($message['nick'] => '');\r | |
253 | $nicklist = is_array($this->chanlist[ltrim($chan, "#")]['userlist']) ? $this->chanlist[ltrim($chan, "#")]['userlist'] :\r | |
254 | array();\r | |
255 | \r | |
256 | $this->chanlist[ltrim($chan, "#")]['userlist'] = array_merge($nicklist, $nick);\r | |
257 | break;\r | |
258 | case "NICK":\r | |
259 | if($message['nick'] == $this->nick){\r | |
260 | $this->nick = $message['trailing'];\r | |
261 | }\r | |
262 | foreach($this->chanlist as $chan_name => $chan){\r | |
263 | foreach($chan['userlist'] as $nick => $status){\r | |
264 | if($nick == $message['nick']){\r | |
265 | $this->chanlist[$chan_name]['userlist'][$message['trailing']] = $this->chanlist[$chan_name]['userlist'][$message['nick']];\r | |
266 | unset($this->chanlist[$chan_name]['userlist'][$message['nick']]);\r | |
267 | break;\r | |
268 | }\r | |
269 | }\r | |
270 | }\r | |
271 | break;\r | |
272 | }\r | |
273 | }\r | |
274 | function OnMODE($message){\r | |
275 | if($message['params'] == $this->nick) {\r | |
276 | $modes = $message['trailing'];\r | |
277 | $size = strlen($modes);\r | |
278 | $add = 1;\r | |
279 | for($i=0;$i<$size;$i++){\r | |
280 | if($modes{$i} == '+'){\r | |
281 | $add = 1;\r | |
282 | continue;\r | |
283 | } elseif($modes{$i} == '-'){\r | |
284 | $add = 0;\r | |
285 | continue;\r | |
286 | }\r | |
287 | if($add && strpos($this->modes, $modes{$i}) === false)\r | |
288 | $this->modes.=$modes{$i};\r | |
289 | if(!$add && strpos($this->modes, $modes{$i}))\r | |
290 | $this->modes = str_replace($modes{$i}, "", $this->modes);\r | |
291 | }\r | |
292 | }\r | |
293 | $params = $message['params'];\r | |
294 | $chan = trim(strtok($params, ' '));\r | |
295 | if(in_array(trim($chan,'#'), $this->chans)){\r | |
296 | $modes = strtok(' ');\r | |
297 | $therest = explode(" ", trim(strtok('')));\r | |
298 | $size = strlen($modes);\r | |
299 | $add = 1;\r | |
300 | $offset = 0;\r | |
301 | for($i=0;$i<$size;$i++){\r | |
302 | if($modes{$i} == '+'){\r | |
303 | $add = 1;\r | |
304 | $offset--;\r | |
305 | continue;\r | |
306 | } elseif($modes{$i} == '-'){\r | |
307 | $add = 0;\r | |
308 | $offset--;\r | |
309 | continue;\r | |
310 | }\r | |
311 | if($modes[$i]=='v')\r | |
312 | {\r | |
313 | $user = $therest[$i+$offset];\r | |
314 | if($add)\r | |
315 | {\r | |
316 | if(stripos($this->chanlist[trim($chan,'#')]['userlist'][$user], '+')===false)\r | |
317 | $this->chanlist[trim($chan,'#')]['userlist'][$user] .= '+';\r | |
318 | }\r | |
319 | else\r | |
320 | $this->chanlist[trim($chan,'#')]['userlist'][$user] = str_replace('+','',$this->chanlist[trim($chan,'#')]['userlist'][$user]);\r | |
321 | continue;\r | |
322 | }\r | |
323 | if($modes[$i]=='o')\r | |
324 | {\r | |
325 | $user = $therest[$i+$offset];\r | |
326 | if($add)\r | |
327 | {\r | |
328 | if(stripos($this->chanlist[trim($chan,'#')]['userlist'][$user], '@')===false)\r | |
329 | $this->chanlist[trim($chan,'#')]['userlist'][$user] .= '@';\r | |
330 | }\r | |
331 | else\r | |
332 | $this->chanlist[trim($chan,'#')]['userlist'][$user] = str_replace('@','',$this->chanlist[trim($chan,'#')]['userlist'][$user]);\r | |
333 | continue;\r | |
334 | }\r | |
335 | if($modes[$i]=='h')\r | |
336 | {\r | |
337 | $user = $therest[$i+$offset];\r | |
338 | if($add)\r | |
339 | {\r | |
340 | if(stripos($this->chanlist[trim($chan,'#')]['userlist'][$user], '%')===false)\r | |
341 | $this->chanlist[trim($chan,'#')]['userlist'][$user] .= '%';\r | |
342 | }\r | |
343 | else\r | |
344 | $this->chanlist[trim($chan,'#')]['userlist'][$user] = str_replace('%','',$this->chanlist[trim($chan,'#')]['userlist'][$user]);\r | |
345 | continue;\r | |
346 | }\r | |
347 | if($modes[$i]=='q')\r | |
348 | {\r | |
349 | $user = $therest[$i+$offset];\r | |
350 | if($add)\r | |
351 | {\r | |
352 | if(stripos($this->chanlist[trim($chan,'#')]['userlist'][$user], '~')===false)\r | |
353 | $this->chanlist[trim($chan,'#')]['userlist'][$user] .= '~';\r | |
354 | }\r | |
355 | else\r | |
356 | $this->chanlist[trim($chan,'#')]['userlist'][$user] = str_replace('~','',$this->chanlist[trim($chan,'#')]['userlist'][$user]);\r | |
357 | continue;\r | |
358 | }\r | |
359 | }\r | |
360 | }\r | |
361 | \r | |
362 | }\r | |
363 | function On353($message){\r | |
364 | // Response to NAMES command\r | |
365 | list($nick,,$chan) = explode(" ", $message['params']);\r | |
366 | $nicks = explode(" ", $message['trailing']);\r | |
367 | $prefixes = array('~', '&', '@','%','+');\r | |
368 | $nicklist = array();\r | |
369 | foreach($nicks as $nick){\r | |
370 | if(in_array($nick{0}, $prefixes)){\r | |
371 | $prefix = $nick{0};\r | |
372 | $nick = substr($nick,1);\r | |
373 | }\r | |
374 | else\r | |
375 | $prefix = '';\r | |
376 | $nicklist[$nick] = $prefix;\r | |
377 | }\r | |
378 | if(sizeof($nicklist)){\r | |
379 | // If we havn't got a list of nicks for this channel yet, create it\r | |
380 | if(!isset($this->chanlist[ltrim($chan, "#")]['userlist'])){\r | |
381 | $this->chanlist[ltrim($chan, "#")]['userlist'] = array();\r | |
382 | }\r | |
383 | $this->chanlist[ltrim($chan, "#")]['userlist'] = array_merge($this->chanlist[ltrim($chan, "#")]['userlist'], $nicklist);\r | |
384 | }\r | |
385 | }\r | |
386 | function ProcessPrivMsg($message){\r | |
387 | //$cmd = strtok($message['trailing'], ' ');\r | |
388 | //switch($cmd){\r | |
389 | //}\r | |
390 | }\r | |
391 | function SendMessage($dest, $text){\r | |
392 | if(!$this->stream)\r | |
393 | return false;\r | |
394 | $this->stream->Write("PRIVMSG $dest :$text\r\n");\r | |
395 | }\r | |
396 | function Join($chan){\r | |
397 | $this->stream->Write("JOIN ".str_replace(":", " ", $chan)."\r\n");\r | |
398 | }\r | |
399 | function SendNotice($dest, $text)\r | |
400 | {\r | |
401 | $this->stream->Write("NOTICE $dest :$text\r\n");\r | |
402 | }\r | |
403 | function QueueMessage($dest, $text)\r | |
404 | {\r | |
405 | $this->msgqueue[] = "PRIVMSG $dest :$text\r\n";\r | |
406 | }\r | |
407 | function QueueNotice($dest, $text)\r | |
408 | {\r | |
409 | $this->msgqueue[] = "NOTICE $dest :$text\r\n"; \r | |
410 | }\r | |
411 | function GetMainChan()\r | |
412 | {\r | |
413 | return '#'.$this->chans[0]; \r | |
414 | }\r | |
415 | } |