Source for file SmartIRC.php
Documentation is available at SmartIRC.php
* $Id: SmartIRC.php 253473 2008-02-21 18:58:34Z amir $
* $Date: 2008-02-22 05:28:34 +1030 (Fri, 22 Feb 2008) $
* This is a PHP class for communication with IRC networks,
* which conforms to the RFC 2812 (IRC protocol).
* It's an API that handles all IRC protocol messages.
* This class is designed for creating IRC bots, chats and show irc related info on webpages.
* Documenation, a HOWTO and examples are in SmartIRC included.
* Here you will find a service bot which I am also developing
* <http://cvs.meebey.net/atbs> and <http://cvs.meebey.net/phpbitch>
* Latest versions of Net_SmartIRC you will find on the project homepage
* or get it through PEAR since SmartIRC is an official PEAR package.
* See <http://pear.php.net/Net_SmartIRC>.
* Official Project Homepage: <http://sf.net/projects/phpsmartirc>
* Net_SmartIRC conforms to RFC 2812 (Internet Relay Chat: Client Protocol)
* Copyright (c) 2002-2005 Mirco Bauer <meebey@meebey.net> <http://www.meebey.net>
* Full LGPL License: <http://www.gnu.org/licenses/lgpl.txt>
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// ------- PHP code ----------
require_once 'SmartIRC/defines.php';
define('SMARTIRC_VERSION', '1.1.0-dev ($Revision: 253473 $)');
define('SMARTIRC_VERSIONSTRING', 'Net_SmartIRC '.SMARTIRC_VERSION );
* @author Mirco 'meebey' Bauer <mail@meebey.net>
var $_actionhandler = array ();
var $_timehandler = array ();
var $_debug = SMARTIRC_DEBUG_NOTICE;
var $_messagebuffer = array ();
var $_usesockets = false;
var $_receivedelay = 100;
var $_logdestination = SMARTIRC_STDOUT;
var $_logfile = 'Net_SmartIRC.log';
var $_disconnecttime = 1000;
var $_benchmark_starttime;
var $_benchmark_stoptime;
var $_actionhandlerid = 0;
var $_channels = array ();
var $_channelsyncing = false;
var $_usersyncing = false;
* Stores the path to the modules that can be loaded.
* Stores all objects of the modules.
var $_autoreconnect = false;
var $_reconnectdelay = 10000;
var $_autoretrycount = 0;
var $_connectionerror = false;
var $_runasdaemon = false;
* All IRC replycodes, the index is the replycode name.
* @see $SMARTIRC_replycodes
* All numeric IRC replycodes, the index is the numeric replycode.
* @see $SMARTIRC_nreplycodes
* Stores all channels in this array where we are joined, works only if channelsyncing is activated.
* Eg. for accessing a user, use it like this: (in this example the SmartIRC object is stored in $irc)
* $irc->channel['#test']->users['meebey']->nick;
* @see setChannelSyncing()
* @see Net_SmartIRC_channel
* @see Net_SmartIRC_channeluser
* Stores all users that had/have contact with us (channel/query/notice etc.), works only if usersyncing is activated.
* Eg. for accessing a user, use it like this: (in this example the SmartIRC object is stored in $irc)
* $irc->user['meebey']->host;
* @see Net_SmartIRC_ircuser
* Constructor. Initiales the messagebuffer and "links" the replycodes from
* global into properties. Also some PHP runtime settings are configured.
$this->_checkPHPVersion ();
$this->replycodes = &$GLOBALS['SMARTIRC_replycodes'];
$this->nreplycodes = &$GLOBALS['SMARTIRC_nreplycodes'];
// hack till PHP allows (PHP5) $object->somemethod($param)->memberofobject
$this->channel = &$this->_channels;
$this->user = &$this->_users;
if (isset ($_SERVER['REQUEST_METHOD'])) {
// the script is called from a browser, lets set default log destination
// to SMARTIRC_BROWSEROUT (makes browser friendly output)
* Enables/disables the usage of real sockets.
* Enables/disables the usage of real sockets instead of fsocks
* (works only if your PHP build has loaded the PHP socket extension)
$this->_usesockets = true;
$load_status = @dl('php_sockets.dll');
$load_status = @dl('sockets.so');
$this->_usesockets = true;
$this->log(SMARTIRC_DEBUG_NOTICE, 'WARNING: your PHP build doesn\'t support real sockets, will use fsocks instead', __FILE__ , __LINE__ );
$this->_usesockets = false;
$this->_usesockets = false;
* Sets the level of debug messages.
* Sets the debug level (bitwise), useful for testing/developing your code.
* Here the list of all possible debug levels:
* SMARTIRC_DEBUG_CONNECTION
* SMARTIRC_DEBUG_IRCMESSAGES
* SMARTIRC_DEBUG_MESSAGETYPES
* SMARTIRC_DEBUG_ACTIONHANDLER
* SMARTIRC_DEBUG_TIMEHANDLER
* SMARTIRC_DEBUG_MESSAGEHANDLER
* SMARTIRC_DEBUG_CHANNELSYNCING
* SMARTIRC_DEBUG_USERSYNCING
* Default: SMARTIRC_DEBUG_NOTICE
* @see SMARTIRC_DEBUG_NOTICE
* Enables/disables the benchmark engine.
* @param boolean $boolean
$this->_benchmark = $boolean;
$this->_benchmark = false;
* Deprecated, use setChannelSyncing() instead!
* @param boolean $boolean
$this->log(SMARTIRC_DEBUG_NOTICE, 'WARNING: you are using setChannelSynching() which is a deprecated method, use setChannelSyncing() instead!', __FILE__ , __LINE__ );
* Enables/disables channel syncing.
* Channel syncing means, all users on all channel we are joined are tracked in the
* channel array. This makes it very handy for botcoding.
* @param boolean $boolean
$this->_channelsyncing = $boolean;
$this->_channelsyncing = false;
if ($this->_channelsyncing == true ) {
* Enables/disables user syncing.
* User syncing means, all users we have or had contact with through channel, query or
* notice are tracked in the $irc->user array. This is very handy for botcoding.
* @param boolean $boolean
$this->_usersyncing = $boolean;
$this->_usersyncing = false;
if ($this->_usersyncing == true ) {
* Sets the CTCP version reply string.
* @param string $versionstring
$this->_ctcpversion = $versionstring;
* Sets the destination of all log messages.
* Sets the destination of log messages.
* SMARTIRC_FILE for saving the log into a file
* SMARTIRC_STDOUT for echoing the log to stdout
* SMARTIRC_SYSLOG for sending the log to the syslog
* Default: SMARTIRC_STDOUT
* @param integer $type must be on of the constants
$this->_logdestination = $type;
$this->log(SMARTIRC_DEBUG_NOTICE, 'WARNING: unknown logdestination type ('. $type. '), will use STDOUT instead', __FILE__ , __LINE__ );
* Sets the file for the log if the destination is set to file.
* Sets the logfile, if {@link setLogdestination logdestination} is set to SMARTIRC_FILE.
* This should be only used with full path!
* Sets the delaytime before closing the socket when disconnect.
* @param integer $milliseconds
if (is_integer($milliseconds) && $milliseconds >= 100 ) {
$this->_disconnecttime = $milliseconds;
$this->_disconnecttime = 100;
* Sets the delaytime before attempting reconnect.
* Value of 0 disables the delay entirely.
* @param integer $milliseconds
$this->_reconnectdelay = $milliseconds;
$this->_reconnectdelay = 10000;
* Sets the delay for receiving data from the IRC server.
* Sets the delaytime between messages that are received, this reduces your CPU load.
* Don't set this too low (min 100ms).
* @param integer $milliseconds
if (is_integer($milliseconds) && $milliseconds >= 100 ) {
$this->_receivedelay = $milliseconds;
$this->_receivedelay = 100;
* Sets the delay for sending data to the IRC server.
* Sets the delaytime between messages that are sent, because IRC servers doesn't like floods.
* This will avoid sending your messages too fast to the IRC server.
* @param integer $milliseconds
$this->_senddelay = $milliseconds;
* Enables/disables autoreconnecting.
* @param boolean $boolean
$this->_autoreconnect = $boolean;
$this->_autoreconnect = false;
* Enables/disables autoretry for connecting to a server.
* @param boolean $boolean
$this->_autoretry = $boolean;
$this->_autoretry = false;
* Sets the maximum number of attempts to connect to a server
* @param integer $autoretrymax
$this->_autoretrymax = $autoretrymax;
$this->_autoretrymax = 5;
* Sets the receive timeout.
* If the timeout occurs, the connection will be reinitialized
* @param integer $seconds
$this->_rxtimeout = $seconds;
* Sets the transmit timeout.
* If the timeout occurs, the connection will be reinitialized
* @param integer $seconds
$this->_txtimeout = $seconds;
* Sets the paths for the modules.
$this->_modulepath = $path;
* Sets wheter the script should be run as a daemon or not
* ( actually disables/enables ignore_user_abort() )
* @param boolean $boolean
$this->_runasdaemon = true;
$this->_runasdaemon = false;
* Starts the benchmark (sets the counters).
$this->_benchmark_starttime = $this->_microint ();
* Stops the benchmark and displays the result.
$this->_benchmark_stoptime = $this->_microint ();
* Shows the benchmark result.
$this->log(SMARTIRC_DEBUG_NOTICE, 'benchmark time: '. ((float) $this->_benchmark_stoptime-(float) $this->_benchmark_starttime), __FILE__ , __LINE__ );
* Adds an entry to the log.
* Adds an entry to the log with Linux style log format.
* Possible $level constants (can also be combined with "|"s)
* SMARTIRC_DEBUG_CONNECTION
* SMARTIRC_DEBUG_IRCMESSAGES
* SMARTIRC_DEBUG_MESSAGETYPES
* SMARTIRC_DEBUG_ACTIONHANDLER
* SMARTIRC_DEBUG_TIMEHANDLER
* SMARTIRC_DEBUG_MESSAGEHANDLER
* SMARTIRC_DEBUG_CHANNELSYNCING
* SMARTIRC_DEBUG_USERSYNCING
* @see SMARTIRC_DEBUG_NOTICE
* @param integer $level bit constants (SMARTIRC_DEBUG_*)
* @param string $entry the new log entry
function log($level, $entry, $file = null , $line = null )
if (!($level & $this->_debug) ||
if (substr($entry, -1 ) != "\n") {
$entry = $file. '('. $line. ') '. $entry;
$entry = 'unknown(0) '. $entry;
$formatedentry = date('M d H:i:s '). $entry;
switch ($this->_logdestination) {
if ($this->_logfilefp === null ) {
// we reconncted and don't want to destroy the old log entries
$this->_logfilefp = fopen($this->_logfile,'a');
$this->_logfilefp = fopen($this->_logfile,'w');
fwrite($this->_logfilefp, $formatedentry);
if (!is_int($this->_logfilefp)) {
$this->_logfilefp = openlog('Net_SmartIRC', LOG_NDELAY , LOG_DAEMON );
* Returns a reference to the channel object of the specified channelname.
* @param string $channelname
if ($this->_channelsyncing != true ) {
$this->log(SMARTIRC_DEBUG_NOTICE, 'WARNING: getChannel() is called and the required Channel Syncing is not activated!', __FILE__ , __LINE__ );
return $this->_channels[strtolower($channelname)];
* Returns a reference to the user object for the specified username and channelname.
* @param string $channelname
* @param string $username
function &getUser($channelname, $username)
if ($this->_channelsyncing != true ) {
$this->log(SMARTIRC_DEBUG_NOTICE, 'WARNING: getUser() is called and the required Channel Syncing is not activated!', __FILE__ , __LINE__ );
if ($this->isJoined($channelname, $username)) {
* Creates the sockets and connects to the IRC server on the given port.
$this->_address = $address;
if ($this->_usesockets == true ) {
$this->_socket = socket_create (AF_INET , SOCK_STREAM , SOL_TCP );
$result = socket_connect ($this->_socket, $this->_address, $this->_port);
$result = fsockopen($this->_address, $this->_port, $errno, $errstr);
if ($this->_usesockets == true ) {
$error = socket_strerror (socket_last_error ($this->_socket));
$error = $errstr. ' ('. $errno. ')';
$error_msg = 'couldn\'t connect to "'. $address. '" reason: "'. $error. '"';
// TODO! needs to be return value
if (($this->_autoretry == true ) &&
($this->_autoretrycount < $this->_autoretrymax)) {
$this->_delayReconnect ();
$this->_autoretrycount++;
$this->_autoretrycount = 0;
$this->_connectionerror = false;
if ($this->_usesockets != true ) {
$this->_socket = $result;
$this->_lasttx = $this->_lastrx;
return $result !== false;
* Disconnects from the IRC server nicely with a QUIT or just destroys the socket.
* Disconnects from the IRC server in the given quickness mode.
* true, just close the socket
* false, send QUIT and wait {@link $_disconnectime $_disconnectime} before closing the socket
* @param boolean $quickdisconnect default: false
if ($quickdisconnect == false ) {
usleep($this->_disconnecttime*1000 );
if ($this->_usesockets == true ) {
@socket_shutdown ($this->_socket);
@socket_close ($this->_socket);
if ($this->_channelsyncing == true ) {
// let's clean our channel array
$this->_channels = array ();
if ($this->_usersyncing == true ) {
// let's clean our user array
$this->_logfilefp = null;
* Reconnects to the IRC server with the same login info,
* it also rejoins the channels
// remember in which channels we are joined
foreach ($this->_channels as $value) {
if (empty ($value->key )) {
$channels[] = array ('name' => $value->name );
$channels[] = array ('name' => $value->name , 'key' => $value->key );
$this->connect($this->_address, $this->_port);
$this->login($this->_nick, $this->_realname, $this->_usermode, $this->_username, $this->_password);
foreach ($channels as $value) {
if (isset ($value['key'])) {
$this->join ($value['name'], $value['key']);
$this->join ($value['name']);
* login and register nickname on the IRC network
* Registers the nickname and user information on the IRC network.
* @param string $realname
* @param integer $usermode
* @param string $username
* @param string $password
function login($nick, $realname, $usermode = 0 , $username = null , $password = null )
$this->_realname = $realname;
if ($username !== null ) {
if ($password !== null ) {
$this->_password = $password;
$this->log(SMARTIRC_DEBUG_NOTICE, 'DEBUG_NOTICE: login() usermode ('. $usermode. ') is not valid, will use 0 instead', __FILE__ , __LINE__ );
* checks if the passed nickname is our own nickname
* @param string $nickname
if ($nickname == $this->_nick) {
* checks if we or the given user is joined to the specified channel and returns the result
* ChannelSyncing is required for this.
* @param string $nickname
function isJoined($channel, $nickname = null )
if ($this->_channelsyncing != true ) {
$this->log(SMARTIRC_DEBUG_NOTICE, 'WARNING: isJoined() is called and the required Channel Syncing is not activated!', __FILE__ , __LINE__ );
if ($nickname === null ) {
$nickname = $this->_nick;
* Checks if we or the given user is opped on the specified channel and returns the result.
* ChannelSyncing is required for this.
* @param string $nickname
function isOpped($channel, $nickname = null )
if ($this->_channelsyncing != true ) {
$this->log(SMARTIRC_DEBUG_NOTICE, 'WARNING: isOpped() is called and the required Channel Syncing is not activated!', __FILE__ , __LINE__ );
if ($nickname === null ) {
$nickname = $this->_nick;
if ($this->isJoined($channel, $nickname)) {
* Checks if we or the given user is voiced on the specified channel and returns the result.
* ChannelSyncing is required for this.
* @param string $nickname
function isVoiced($channel, $nickname = null )
if ($this->_channelsyncing != true ) {
$this->log(SMARTIRC_DEBUG_NOTICE, 'WARNING: isVoiced() is called and the required Channel Syncing is not activated!', __FILE__ , __LINE__ );
if ($nickname === null ) {
$nickname = $this->_nick;
if ($this->isJoined($channel, $nickname)) {
* Checks if the hostmask is on the specified channel banned and returns the result.
* ChannelSyncing is required for this.
* @param string $hostmask
if ($this->_channelsyncing != true ) {
$this->log(SMARTIRC_DEBUG_NOTICE, 'WARNING: isBanned() is called and the required Channel Syncing is not activated!', __FILE__ , __LINE__ );
* Goes into receive and idle mode. Only call this if you want to "spawn" the bot.
* No further lines of PHP code will be processed after this call, only the bot methods!
* goes into receive mode _only_ for one pass
* Goes into receive mode. It will return when one pass is complete.
* Use this when you want to connect to multiple IRC servers.
if ($this->_connectionerror) {
if ($this->_autoreconnect) {
* waits for a special message type and puts the answer in $result
* Creates a special actionhandler for that given TYPE and returns the answer.
* This will only receive the requested type, immediately quit and disconnect from the IRC server.
* Made for showing IRC statistics on your homepage, or other IRC related information.
* @param integer $messagetype see in the documentation 'Message Types'
* @return array answer from the IRC server for this $messagetype
$listenfor = &new Net_SmartIRC_listenfor ();
$result = $listenfor->result;
* registers a new actionhandler and returns the assigned id
* Registers an actionhandler in Net_SmartIRC for calling it later.
* The actionhandler id is needed for unregistering the actionhandler.
* @param integer $handlertype bits constants, see in this documentation Message Types
* @param string $regexhandler the message that has to be in the IRC message in regex syntax
* @param object $object a reference to the objects of the method
* @param string $methodname the methodname that will be called when the handler happens
* @return integer assigned actionhandler id
if (!$this->_isValidType ($handlertype)) {
$this->log(SMARTIRC_DEBUG_NOTICE, 'WARNING: passed invalid handlertype to registerActionhandler()', __FILE__ , __LINE__ );
$id = $this->_actionhandlerid++;
$newactionhandler = &new Net_SmartIRC_actionhandler ();
$newactionhandler->id = $id;
$newactionhandler->type = $handlertype;
$newactionhandler->message = $regexhandler;
$newactionhandler->object = &$object;
$newactionhandler->method = $methodname;
$this->_actionhandler[] = &$newactionhandler;
* unregisters an existing actionhandler
* @param integer $handlertype
* @param string $regexhandler
* @param string $methodname
if (!$this->_isValidType ($handlertype)) {
$this->log(SMARTIRC_DEBUG_NOTICE, 'WARNING: passed invalid handlertype to unregisterActionhandler()', __FILE__ , __LINE__ );
$handler = &$this->_actionhandler;
$handlercount = count($handler);
for ($i = 0; $i < $handlercount; $i++ ) {
$handlerobject = &$handler[$i];
if ($handlerobject->type == $handlertype &&
$handlerobject->message == $regexhandler &&
$handlerobject->method == $methodname) {
$id = $handlerobject->id;
if (isset ($this->_actionhandler[$i])) {
unset ($this->_actionhandler[$i]);
$this->_reorderactionhandler ();
$this->log(SMARTIRC_DEBUG_ACTIONHANDLER, 'DEBUG_ACTIONHANDLER: could not find actionhandler type: "'. $handlertype. '" message: "'. $regexhandler. '" method: "'. $methodname. '" from object "'. get_class($object). '" _not_ unregistered', __FILE__ , __LINE__ );
* unregisters an existing actionhandler via the id
$handler = &$this->_actionhandler;
$handlercount = count($handler);
for ($i = 0; $i < $handlercount; $i++ ) {
$handlerobject = &$handler[$i];
if ($handlerobject->id == $id) {
if (isset ($this->_actionhandler[$i])) {
unset ($this->_actionhandler[$i]);
$this->_reorderactionhandler ();
* registers a timehandler and returns the assigned id
* Registers a timehandler in Net_SmartIRC, which will be called in the specified interval.
* The timehandler id is needed for unregistering the timehandler.
* @param integer $interval interval time in milliseconds
* @param object $object a reference to the objects of the method
* @param string $methodname the methodname that will be called when the handler happens
* @return integer assigned timehandler id
$id = $this->_timehandlerid++;
$newtimehandler = &new Net_SmartIRC_timehandler ();
$newtimehandler->id = $id;
$newtimehandler->interval = $interval;
$newtimehandler->object = &$object;
$newtimehandler->method = $methodname;
$newtimehandler->lastmicrotimestamp = $this->_microint ();
$this->_timehandler[] = &$newtimehandler;
if (($interval < $this->_mintimer) || ($this->_mintimer == false )) {
$this->_mintimer = $interval;
* unregisters an existing timehandler via the id
$handler = &$this->_timehandler;
$handlercount = count($handler);
for ($i = 0; $i < $handlercount; $i++ ) {
$handlerobject = &$handler[$i];
if ($handlerobject->id == $id) {
if (isset ($this->_timehandler[$i])) {
unset ($this->_timehandler[$i]);
$this->_reordertimehandler ();
$this->_updatemintimer ();
// is the module already loaded?
$filename = $this->_modulepath. '/'. $name. '.php';
$this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: couldn\'t load module "'. $filename. '" file doesn\'t exist', __FILE__ , __LINE__ );
// pray that there is no parse error, it will kill us!
$classname = 'Net_SmartIRC_module_'. $name;
if (!in_array('module_init', $methods)) {
$this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: required method'. $classname. '::module_init not found, aborting...', __FILE__ , __LINE__ );
if (!in_array('module_exit', $methods)) {
$this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: required method'. $classname. '::module_exit not found, aborting...', __FILE__ , __LINE__ );
$this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: required variable '. $classname. '::name not found, aborting...', __FILE__ , __LINE__ );
$this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: required variable '. $classname. '::description not found, aborting...', __FILE__ , __LINE__ );
$this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: required variable '. $classname. '::author not found, aborting...', __FILE__ , __LINE__ );
$this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: required variable '. $classname. '::license not found, aborting...', __FILE__ , __LINE__ );
// looks like the module satisfies us
$module = &new $classname;
$module->module_init ($this);
$this->_modules[$name] = &$module;
$modulecount = count($modules_keys);
for ($i = 0; $i < $modulecount; $i++ ) {
$module = &$this->_modules[$modules_keys[$i]];
if ($modulename == 'net_smartirc_module_'. $name) {
$module->module_exit ($this);
unset ($this->_modules[$i]);
$this->_reordermodules ();
$this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: couldn\'t unloaded module: '. $name. ' (it\'s not loaded!)', __FILE__ , __LINE__ );
* changes a already used nickname to a new nickname plus 3 random digits
function _nicknameinuse ()
$newnickname = substr($this->_nick, 0 , 5 ). rand(0 , 999 );
* Adds a message to the messagequeue, with the optional priority.
* @param integer $priority must be one of the priority constants
function send($data, $priority = SMARTIRC_MEDIUM )
return $this->_send ($data, $priority);
* Adds a message to the messagequeue, with the optional priority.
* @param integer $priority must be one of the priority constants
function _send ($data, $priority = SMARTIRC_MEDIUM )
$this->_messagebuffer[$priority][] = $data;
$this->log(SMARTIRC_DEBUG_NOTICE, 'WARNING: message ('. $data. ') with an invalid priority passed ('. $priority. '), message is ignored!', __FILE__ , __LINE__ );
* checks the buffer if there are messages to send
static $lastmicrotimestamp = 0;
if ($lastmicrotimestamp == 0 ) {
$lastmicrotimestamp = $this->_microint ();
$highcount = count ($this->_messagebuffer [SMARTIRC_HIGH ]);
$mediumcount = count ($this->_messagebuffer [SMARTIRC_MEDIUM ]);
$lowcount = count ($this->_messagebuffer [SMARTIRC_LOW ]);
$this->_messagebuffersize = $highcount+ $mediumcount+ $lowcount;
// don't send them too fast
if ($this->_microint () >= ($lastmicrotimestamp+ ($this->_senddelay/1000 ))) {
if ($highcount > 0 && $highsent <= 2 ) {
$this->_rawsend (array_shift ($this->_messagebuffer [SMARTIRC_HIGH ]));
$lastmicrotimestamp = $this->_microint ();
} else if ($mediumcount > 0 ) {
$this->_rawsend (array_shift ($this->_messagebuffer [SMARTIRC_MEDIUM ]));
$lastmicrotimestamp = $this->_microint ();
} else if ($lowcount > 0 ) {
$lastmicrotimestamp = $this->_microint ();
* Checks the running timers and calls the registered timehandler,
* when the interval is reached.
// has to be count() because the array may change during the loop!
for ($i = 0; $i < count($this->_timehandler ); $i++ ) {
$handlerobject = &$this->_timehandler [$i];
$microtimestamp = $this->_microint ();
if ($microtimestamp >= ($handlerobject->lastmicrotimestamp+ ($handlerobject->interval/1000 ))) {
$methodobject = &$handlerobject->object;
$method = $handlerobject->method;
$handlerobject->lastmicrotimestamp = $microtimestamp;
$methodobject->$method($this);
* Checks if a receive or transmit timeout occured and reconnects if configured
if ($this->_autoreconnect == true ) {
if ($this->_lastrx < ($timestamp - $this->_rxtimeout )) {
$this->_delayReconnect ();
} else if ($this->_lasttx < ($timestamp - $this->_txtimeout )) {
$this->_delayReconnect ();
* sends a raw message to the IRC server (don't use this!!)
* Use message() or send() instead.
if ($this->_usesockets == true ) {
// writing to the socket failed, means the connection is broken
$this->_connectionerror = true;
* goes into main receive mode _once_ per call and waits for messages from the IRC server
$timeout = $this->_selecttimeout ();
if ($this->_usesockets == true ) {
$sread = array ($this->_socket );
$result = socket_select ($sread, $w = null , $e = null , 0 , $timeout*1000 );
// the socket got data to read
$rawdata = socket_read ($this->_socket , 10240 );
} else if ($result === false ) {
// panic! panic! something went wrong!
$this->log(SMARTIRC_DEBUG_NOTICE, 'WARNING: socket_select() returned false, something went wrong! Reason: '.socket_strerror (socket_last_error ()), __FILE__ , __LINE__ );
usleep($this->_receivedelay*1000 );
$rawdata = fread($this->_socket , 10240 );
if ($rawdata === false ) {
// reading from the socket failed, the connection is broken
$this->_connectionerror = true;
if ($rawdata !== null && !empty ($rawdata)) {
$rawdata = $lastpart. $rawdata;
$rawdataar = explode("\n", $rawdata);
// loop through our received messages
while (count($rawdataar) > 0 ) {
// building our data packet
$ircdata = &new Net_SmartIRC_data ();
$ircdata->rawmessage = $rawline;
$ircdata->rawmessageex = $lineex;
$messagecode = $lineex[0 ];
if (substr($rawline, 0 , 1 ) == ':') {
$messagecode = $lineex[1 ];
$exclamationpos = strpos($from, '!');
$colonpos = strpos($line, ' :');
if ($colonpos !== false ) {
// we want the exact position of ":" not beginning from the space
$ircdata->nick = substr($from, 0 , $exclamationpos);
$ircdata->ident = substr($from, $exclamationpos+1 , ($atpos- $exclamationpos)-1 );
$ircdata->host = substr($from, $atpos+1 );
$ircdata->type = $this->_gettype ($rawline);
if ($colonpos !== false ) {
$ircdata->message = substr($line, $colonpos+1 );
$ircdata->messageex = explode(' ', $ircdata->message );
'" ident: "'. $ircdata->ident.
'" host: "'. $ircdata->host.
'" type: "'. $ircdata->type.
'" from: "'. $ircdata->from.
'" message: "'. $ircdata->message.
'"', __FILE__ , __LINE__ );
// lets see if we have a messagehandler for it
$this->_handlemessage ($messagecode, $ircdata);
if ($validmessage == true ) {
// now the actionhandlers are comming
$this->_handleactionhandler ($ircdata);
* sends the pong for keeping alive
* Sends the PONG signal as reply of the PING from the IRC server.
* returns the calculated selecttimeout value
* @return integer selecttimeout in microseconds
function _selecttimeout ()
if ($this->_messagebuffersize == 0 ) {
$this->_selecttimeout = null;
if ($this->_mintimer != false ) {
$this->_calculateselecttimeout ($this->_mintimer );
if ($this->_autoreconnect == true ) {
$this->_calculateselecttimeout ($this->_rxtimeout*1000 );
$this->_calculateselecttimeout ($this->_maxtimer );
return $this->_selecttimeout;
return $this->_senddelay;
* calculates the selecttimeout value
function _calculateselecttimeout ($microseconds)
if (($this->_selecttimeout > $microseconds) || $this->_selecttimeout === null ) {
$this->_selecttimeout = $microseconds;
* updates _mintimer to the smallest timer interval
function _updatemintimer ()
foreach ($this->_timehandler as $values) {
$timerarray[] = $values->interval;
if ($result == true && isset ($timerarray[0 ])) {
$this->_mintimer = $timerarray[0 ];
$this->_mintimer = false;
* reorders the actionhandler array, needed after removing one
function _reorderactionhandler ()
$orderedactionhandler = array ();
foreach ($this->_actionhandler as $value) {
$orderedactionhandler[] = $value;
$this->_actionhandler = &$orderedactionhandler;
* reorders the timehandler array, needed after removing one
function _reordertimehandler ()
$orderedtimehandler = array ();
foreach ($this->_timehandler as $value) {
$orderedtimehandler[] = $value;
$this->_timehandler = &$orderedtimehandler;
* reorders the modules array, needed after removing one
function _reordermodules ()
$orderedmodules = array ();
foreach ($this->_modules as $value) {
$orderedmodules[] = $value;
$this->_modules = &$orderedmodules;
* determines the messagetype of $line
* Analyses the type of an IRC message and returns the type.
* @return integer SMARTIRC_TYPE_* constant
if (preg_match('/^:[^ ]+? [0-9]{3} .+$/', $line) == 1 ) {
case SMARTIRC_RPL_WELCOME:
case SMARTIRC_RPL_YOURHOST:
case SMARTIRC_RPL_CREATED:
case SMARTIRC_RPL_MYINFO:
case SMARTIRC_RPL_BOUNCE:
case SMARTIRC_RPL_LUSERCLIENT:
case SMARTIRC_RPL_LUSEROP:
case SMARTIRC_RPL_LUSERUNKNOWN:
case SMARTIRC_RPL_LUSERME:
case SMARTIRC_RPL_LUSERCHANNELS:
case SMARTIRC_RPL_MOTDSTART:
case SMARTIRC_RPL_ENDOFMOTD:
case SMARTIRC_RPL_NAMREPLY:
case SMARTIRC_RPL_ENDOFNAMES:
case SMARTIRC_RPL_WHOREPLY:
case SMARTIRC_RPL_ENDOFWHO:
case SMARTIRC_RPL_LISTSTART:
case SMARTIRC_RPL_LISTEND:
case SMARTIRC_RPL_BANLIST:
case SMARTIRC_RPL_ENDOFBANLIST:
case SMARTIRC_RPL_WHOISUSER:
case SMARTIRC_RPL_WHOISSERVER:
case SMARTIRC_RPL_WHOISOPERATOR:
case SMARTIRC_RPL_WHOISIDLE:
case SMARTIRC_RPL_ENDOFWHOIS:
case SMARTIRC_RPL_WHOISCHANNELS:
case SMARTIRC_RPL_WHOWASUSER:
case SMARTIRC_RPL_ENDOFWHOWAS:
case SMARTIRC_RPL_UMODEIS:
case SMARTIRC_RPL_CHANNELMODEIS:
case SMARTIRC_ERR_NICKNAMEINUSE:
case SMARTIRC_ERR_NOTREGISTERED:
if (preg_match('/^:.*? PRIVMSG .* :'. chr(1 ). 'ACTION .*'. chr(1 ). '$/', $line) == 1 ) {
} else if (preg_match('/^:.*? PRIVMSG .* :'. chr(1 ). '.*'. chr(1 ). '$/', $line) == 1 ) {
} else if (preg_match('/^:.*? NOTICE .* :'. chr(1 ). '.*'. chr(1 ). '$/', $line) == 1 ) {
} else if (preg_match('/^:.*? PRIVMSG (\&|\#|\+|\!).* :.*$/', $line) == 1 ) {
} else if (preg_match('/^:.*? PRIVMSG .*:.*$/', $line) == 1 ) {
} else if (preg_match('/^:.*? NOTICE .* :.*$/', $line) == 1 ) {
} else if (preg_match('/^:.*? INVITE .* .*$/', $line) == 1 ) {
} else if (preg_match('/^:.*? JOIN .*$/', $line) == 1 ) {
} else if (preg_match('/^:.*? TOPIC .* :.*$/', $line) == 1 ) {
} else if (preg_match('/^:.*? NICK .*$/', $line) == 1 ) {
} else if (preg_match('/^:.*? KICK .* .*$/', $line) == 1 ) {
} else if (preg_match('/^:.*? PART .*$/', $line) == 1 ) {
} else if (preg_match('/^:.*? MODE .* .*$/', $line) == 1 ) {
} else if (preg_match('/^:.*? QUIT :.*$/', $line) == 1 ) {
* updates the current connection state
if (($this->_socket !== false ) &&
($rtype == 'socket' || $rtype == 'Socket' || $rtype == 'stream')) {
$this->_loggedin = false;
* returns the current connection state
* @return integer SMARTIRC_STATE_CONNECTED or SMARTIRC_STATE_DISCONNECTED
$result = $this->_updatestate ();
* tries to find a messagehandler for the received message ($ircdata) and calls it
* @param string $messagecode
function _handlemessage ($messagecode, &$ircdata)
$_methodname = '_'. $methodname;
$_codetype = 'by numeric';
} else if (is_string($messagecode)) { // its not numericcode so already a name/string
$_methodname = '_'. $methodname;
$_codetype = 'by string';
// if exists call internal method for the handling
$this->$_methodname($ircdata);
// if exist, call user defined method for the handling
$this->$methodname($ircdata);
* tries to find a actionhandler for the received message ($ircdata) and calls it
function _handleactionhandler (&$ircdata)
$handler = &$this->_actionhandler;
$handlercount = count($handler);
for ($i = 0; $i < $handlercount; $i++ ) {
$handlerobject = &$handler[$i];
if (substr($handlerobject->message , 0 , 1 ) == '/') {
$regex = $handlerobject->message;
$regex = '/'. $handlerobject->message. '/';
if (($handlerobject->type & $ircdata->type ) &&
$this->log(SMARTIRC_DEBUG_ACTIONHANDLER, 'DEBUG_ACTIONHANDLER: actionhandler match found for id: '. $i. ' type: '. $ircdata->type. ' message: "'. $ircdata->message. '" regex: "'. $regex. '"', __FILE__ , __LINE__ );
$methodobject = &$handlerobject->object;
$method = $handlerobject->method;
$methodobject->$method($this, $ircdata);
function _delayReconnect ()
if ($this->_reconnectdelay > 0 ) {
usleep($this->_reconnectdelay * 1000 );
* getting current microtime, needed for benchmarks
$floattime = (float) $parts[0 ] + (float) $parts[1 ];
* adds an user to the channelobject or updates his info
function _adduser (&$channel, &$newuser)
if ($this->isJoined($channel->name , $newuser->nick )) {
// lets update the existing user
$currentuser = &$channel->users [$lowerednick];
if ($newuser->ident !== null ) {
$currentuser->ident = $newuser->ident;
if ($newuser->host !== null ) {
$currentuser->host = $newuser->host;
if ($newuser->realname !== null ) {
$currentuser->realname = $newuser->realname;
if ($newuser->op !== null ) {
$currentuser->op = $newuser->op;
if ($newuser->voice !== null ) {
$currentuser->voice = $newuser->voice;
if ($newuser->ircop !== null ) {
$currentuser->ircop = $newuser->ircop;
if ($newuser->away !== null ) {
$currentuser->away = $newuser->away;
if ($newuser->server !== null ) {
$currentuser->server = $newuser->server;
if ($newuser->hopcount !== null ) {
$currentuser->hopcount = $newuser->hopcount;
// he is new just add the reference to him
$channel->users [$lowerednick] = &$newuser;
$user = &$channel->users [$lowerednick];
$channel->ops [$user->nick ] = true;
|