2 /********************************* PHP-P10 ******************************
3 * P10 uplink class by pk910 (c)2011 pk910 *
4 ************************************************************************
7 * PHP-P10 is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
17 * You should have received a copy of the GNU General Public License *
18 * along with PHP-P10; if not, write to the Free Software Foundation, *
19 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
21 ************************************************************************
23 * Uplink/Uplink.class.php
25 * This file contains the basic P10 Protocol handler.
27 ************************************************************************
31 * has to be called after the settings have been set.
34 * loop function that should be calles as many times as possible.
35 * It reads from the socket and BLOCKS the script execution for a
36 * specific time if nothing is received.
38 * void setUplinkHost(String $host, int $port, bool $ssl = false, String $bind = null)
39 * sets the Uplink connection information.
41 * void setLoopTimeout(int $timeout)
42 * sets the maximum time loop() is blocking the script execution.
44 * void setUplinkServer(int $numeric, String $name, String $password, String $description)
45 * sets the own P10 Server information.
47 * void setValidateServer(String $name, String $password)
48 * sets additional security relevant information about the remote server.
50 * void setHISOptions(String $serverName, String $serverDescription String $usermask)
51 * sets the default Account fakehost
53 * void setEventHandler(EventHandler $event_handler)
54 * sets the EventHandlder
56 require_once("Client.class.php");
57 require_once("Numerics.class.php");
58 require_once("P10Formatter.class.php");
59 require_once("P10_Server.class.php");
60 require_once("P10_User.class.php");
61 require_once("P10_Channel.class.php");
62 require_once("P10_ModeSets.class.php");
63 require_once("EventHandler.interface.php");
64 require_once("IPAddr.class.php");
67 define("ERR_NICK_IN_USE", $e++);
68 define("ERR_INVALID_USER", $e++);
69 define("ERR_INVALID_CHANNAME", $e++);
70 define("ERR_NOT_CONNECTED", $e++);
71 define("ERR_NOT_ON_CHANNEL", $e++);
75 private $settings = array();
77 private $eventHandler = null;
78 private $last_local_numeric = 0;
80 const FLAG_P10SESSION = 0x0001; //connection is in P10 mode (server is connected)
81 const FLAG_SECURITY_QUIT = 0x0002; //local connection abort because of security issues
82 const FLAG_NOT_CONNECTABLE = 0x0004; //remote server is not connectable
83 const FLAG_BURST_PENDING = 0x0008; //we still have to burst
84 const FLAG_CONNECTED = 0x0010; //connected and synced (ready)
85 const FLAG_GOT_PASS = 0x0020; //got PASS from the remote Server
88 public function __construct() {
89 $this->client = new Client();
90 $this->setSetting("recv_timeout", 1000);
91 $this->setSetting("his_usermask", "user.NoMask");
94 public function initialize() {
96 trigger_error("Uplink already initialized.", E_USER_ERROR);
99 $self_numeric = $this->getSetting("numeric");
100 $self_name = $this->getSetting("name");
101 $self_description = $this->getSetting("description");
102 if(!$self_numeric || !$self_name) {
103 trigger_error("Server Settings missing.", E_USER_ERROR);
106 $this->server = new P10_Server($self_name, $self_numeric, null, time(), time(), $self_description);
109 public function loop() {
110 if($this->server == null) {
111 trigger_error("Uplink not initialized.", E_USER_ERROR);
114 if(!$this->client->connected()) {
115 if(($this->flags & self::FLAG_P10SESSION)) {
116 //Server got disconnected
117 $this->server->disconnectServer($this->eventHandler, true);
118 $this->flags &= ~self::FLAG_P10SESSION;
120 $host = $this->getSetting("host");
121 $port = $this->getSetting("port");
122 if($host == null || $port == null) {
123 trigger_error("Uplink Settings missing.", E_USER_ERROR);
126 if(($this->flags & self::FLAG_SECURITY_QUIT) || ($this->flags & self::FLAG_NOT_CONNECTABLE)) {
129 $state = $this->client->connect($host, $port, $this->getSetting("bind"), $this->getSetting("ssl"), $this->getSetting("recv_timeout"));
131 usleep($this->getSetting("recv_timeout") / 1000);
132 $this->flags |= self::FLAG_NOT_CONNECTABLE;
136 $this->loginServer();
138 //try to receive new data from the Uplink
139 $lines = $this->client->recv();
140 if($lines == null) return;
141 foreach($lines as $line) {
142 $this->parseLine($line);
146 public function shutdown() {
147 if($this->client->connected()) {
148 if(($this->flags & self::FLAG_P10SESSION)) {
149 $this->send("SQ", "Shutdown requested.");
151 $this->client->disconnect();
155 public function setUplinkHost($host, $port, $ssl = false, $bind = null) {
156 $this->setSetting("host", $host);
157 $this->setSetting("port", $port);
158 $this->setSetting("ssl", $ssl);
159 $this->setSetting("bind", $bind);
162 public function setLoopTimeout($timeout) {
163 $this->setSetting("recv_timeout", $timeout);
166 public function setUplinkServer($numeric, $name, $password, $description) {
167 $this->setSetting("numeric", Numerics::intToNum($numeric, 2));
168 $this->setSetting("name", $name);
169 $this->setSetting("password", $password);
170 $this->setSetting("description", $description);
173 public function setValidateServer($name, $password) {
174 $this->setSetting("their_name", $name);
175 $this->setSetting("their_password", $password);
178 public function setHISOptions($servername, $serverdesc, $usermask) {
179 $this->setSetting("his_name", $servername);
180 $this->setSetting("his_desc", $serverdesc);
181 $this->setSetting("his_usermask", $usermask);
184 public function setEventHandler($event_handler) {
185 if(!is_a($event_handler, "EventHandler")) {
186 trigger_error((is_object($event_handler) ? get_class($event_handler) : gettype($event_handler))." is NOT a valid EventHandler.", E_USER_ERROR);
189 $this->eventHandler = $event_handler;
192 private function setSetting($setting, $value) {
193 $this->settings[$setting] = $value;
196 private function getSetting($setting) {
197 if(array_key_exists($setting, $this->settings)) {
198 return $this->settings[$setting];
204 private function loginServer() {
205 $password = $this->getSetting("password");
206 $this->send("PASS", $password);
207 $this->send("SERVER", $this->server->getName(), $this->server->getStartTime(), $this->server->getLinkTime(), $this->server->getNumeric(), $this->server->getDescription());
210 private function parseLine($line) {
211 $highExplode = explode(" :", $line, 2);
212 $tokens = explode(" ", $highExplode[0]);
213 if(count($highExplode) > 1)
214 $tokens[] = $highExplode[1];
215 $cmdPos = (($this->flags & self::FLAG_P10SESSION) ? 1 : 0);
216 if($cmdPos == 1) $from = $tokens[0];
218 $arguments = array_slice($tokens, $cmdPos + 1);
219 if(($this->flags & self::FLAG_P10SESSION) && $this->eventHandler) {
220 $this->eventHandler->event_preparse($from, strtoupper($tokens[$cmdPos]), $arguments);
222 switch(strtoupper($tokens[$cmdPos])) {
225 $this->recv_pass($from, $arguments);
228 $this->recv_server($from, $arguments);
231 $this->recv_error($from, $arguments);
235 $this->recv_server($from, $arguments);
238 $this->recv_ping($from, $arguments);
241 $this->recv_nick($from, $arguments);
244 $this->recv_end_of_burst($from, $arguments);
247 $this->recv_end_of_burst_ack($from, $arguments);
250 $this->recv_server_quit($from, $arguments);
253 $this->recv_quit($from, $arguments);
256 $this->recv_burst($from, $arguments);
260 $this->recv_join($from, $arguments);
263 $this->recv_part($from, $arguments);
266 $this->recv_kick($from, $arguments);
269 $this->recv_kill($from, $arguments);
272 $this->recv_privmsg($from, $arguments);
275 $this->recv_notice($from, $arguments);
278 $this->recv_whois($from, $arguments);
281 $this->recv_away($from, $arguments);
285 $this->recv_mode($from, $arguments);
290 if($this->eventHandler)
291 $this->eventHandler->event_unknown_cmd($from, strtoupper($tokens[$cmdPos]), $arguments);
296 private function send($command) {
297 if(func_num_args() > 1) {
298 $args = array_slice(func_get_args(), 1);
299 $command = P10Formatter::formatCMD($this->getSetting("numeric"), $command, $args);
301 $command = P10Formatter::formatCMD($this->getSetting("numeric"), $command, array());
303 $this->client->send($command);
306 /********************************************************************************************
307 * INCOMING COMMANDS *
308 ********************************************************************************************/
310 private function recv_pass($from, $args) {
311 $their_pass = $this->getSetting("their_password");
313 if($args[0] != $their_pass) {
315 $this->flags |= self::FLAG_SECURITY_QUIT;
316 $this->send("ERROR", "Incorrect password received.");
317 $this->client->disconnect();
320 $this->flags |= self::FLAG_GOT_PASS;
324 private function recv_error($from, $args) {
325 //we received an error - the socket is dead for us, now
326 //maybe we want to log this, later
327 $this->client->disconnect();
330 private function recv_server($from, $args) {
333 $their_name = $this->getSetting("their_name");
334 if($their_name && $args[0] != $their_name) {
335 $this->flags |= self::FLAG_SECURITY_QUIT;
336 $this->send("ERROR", "Invalid Server name");
337 $this->client->disconnect();
340 if($this->getSetting("their_password") && !($this->flags & self::FLAG_GOT_PASS)) {
341 $this->flags |= self::FLAG_SECURITY_QUIT;
342 $this->send("ERROR", "PASS missing.");
343 $this->client->disconnect();
346 $new_server = new P10_Server($args[0], substr($args[5],0,2), $this->server, $args[2], $args[3], $args[7]);
347 $this->server->addServer($new_server);
348 $this->flags |= self::FLAG_P10SESSION | self::FLAG_BURST_PENDING;
349 if($this->eventHandler)
350 $this->eventHandler->event_newserver($new_server, !($this->flags & self::FLAG_CONNECTED));
352 //another server got a new slave server ^^
353 $server = P10_Server::getServerByNum($from);
354 if($server == null) {
355 trigger_error("Parent Server (".$from.") does not exist or was not found on recv_server.", E_USER_ERROR);
358 $new_server = new P10_Server($args[0], substr($args[5],0,2), $server, $args[2], $args[3], $args[7]);
359 $server->addServer($new_server);
360 if($this->eventHandler)
361 $this->eventHandler->event_newserver($new_server, !($this->flags & self::FLAG_CONNECTED));
365 private function recv_ping($from, $args) {
366 $this->send("Z", $args[0]); //simply PONG
367 P10_Channel::recheckAllChannels();
370 private function recv_nick($from, $args) {
371 if(count($args) <= 2) {
373 $user = P10_User::getUserByNum($from);
375 trigger_error("Server tries to change the nick of an user that does not exist or was not found on recv_nick.", E_USER_ERROR);
378 if($this->eventHandler)
379 $this->eventHandler->event_nick($user, $args[0]);
380 $user->setNick($args[0]);
383 $numeric = $args[count($args)-2];
385 $server = P10_Server::getServerByNum($from);
386 if($server == null) {
387 trigger_error("Server (".$from.") the User (".$nick.") is coming from does not exist or was not found on recv_nick.", E_USER_ERROR);
390 if(substr($numeric,0,2) != $from) {
391 trigger_error("A Server (".$from.") tries to connect a User with an invalid User numeric ('".$numeric."' does not belong to the Server)", E_USER_WARNING);
393 $connect_time = $args[2];
396 $modes = implode(" ",array_slice($args, 5, count($args)-8));
397 $modes = new P10_UserModeSet($modes);
398 $ip = new IPAddr($args[count($args)-3]);
399 $realname = $args[count($args)-1];
400 $user = new P10_User($nick, $numeric, $server, $connect_time, $ident, $host, $ip, $realname, $modes);
401 if($this->eventHandler)
402 $this->eventHandler->event_connect($user, !($this->flags & self::FLAG_CONNECTED));
406 private function recv_end_of_burst($from, $args) {
407 if(($this->flags & self::FLAG_BURST_PENDING)) {
410 $this->flags &= ~self::FLAG_BURST_PENDING;
414 private function recv_end_of_burst_ack($from, $args) {
415 $this->flags |= self::FLAG_CONNECTED;
418 private function recv_server_quit($from, $args) {
419 $server = P10_Server::getServerByName($args[0]);
420 if($server == null) {
421 trigger_error("Server (".$args[0].") not found.", E_USER_ERROR);
424 $server->disconnectServer($this->eventHandler);
427 private function recv_quit($from, $args) {
428 $user = P10_User::getUserByNum($from);
430 trigger_error("Server tries to quit an user that does not exist or was not found on recv_quit.", E_USER_ERROR);
433 if($this->eventHandler)
434 $this->eventHandler->event_quit($user, $args[0]);
435 $user->quit($args[0]);
438 private function recv_burst($from, $args) {
440 $create_time = $args[1];
441 $channel = P10_Channel::getChannelByName($name);
443 $channel = new P10_Channel($name);
444 $channel->setCreateTime($create_time);
445 $modes = $channel->getModes();
446 $userstr = $args[count($args)-1];
447 $modeparamcount = count($args)-3;
448 if($userstr[0] == "%") {
450 $banlist = explode(" ", substr($userstr, 1));
451 foreach($banlist as $ban) {
454 $userstr = $args[count($args)-2];
457 if($userstr[0] == "+") { //MODE String
461 $users = explode(",",$userstr);
462 $isop = false; $isvoice = false;
463 foreach($users as $user) {
464 if($user == "") continue;
465 $uexp = explode(":", $user);
466 if(strlen($uexp[0]) != 5) {
467 trigger_error("burst parse error: '".$uexp[0]."' is not an user numeric.", E_USER_ERROR);
470 if(count($uexp) > 1) {
473 for($i = 0; $i < strlen($uexp[1]); $i++) {
474 if($uexp[1][0] == "@") $isop = true;
475 if($uexp[1][0] == "+") $isvoice = true;
478 $user = P10_User::getUserByNum($uexp[0]);
480 trigger_error("burst parse error: cant find User '".$uexp[0]."'.", E_USER_ERROR);
483 $channel->burstUser($user, $isop, $isvoice);
484 if($this->eventHandler)
485 $this->eventHandler->event_join($user, $channel, true);
487 $modes->parseModes(implode(" ", array_slice($args, 2, $modeparamcount)));
490 private function recv_join($from, $args) {
491 $user = P10_User::getUserByNum($from);
493 trigger_error("Server tries to join an user that does not exist or was not found on recv_join.", E_USER_ERROR);
496 $channel = P10_Channel::getChannelByName($args[0]);
498 $channel = new P10_Channel($args[0]);
499 $channel->joinUser($user);
500 if($this->eventHandler)
501 $this->eventHandler->event_join($user, $channel, false);
504 private function recv_part($from, $args) {
505 $user = P10_User::getUserByNum($from);
507 trigger_error("Server tries to part an user that does not exist or was not found on recv_join.", E_USER_ERROR);
510 $channel = P10_Channel::getChannelByName($args[0]);
512 $channel = new P10_Channel($args[0]);
513 if($this->eventHandler)
514 $this->eventHandler->event_part($user, $channel, $args[1]);
515 $channel->partUser($user);
518 private function recv_kick($from, $args) {
519 $user = P10_User::getUserByNum($from);
521 trigger_error("An unknown user tries to kick another user on recv_kick.", E_USER_ERROR);
524 $channel = P10_Channel::getChannelByName($args[0]);
526 $channel = new P10_Channel($args[0]);
527 $target = P10_User::getUserByNum($args[1]);
528 if($target == null) {
529 trigger_error("Someone tries to kick an user that does not exist or was not found on recv_kick.", E_USER_ERROR);
532 if($this->eventHandler)
533 $this->eventHandler->event_kick($user, $target, $channel, $args[1]);
534 $channel->partUser($user);
537 private function recv_kill($from, $args) {
538 $user = P10_User::getUserByNum($args[0]);
540 trigger_error("Server tries to kill an user that does not exist or was not found on recv_quit.", E_USER_ERROR);
543 if($this->eventHandler)
544 $this->eventHandler->event_quit($user, "Killed (".$args[1].")");
545 $user->quit($args[1]);
548 private function recv_privmsg($from, $args) {
549 $user = P10_User::getUserByNum($from);
551 trigger_error("Server tries to send a privmsg from an user that does not exist or was not found on recv_privmsg.", E_USER_ERROR);
554 if($this->eventHandler) {
555 if($args[0][0] == "#") {
556 $channel = P10_Channel::getChannelByName($args[0]);
558 $channel = new P10_Channel($args[0]);
559 if(strlen($args[1]) > 0 && $args[1][0] == "\001") {
561 $args[1] = substr($args[1],1);
562 if($args[1][strlen($args[1])-1] == "\001")
563 $args[1] = substr($args[1],0,-1);
564 $ctcpexp = explode(" ",$args[1],2);
565 $this->eventHandler->event_chanctcp($user, $channel, strtoupper($ctcpexp[0]), (count($ctcpexp) > 1 ? $ctcpexp[1] : null));
567 $this->eventHandler->event_chanmessage($user, $channel, $args[1]);
569 $targetUser = P10_User::getUserByNum($args[0]);
570 if($targetUser == null) {
571 trigger_error("Server tries to send a privmsg to an user that does not exist or was not found on recv_privmsg.", E_USER_ERROR);
574 if(strlen($args[1]) > 0 && $args[1][0] == "\001") {
576 $args[1] = substr($args[1],1);
577 if($args[1][strlen($args[1])-1] == "\001")
578 $args[1] = substr($args[1],0,-1);
579 $ctcpexp = explode(" ",$args[1],2);
580 $this->eventHandler->event_privctcp($user, $targetUser, strtoupper($ctcpexp[0]), (count($ctcpexp) > 1 ? $ctcpexp[1] : null));
582 $this->eventHandler->event_privmessage($user, $targetUser, $args[1]);
587 private function recv_notice($from, $args) {
588 $user = P10_User::getUserByNum($from);
590 trigger_error("Server tries to send a notice from an user that does not exist or was not found on recv_notice.", E_USER_ERROR);
593 if($this->eventHandler) {
594 if($args[0][0] == "#") {
595 $channel = P10_Channel::getChannelByName($args[0]);
597 $channel = new P10_Channel($args[0]);
598 if(strlen($args[1]) > 0 && $args[1][0] == "\001") {
600 $args[1] = substr($args[1],1);
601 if($args[1][strlen($args[1])-1] == "\001")
602 $args[1] = substr($args[1],0,-1);
603 $ctcpexp = explode(" ",$args[1],2);
604 $this->eventHandler->event_chanctcpreply($user, $channel, strtoupper($ctcpexp[0]), (count($ctcpexp) > 1 ? $ctcpexp[1] : null));
606 $this->eventHandler->event_channotice($user, $channel, $args[1]);
608 $targetUser = P10_User::getUserByNum($args[0]);
609 if($targetUser == null) {
610 trigger_error("Server tries to send a notice to an user that does not exist or was not found on recv_notice.", E_USER_ERROR);
613 if(strlen($args[1]) > 0 && $args[1][0] == "\001") {
615 $args[1] = substr($args[1],1);
616 if($args[1][strlen($args[1])-1] == "\001")
617 $args[1] = substr($args[1],0,-1);
618 $ctcpexp = explode(" ",$args[1],2);
619 $this->eventHandler->event_privctcpreply($user, $targetUser, strtoupper($ctcpexp[0]), (count($ctcpexp) > 1 ? $ctcpexp[1] : null));
621 $this->eventHandler->event_privnotice($user, $targetUser, $args[1]);
626 private function recv_whois($from, $args) {
627 /* [get] ACAAF W AX :NetworkServ */
628 $fromUser = P10_User::getUserByNum($from);
629 if($fromUser == null) {
630 trigger_error("Server tries to send a whois from an user that does not exist or was not found on recv_whois.", E_USER_ERROR);
633 $users=explode(",",$args[1]);
634 foreach($users as $nick) {
635 $user = P10_User::getUserByNick($nick);
637 $this->send("401", $from, $nick);
640 $nick = $user->getNick();
641 $ident = $user->getIdent();
642 $hostmask = $user->getHost();
643 $modes = $user->getModes();
644 if($modes->hasMode('x')) {
645 if(($fakehost = $modes->hasMode('f'))) {
646 $hostmask = $fakehost;
647 } elseif(($account = $modes->hasMode('r'))) {
648 $hostmask = $account.".".$this->getSetting("his_usermask");
651 $realname = $user->getRealname();
652 $this->send("311", $from , $nick, $ident, $hostmask, $realname);
653 if(((!$modes->hasMode('n') && !$modes->hasMode('k')) || $from == $user->getNumeric()) && count($user->getChannels()) != 0) {
655 foreach($user->getChannels() as $channel) {
656 $cmodes = $channel->getModes();
657 $privs = $channel->getUserPrivs($user);
658 if($cmodes->hasMode("s") && !$fromUser->isOnChannel($channel) && $from != $user->getNumeric()) continue;
659 if($cmodes->hasMode("u") && ($privs & (P10_Channel::USERPRIV_OPED | P10_Channel::USERPRIV_VOICE)) == 0 && $from != $user->getNumeric()) continue;
660 $chanstr = ($channels == "" ? "" : " ");
662 if(($privs & P10_Channel::USERPRIV_OPED)) {
664 } else if(($privs & P10_Channel::USERPRIV_VOICE)) {
667 $chanstr .= $prefix.$channel->getName();
668 if(strlen($channels) + strlen($chanstr) > 450) {
669 $this->send("319", $from, $nick, $channels);
670 $channels = $prefix.$channel->getName();
673 if($channels != "") {
674 $this->send("319", $from, $nick, $channels);
677 if($fromUser->getModes()->hasMode("o") || $from == $user->getNumeric() || !$this->getSetting("his_name")) {
678 $this->send("312", $from, $nick, $user->getServer()->getName(), $user->getServer()->getDescription());
680 $this->send("312", $from, $nick, $this->getSetting("his_name"), $this->getSetting("his_desc"));
682 if($modes->hasMode("o") && (!$modes->hasMode("H") || $fromUser->getModes()->hasMode("o"))) {
683 if($modes->hasMode("S")) {
684 if($modes->hasMode("D"))
685 $this->send("313", $from, $nick, "is a Network Service");
687 $this->send("313", $from, $nick, "is an illegal Network Service - HACKER!");
689 $this->send("313", $from, $nick, "is an IRC Operator");
691 if(($auth = $modes->hasMode("r"))) {
692 $this->send("330", $from, $nick, $auth);
695 $this->send("318", $from, $args[1]);
698 private function recv_away($from, $args) {
699 $user = P10_User::getUserByNum($from);
701 trigger_error("Server tries to send an away command from an user that does not exist or was not found on recv_away.", E_USER_ERROR);
704 if(count($args) > 0) {
705 $user->setAway($args[0]);
706 if($this->eventHandler)
707 $this->eventHandler->event_away($user, $args[0]);
709 $user->setAway(null);
710 if($this->eventHandler)
711 $this->eventHandler->event_away($user, null);
715 private function recv_mode($from, $args) {
716 $user = P10_User::getUserByNum($from);
718 trigger_error("Server tries to send a modechange from an user that does not exist or was not found on recv_mode.", E_USER_ERROR);
721 $modes = implode(" ",array_slice($args,1));
722 if($args[0][0] == "#") {
723 $channel = P10_Channel::getChannelByName($args[0]);
725 $channel = new P10_Channel($args[0]);
726 $channel->getModes()->setModes($modes);
727 if($this->eventHandler)
728 $this->eventHandler->event_chanmode($user, $channel, $modes);
730 $targetUser = P10_User::getUserByNick($args[0]);
731 if($targetUser == null) {
732 trigger_error("Server tries to send a mode to an user that does not exist or was not found on recv_mode.", E_USER_ERROR);
735 $targetUser->getModes()->setModes($modes);
736 if($this->eventHandler)
737 $this->eventHandler->event_usermode($targetUser, $modes);
741 /********************************************************************************************
743 ********************************************************************************************/
745 private function burst() {
746 foreach($this->server->getUsers() as $user) {
747 $nick = $user->getNick();
748 $connect_time = $user->getConnectTime();
749 $ident = $user->getIdent();
750 $host = $user->getHost();
751 $modes = $user->getModes()->getModeString();
752 $ip = $user->getIP()->getNumeric();
753 $numeric = $user->getNumeric();
754 $realname = $user->getRealname();
755 $this->send("N", $nick, $connect_time, $ident, $host, $modes, $ip, $numeric, $realname);
757 foreach(P10_Channel::getChannels() as $channel) {
758 $sorted_users = array('ov' => array(), 'o' => array(), 'v' => array(), '-' => array());
759 $local_users = false;
760 foreach($channel->getUsers() as $user) {
761 if(substr($user->getNumeric(), 0, 2) != $this->server->getNumeric()) continue; //skip users that are not on the local server
762 $privs = $channel->getUserPrivs($user);
764 if(($privs & P10_Channel::USERPRIV_OPED)) $strPrivs .= "o";
765 if(($privs & P10_Channel::USERPRIV_VOICE)) $strPrivs .= "v";
766 if($strPrivs == "") $strPrivs = "-";
768 $sorted_users[$strPrivs][] = $user;
770 if(!$local_users) continue;
772 foreach($sorted_users['-'] as $user) {
773 if($userStr != "") $userStr.=",";
774 $userStr .= $user->getNumeric();
776 foreach($sorted_users['ov'] as $i => $user) {
777 if($userStr != "") $userStr.=",";
778 $userStr .= $user->getNumeric();
779 if($i == 0) $userStr .= ":ov";
781 foreach($sorted_users['o'] as $i => $user) {
782 if($userStr != "") $userStr.=",";
783 $userStr .= $user->getNumeric();
784 if($i == 0) $userStr .= ":o";
786 foreach($sorted_users['v'] as $i => $user) {
787 if($userStr != "") $userStr.=",";
788 $userStr .= $user->getNumeric();
789 if($i == 0) $userStr .= ":v";
792 //TODO: Build ban String
794 $modeString = $channel->getModes()->getModeString();
795 if($modeString != "+") {
796 $burstString .= $modeString;
799 if($burstString != "") $burstString .= " ";
800 $burstString .= $userStr;
802 if($banString != "") {
803 if($burstString != "") $burstString .= " ";
804 $burstString .= ":%".$banString;
806 $this->send("B", $channel->getName(), $channel->getCreateTime(), $burstString);
811 /********************************************************************************************
812 * LOCAL USER FUNCTIONS *
813 ********************************************************************************************/
815 public function addUser($nick, $ident, $host, $ip, $modes, $realname) {
816 $user = P10_User::getUserByNick($nick);
817 if($user != null) return ERR_NICK_IN_USE;
818 $numeric = substr($this->server->getNumeric(),0,2).Numerics::intToNum($this->last_local_numeric, 3);
819 while(P10_User::getUserByNum($numeric)) {
820 $this->last_local_numeric++;
821 $numeric = substr($this->server->getNumeric(),0,2).Numerics::intToNum($this->last_local_numeric, 3);
823 $this->last_local_numeric++;
824 $modes = new P10_UserModeSet($modes);
825 $ip = new IPAddr($ip);
826 $user = new P10_User($nick, $numeric, $this->server, time(), $ident, $host, $ip, $realname, $modes);
827 if(($this->flags & self::FLAG_CONNECTED)) {
828 $ip = $user->getIP()->getNumeric();
829 $this->send("N", $nick, $user->getConnectTime(), $ident, $host, $user->getModes()->getModeString(), $ip, $numeric, $realname);
834 public function delUser($user, $reason) {
835 if(!is_a($user, "P10_User")) return ERR_INVALID_USER;
836 if($user->getServer() === $this->server) {
838 $user->quit($reason);
839 if(($this->flags & self::FLAG_CONNECTED))
840 $this->send("Q", $user->getNumeric(), $reason);
843 if(!($this->flags & self::FLAG_CONNECTED))
844 return ERR_NOT_CONNECTED;
845 if($this->eventHandler)
846 $this->eventHandler->event_quit($user, "Killed (".$reason.")");
847 $user->quit("Killed (".$reason.")");
848 $name = ($this->getSetting('his_name') ? $this->getSetting('his_name') : $this->getSetting('name'));
850 $this->send("D", $user->getNumeric(), $name, $reason);
854 public function join($user, $chanName, $privs = 0) {
855 if(!is_a($user, "P10_User") || !($user->getServer() === $this->server))
856 return ERR_INVALID_USER;
857 if($chanName[0] != "#")
858 return ERR_INVALID_CHANNAME;
859 $channel = P10_Channel::getChannelByName($chanName);
861 $channel = new P10_Channel($chanName);
862 $channel->joinUser($user);
863 if(($this->flags & self::FLAG_CONNECTED))
864 $this->send("J", $user->getNumeric(), $chanName, time(), 0);
866 $channel->setUserPrivs($user, $privs);
867 if(($this->flags & self::FLAG_CONNECTED)) {
868 $modestr = "+".(($privs & P10_Channel::USERPRIV_OPED) ? "o" : "").(($privs & P10_Channel::USERPRIV_VOICE) ? "v" : "");
869 $modestr .= (($privs & P10_Channel::USERPRIV_OPED) ? " ".$user->getNumeric() : "");
870 $modestr .= (($privs & P10_Channel::USERPRIV_VOICE) ? " ".$user->getNumeric() : "");
871 $this->send("OM", $user->getNumeric(), $chanName, $modestr);
874 if($this->eventHandler)
875 $this->eventHandler->event_join($user, $channel, false);
878 public function part($user, $chanName, $reason) {
879 if(!is_a($user, "P10_User") || !($user->getServer() === $this->server))
880 return ERR_INVALID_USER;
881 if(!((is_string($chanName) && $chanName[0] == "#") || is_a($chanName, "P10_Channel")))
882 return ERR_INVALID_CHANNAME;
883 if(is_a($chanName, "P10_Channel"))
884 $channel = $chanName;
886 $channel = P10_Channel::getChannelByName($chanName);
888 $channel = new P10_Channel($chanName);
889 if(!$user->isOnChannel($channel))
890 return ERR_NOT_ON_CHANNEL;
891 if($this->eventHandler)
892 $this->eventHandler->event_part($user, $channel, $reason);
893 $channel->partUser($user, $reason);
894 if(($this->flags & self::FLAG_CONNECTED))
895 $this->send("L", $user->getNumeric(), $chanName, $reason);
898 public function kick($user, $target, $chanName, $reason) {
899 if(!is_a($user, "P10_User") || !($user->getServer() === $this->server))
900 return ERR_INVALID_USER;
901 if(!is_a($target, "P10_User"))
902 return ERR_INVALID_USER;
903 if(!((is_string($chanName) && $chanName[0] == "#") || is_a($chanName, "P10_Channel")))
904 return ERR_INVALID_CHANNAME;
905 if(is_a($chanName, "P10_Channel"))
906 $channel = $chanName;
908 $channel = P10_Channel::getChannelByName($chanName);
910 $channel = new P10_Channel($chanName);
911 if(!$target->isOnChannel($channel))
912 return ERR_NOT_ON_CHANNEL;
913 if($this->eventHandler)
914 $this->eventHandler->event_kick($user, $target, $channel, $reason);
915 $channel->partUser($target, $reason);
916 if(($this->flags & self::FLAG_CONNECTED))
917 $this->send("K", $user->getNumeric(), $chanName, $target->getNumeric(), $reason);
920 public function privmsg($user, $target, $message) {
921 if(!is_a($user, "P10_User") || !($user->getServer() === $this->server))
922 return ERR_INVALID_USER;
923 if(!is_a($target, "P10_User") && !is_a($target, "P10_Channel") && !(is_string($target) && ($target[0] == "#" || P10_User::getUserByNick($target))))
924 return ERR_INVALID_USER;
925 if(is_a($target, "P10_Channel"))
926 $targetStr = $target->getName();
927 else if(is_a($target, "P10_User"))
928 $targetStr = $target->getNumeric();
929 else if($target[0] == "#")
930 $targetStr = $target;
932 $targetStr = P10_User::getUserByNick($target)->getNumeric();
934 if($this->eventHandler) {
935 if($targetStr[0] == "#") {
936 $channel = P10_Channel::getChannelByName($targetStr);
938 $channel = new P10_Channel($targetStr);
939 $this->eventHandler->event_chanmessage($user, $channel, $message);
941 $targetUser = P10_User::getUserByNum($targetStr);
942 $this->eventHandler->event_privmessage($user, $targetUser, $message);
945 if(($this->flags & self::FLAG_CONNECTED))
946 $this->send("P", $user->getNumeric(), $targetStr, $message);
949 public function notice($user, $target, $message) {
950 if(!is_a($user, "P10_User") || !($user->getServer() === $this->server))
951 return ERR_INVALID_USER;
952 if(!is_a($target, "P10_User") && !is_a($target, "P10_Channel") && !(is_string($target) && ($target[0] == "#" || P10_User::getUserByNick($target))))
953 return ERR_INVALID_USER;
954 if(is_a($target, "P10_Channel"))
955 $targetStr = $target->getName();
956 else if(is_a($target, "P10_User"))
957 $targetStr = $target->getNumeric();
958 else if($target[0] == "#")
959 $targetStr = $target;
961 $targetStr = P10_User::getUserByNick($target)->getNumeric();
963 if($this->eventHandler) {
964 if($targetStr[0] == "#") {
965 $channel = P10_Channel::getChannelByName($targetStr);
967 $channel = new P10_Channel($targetStr);
968 $this->eventHandler->event_channotice($user, $channel, $message);
970 $targetUser = P10_User::getUserByNum($targetStr);
971 $this->eventHandler->event_privnotice($user, $targetUser, $message);
974 if(($this->flags & self::FLAG_CONNECTED))
975 $this->send("O", $user->getNumeric(), $targetStr, $message);
978 public function mode($user, $target, $modes, $force = false) {
979 if(!is_a($user, "P10_User") || !($user->getServer() === $this->server))
980 return ERR_INVALID_USER;
981 if(!is_a($target, "P10_User") && !is_a($target, "P10_Channel") && !(is_string($target) && ($target[0] == "#" || P10_User::getUserByNick($target))))
982 return ERR_INVALID_USER;
983 if(is_a($target, "P10_Channel"))
984 $targetStr = $target->getName();
985 else if(is_a($target, "P10_User"))
986 $targetStr = $target->getNumeric();
987 else if($target[0] == "#")
988 $targetStr = $target;
990 $targetStr = P10_User::getUserByNick($target)->getNumeric();
992 if($targetStr[0] == "#") {
993 $channel = P10_Channel::getChannelByName($targetStr);
995 $channel = new P10_Channel($targetStr);
996 $modes = $channel->getModes()->setModes($modes, true);
997 if(($this->flags & self::FLAG_CONNECTED))
998 $this->send(($force ? "OM" : "M"), $user->getNumeric(), $targetStr, $modes);
999 if($this->eventHandler)
1000 $this->eventHandler->event_chanmode(($force ? $this->server : $user), $channel, $modes);
1002 $targetUser = P10_User::getUserByNum($targetStr);
1003 if($targetUser->getServer() === $this->server) {
1005 $modes = $targetUser->getModes()->setModes($modes, true);
1006 if(($this->flags & self::FLAG_CONNECTED))
1007 $this->send("M", $targetUser->getNumeric(), $targetUser->getNick(), $modes);
1008 if($this->eventHandler)
1009 $this->eventHandler->event_usermode($targetUser, $modes);
1012 if(($this->flags & self::FLAG_CONNECTED))
1013 $this->send("SM", $user->getNumeric(), $targetUser->getNumeric(), $modes);