<?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");
?>