Location: PHPKode > projects > Nzbirc > nzbirc-1.4/irc.php
<?php

/**************************************************
 * NZBirc v1
 * Copyright (c) 2006 Harry Bragg
 * tiberious.org
 * Main IRC file [extends SmartIRC]
 **************************************************
 *
 * Full GPL License: <http://www.gnu.org/licenses/gpl.txt>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 */



class irc extends Net_SmartIRC {
	
	/**
	 * @var string
	 * @access private
	 */
	var $_phpPath = '';
	
	/**
	 * The current nzbirc version
	 *
	 * @var string
	 */
	var $version = 'NZBirc v1.4~pre3';

	/**
	 * List of configuration options
	 */
	var $config = array();
	

	/**
	 * Version String
	 * 
	 * STARTCONFIG
	 * @var string version - NZBirc Version
	 * ENDCONFIG
	 */

	/**
	 * IRC server settings (only 1 server is allowed at the moment)
	 *
	 * STARTCONFIG
	 * @var array irc
	 * @var     array server_
	 * @var         string host     - Server hostname
	 * @var         int port        - Server Port
	 * @var         string password - Server password (optional)
	 * @var         string nick     - Bots Nickname
	 * @var         string altNick  - Alternative Nickname
	 * @var         string name     - Bots Name
	 * @var         string ident    - Bots ident
	 * @var         array channels  - Channels to join on the server
	 * @var         array perform   - Actions to perform when connected to the server (this is in raw irc RFC format ie: 'PRIVMSG nickserv :identify my nads'
	 * ENDCONFIG
	 */

	/**
	 * Admin settings
	 * 
	 * STARTCONFIG
	 * @var array admin
	 * @var     array nick - List of admin nicknames
	 * @var     bool only  - Only the admins can use the bot
	 * ENDCONFIG
	 */
		
	/**
	 * List of extensions that will be attempted to be loaded (the program will still run if an extension is not loaded)
	 *
	 * STARTCONFIG
	 * @var array extensions - List of extensions to load
	 * ENDCONFIG
	 */
	
	/**
	 * Commands
	 *
	 * STARTCONFIG
	 * @var array commands
	 * @var     array kill    - The commands that will kill the bot
	 * @var     array rehash  - The commands that will rehash a module
	 * @var     array respawn - The commands that will respawn the worker thread 
	 * ENDCONFIG
	 */
	
	/**
	 * Paths
	 *
	 * STARTCONFIG
	 * @var string userPath      - Path of .nzbirc/ (where to put all the config/store/pid files)
	 * @var string basePath      - Path of nzbirc (specifically where nzbirc.php is)
	 * @var string modulePath    - Path of the module
	 * @var string debugLogPath  - Filename for the debug file
	 * @var string phpPath       - Path of the php executable (for testing modules)
	 * @var string workerPath    - path to worker thread
	 * @var string workerPidPath - path to worker .pid file
	 * @var string workerLogPath - Path to the worker log file
	 * ENDCONFIG
	 */
	 
	/**
	 * Modules
	 *
	 * STARTCONFIG
	 * @var array modules - List of modules to load
	 * ENDCONFIG
	 */
	 
	/**
	 * Worker Thread
	 *
	 * STARTCONFIG
	 * @var bool useWorker - Use the worker thead (POSIX OS's only [i.e. not Windows])
	 * ENDCONFIG
	 */
	 
	/**
	 * Encoding
	 *
	 * STARTCONFIG
	 * @var string encoding - What encoding to use (Default ISO-8859-1)
	 * ENDCONFIG
	 */
	 
	/**
	 * Memory Usage
	 *
	 * STARTCONFIG
	 * @var int memoryAllocation - How much memory in MB to allocate
	 * ENDCONFIG
	 */
	 
	/**
	 * Fork the process
	 *
	 * STARTCONFIG
	 * @var bool useFork - Fork the process into a daemon
	 * ENDCONFIG
	 */
	
	/**
	 * Proxy configuration
	 * 
	 * STARTCONFIG
	 * @var array proxy
	 * @var     string host     - hostname, leave blank for no proxy
	 * @var     int port        - port
	 * @var     string username - username [optional]
	 * @var     string password - password [optional]
	 * ENDCONFIG
	 */
		
	/**
	 * Module definitions
	 *
	 * @var array
	 * @access private
	 */
	var $_modDef = array(
		'base' => array(),
		'tvrage' => array(
			'includes' => array(
				'HTTP/Request.php' ),
			'depends' => array(
				'func',
				'base',
				'store' ),
		),
		'ed' => array(
			'depends' => array(
				'tvrage',
				'func',
				'base',
				'imdb' ),
		),
		'func' => array(),
		'hella' => array(
			'includes' => array(
				'XML/RPC.php' ),
			'extensions' => array(
				'xml' ),
			'depends' => array(
				'func',
				'base',
				'store' )
		),
		'nzb' => array(
			'includes' => array(
				'HTTP/Request.php' ),
			'depends' => array(
				'tvrage',
				'ed',
				'func',
				'base',
				'store' ),
			'optional' => array(
				'hella')
		),
		'store' => array(
			'includes' => array(
				'XML/Serializer.php',
				'XML/Unserializer.php' ),
			'extensions' => array(
				'xml'
			)
		),
		'config' => array(),
		'autotv' => array(
			'depends' => array(
				'tvrage',
				'nzb',
				'store',
				'base',
				'func'
			)
		),
		'autoquery' => array(
			'depends' => array(
				'nzb',
				'store',
				'base',
				'func'
			)
		),
		'tvnzb' => array(
			'includes' => array(
				'HTTP/Request.php' ),		
			'depends' => array(
				'tvrage',
				'store',
				'base',
				'func'
			)
		),
		'nzbv2' => array(
			'includes' => array(
				'HTTP/Request.php' ),		
			'depends' => array(
				'tvrage',
				'store',
				'base',
				'func',
				'ed'
			)
		),
		'imdb' => array(
			'depends' => array(
				'base',
				'func',
				'store'
			)
		)
	);
	
	/**
	 * Setup
	 *
	 * @access public
	 * @return void
	 */
	function setup()
	{	
		global $config;
		
		$this->config = $config;
		
		$this->configCheck();
				
		// lets fork

		if ( $this->config->useFork )
		{
			if (strtoupper(substr(PHP_OS, 0,3) != 'WIN')) {
				$pid = pcntl_fork();
				if ( $pid == -1 )
				{
					echo 'Could not fork, Disable useFork in the config to not try and fork the process';
				}
				else if ( $pid )
				{
					// we are the parent
					echo 'NZBirc running in (dodgy) daemon mode, PID: '.$pid."\n";
					exit(0);
				}
			}
		}
		
		// setup the irc connection

		ini_set( 'memory_limit', $this->config->memoryAllocation.'M' );

		$this->setLogdestination( SMARTIRC_FILE );
		$this->setLogfile( $this->config->debugLogPath );
		if ( is_file( $this->_logfile ) )
			unlink( $this->_logfile );
		$this->setModulepath( $this->config->modulePath );
		$this->setPhpPath( $this->config->phpPath );
		$this->setChannelSyncing( true );
		$this->setDebug( SMARTIRC_DEBUG_MODULES | SMARTIRC_DEBUG_ACTIONHANDLER | SMARTIRC_DEBUG_CONNECTION | SMARTIRC_DEBUG_NOTICE );
		//$this->setDebug( SMARTIRC_DEBUG_ALL );
		$this->setUseSockets( true );
		$this->setAutoRetry( true );
		$this->setAutoReconnect( true );
		$this->setReceiveTimeout( 600 );
		$this->setTransmitTimeout( 600 );
		$this->setSendDelay( 500 );
		
		foreach( $this->config->irc as $name => $server )
		{
			if ( substr( $server['host'], 0, 3 ) == 'ssl' )
			{
				$this->setUseSockets( false );
			}
			$this->connect( $server['host'], $server['port'] );
			$this->setCtcpVersion( $this->config->version );
			if ( strlen( $server['password'] ) == 0 )
			{
				$server['password'] = null;
			}
			$this->login( $server['nick'], 
				$server['name'], 6,
				$server['ident'], $server['password'] );
				
			foreach( $server['channels'] as $id => $chan )
			{
				if ( !is_numeric( $id ) )
				{
					$this->join( $id, $chan );
				}
				else
				{
					$this->join( $chan );
				}
			}
			
			if ( is_array( $server['perform'] ) ) {
				foreach( $server['perform'] as $action ) {
					$this->send( $action );
				}
			}			
		}

		// check custom extensions
		foreach( $this->config->extensions as $ext ) {
			$this->checkExtension( $ext );
		}

		$this->loadModules();
		
		if ( @extension_loaded('runkit') ) 
		{
			// register basic commands
			// check functions
			foreach( $this->config->commands['rehash'] as $command )
			{
				$this->registerActionhandler( SMARTIRC_TYPE_CHANNEL, $command, $this, 'rehash' );	
			}		
		}
		foreach( $this->config->commands['kill'] as $command )
		{
			$this->registerActionhandler( SMARTIRC_TYPE_CHANNEL, $command, $this, 'terminate' );	
		}
		$this->registerActionhandler( SMARTIRC_TYPE_CHANNEL, '^!explode', $this, 'explode' );
		
		$this->_modules['config']->_writeConfig( $this->config->userPath.$this->_modules['config']->_config['configFile'] );
		
		if ( $this->config->useWorker )
		{
			$this->_respawn();
			
			foreach( $this->config->commands['respawn'] as $command )
			{
				$this->registerActionhandler( SMARTIRC_TYPE_CHANNEL, $command, $this, 'respawn' );
			}
		}
				
		$this->listen();
		$this->disconnect();
	
	}
	
	/**
	 * Some backwards compatibility checks for the config file
	 */
	function configCheck()
	{
		if ( $this->version != $this->config->version )
		{
			$this->config->version = $this->version;
		}
		
		if ( !isset( $this->config->encoding ) )
		{
			$this->config->encoding = 'UTF-8';
		}
		
		if ( !isset( $this->config->memoryAllocation ) )
		{
			$this->config->memoryAllocation = 15;
		}
		
		if ( !isset( $this->config->commands ) )
		{
			$this->config->commands = array(
				'rehash' => array( '^!rehash' ),
				'kill' => array( '^!kill', '^!terminate' ),
				'respawn' => array( '^!respawn' )
			);
		}
		
		if ( $this->config->phpPath == '' )
		{
			$this->config->phpPath = 'php';
		}
		
		if ( !isset( $this->config->basePath ) )
		{
			$this->config->basePath = dirname(__FILE__).'/';
		}
		
		if ( substr( $this->config->basePath, -1 ) != '/' )
		{
			$this->config->basePath = $this->config->basePath.'/';
		}
		
		if ( !isset( $this->config->workerPidPath ) )
		{
			$this->config->workerPidPath = $this->config->userPath.'worker.pid';
		}
		
		if ( ( $this->config->useWorker ) && ( strtoupper(substr(PHP_OS, 0,3) == 'WIN') ) )
		{
			$this->config->useWorker = false;
			trigger_error( 'You can not use the worker thread with windows', E_USER_WARNING );
		}
	}
	
	/**
	 * ReHash (reload) all (or one of) the modules, on the fly
	 */
	function rehash( &$irc, &$data )
	{		
		// admin check
		if ( !$irc->_modules['func']->isAdmin( $irc, $data->nick ) )
		{
			$irc->_modules['func']->reply( $irc, $data, 'Error: Insufficient privaleges to run this command', false );
			return;
		}
		
		if ( isset( $data->messageex[1] ) ) {
			$aout = $irc->reloadModule( $data->messageex[1] );
		}
		else
		{
		 	// reload the config file
		 	$this->log( SMARTIRC_DEBUG_NOTICE, 'DEBUG_CONFIG: Reloading configuration file', $file, $line );
			include( 'config.inc.php' );
			
			$this->config = $config;
			
			$aout = array();
			
			$modKeys = array_keys($irc->_modules);
			foreach( $modKeys as $key )
			{
				$out = $irc->reloadModule( $key );
				if ( is_array( $out ) )
					$irc->_modules['func']->array_append( $aout, $out);
				else
					$aout[] = $out;
			}
		}
		
		$irc->_modules['func']->reply( $irc, $data, $aout );
		
	}
	
	function respawn( &$irc, &$data )
	{
		// admin check
		if ( !$irc->_modules['func']->isAdmin( $irc, $data->nick ) )
		{
			$irc->_modules['func']->reply( $irc, $data, 'Error: Insufficient privaleges to run this command', false );
			return;
		}
		
		$this->_respawn();
		
		$irc->_modules['func']->reply( $irc, $data, 'Worker Thread respawned' );
	}

	function _respawn()
	{
		if ( file_exists( $this->config->workerPidPath ) )
		{
			$pid = trim(file_get_contents( $this->config->workerPidPath ));
			if ( strlen( $pid ) > 0 )
			{
				// check its a number and if its running
				if ( ( is_numeric( $pid ) ) &&
				     ( posix_kill( $pid, 0 ) ) )
				{
					if ( posix_kill( $pid, SIGKILL ) )
					{
						$this->log( SMARTIRC_DEBUG_NOTICE, 'DEBUG_WORKER: killed worker thread, pid: '.$pid, __FILE__, __LINE__ );
					}
					else
					{
						$this->log( SMARTIRC_DEBUG_NOTICE, 'DEBUG_WORKER: Failed to kill worker thread, pid: '.$pid, __FILE__, __LINE__ );
					}
				}
				else
				{
					$this->log( SMARTIRC_DEBUG_NOTICE, 'DEBUG_WORKER: Worker thread pid found, however nothing was running, pid: '.$pid, __FILE__, __LINE__ );
				}
				unlink( $this->config->workerPidPath );
			}
		}
		$this->log( SMARTIRC_DEBUG_NOTICE, 'DEBUG_WORKER: spawning worker thread', __FILE__, __LINE__ );
		system( sprintf( 'nohup %s %s %s 1>%s 2>&1 &', $this->_phpPath, $this->config->workerPath, $this->config->userPath, $this->config->workerLogPath ) );	
	}
	
	function terminate( &$irc, &$data )
	{
		// admin check
		if ( !$irc->_modules['func']->isAdmin( $irc, $data->nick ) )
		{
			$irc->_modules['func']->reply( $irc, $data, 'Error: Insufficient privaleges to run this command', false );
			return;
		}
		if ( $this->config->useWorker )
		{
			$pid = trim(file_get_contents( $this->config->workerPidPath ));
			if ( strlen( $pid ) > 0 )
			{
				// check its a number and if its running
				if ( ( is_numeric( $pid ) ) &&
				     ( posix_kill( $pid, 0 ) ) )
				{
					if ( posix_kill( $pid, SIGKILL ) )
					{
						$this->log( SMARTIRC_DEBUG_NOTICE, 'DEBUG_WORKER: killed worker thread, pid: '.$pid, __FILE__, __LINE__ );
					}
					else
					{
						$this->log( SMARTIRC_DEBUG_NOTICE, 'DEBUG_WORKER: Failed to kill worker thread, pid: '.$pid, __FILE__, __LINE__ );
					}
				}
				else
				{
					$this->log( SMARTIRC_DEBUG_NOTICE, 'DEBUG_WORKER: Worker thread pid found, however nothing was running, pid: '.$pid, __FILE__, __LINE__ );
				}
				unlink( $this->config->workerPidPath );
			}
		}
		exit(0);				
	}
	
	function explode( &$irc, &$data )
	{
		$irc->_modules['func']->reply( $irc, $data, 'BOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOMM!!!!!!' );
	}
	
	/*******************************************************
	 * A Couple of Overrides
	 *******************************************************/
	
	function setPhpPath( $path )
	{
		$this->_phpPath = $path;
	} 

	/*******************************************************
	 * Time Handler Override
	 *******************************************************/
	 
    /**
     * 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.
     *
     * @see example7.php
     * @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
     * @access public
     */
    function registerTimehandler($interval, &$object, $methodname, $vars = false)
    {
        $id = $this->_timehandlerid++;
        $newtimehandler = new NZBIRC_Net_SmartIRC_timehandler();
        
        $newtimehandler->id = $id;
        $newtimehandler->interval = $interval;
        $newtimehandler->object = &$object;
        $newtimehandler->method = $methodname;
        $newtimehandler->lastmicrotimestamp = $this->_microint();
		$newtimehandler->vars = $vars;
        
        $this->_timehandler[] = &$newtimehandler;
        $this->log(SMARTIRC_DEBUG_TIMEHANDLER, 'DEBUG_TIMEHANDLER: timehandler('.$id.') registered', __FILE__, __LINE__);
        
        if (($interval < $this->_mintimer) || ($this->_mintimer == false)) {
            $this->_mintimer = $interval;
        }
		   
        return $id;
    }	
	
   /**
     * Checks the running timers and calls the registered timehandler,
     * when the interval is reached.
     *
     * @return void
     * @access private
     */
    function _checktimer()
    {
        if (!$this->_loggedin) {
            return;
        }
        
        // 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;
                
                if (@method_exists($methodobject, $method)) {
                    $this->log(SMARTIRC_DEBUG_TIMEHANDLER, 'DEBUG_TIMEHANDLER: calling method "'.get_class($methodobject).'->'.$method.'"', __FILE__, __LINE__);
                    $methodobject->$method($this, $handlerobject->vars);
                }
            }
        }
    }

	/*******************************************************
	 * Action Handler Overrides
	 *******************************************************/

    /**
     * add trigger to ircdata
     *
     * @param object $ircdata
     * @return void
     * @access private
     */
    function _handleactionhandler(&$ircdata)
    {
     	global $irc;
     	
        $handler = &$this->_actionhandler;
        $handlercount = count($handler);
        for ($i = 0; $i < $handlercount; $i++) {
            $handlerobject = &$handler[$i];
            
			if ( !$this->config->advancedTriggers ) {
				$regex = '^'.preg_quote( $regex, '/' );
			}
			
            if (substr($handlerobject->message, 0, 1) == '/') {
                $regex = $handlerobject->message;
            } else {
                $regex = '/'.$handlerobject->message.'/i';
            }
            
            if (($handlerobject->type & $ircdata->type) &&
                (preg_match($regex, $ircdata->message, $trig) == 1))
			{                
                $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;
                
                if (@method_exists($methodobject, $method))
				{
                    $this->log(SMARTIRC_DEBUG_ACTIONHANDLER, 'DEBUG_ACTIONHANDLER: calling method "'.get_class($methodobject).'->'.$method.'"', __FILE__, __LINE__);
					
					// custom trigger var
					$ircdata->trigger = $trig[0];
					$ircdata->strippedMessage = trim( preg_replace( $regex, '', $ircdata->message, 1 ) );
					
                    $methodobject->$method($this, $ircdata);
                } else {
                    $this->log(SMARTIRC_DEBUG_ACTIONHANDLER, 'DEBUG_ACTIONHANDLER: method doesn\'t exist! "'.get_class($methodobject).'->'.$method.'"', __FILE__, __LINE__);
                }
            }
        }
    }
    
    /**
     * sends a new message
     *
     * Sends a message to a channel or user.
     *
     * @see DOCUMENTATION
     * @param integer $type specifies the type, like QUERY/ACTION or CTCP see 'Message Types'
     * @param string $destination can be a user or channel
     * @param mixed $message the message
     * @return boolean
     * @access public
     */
    function message($type, $destination, $messagearray, $priority = SMARTIRC_MEDIUM)
    {
        if (!is_array($messagearray)) {
            $messagearray = array($messagearray);
        }
        
        foreach( $messagearray as $id => $msg )
        {
			if ( strlen( $msg ) > 400 )
			{
				$split = wordwrap( $msg, 400, "\n", 1 );
				$sA = explode( "\n", $split );
				array_splice( $messagearray, $id, 1, $sA );
			}
		}
        
        switch ($type) {
            case SMARTIRC_TYPE_CHANNEL:
            case SMARTIRC_TYPE_QUERY:
                foreach ($messagearray as $message) {
                    $this->_send('PRIVMSG '.$destination.' :'.$message, $priority);
                }
            break;
            case SMARTIRC_TYPE_ACTION:
                foreach ($messagearray as $message) {
                    $this->_send('PRIVMSG '.$destination.' :'.chr(1).'ACTION '.$message.chr(1), $priority);
                }
            break;
            case SMARTIRC_TYPE_NOTICE:
                foreach ($messagearray as $message) {
                    $this->_send('NOTICE '.$destination.' :'.$message, $priority);
                }
            break;
            case SMARTIRC_TYPE_CTCP: // backwards compatibilty
            case SMARTIRC_TYPE_CTCP_REPLY:
                foreach ($messagearray as $message) {
                    $this->_send('NOTICE '.$destination.' :'.chr(1).$message.chr(1), $priority);
                }
            break;
            case SMARTIRC_TYPE_CTCP_REQUEST:
                foreach ($messagearray as $message) {
                    $this->_send('PRIVMSG '.$destination.' :'.chr(1).$message.chr(1), $priority);
                }
            break;
            default:
                return false;
        }
        
        return true;
    }    

    /**
     * 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_NONE
     * SMARTIRC_DEBUG_NOTICE
     * SMARTIRC_DEBUG_CONNECTION
     * SMARTIRC_DEBUG_SOCKET
     * SMARTIRC_DEBUG_IRCMESSAGES
     * SMARTIRC_DEBUG_MESSAGETYPES
     * SMARTIRC_DEBUG_ACTIONHANDLER
     * SMARTIRC_DEBUG_TIMEHANDLER
     * SMARTIRC_DEBUG_MESSAGEHANDLER
     * SMARTIRC_DEBUG_CHANNELSYNCING
     * SMARTIRC_DEBUG_MODULES
     * SMARTIRC_DEBUG_USERSYNCING
     * SMARTIRC_DEBUG_ALL
     *
     * @see SMARTIRC_DEBUG_NOTICE
     * @param integer $level bit constants (SMARTIRC_DEBUG_*)
     * @param string $entry the new log entry
     * @return void
     * @access public
     */
    function log($level, $entry, $file = null, $line = null)
    {
        // prechecks
        if (!(is_integer($level)) ||
            !($level & SMARTIRC_DEBUG_ALL)) {
            $this->log(SMARTIRC_DEBUG_NOTICE, 'WARNING: invalid log level passed to log() ('.$level.')', __FILE__, __LINE__);
            return;
        }
        
        if (!($level & $this->_debug) ||
             ($this->_logdestination == SMARTIRC_NONE)) {
            return;
        }
        
        if (substr($entry, -1) != "\n") {
            $entry .= "\n";
        }
        
        if ($file !== null &&
            $line !== null) {
            $file = basename($file);
            $entry = $file.'('.$line.') '.$entry;
        } else {
            $entry = 'unknown(0) '.$entry;
        }
        
		list($usec, $sec) = explode(" ", microtime());
		$msec = sprintf( '%03d', $usec * 1000);
        $formatedentry = date('M d H:i:s.').$msec.' '.$entry;
        switch ($this->_logdestination) {
            case SMARTIRC_STDOUT:
                echo $formatedentry;
                flush();
            break;
            case SMARTIRC_BROWSEROUT:
                echo '<pre>'.htmlentities($formatedentry).'</pre>';
            break;
            case SMARTIRC_FILE:
                $this->_logfilefp = fopen($this->_logfile,'a');
                fwrite($this->_logfilefp, $formatedentry);
                fflush($this->_logfilefp);
				fclose($this->_logfilefp);
            break;
            case SMARTIRC_SYSLOG:
                define_syslog_variables();
                if (!is_int($this->_logfilefp)) {
                    $this->_logfilefp = openlog('Net_SmartIRC', LOG_NDELAY, LOG_DAEMON);
                }
                syslog(LOG_INFO, $entry);
            break;
        }
    }

	/**
	 * Check if an extension is loaded, if not... load it
	 *
	 * @access public
	 * @return bool
	 */
	function checkExtension( $name )
	{		
		if (extension_loaded($name)) {
			return true;
		}
		else
		{
			$this->log(SMARTIRC_DEBUG_NOTICE, 'WARNING: '.$name.' extension not loaded, trying to load it...', __FILE__, __LINE__);
			// attempt to load the module
			
			if (strtoupper(substr(PHP_OS, 0,3) == 'WIN')) {
				$load_status = dl('php_'.$name.'.dll');
			} else {
				$load_status = dl($name.'.so');
			}
			
			if ($load_status)
			{
				$this->log(SMARTIRC_DEBUG_NOTICE, $name.' extension sucessfully loaded', __FILE__, __LINE__ );				
				return true;
			} else
			{
				$this->log(SMARTIRC_DEBUG_NOTICE, 'WARNING: '.$name.' extension failed to load', __FILE__, __LINE__ );
				return false;
			}
		}
	}
	
	/*******************************************************
	 * Module code
	 *******************************************************/
	
	/**
	 * Loads a module
	 * 1. Checks the config to see if it is listed
	 * 2. checks to see if it has any dependencies
	 * 3. checks to see if needs any extensions
	 * 4. checks to see if it has any includes
	 * 5. loads the module
	 *
	 * @param string $name - String input
	 * @return void
	 * @access public
	 */
    function loadModule($name)
    {

        // is the module already loaded?
        if ( $this->isLoadedModule( $name ) )
		{
            $this->log(SMARTIRC_DEBUG_MODULES, 'WARNING! module with the name "'.$name.'" already loaded!', __FILE__, __LINE__);
            return false;
        }
        
        $filename = $this->_modulepath.'/'.$name.'.php';
        if (!file_exists($filename))
		{
            $this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: couldn\'t load module "'.$filename.'" file doesn\'t exist', __FILE__, __LINE__);
            return false;
        }
		
		// check module definition
		if ( isset( $this->_modDef[$name] ) )
		{

			// check the dependencies
			if ( is_array( $this->_modDef[$name]['depends'] ) ) {
				foreach( $this->_modDef[$name]['depends'] as $depName ) {
					if ( !$this->isLoadedModule( $depName ) )
					{
						// load the module first
						$this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: [Module: '.$name.'] depends on module: '.$depName.', loading that module first', __FILE__, __LINE__);
						if ( !$this->loadModule( $depName ) )
						{
							$this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: [Module: '.$name.'] Can no load as it depends on '.$depName, __FILE__, __LINE__);
							return false;
						}
					}
				}
			}
			
			// check optionals
			if ( is_array( $this->_modDef[$name]['optional'] ) ) 
			{
				foreach( $this->_modDef[$name]['optional'] as $optName ) 
				{
					if ( ( !$this->isLoadedModule( $optName ) ) &&
					     ( in_array( $optName, $this->config->modules ) ) )
					{
						// load the module first
						$this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: [Module: '.$name.'] optionally depends on module: '.$optName.', attempting to load that module first', __FILE__, __LINE__);
						$this->loadModule( $optName );
					}
				}
			}
		 
			// check extensions 		
			if ( is_array( $this->_modDef[$name]['extensions'] ) ) {
				foreach( $this->_modDef[$name]['extensions'] as $ext ) {
					if ( !@extension_loaded( $ext ) ) {
						if ( $this->checkExtension( $ext ) ) {
							$this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: [Module: '.$name.'] included extension: '.$ext, __FILE__, __LINE__);
						} else {
							$this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: [Module: '.$name.'] extension: '.$ext.' failed to load', __FILE__, __LINE__);	
						}
					}
				}
			}
			
			// check includes
			if ( is_array( $this->_modDef[$name]['includes'] ) ) {
				foreach( $this->_modDef[$name]['includes'] as $file ) {
					if ( !in_array( $file, get_included_files()) )
					{
						// todo add checks to see if the file was included
						include_once( $file );
						$this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: [Module: '.$name.'] included file: '.$file, __FILE__, __LINE__);						
					}
				}
			}
						
		}
		
		
		if ( !empty( $this->_phpPath ) )
		{  
			// check to see if the file compiles using php -l
			$ret = exec($this->_phpPath.' -l '.$filename, $output);
			if (!preg_match('/^No syntax errors/i', $output[0]))
			{
				$this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: couldn\'t load module: "'.$name.'", failed to compile, output:', __FILE__, __LINE__);
				//echo 'DEBUG_MODULES: couldn\'t load module: "'.$name.'", failed to compile, output:'."\n";
				foreach( $output as $string )
				{
					$this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: '.$string, __FILE__, __LINE__);
					//echo 'DEBUG_MODULES: '.$string."\n";
				}
				return $output;
			}
		}
		      	
        if (@extension_loaded('runkit')) {
        	$this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: loading module: "'.$name.'" using runkit...', __FILE__, __LINE__); 
			runkit_import( $filename ); 
		} else {
			$this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: loading module: "'.$name.'"...', __FILE__, __LINE__);
			include_once( $filename );
		}
        $classname = 'Net_SmartIRC_module_'.$name;
        
        if (!class_exists($classname))
		{
            $this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: class '.$classname.' not found in '.$filename, __FILE__, __LINE__);
            return false;
        }
        
        $methods = get_class_methods($classname);
        if (!in_array('module_init', $methods))
		{
            $this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: required method'.$classname.'::module_init not found, aborting...', __FILE__, __LINE__);
            return false;
        }
        
        if (!in_array('module_exit', $methods))
		{
            $this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: required method'.$classname.'::module_exit not found, aborting...', __FILE__, __LINE__);
            return false;
        }
        
        $vars = array_keys(get_class_vars($classname));
        if (!in_array('name', $vars))
		{
            $this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: required variable '.$classname.'::name not found, aborting...', __FILE__, __LINE__);
            return false;
        }
        
        if (!in_array('description', $vars))
		{
            $this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: required variable '.$classname.'::description not found, aborting...', __FILE__, __LINE__);
            return false;
        }
        
        if (!in_array('author', $vars))
		{
            $this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: required variable '.$classname.'::author not found, aborting...', __FILE__, __LINE__);
            return false;
        }
        
        if (!in_array('license', $vars))
		{
            $this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: required variable '.$classname.'::license not found, aborting...', __FILE__, __LINE__);
            return false;
        }
        
        // looks like the module satisfies us
        $module = new $classname;
        $this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: successful created instance of: '.$classname, __FILE__, __LINE__);
        
        $this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: calling '.$classname.'::module_init()', __FILE__, __LINE__);
        $module->module_init($this);
        $this->_modules[$name] = &$module;
        
        $this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: successful loaded module: '.$name, __FILE__, __LINE__);
        return true;
    }
    
    function unloadModule($name)
    {
        $this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: unloading module: '.$name.'...', __FILE__, __LINE__);
        
        foreach( $this->_modules as $modName => $modVals )
        {
            if ($modName == $name)
			{
                $this->_modules[$modName]->module_exit($this);
                unset( $this->_modules[$name] );
                $this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: successfully unloaded module: '.$name, __FILE__, __LINE__);
                return true;
            }
			
		}
        
        $this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: couldn\'t unloaded module: '.$name.' (it\'s not loaded!)', __FILE__, __LINE__);
        return false;
    }	 
	
	/**
	 * Reload a module
	 *
	 * @param string name - Module name
	 * @return string - textual result of the function
	 * @access private
	 */
	function reloadModule( $name )
	{

		$this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: reloading module: '.$name.'...', __FILE__, __LINE__);
		
		$this->unloadModule( $name );
		if ( ( $out = $this->loadModule( $name ) ) === true )
		{
			$out = 'Rehash of: '.$name.', successful';
			$this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: successful reloaded module: '.$name.'...', __FILE__, __LINE__);
		} else
		{
			$this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: failed to reload module: '.$name.' output: ', __FILE__, __LINE__);
			
			if ( is_array( $out ) ) {
				foreach( $out as $line ) {
					$this->log(SMARTIRC_DEBUG_MODULES, 'DEBUG_MODULES: '.$line, __FILE__, __LINE__);
				}	
			}
		}
		return $out;
	}

	/**
	 * Loads the modules (for startup)
	 */
	function loadModules()
	{
		foreach( $this->config->modules as $id => $module )
		{
			$modName = ( is_array( $module ) )? $id:$module;
			$this->loadModule( $modName );
		}
	}
	
	/**
	 * module Debug
	 */
	function modDebug( $module, $msg, $file, $line, $level = SMARTIRC_DEBUG_NOTICE )
	{
		$this->log( $level, sprintf( 'DEBUG_MODULES: [Module: %s] %s', $module, $msg ), $file, $line );
	}
	
	/**
	 * Is the module loaded?
	 *
	 * @param string $name - Name of the module
	 * @return bool
	 * @access public
	 */
	function isLoadedModule( $name )
	{
		if ( isset( $this->_modules[$name] ) )
			return true;
		else
			return false;
	}
	
	/**
	 * Fix if you have a stoooooooooopid bouncer
	 */
    function _event_pong(&$ircdata)
    {
        $this->_pong(substr($ircdata->rawmessage, 5));
    }
	 
}

/**
 * @access public
 */
class NZBIRC_Net_SmartIRC_timehandler extends Net_SmartIRC_timehandler
{	
	/**
	 * We might want to pass variables with the timer too
	 *
	 * @var array
	 * @access public
	 */
	var $vars;
}


	
/*******************************************************
 * Lets do our own error handling
 *******************************************************/
 
// user defined error handling function
function userErrorHandler($errno, $errmsg, $filename, $linenum, $vars)
{
	global $irc;
	
	$errortype = array (
		E_ERROR				=> 'Error',
		E_WARNING			=> 'Warning',
		E_PARSE				=> 'Parsing Error',
		E_NOTICE			=> 'Notice',
		E_CORE_ERROR		=> 'Core Error',
		E_CORE_WARNING		=> 'Core Warning',
		E_COMPILE_ERROR		=> 'Compile Error',
		E_COMPILE_WARNING	=> 'Compile Warning',
		E_USER_ERROR		=> 'User Error',
		E_USER_WARNING		=> 'User Warning',
		E_USER_NOTICE		=> 'User Notice',
		E_STRICT			=> 'Runtime Notice',
		E_RECOVERABLE_ERROR	=> 'Catchable Fatal Error'
		);
	
	// set of errors for which a var trace will be saved
	$user_errors = array(E_USER_ERROR, E_USER_WARNING, E_USER_NOTICE);
	
	$dont_report_errors = array(E_NOTICE, E_STRICT);
	
	if ( !in_array($errno, $dont_report_errors) )
	{
		$eMsg = "PHP Error\n".
			"Error Number: ".$errno."\n".
			"Error Type: ".$errortype[$errno]."\n".
			"Error Message: ".$errmsg."\n".
			"File Name: ".$filename."\n".
			"Line Number: ".$linenum."\n";
			
		if (in_array($errno, $user_errors)) {
			$eMsg .= "Variable Trace: ".wddx_serialize_value($vars, "Variables") . "\n";
		}
		
		if ( !$irc->config->useFork )
			echo $eMsg;
	
		$irc->log( SMARTIRC_DEBUG_NOTICE, $eMsg, __FILE__, __LINE__ );
	}
}

set_error_handler("userErrorHandler");
?>
Return current item: Nzbirc