#!/usr/local/bin/php
<?php
  
/* CBX's IRC Server Bridge Bot
     - This is an IRC bot that bridges a channel on two seperate IRC networks together by relaying messages across the gap
     created by Naram Qashat (CyberBotX) [idea "stolen" from Iyouboushi, but no code copied since his was a mIRC "bot"]
     last modified: 11-26-2009 */
  
chdir(dirname(__FILE__)); // Change to the bot's directory if we weren't run from that directory.
  
require_once 'settings.php'// bot settings
  
ignore_user_abort(true); // Don't allow aborting (which is probably useless in a command-line script)
  
set_time_limit(0); // Never time out, run indefinitely
  
function dated_fwrite($file$line$extradata '')
  {
    
// This function will write a line to a file with a date before the line
    
fwrite($file'[' date('D m/d/y H:i:s O') . ($extradata $extradata'') . "] $line\n");
  }
  class 
ircServerInfo // This is a class I made to keep the details of each server seperate from each other
  
{
    public 
$server// Address of the server
    
public $network// Network name (set during connect)
    
public $chans// Array of channels the bot is in
    
public $myircNick// The bot's current nick
    
public $needchangeIRCnick// Does the nick need to be changed to the real nick? (if myircNick is not mainIRCnick)
    
public $istheremore// This is true if more data is waiting in the socket and needs to be read immediatly
    
public $socket// This is the actual socket handle
    
public $endOfMOTD// Stores the timestamp of when the MOTD ended
    
public $seeIfInChannels// Stores the timestamp to check if the bot is in any channels or not
    
public $users// Array of the users in the channels the bot is in
    
public $prefixes// Array of the allowed mode prefixes in channels (for users, such as @ for op and so on)
    
public $chanmodes// Array of the supported channel modes
    
public $timeoflastnick// Timestamp of the last time the bot's nick has changed
    
public $prevstr// The previous string, set when there is more data and this string is incomplete as a result
    
function reconstruct()
    {
      
// This function is like a constructor, setting up the variables
      
global $mainIRCnick$debugging$debugfile;
      
$this->network '';
      
$this->chans = array();
      
$this->myircNick $mainIRCnick;
      
$this->needchangeIRCnick false;
      
$this->istheremore false;
      
$this->socket null;
      
$this->endOfMOTD 0;
      
$this->seeIfInChannels 0;
      
$this->users = array();
      
$this->prefixes = array();
      
$this->chanmodes = array('param' => array(), 'paramaddonly' => array(), 'paramless' => array());
      
$this->timeoflastnick 0;
      
$this->prevstr '';
      if (
$debugging)
        
dated_fwrite($debugfile"ircServerInfo()::reconstruct() for {$this->server}.");
    }
    function 
__construct($new_server)
    {
      
// Constructor, sets the server's address
      
global $debugging$debugfile;
      if (
$debugging)
        
dated_fwrite($debugfile"ircServerInfo()::__construct($new_server).");
      
$this->server $new_server;
      
$this->reconstruct();
    }
    function 
connected()
    {
      
// Returns the result of socket, which will either have a value or be false/null
      //global $debugging, $debugfile;
      /*if ($debugging)
        dated_fwrite($debugfile, "ircServerInfo::connected() for {$this->server}, status is {$this->socket}.");*/
      
return $this->socket;
    }
    function 
connect()
    {
      
// Start the initial connection to the IRC server
      
global $mainIRCnick$commonChans$debugging$debugfile;
      if (
$debugging)
        
dated_fwrite($debugfile"ircServerInfo::connect() for {$this->server}.");
      
$this->reconstruct();
      
$this->socket = @fsockopen($this->server6667$errno$errstr); // Open a socket connection to the IRC server
      
if ($this->socket// If we were able to connect to the IRC server
      
{
        if (
$debugging)
          
dated_fwrite($debugfile"Connected successfully to {$this->server}.");
        
stream_set_timeout($this->socket10); // Set the timeout on the IRC socket to 10 seconds.
        
$this->sendIRCcommand('NICK'$mainIRCnick);
        
$this->sendIRCcommand('USER'"cbxbridge cyberbotx.com {$this->server}"'CBX Server Bridge Bot');
        if (
count($commonChans))
          foreach (
$commonChans as $key => $chan)
            
$this->chans[] = array('name' => $chan'status' => 'pendingjoin');
      }
      elseif (
$debugging)
        
dated_fwrite($debugfile"Unable to connect to {$this->server}.");
    }
    function 
disconnect()
    {
      
// Disconnect from the IRC server
      
global $debugging$debugfile;
      if (
$debugging)
        
dated_fwrite($debugfile"ircServerInfo::disconnect() for {$this->server}.");
      
fclose($this->socket);
      
$this->reconstruct();
    }
    function 
sendIRCcommand($cmd$who ''$msg ''$colonmsg true)
    {
      
// This function will send an IRC command to the IRC server, and log if enabled
      
global $log$logging$debugging$debugfile;
      if (
$debugging)
        
dated_fwrite($debugfile"ircServerInfo::sendIRCcommand($cmd$who$msg$colonmsg) for {$this->server}.");
      
$line $cmd;
      if (
$who$line .= $who";
      if (
$msg)
      {
        
$line .= ' ';
        if (
$colonmsg)
          
$line .= ':';
        
$line .= $msg;
      }
      
fwrite($this->socket"$line\n");
      if (
$logging)
      {
        
$ID $this->ID();
        
dated_fwrite($log"-> $line"$ID);
      }
    }
    function 
inChan($chan$status 'joined')
    {
      
// This function will check if the bot is within a channel, and if $status is given as 'pendingjoin' or 'pendingpart', if either of those is true
      
global $debugging$debugfile;
      if (
$debugging)
        
dated_fwrite($debugfile"ircServerInfo::inChan($chan$status) for {$this->server}.");
      if (
count($this->chans))
        foreach (
$this->chans as $key => $ircChan)
        {
          if (
$debugging)
            
dated_fwrite($debugfile"Comparing {$ircChan['name']} with $chan and {$ircChan['status']} with $status.");
          if (
strtolower($ircChan['name']) == strtolower($chan) && $ircChan['status'] == $status)
            return 
$key;
        }
      if (
$debugging)
        
dated_fwrite($debugfile"Did not find channel $chan with status $status in chans array.");
      return 
false;
    }
    function 
find_mode($mode_to_find$check_char true)
    {
      
// This function will find the given prefix mode in the $prefixes array and return the key within that array
      
global $debugging$debugfile;
      if (
$debugging)
        
dated_fwrite($debugfile"ircServerInfo::find_mode($mode_to_find) for {$this->server}.");
      
$mkey false;
      foreach (
$this->prefixes as $key => $prefix)
      {
        if (
$debugging)
          
dated_fwrite($debugfile"Comparing mode symbol {$prefix['mode']} to $mode_to_find.");
        if (
$prefix['mode'] == $mode_to_find)
          
$mkey $key// Try to find the key using the mode symbol
      
}
      if (
$mkey === false && $check_char)
      {
        if (
$debugging)
          
dated_fwrite($debugfile"Did not find the mode by symbol, checking by character now.");
        foreach (
$this->prefixes as $key => $prefix)
        {
          if (
$debugging)
            
dated_fwrite($debugfile"Comparing mode character {$prefix['char']} to $mode_to_find.");
          if (
$prefix['char'] == $mode_to_find)
            
$mkey $key// Try to find the key using the character only if the symbol was not found
        
}
      }
      if (
$debugging)
        
dated_fwrite($debugfile"Returning $mkey.");
      return 
$mkey// Return the symbol (or false if not found)
    
}
    function 
sort_modes(&$modes)
    {
      
// This function will sort a user's prefix modes to be in the order the 005 server reply had them in and returns the new order
      
global $debugging$debugfile;
      if (
$debugging)
      {
        
$printedmodes rtrim(print_r($modestrue));
        
dated_fwrite($debugfile"ircServerInfo::sort_modes(\$modes) for {$this->server} with \$modes being:\n$printedmodes");
      }
      
$new_modes = array();
      for (
$x 0$x count($modes); ++$x)
      {
        
$key $this->find_mode($modes[$x]);
        for (
$y 0$y count($new_modes); ++$y)
        {
          
$nkey $this->find_mode($new_modes[$y]);
          if (
$key $nkey// Check if the current mode in $modes is a higher mode than the one within the $new_modes array, and add it before that if it is
          
{
            
array_splice($new_modes$y $y 00$modes[$x]);
            break;
          }
        }
        if (!
in_array($modes[$x], $new_modes))
          
$new_modes[] = $modes[$x]; // If we never added the mode, it's the smallest, add to the end
      
}
      if (
$debugging)
      {
        
$printednew_modes rtrim(print_r($new_modestrue));
        
dated_fwrite($debugfile"Setting \$modes to \$new_modes with \$new_modes being:\n$printednew_modes");
      }
      
$modes $new_modes;
    }
    function 
find_chanmode($mode_to_find)
    {
      
// This function finds a channel mode and returns it's type
      
global $debugging$debugfile;
      if (
$debugging)
        
dated_fwrite($debugfile"ircServerInfo::find_chanmode($mode_to_find) for {$this->server}.");
      if (
$this->find_mode($mode_to_find) !== false)
        return 
'prefix'// Check if the mode was a prefix mode
      
if (count($this->chanmodes['param']))
        foreach (
$this->chanmodes['param'] as $mode)
        {
          if (
$debugging)
            
dated_fwrite($debugfile"Comparing param mode $mode with $mode_to_find.");
          if (
$mode == $mode_to_find)
            return 
'param'// Check if the mode was a param mode
        
}
      if (
count($this->chanmodes['paramaddonly']))
        foreach (
$this->chanmodes['paramaddonly'] as $mode)
        {
          if (
$debugging)
            
dated_fwrite($debugfile"Comparing paramaddonly mode $mode to $mode_to_find.");
          if (
$mode == $mode_to_find)
            return 
'paramaddonly'// Check if the mode was a param mode that only uses a param on adding, not removing
        
}
      if (
count($this->chanmodes['paramless']))
        foreach (
$this->chanmodes['paramless'] as $mode)
        {
          if (
$debugging)
            
dated_fwrite($debugfile"Comparing paramless mode $mode to $mode_to_find.");
          if (
$mode == $mode_to_find)
            return 
'paramless'// Check if the mode was a paramless mode
        
}
      if (
$debugging)
        
dated_fwrite($debugfile"Did not find the channel mode.");
      return 
'none'// The mode was not found
    
}
    function 
find_user($user_to_find)
    {
      
// This function will find a user within the $users array and return the key within that array
      
global $debugging$debugfile;
      if (
$debugging)
        
dated_fwrite($debugfile"ircServerInfo::find_user($user_to_find) for {$this->server}.");
      foreach (
$this->users as $key => $user)
      {
        if (
$debugging)
          
dated_fwrite($debugfile"Comparing {$user['user']} to $user_to_find (both lowercase).");
        if (
strtolower($user['user']) == strtolower($user_to_find))
          return 
$key;
      }
      if (
$debugging)
        
dated_fwrite($debugfile"Could not find the user.");
      return 
false;
    }
    function 
add_user($user$chan$status null)
    {
      
// This function will add a user to the $users array if they aren't there already and add them to the given channel regardless
      
global $debugging$debugfile;
      if (
$debugging)
        
dated_fwrite($debugfile"ircServerInfo::add_user($user$chan$status) for {$this->server}.");
      
$key $this->find_user($user); // Try to find the user
      
if ($key === false)
      {
        if (
$debugging)
        {
          
$printedusers rtrim(print_r($this->userstrue));
          
dated_fwrite($debugfile"User doesn't exist in user array, adding as a new user.\nUser list prior to add:\n$printedusers");
        }
        
$this->users[] = array('user' => $user'chans' => array()); // User wasn't found, add a whole new entry
        
if ($debugging)
        {
          
$printedusers rtrim(print_r($this->userstrue));
          
dated_fwrite($debugfile"User list after add:\n$printedusers");
        }
      }
      
add_chan($this->users[$key === false count($this->users) - $key], $chan$status); // Add the channel to the user, new or old
      
usort($this->users'sort_users');
    }
    function 
remove_user_from_chan($user$chan)
    {
      
// This function will remove a user from the given channel, and delete their user entry if they are not in any channels the bot is in anymore
      
global $debugging$debugfile;
      if (
$debugging)
        
dated_fwrite($debugfile"ircServerInfo::remove_user_from_chan($user$chan) for {$this->server}.");
      
$key $this->find_user($user); // Try to find the user
      
if ($key !== false// If the user was found (should be, but better safe than sorry)
      
{
        if (
$debugging)
          
dated_fwrite($debugfile"User was found...");
        
$ckey find_chan($this->users[$key]['chans'], $chan); // Find the key of the channel (also should be found)
        
if ($ckey !== false)
        {
          if (
$debugging)
          {
            
$printedchans rtrim(print_r($this->users[$key]['chans'], true));
            
dated_fwrite($debugfile"Channel to remove user from was found, removing channel entry...\nChannel list prior to removal:\n$printedchans");
          }
          
array_splice($this->users[$key]['chans'], $ckey1); // Remove the channel from the array
          
if ($debugging)
          {
            
$printedchans rtrim(print_r($this->users[$key]['chans'], true));
            
dated_fwrite($debugfile"Channel list after removal:\n$printedchans");
          }
        }
        elseif (
$debugging)
          
dated_fwrite($debugfile"POSSIBLE ERROR, channel $chan was not found in the user's channel list!");
        if (!
count($this->users[$key]['chans']))
        {
          if (
$debugging)
          {
            
$printedusers rtrim(print_r($this->userstrue));
            
dated_fwrite($debugfile"User is no longer in any channels, remove the user from the user's list.\nUser list prior to removal:\n$printedusers");
          }
          
array_splice($this->users$key1); // If the user is not in any of the bot's channel, remove the user
          
if ($debugging)
          {
            
$printedusers rtrim(print_r($this->userstrue));
            
dated_fwrite($debugfile"User list after removal:\n$printedusers");
          }
        }
      }
      elseif (
$debugging)
        
dated_fwrite($debugfile"POSSIBLE ERROR, user $user was not found in the user's list!");
    }
    function 
remove_user($user)
    {
      
// This will completely remove the user from the $users array
      
global $debugging$debugfile;
      if (
$debugging)
        
dated_fwrite($debugfile"ircServerInfo::remove_user($user) for {$this->server}.");
      
$key $this->find_user($user); // Try to find the user (again, should be found)
      
if ($key !== false)
      {
        if (
$debugging)
        {
          
$printedusers rtrim(print_r($this->userstrue));
          
dated_fwrite($debugfile"User was found, removing...\nUser list prior to removal:\n$printedusers");
        }
        
array_splice($this->users$key1); // Remove the user's entry entirely
        
if ($debugging)
        {
          
$printedusers rtrim(print_r($this->userstrue));
          
dated_fwrite($debugfile"User list after removal:\n$printedusers");
        }
      }
      elseif (
$debugging)
        
dated_fwrite($debugfile"POSSIBLE ERROR, user $user was not found in the user's list!");
    }
    function 
merge_users($newusers$chan)
    {
      
// This function adds a new set of users (usually from a NAMES reply) into the $users array
      
global $debugging$debugfile;
      if (
$debugging)
      {
        
$printednewusers rtrim(print_r($newuserstrue));
        
dated_fwrite($debugfile"ircServerInfo::merge_users(\$newusers, $chan) for {$this->server} with \$newusers begin:\n$printednewusers");
      }
      foreach (
$newusers as $user)
      {
        if (
$debugging)
          
dated_fwrite($debugfile"Adding {$user['user']} to the user's list.");
        
$this->add_user($user['user'], $chan$user['status']);
      }
    }
    function 
remove_all_users_from_chan($chan)
    {
      
// This function will remove all users from the given channel
      
global $debugging$debugfile;
      if (
$debugging)
        
dated_fwrite($debugfile"ircServerInfo::remove_all_users_from_chan($chan) for {$this->server}.");
      for (
$x 0$x count($this->users); ++$x)
      {
        if (
$debugging)
          
dated_fwrite($debugfile"About to remove {$this->users[$x]['user']} from $chan...\n");
        
$oldusername $this->users[$x]['user']; // This stores the old username
        
$this->remove_user_from_chan($this->users[$x]['user'], $chan); // Remove the user from the channel
        
if ($x count($this->users) && $this->users[$x]['user'] != $oldusername)
          --
$x// Lower $x by one if the user's entry was removed, needed so we don't miss users
      
}
    }
    function 
check_status()
    {
      
// This function will check the status of the connection, to see if the nick needs changing or if it needs to join any channels
      
global $mainIRCnick$quit$server1$server2;#, $debugging, $debugfile;
      /*if ($debugging)
        dated_fwrite($debugfile, "ircServerInfo::check_status() for {$this->server}.");*/
      
if ($this->needchangeIRCnick && isset($this->timeoflastnick) && time() - $this->timeoflastnick 30// If we need to change the bot's nick and it's been more than 30 seconds since the last change
      
{
        
$this->sendIRCcommand('NICK'$mainIRCnick);
        
$this->timeoflastnick time();
      }
      if (
$this->endOfMOTD && time() - $this->endOfMOTD 30// Join any pending channels 30 seconds after the MOTD ends and the bot hasn't joined any yet
      
{
        foreach (
$this->chans as $key => $ircChan)
          if (
$ircChan['status'] == 'pendingjoin')
            
$this->sendIRCcommand('JOIN'$ircChan['name']);
        
$this->endOfMOTD 0;
      }
      if (
$this->seeIfInChannels && time() - $this->seeIfInChannels 15// Quit only if the bot is in no IRC channels after being blocked from any
      
{
        if (!
count($this->chans))
        {
          
$otherServer = ($this === $server1) ? $server2 $server1// This stores the opposing IRC server
          
$this->sendIRCcommand('QUIT'''"Quitting because I couldn't join any channels on {$this->network}...");
          
$otherServer->sendIRCcommand('QUIT'''"Quitting because I couldn't join any channels on {$this->network}...");
          
$quit true;
        }
        
$this->seeIfInChannels 0;
      }
    }
    
// The next function returns the "ID" of the connection, either the network name if it has been set, or the server address if the network name isn't set yet
    
function ID() { return $this->network $this->network $this->server; }
    function 
process_data()
    {
      
// This function is called repeatedly from the main loop and processes the incoming IRC data
      
global $server1$server2$mainIRCnick$altIRCnick$quit$logging$log$botStartupTime$ns_key$ns_password$quit_password$debugging$debugfile;
      if (
$debugging)
        
dated_fwrite($debugfile"ircServerInfo::process_data() for {$this->server}.");
      
$otherServer = ($this === $server1) ? $server2 $server1// This stores the opposing IRC server
      
$ID $this->ID(); // The ID to display next to the logging commands
      
$data fread($this->socket1024); // Read the data
      
if (!strlen($data)) // If there was no data, assume that the link was closed and close the socket
      
{
        
fclose($this->socket);
        
$this->socket null;
        return;
      }
      
// tmpstr will store the full string, including the previous one if there was more data incoming
      
$tmpstr '';
      if (
$this->istheremore)
        
$tmpstr .= $this->prevstr;
      
$tmpstr .= $data;
      
$tokens IRCtoks($tmpstr); // Split the message into seperate "tokens"
      
$this->istheremore substr($data, -1) == "\n" false true// istheremore will only be true if the last character is NOT a \n character
      
$this->morecoming false// morecoming will be false until the last line if the last line doesn't end in \n
      
if (count($tokens))
        foreach (
$tokens as $key => $token// For every token, check it
        
{
          if (!
$this->morecoming)
          {
            if (
$logging)
              
dated_fwrite($log"<- $token"$ID); // Log the line as incoming if we are logging
            
$msgparts IRCmsg($token); // Split the line into seperate parts
            
if ($msgparts[0] == 'PING'// PING request
            
{
              
$this->sendIRCcommand('PONG'''$msgparts[1]); // Reply with PONG
              
continue;
            }
            
$who IRCwho($msgparts[0]); // Get the nick (or servername) without the : or the ident/host
            
$whoid IRCidenthost($msgparts[0]); // Get the ident@hostname of the user if it's not a server
            
if (!isset($msgparts[1]))
              continue; 
// If there is nothing after the first "command", skip the line (it might not be correct anyways)
            
if ($msgparts[1] == '001'// 001 is the WELCOME string, get the network name from that, just in case
            
{
              
$space strpos($msgparts[3], ' '16);
              
$network_name substr($msgparts[3], 15$space 15);
              if (
$debugging)
                
dated_fwrite($debugfile"Changing network name to $network_name based on 001 reply.");
              
$this->network $network_name;
              
$ID $this->ID();
            }
            if (
$msgparts[1] == '005'// ISUPPORT numeric for PREFIX, CHANMODES, and NAMESX
            
{
              for (
$x 3$x count($msgparts) - 1; ++$x// Go from the 4th part of the message on (1st is from, 2nd is 005, 3rd is to)
              
{
                if (
substr($msgparts[$x], 07) == 'PREFIX='// PREFIX 005, format of PREFIX=(qaohv)~&@%+ for example
                
{
                  
$openparen strpos($msgparts[$x], '(');
                  
$closeparen strpos($msgparts[$x], ')');
                  
$y $openparen 1;
                  
$z $closeparen 1;
                  for (; 
$y $closeparen; ++$y, ++$z)
                    
$this->prefixes[] = array('char' => $msgparts[$x][$y], 'mode' => $msgparts[$x][$z]);
                  if (
$debugging)
                  {
                    
$printedprefixes rtrim(print_r($this->prefixestrue));
                    
dated_fwrite($debugfile"Got PREFIX from 005, set prefixes to:\n$printedprefixes");
                  }
                }
                if (
substr($msgparts[$x], 010) == 'CHANMODES='// CHANMODES 005, format of CHANMODES=listmodes,parammodes,paramaddonlymodes,paramlessmodes
                
{
                  
$modes substr($msgparts[$x], 10);
                  list(
$a$b$c$d) = explode(','$modes);
                  
$parammodes "$a{$b}";
                  for (
$y 0$y strlen($parammodes); ++$y)
                    
$this->chanmodes['param'][] = $parammodes[$y];
                  for (
$y 0$y strlen($c); ++$y)
                    
$this->chanmodes['paramaddonly'][] = $c[$y];
                  for (
$y 0$y strlen($d); ++$y)
                    
$this->chanmodes['paramless'][] = $d[$y];
                  if (
$debugging)
                  {
                    
$printedchanmodes rtrim(print_r($this->chanmodestrue));
                    
dated_fwrite($debugfile"Got CHANMODES from 005, set chanmodes to:\n$printedchanmodes");
                  }
                }
                if (
substr($msgparts[$x], 08) == 'NETWORK=')
                {
                  
$network_name substr($msgparts[$x], 8);
                  
$this->network $network_name;
                  if (
$debugging)
                    
dated_fwrite($debugfile"Got NETWORK from 005, changing network name to $network_name.");
                  
$ID $this->ID();
                }
                if (
$msgparts[$x] == 'NAMESX')
                {
                  if (
$debugging)
                    
dated_fwrite($debugfile"Got NAMESX from 005, telling IRC server we support NAMESX.");
                  
$this->sendIRCcommand('PROTOCTL NAMESX'); // Send a PROTOCTL NAMESX if the IRC server supports it
                
}
              }
            }
            if (
$msgparts[1] == '433' && strtolower($msgparts[2]) == strtolower($this->myircNick)) // Failed NICK reply for bot
            
{
              if (
$debugging)
                
dated_fwrite($debugfile"Failed NICK for bot.");
              if (
strtolower($msgparts[3]) == strtolower($mainIRCnick) && !$this->needchangeIRCnick// If the failed NICK was for the main nick and we haven't yet needed to change
              
{
                
$this->sendIRCcommand('NICK'$altIRCnick);
                
$this->myircNick $altIRCnick;
                
$this->needchangeIRCnick true;
                
$this->timeoflastnick time();
              }
            }
            
// If any of the following comes up, remove the channel from the list and set up to kill the bot if it could not enter any channels
            // 471 = full channel, 473 = invite only, 474 = banned, 475 = key'd channel, 477 = channel needs registered nicks
            
if ($msgparts[1] == '471' || $msgparts[1] == '473' || $msgparts[1] == '474' || $msgparts[1] == '475' || $msgparts[1] == '477')
            {
              
$key find_chan($this->chans$msgparts[3]);
              if (
$debugging)
              {
                
$printedchans rtrim(print_r($this->chanstrue));
                
dated_fwrite($debugfile"Unable to join {$msgparts[3]}, removing channel from channel list.\nChannel list prior to removal:\n$printedchans");
              }
              if (
$key !== false)
                
array_splice($this->chans$key1);
              if (
$debugging)
              {
                
$printedchans rtrim(print_r($this->chanstrue));
                
dated_fwrite($debugfile"Channel list after removal:\n$printedchans");
              }
              
$this->seeIfInChannels time();
            }
            if (
$msgparts[1] == 'NICK'// NICK for anyone
            
{
              if (
$who == 'Geemer1')
                
$who 'Geemer';
              if (
$debugging)
                
dated_fwrite($debugfile"Got a NICK change for $who -> {$msgparts[2]}.");
              foreach (
$otherServer->chans as $ircChan// For each channel on the opposing connection, check if the bot is in that channel on that network and check if the user changing nicks is in the channel on this side of the connection, and if those are true, send out the nick message to the opposing connection
              
{
                if (
$otherServer->inChan($ircChan['name']) !== false)
                {
                  
$ukey $this->find_user($who);
                  if (
$ukey !== false)
                  {
                    
$ckey find_chan($this->users[$ukey]['chans'], $ircChan['name']);
                    if (
$ckey !== false)
                      
$otherServer->sendIRCcommand('PRIVMSG'$ircChan['name'], chr(3) . "03* $who@{$this->network} is now known as {$msgparts[2]}@{$this->network}chr(15));
                  }
                }
              }
              if (
strtolower($who) == strtolower($this->myircNick)) // NICK for the bot
              
{
                
$this->needchangeIRCnick strtolower($msgparts[2]) == strtolower($mainIRCnick) ? false true// If the NICK is the bot's main nick, we don't need to change it
                
$this->myircNick $msgparts[2];
              }
              else 
// NICK for everyone else
              
{
                
$key $this->find_user($who);
                if (
$key !== false)
                  
$this->users[$key]['user'] = $msgparts[2];
              }
            }
            if (
$msgparts[1] == 'PRIVMSG'// PRIVMSG from anyone (including CTCPs and actions)
            
{
              if (
substr($msgparts[3], 01) == chr(1)) // CTCP
              
{
                
$space strpos($msgparts[3], ' ');
                
$ctcpdata $space === false '' substr($msgparts[3], $space 1);
                
$msgpart1 $space === false $msgparts[3] : substr($msgparts[3], 0$space);
                if (
substr($ctcpdata, -1) == chr(1))
                  
$ctcpdata substr($ctcpdata0, -1);
                
$ctcp strtoupper(substr($msgpart11));
                if (
substr($ctcp, -1) == chr(1))
                  
$ctcp substr($ctcp0, -1);
                if (
$ctcp != 'ACTION'// If the CTCP was not an ACTION event
                
{
                  if (
$who == 'Geemer1')
                    
$who 'Geemer';
                  if (
$debugging)
                    
dated_fwrite($debugfile"Got a CTCP of $ctcp from $who.");
                  
$ctcpreply '';
                  if (
$ctcp == 'VERSION')
                    
$ctcpreply chr(1) . 'VERSION IRC Server Bridge Bot by Naram Qashat a.k.a. CyberBotX' chr(1);
                  if (
$ctcp == 'PING')
                    
$ctcpreply $msgparts[3];
                  if (
$ctcp == 'TIME')
                    
$ctcpreply chr(1) . 'TIME ' date('D m/d/Y H:i:s O'). chr(1);
                  if (
$ctcp == 'NAMES' && $ctcpdata && $ctcpdata[0] == '#' && strpos($ctcpdata' ') === false && $this->inChan($ctcpdata) !== false)
                  {
                    if (
$debugging)
                      
dated_fwrite($debugfile"Got a NAMES request for channel $ctcpdata.");
                    
$names = array();
                    foreach (
$otherServer->users as $key => $user// For each user...
                    
{
                      
$ckey find_chan($user['chans'], $ctcpdata); // Find the channel entry in their user entry
                      
if ($ckey === false)
                        continue;
                      
$modes '';
                      foreach (
$user['chans'][$ckey]['status'] as $mode)
                        
$modes .= $mode;
                      
$names[] = "$modes{$user['user']}";
                    }
                    
$names_reply implode(' '$names);
                    
$ctcpreply chr(1) . "NAMES $ctcpdata $names_replychr(1);
                  }
                  if (
$ctcpreply)
                    
$this->sendIRCcommand('NOTICE'$who$ctcpreply);
                  if (
$ctcp == 'REMCMD' && $ctcpdata// This handles remote commands via CTCP command
                  
{
                    if (!
allow_remcmd(substr($msgparts[0], 1)))
                    {
                      
$this->sendIRCcommand('NOTICE'$who"I could not find {$msgparts[0]} in my list of those allowed to run remote commands!");
                      continue;
                    }
                    
$space strpos($ctcpdata' ');
                    
$remcmd StripControlCodes(substr($ctcpdata0$space === false strlen($ctcpdata) : $space));
                    if (
strtolower($remcmd) != 'dump')
                    {
                      if (
$space !== false)
                      {
                        
$space2 strpos($ctcpdata' '$space 1);
                        
$chan StripControlCodes(substr($ctcpdata$space 1$space2 === false strlen($ctcpdata) : $space2 $space 1));
                      }
                      else
                        continue;
                    }
                    if (
$debugging)
                      
dated_fwrite($debugfile"Got a valid REMCMD of $remcmd.");
                    switch (
strtolower($remcmd))
                    {
                      case 
'msg'// Remote command to make the bot say something in the channel
                        
if ($space2 === false || $this->inChan($chan) === false)
                          break;
                        
$msg substr($ctcpdata$space2 1);
                        
$this->sendIRCcommand('PRIVMSG'$chan$msg);
                        if (
$otherServer->inChan($chan) !== false)
                          
$otherServer->sendIRCcommand('PRIVMSG'$chan$msg);
                        break;
                      case 
'action'// Remote command to make the bot do an action in the channel
                        
if ($space2 === false || $this->inChan($chan) === false)
                          break;
                        
$action substr($ctcpdata$space2 1);
                        
$this->sendIRCcommand('PRIVMSG'$chanchr(1) . "ACTION $actionchr(1));
                        if (
$otherServer->inChan($chan) !== false)
                          
$otherServer->sendIRCcommand('PRIVMSG'$chanchr(1) . "ACTION $actionchr(1));
                        break;
                      case 
'join'// Remote command to make the bot join a channel
                        
if ($chan[0] != '#')
                          break;
                        
$key $this->inChan($chan);
                        if (
$key === false)
                        {
                          
$this->chans[] = array('name' => $chan'status' => 'pendingjoin');
                          
$this->sendIRCcommand('JOIN'$chan);
                        }
                        
$key $otherServer->inChan($chan);
                        if (
$key === false)
                        {
                          
$otherServer->chans[] = array('name' => $chan'status' => 'pendingjoin');
                          
$otherServer->sendIRCcommand('JOIN'$chan);
                        }
                        break;
                      case 
'part'// Remote command to make the bot part a channel
                        
if ($chan[0] != '#')
                          break;
                        
$key $this->inChan($chan);
                        if (
$key !== false)
                        {
                          if (
count($this->chans) == 1// Don't allow the bot to be parted from it's last channel
                          
{
                            
$this->sendIRCcommand('NOTICE'$who"Ignoring part request becasue you would be parting me from the last channel I'm in on $ID.");
                            break;
                          }
                          
$this->chans[$key]['status'] = 'pendingpart';
                          
$this->sendIRCcommand('PART'$chan);
                        }
                        
$key $otherServer->inChan($chan);
                        if (
$key !== false)
                        {
                          if (
count($otherServer->chans) == 1// Don't allow the bot to be parted from it's last channel
                          
{
                            
$OID $otherServer->ID();
                            
$this->sendIRCcommand('NOTICE'$who"Ignoring part request becasue you would be parting me from the last channel I'm in on $OID.");
                            break;
                          }
                          
$otherServer->chans[$key]['status'] = 'pendingpart';
                          
$otherServer->sendIRCcommand('PART'$chan);
                        }
                        break;
                      case 
'dump'// THIS IS MAINLY FOR DEBUGGING
                        
if ($fp fopen('dump.txt''wb'))
                        {
                          
fwrite($fp"botStartupTime: $botStartupTime\n");
                          
fwrite($fp'Dump run from SERVER ' . ($this === $server1 '1' '2') . "\n");
                          
fwrite($fp'Dump run at ' date('D m/d/y H:i:s O') . "\n");
                          
fwrite($fp"SERVER 1:\n");
                          
fwrite($fp"=========\n");
                          
fwrite($fp"server: {$server1->server}\n");
                          
fwrite($fp"network: {$server1->network}\n");
                          
fwrite($fp"myircNick: {$server1->myircNick}\n");
                          
fwrite($fp'needchangeIRCnick: ' . ($server1->needchangeIRCnick 'true' 'false') . "\n");
                          
fwrite($fp'istheremore: ' . ($server1->istheremore 'true' 'false') . "\n");
                          
fwrite($fp"endOfMOTD: {$server1->endOfMOTD}\n");
                          
$output print_r($server1->userstrue);
                          
fwrite($fp"users: $output");
                          
$output print_r($server1->prefixestrue);
                          
fwrite($fp"prefixes: $output");
                          
$output print_r($server1->chanmodestrue);
                          
fwrite($fp"chanmodes: $output");
                          
$output print_r($server1->chanstrue);
                          
fwrite($fp"ircChan: $output");
                          
fwrite($fp"SERVER 2:\n");
                          
fwrite($fp"=========\n");
                          
fwrite($fp"server: {$server2->server}\n");
                          
fwrite($fp"network: {$server2->network}\n");
                          
fwrite($fp"myircNick: {$server2->myircNick}\n");
                          
fwrite($fp'needchangeIRCnick: ' . ($server2->needchangeIRCnick 'true' 'false') . "\n");
                          
fwrite($fp'istheremore: ' . ($server2->istheremore 'true' 'false') . "\n");
                          
fwrite($fp"endOfMOTD: {$server2->endOfMOTD}\n");
                          
$output print_r($server2->userstrue);
                          
fwrite($fp"users: $output");
                          
$output print_r($server2->prefixestrue);
                          
fwrite($fp"prefixes: $output");
                          
$output print_r($server2->chanmodestrue);
                          
fwrite($fp"chanmodes: $output");
                          
$output print_r($server2->chanstrue);
                          
fwrite($fp"ircChan: $output");
                          
fclose($fp);
                          
$this->sendIRCcommand('NOTICE'$who'Data dumped to dump.txt successfully.');
                        }
                        else
                          
$this->sendIRCcommand('NOTICE'$who'Unable to open dump.txt for writing.');
                    }
                  }
                }
                else
                {
                  
// Was an ACTION command, check to make sure the bot is in the channel on both ends, then send the message to the opposing connection
                  
if ($this->inChan($msgparts[2]) !== false && $otherServer->inChan($msgparts[2]) !== false)
                    
$otherServer->sendIRCcommand('PRIVMSG'$msgparts[2], chr(3) . "06* $who@{$this->network} $ctcpdatachr(15));
                }
              }
              else 
// normal PRIVMSG
              
{
                
$stripped_msg StripControlCodes($msgparts[3]);
                if (
$this->inChan($msgparts[2]) !== false// As long as the bot is in the channel in question
                
{
                  if (
strtolower($stripped_msg) == '~help' || strtolower($stripped_msg) == '!help')
                  {
                    if (
$who == 'Geemer1')
                      
$who 'Geemer';
                    
// Help, obviously, sent as a NOTICE to the person who asked for it
                    
$this->sendIRCcommand('NOTICE'$who'Help for IRC Server Bridge Bot');
                    
$this->sendIRCcommand('NOTICE'$who'==============================');
                    
$this->sendIRCcommand('NOTICE'$who'!uptime or ~uptime: Shows the bot\'s uptime and the uptime of the operating system the bot is running on');
                  }
                  if (
strtolower($stripped_msg) == '~uptime' || strtolower($stripped_msg) == '!uptime')
                  {
                    
// Bot's uptime, uses *nix's uptime command line option to get the system's uptime if it's available
                    
$botuptime convertTime(time() - $botStartupTime); // Convert the bot's uptime (current time minus the start time) to human readable format
                    
$uptime = @explode(',', `uptime`); // Try to get the system's uptime using the *nix "uptime" utility, if it exists
                    // uptime is in the following format:
                    //  11:51PM  up 5 days, 44 mins, 0 users, load averages: 1.40, 1.29, 1.41
                    
if (count($uptime)) // If we got a result, parse it
                    
{
                      
$first 0// First subscript that contains the uptime
                      
$last count($uptime) - 1// Last subscript that contains the uptime
                      
for ($x 0$x count($uptime); ++$x)
                      {
                        
$uptime[$x] = trim($uptime[$x]); // Trim all preceeding and trailing whitespace from the current part of uptime result
                        
if (($up strpos($uptime[$x], 'up ')) !== false// If we find the word up was found, the beginning of the uptime is in this string
                        
{
                          
$uptime[$x] = substr($uptime[$x], $up 3); // Remove everything before the time itself
                          
$first $x// Set this subscript as the first one
                        
}
                        if (
strpos($uptime[$x], 'user') !== false)
                          
$last $x 1// If user was found in this subscript, the previous one was the last
                      
}
                      
$uptime implode(', 'array_slice($uptime$first$last $first 1)); // Recombine only the needed uptime parts
                    
}
                    else
                      
$uptime ''// No uptime command, make a blank string
                    
$this->sendIRCcommand('PRIVMSG'$msgparts[2], "Bot uptime: $botuptime");
                    if (
$uptime)
                      
$this->sendIRCcommand('PRIVMSG'$msgparts[2], "System uptime: $uptime");
                  }
                  else
                  {
                    
// Just a normal channel message, send it to the opposing connection if the bot is in the channel on that end
                    
if ($otherServer->inChan($msgparts[2]) !== false)
                      
$otherServer->sendIRCcommand('PRIVMSG'$msgparts[2], "<$who@{$this->network}$msgparts[3]");
                  }
                }
                elseif (
strtolower($msgparts[2]) == strtolower($this->myircNick)) // If it wasn't for a channel, it might've been for the bot
                
{
                  if (
$who =='Geemer1')
                    
$who 'Geemer';
                  
$this->sendIRCcommand('PRIVMSG'$who'Sorry, I am just a bot.'); // Tell the person this is a bot
                
}
              }
            }
            if (
$msgparts[1] == 'NOTICE'// NOTICE from anyone
            
{
              
$stripped_msg StripControlCodes($msgparts[3]);
              if (
strtolower($msgparts[2]) == strtolower($this->myircNick) && strpos($who'.') === false// If NOTICE is for the bot and not from a server
              
{
                if (
strtolower($who) == 'nickserv'// If it's from NickServ
                
{
                  
// When the message is about changing nickname, send the NickServ password
                  // When the message is that the password was accepted, try to join the channels
                  
if ($stripped_msg == 'If you do not change your nickname within one minute, you will be disconnected.' || $stripped_msg == 'If you do not change within one minute, I will change your nick.')
                  {
                    
$password rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128$ns_keypack('H*'$ns_password), MCRYPT_MODE_ECBstr_pad("\0"16"\0")), "\0");
                    
$this->sendIRCcommand('PRIVMSG''NickServ'"identify $password");
                  }
                  elseif (
$stripped_msg == 'Password accepted -- you are now recognized.')
                    foreach (
$this->chans as $key => $ircChan)
                      if (
$ircChan['status'] == 'pendingjoin')
                        
$this->sendIRCcommand('JOIN'$ircChan['name']);
                }
                else 
// For all other NOTICES
                
{
                  
// If the NOTICE was ~quit followed by the correct password, make the bot quit from IRC
                  
if (strtolower(substr($stripped_msg05)) == '~quit' && md5(substr($stripped_msg6)) == $quit_password)
                  {
                    
$this->sendIRCcommand('QUIT'''"CBX IRC Server Bridge Bot quitting on request of $who@{$this->network}");
                    
$otherServer->sendIRCcommand('QUIT'''"CBX IRC Server Bridge Bot quitting on request of $who@{$this->network}");
                    
$quit true;
                  }
                }
              }
              elseif (
$this->inChan($msgparts[2]) !== false && $otherServer->inChan($msgparts[2])) // A channel-wide notice, send it out to the opposing connection if it's in the channel as well
              
{
                
$otherServer->sendIRCcommand('PRIVMSG'$msgparts[2], chr(3) . "05-$who@{$this->network}$msgparts[3]chr(15));
              }
            }
            if (
$msgparts[1] == 'JOIN'// JOIN for anyone
            
{
              if (
$who == 'Geemer1')
                
$who 'Geemer';
              if (
$debugging)
                
dated_fwrite($debugfile"Got a JOIN to {$msgparts[2]} for $who.");
              if (
strtolower($who) == strtolower($this->myircNick)) // If the JOIN is for the bot
              
{
                if ((
$key $this->inChan($msgparts[2], 'pendingjoin')) === false)
                {
                  
$this->sendIRCcommand('PART'$msgparts[2]); // PART if the bot isn't pending a join to the channel
                  
continue;
                }
                else
                  
$this->chans[$key]['status'] = 'joined'// Otherwise make it joined
              
}
              else 
// JOIN for anyone else
              
{
                if (
$this->inChan($msgparts[2]) !== false)
                  
$this->add_user($who$msgparts[2]); // Add that user to the channel
              
}
              
// If the bot is in the channel on the opposing connection, display the join there
              
if ($otherServer->inChan($msgparts[2]) !== false)
                
$otherServer->sendIRCcommand('PRIVMSG'$msgparts[2], chr(3) . "03* Joins: $who@{$this->network} ($whoid)" chr(15));
            }
            if (
$msgparts[1] == 'PART'// PART for anyone
            
{
              if (
$who == 'Geemer1')
                
$who 'Geemer';
              if (
$debugging)
                
dated_fwrite($debugfile"Got a PART to {$msgparts[2]} for $who.");
              if (
strtolower($who) != strtolower($this->myircNick))
                
$this->remove_user_from_chan($who$msgparts[2]); // PART for everyone but the bot, remove them from the channel
              
elseif (($key $this->inChan($msgparts[2], 'pendingpart')) !== false// PART for the bot and it's pending a part
              
{
                
$this->remove_all_users_from_chan($msgparts[2]); // Remove all user entries from that channel
                
array_splice($this->chans$key1); // Remove the channel from the bot's list of channels
              
}
              elseif ((
$key $this->inChan($msgparts[2])) !== false// PART for the bot but it isn't pending a part from it
              
{
                
$this->remove_all_users_from_chan($msgparts[2]); // Remove the user entries for the channel to regenerate them
                
$this->chans[$key]['status'] = 'pendingjoin'// Make the channel pending a join
                
$this->sendIRCcommand('JOIN'$msgparts[2]); // Re-JOIN the channel
              
}
              
// If the bot is in the channel on the opposing connection, display the part there
              
if ($otherServer->inChan($msgparts[2]) !== false)
                
$otherServer->sendIRCcommand('PRIVMSG'$msgparts[2], chr(3) . "03* Parts: $who@{$this->network} ($whoid)" . (isset($msgparts[3]) ? " ($msgparts[3])" '') . chr(15));
            }
            if (
$msgparts[1] == '353' && $this->inChan($msgparts[4]) !== false// NAMES reply for a joined channel
            
{
              if (
$debugging)
                
dated_fwrite($debugfile"Got a NAMES reply for {$msgparts[4]}.");
              
// This will set up an array of the users in the channel and merge them into the list for the channel
              
$users_tmp explode(' '$msgparts[5]);
              
$users_this = array();
              foreach (
$users_tmp as $user)
              {
                if (!
$user)
                  continue;
                
$status = array();
                if (
$this->find_mode($user[0], false) !== false)
                {
                  for (
$x 1$x strlen($user); ++$x)
                    if (
$this->find_mode($user[$x], false) === false)
                      break;
                  for (
$y 0$y $x; ++$y)
                    
$status[] = $user[$y];
                  
$this->sort_modes($status);
                  
$user substr($user$x);
                }
                
$users_this[] = array('status' => $status'user' => $user);
              }
              
$this->merge_users($users_this$msgparts[4]);
            }
            if (
$msgparts[1] == 'KICK' && ($key $this->inChan($msgparts[2])) !== false// KICK for a joined channel
            
{
              if (
$who == 'Geemer1')
                
$who 'Geemer';
              if (
$debugging)
                
dated_fwrite($debugfile"Got a KICK for {$msgparts[3]} by $who in {$msgparts[2]}.");
              
$this->remove_user_from_chan($msgparts[3], $msgparts[2]); // Remove the user from the channel regardless of if it's the bot or not
              
if (strtolower($msgparts[3]) == strtolower($this->myircNick)) // If the KICK was for the bot
              
{
                
$this->remove_all_users_from_chan($msgparts[2]); // Remove the user entries for the channel to regenerate them
                
$this->chans[$key]['status'] = 'pendingjoin'// Make the channel pending a join
                
$this->sendIRCcommand('JOIN'$msgparts[2]); // Re-JOIN the channel
              
}
              
// If the bot is in the channel on the opposing connection, display the kick there
              
if ($otherServer->inChan($msgparts[2]) !== false)
                
$otherServer->sendIRCcommand('PRIVMSG'$msgparts[2], chr(3) . "03* {$msgparts[3]}@{$this->network} was kicked by $who@{$this->network}. (isset($msgparts[4]) ? " ({$msgparts[4]})" '') . chr(15));
            }
            if (
$msgparts[1] == 'QUIT')
            {
              if (
$who == 'Geemer1')
                
$who 'Geemer';
              if (
$debugging)
                
dated_fwrite($debugfile"Got a QUIT for $who.");
              foreach (
$otherServer->chans as $ircChan// For each channel on the opposing connection, check if the bot is in that channel on that network and that the user who quit was in that channel, and if they were, send out the quit message to the opposing connection
              
{
                if (
$otherServer->inChan($ircChan['name']) !== false)
                {
                  
$ukey $this->find_user($who);
                  if (
$ukey !== false)
                  {
                    
$ckey find_chan($this->users[$ukey]['chans'], $ircChan['name']);
                    if (
$ckey !== false)
                      
$otherServer->sendIRCcommand('PRIVMSG'$ircChan['name'], chr(3) . "02* Quits: $who@{$this->network} ($whoid) ({$msgparts[2]})" chr(15));
                  }
                }
              }
              if (
strtolower($who) != strtolower($this->myircNick))
                
$this->remove_user($who); // QUIT for anyone except the bot, remove their user entry
              
else // Make the bot completely quit, if need be
              
{
                
$quit true;
                
$this->disconnect();
                return;
              }
            }
            if (
$msgparts[1] == 'MODE' && $this->inChan($msgparts[2]) !== false// MODE for a joined channel
            
{
              if (
$debugging)
                
dated_fwrite($debugfile"Got a MODE change for {$msgparts[2]}.");
              
// The following will get the modes and their params (if any), then update users for any prefix modes
              
$mode_str $modes $msgparts[3];
              
$params = array();
              for (
$x 4$x count($msgparts); ++$x)
              {
                
$params[] = $msgparts[$x]; // Get all the params
                
$mode_str .= {$msgparts[$x]}";
              }
              if (
$otherServer->inChan($msgparts[2]) !== false)
                
$otherServer->sendIRCcommand('PRIVMSG'$msgparts[2], chr(3) . "03* $who@{$this->network} sets mode: $mode_strchr(15));
              
$add 1;
              for (
$x 0$y 0$x strlen($modes); ++$x)
              {
                
$modechar $modes[$x];
                switch (
$modechar)
                {
                  case 
'+'$add 1; break; // + = Mode being added
                  
case '-'$add 0; break; // - = Mode being removed
                  
default:
                    
$modetype $this->find_chanmode($modechar);
                    
$param $modetype == 'param' || ($modetype == 'paramaddonly' && $add) || $modetype == 'prefix' $params[$y++] : '';
                    if (
$modetype == 'prefix'// If the mode character is a prefix mode
                    
{
                      
$ukey $this->find_user($param); // Find the user
                      
$ckey find_chan($this->users[$ukey]['chans'], $msgparts[2]); // Find the channel entry in their user entry
                      
$mkey $this->find_mode($modechar); // Find the key of the mode
                      
$prefix $this->prefixes[$mkey]['mode']; // Get the actual prefix symbol
                      
$skey false;
                      
// The following finds the key of the prefix symbol if it exists
                      
if (count($this->users[$ukey]['chans'][$ckey]['status']))
                        foreach (
$this->users[$ukey]['chans'][$ckey]['status'] as $key => $mode)
                          if (
$mode == $prefix)
                            
$skey $key;
                      if (
$add && $skey === false// If we are adding a mode and the prefix symbol does not exist, add it and sort the modes in correct order
                      
{
                        
$this->users[$ukey]['chans'][$ckey]['status'][] = $prefix;
                        
$this->sort_modes($this->users[$ukey]['chans'][$ckey]['status']);
                      }
                      else 
// We aren't adding, and the next will only happen if the prefix symbol was found
                      
{
                        if (
$skey !== false)
                          
array_splice($this->users[$ukey]['chans'][$ckey]['status'], $skey1); // Remove the prefix symbol
                      
}
                    }
                }
              }
            }
            if (
$msgparts[1] == '376' || $msgparts[1] == '422')
              
$this->endOfMOTD time(); // 376 = End of MOTD, 422 = No MOTD
          
}
          
$this->morecoming $key == count($tokens) - && $this->istheremore true false// morecoming will be true if we are on the second to last token and there is more coming
          
$laststr $token// This token will be the last one
        
}
      if (
$this->istheremore)
        
$this->prevstr $laststr// If there is more coming, set the previous string to the last one
    
}
  }
  function 
output_help()
  {
    
// This function output's the syntax of the command
    
global $argv
?>
This script is an IRC Server Bridge Bot.  It will attempt to fork itself
into the background if your Operating System allows it.

 Usage: <?php echo $argv[0?> [<options>] <IRC Server 1> <IRC Server 2> <Initial IRC Channels>
     or <?php echo $argv[0?> -p|--password <Quit Password> [<NickServ Password>]

IRC Servers must contain a dot if not localhost.
IRC Channels must start with #'s.  If you need the bot in more than one IRC
channel, seperate the channel names with commas only (no spaces).

Options:
 --help, -h, or -?  to get this help
 --nofork or -f     don't let the bot fork into the background
 --debug or -d      debugging mode (implies no forking [not])
 --log or -l        log IRC traffic to irc.log
 --password or -p   change the passwords, does not run the bot, if NickServ
                    password left out, Quit password will be used for NickServ
<?php
  
}
  function 
rewrite_settings_file()
  {
    
// This funtion will rewrite the settings file, in case it changed
    
global $mainIRCnick$altIRCnick$allowed_remcmd$quit_password$ns_key$ns_password;
    if (
$fp = @fopen('settings.php''wb'))
    {
      
fwrite($fp"<?php\n");
      
fwrite($fp"  /* CBX's IRC Server Bridge Bot\n");
      
fwrite($fp"     - This is the settings file for the bot\n");
      
fwrite($fp"     created by Naram Qashat (CyberBotX)\n");
      
fwrite($fp'     last modified: ' date('n-j-Y') . " */\n");
      
fwrite($fp"  \$mainIRCnick = '$mainIRCnick'; // The bot's main nick to use\n");
      
fwrite($fp"  \$altIRCnick = '$altIRCnick'; // An alternate nick for the bot if the main nick is in use\n");
      
fwrite($fp"  \$allowed_remcmd = array(\n");
      for (
$x 0$x count($allowed_remcmd); ++$x)
      {
        
fwrite($fp"    '{$allowed_remcmd[$x]}'");
        if (
$x != count($allowed_remcmd) - 1)
          
fwrite($fp',');
        
fwrite($fp"\n");
      }
      
fwrite($fp"  ); // Array of hostmasks allowed to use remote commands\n");
      
fwrite($fp"  \$quit_password = '$quit_password'; // MD5 of quit password\n");
      
fwrite($fp"  \$ns_key = '$ns_key'; // MD5 of NickServ password\n");
      
fwrite($fp"  \$ns_password = '$ns_password'; // mcrypt'd NickServ password\n");
      
fwrite($fp"?>");
      
fclose($fp);
    }
  }
  function 
allow_remcmd($who)
  {
    
// This function checks if the given user is on the list of hostmasks allowed to use remote commands on the bot
    
global $allowed_remcmd$debugging$debugfile;
    if (
$debugging)
      
dated_fwrite($debugfile"allow_remcmd($who)");
    foreach (
$allowed_remcmd as $remcmd// For every user in the $allowed_remcmd array...
    
{
      
$regex str_replace(array('.''*''!'), array('\.''.*''\!'), $remcmd); // Replaces . with \., * with .*, and ! with \!, for the regex
      
if ($debugging)
        
dated_fwrite($debugfile"regex checking against $regex.");
      if (
preg_match("/$regex/i"$who))
        return 
true;
    }
    return 
false;
  }
  function 
convertTime($weeks)
  {
    
// This function converts a *nix timestamp of how many seconds between two times into a human readable time
    
global $debugging$debugfile;
    if (
$debugging)
      
dated_fwrite($debugfile"convertTime($weeks)");
    
$seconds $weeks 60;
    
$weeks -= $seconds;
    
$weeks /= 60;
    
$minutes $weeks 60;
    
$weeks -= $minutes;
    
$weeks /= 60;
    
$hours $weeks 24;
    
$weeks -= $hours;
    
$weeks /= 24;
    
$days $weeks 7;
    
$weeks -= $days;
    
$weeks /= 7;
    
$times = array();
    if (
$weeks)
      
$times[] = "$weeks week" . ($weeks 's' '');
    if (
$days)
      
$times[] = "$days day" . ($days 's' '');
    if (
$hours)
      
$times[] = "$hours hour" . ($hours 's' '');
    if (
$minutes)
      
$times[] = "$minutes minute" . ($minutes 's' '');
    if (
$seconds)
      
$times[] = "$seconds second" . ($seconds 's' '');
    return 
implode(' '$times);
  }
  
// The next function sorts a channels array based on a case-insensitive sort of everything after the #
  
function sort_chans($a$b) { return strcasecmp(substr($a['name'], 1), substr($b['name'], 1)); }
  function 
find_chan($chans$chan)
  {
    
// This function will find a channel within the given $chans array and return the key within that array
    
global $debugging$debugfile;
    if (
$debugging)
    {
      
$printedchans rtrim(print_r($chanstrue));
      
dated_fwrite($debugfile"find_chan(\$chans, $chan) with \$chans being:\n$printedchans");
    }
    foreach (
$chans as $key => $channel)
      if (
strtolower($channel['name']) == strtolower($chan))
        return 
$key;
    return 
false;
  }
  function 
add_chan(&$user$chan$status null)
  {
    
// This function will see if a user is within the given channel, if not, adds them, if they are, will just update their status if given
    
global $debugging$debugfile;
    if (
$debuggingdated_fwrite($debugfile"add_chan({$user['user']}$chan$status)");
    
$key find_chan($user['chans'], $chan);
    if (
$key === false)
    {
      if (
$debugging)
      {
        
$printedchans rtrim(print_r($user['chans'], true));
        
dated_fwrite($debugfile"Channel was not found, adding new channel entry and sorting channels.\nChannel list prior to add:\n$printedchans");
      }
      
$user['chans'][] = array('name' => $chan'status' => $status $status : array()); // Add the channel
      
usort($user['chans'], 'sort_chans'); // Sort the channels
      
if ($debugging)
      {
        
$printedchans rtrim(print_r($user['chans'], true));
        
dated_fwrite($debugfile"Channel list after add:\n$printedchans");
      }
    }
    else
    {
      if (
$debugging)
        
dated_fwrite($debugfile"Channel was found, updating status only...");
      
$user['chans'][$key]['status'] = $status $status : array(); // Update the status for an existing channel
    
}
  }
  
// The next function does a case-insensitive sort of a user's nick
  
function sort_users($a$b) { return strcasecmp($a['user'], $b['user']); }
  function 
IRCtoks($str)
  {
    
// This function takes the received string from an IRC server and splits it apart at the \r\n gaps, returning an array with each of the "tokens"
    
$toks = array('');
    for (
$x 0$y 0$x strlen($str); ++$x)
    {
      if (
$str[$x] != "\n" && $str[$x] != "\r")
        
$toks[$y] .= $str[$x];
      if (
$str[$x] == "\n")
      {
        ++
$y;
        if (
$x != strlen($str) - 1)
          
$toks[$y] = '';
      }
    }
    return 
$toks;
  }
  function 
IRCmsg($str)
  {
    
// This function will take a single IRC command received from the server and split it apart at the spaces, handling the special case of a : marking the beginning of a full string
    
$msgparts = array(''); // Array for the message parts, starts with a single blank string
    
for ($x 0$y 0$x strlen($str); ++$x)
    {
      if (
$str[$x] == ':' && $x && !$msgparts[$y]) // The current character is a : that is not at the beginning, everything after it will be a single message
      
{
        
$msgparts[$y] = substr($str$x 1);
        break;
      }
      if (
$str[$x] == ' '// If we are at a space
      
{
        ++
$y;
        if (
$x != strlen($str))
          
$msgparts[$y] = ''// Add another blank string to the message parts if we are not at the end yet
        
continue; // Skip the space
      
}
      
$msgparts[$y] .= $str[$x]; // Add the character to the current message part
    
}
    return 
$msgparts// Return the message parts
  
}
  function 
IRCwho($who)
  {
    
// This function will return the username only of a full hostmask, removing the : from the beginning
    
$markpos strpos($who'!');
    
$new_who substr($who1$markpos === false strlen($who) : $markpos 1);
    if (
$new_who == 'Geemer')
      
$new_who .= '1';
    return 
$new_who;
  }
  function 
IRCidenthost($who)
  {
    
// This function will return the ident@hostname of a full hostmask
    
$markpos strpos($who'!');
    if (
$markpos === false)
      return 
'';
    return 
substr($who$markpos 1);
  }
  function 
StripControlCodes($text)
  {
    
// This function was taken from UnrealIRCd and modified for PHP by me, it removes all IRC control codes from a string and returns the stripped string
    
$i $save_len $nc $col $rgb 0;
    
$len strlen($text);
    
$save_text = -1;
    
$new_str '';
    for (; 
$len; --$len)
    {
      if (
$col && ((ctype_digit($text[$i]) && $nc 2) || ($text[$i] == ',' && $nc 3)))
      {
        ++
$nc;
        if (
$text[$i] == ',')
          
$nc 0;
      }
      
/* Syntax for RGB is ^DHHHHHH where H is a hex digit.
       * If < 6 hex digits are specified, the code is displayed
       * as text */
      
elseif ($rgb && ((ctype_xdigit($text[$i]) && $nc 6) || ($text[$i] == ',' && $nc 7)))
      {
        ++
$nc;
        if (
$text[$i] == ',')
          
$nc 0;
      }
      else
      {
        if (
$col$col 0;
        if (
$rgb)
        {
          if (
$nc != 6)
          {
            
$i $save_text 1;
            
$len $save_len 1;
            
$rgb 0;
            continue;
          }
          
$rgb 0;
        }
        switch (
ord($text[$i]))
        {
          case 
3// color
            
$col 1;
            
$nc 0;
            break;
          case 
4// RGB
            
$save_text $i;
            
$save_len $len;
            
$rgb 1;
            
$nc 0;
            break;
          case 
2// bold
          
case 31// underline
          
case 22// reverse
          
case 15// plain
            
break;
          default:
            
$new_str .= $text[$i];
        }
      }
      ++
$i;
    }
    return 
$new_str;
  }
  
$nofork $debugging $logging $resetPasswords false;
  
// The following is an argument parser I wrote to handle arguments with -'s (doesn't handle things like -fl yet)
  
$args = array();
  for (
$i 1$i $argc; ++$i)
  {
    switch (
$argv[$i])
    {
      case 
'-p':
      case 
'--password':
        
$resetPasswords true;
        break;
      case 
'-f':
      case 
'--nofork':
        
$nofork true;
        break;
      case 
'-l':
      case 
'--log':
        
$logging true;
        break;
      case 
'-d':
      case 
'--debug':
        
$debugging true;
        
//$nofork = true;
        
break;
      case 
'-?':
      case 
'-h':
      case 
'--help':
        
output_help();
        exit(
1);
      default:
        if (
substr($argv[$i], 01) == '-'// If it's an argument that started with - but wasn't handled already, it's an error
        
{
          
output_help();
          exit(
1);
        }
        
$args[] = $argv[$i]; // Add the argument to the args array as non-dash arguments
    
}
  }
  if (
$resetPasswords// If we are resetting the passwords...
  
{
    if (!
count($args)) // Needs at least 1 non-dash argument
    
{
      
output_help();
      exit(
1);
    }
    
$quit_password md5($args[0]); // md5 of the password
    
$ns_raw $args[count($args) == 0]; // If we had a 2nd argument, use that, otherwise, use the first one
    
$ns_key md5($ns_raw); // md5 of the password to use as a key for mcrypt
    
$ns_password bin2hex(mcrypt_encrypt(MCRYPT_RIJNDAEL_128$ns_key$ns_rawMCRYPT_MODE_ECBstr_pad("\0"16"\0"))); // mcrypt of the password
    
rewrite_settings_file(); // rewrite the settings file with the new data
    
exit(0); // Exit afterwards, don't run the bot
  
}
  if (
count($args) < 3// Needs at least 3 non-dash arguments
  
{
    
output_help();
    exit(
1);
  }
  if (
$args[0] != 'localhost' && strpos($args[0], '.') === false// Argument 1 wasn't localhost and didn't have a dot (meaning not a server name or IP)
  
{
    echo 
"First server name (if not localhost) must have a dot in it!\n";
    exit(
1);
  }
  if (
$args[1] != 'localhost' && strpos($args[1], '.') === false// Argument 2 wasn't localhost and didn't have a dot (meaning not a server name or IP)
  
{
    echo 
"Second server name (if not localhost) must have a dot in it!\n";
    exit(
1);
  }
  if (
$debugging// If -d or --debug was given, attempt to open the debug file for writing
  
{
    
$debugfile = @fopen('debug.txt''w');
    if (!
$debugfile// Couldn't open the file
    
{
      echo 
"Unable to open debug.txt for debugging purposes: $php_errormsg\n";
      exit(
1);
    }
  }
  
$server1 = new ircServerInfo($args[0]);
  
$server2 = new ircServerInfo($args[1]);
  
$commonChans explode(','$args[2]); // This array will store all the channels the bot is in or going to be in
  
foreach ($commonChans as $initChan// Go through each of the given channels that were in argument 1
  
{
    if (
$initChan[0] != '#'// If the first character isn't a #, it's not a channel name
    
{
      echo 
"Initial IRC Channels must begin with #'s!\n";
      exit(
1);
    }
    
$server1->chans[] = array('name' => $initChan'status' => 'pendingjoin'); // Add the channel to the array as pending join
    
$server2->chans[] = array('name' => $initChan'status' => 'pendingjoin'); // Add the channel to the array as pending join
  
}
  if (
$logging// If -l or --log was given, attempt to open the log file for writing
  
{
    
$log = @fopen('irc.log''w');
    if (!
$log// Couldn't open the file
    
{
      echo 
"Unable to open irc.log for logging purposes: $php_errormsg\n";
      exit(
1);
    }
  }
  if (!
$nofork && function_exists('pcntl_fork')) // If we didn't turn off forking and we are on a system that supports forking, daemonize the bot
  
{
    
$pid pcntl_fork();
    if (
$pid == -1// A -1 result means the daemonizing failed
    
{
      echo 
"Unable to daemonize...\n";
      exit(
1);
    }
    elseif (
$pid)
      exit(
0); // If the result wasn't 0, then this is the parent, it'll quit, leaving only the child
  
}
  
$botStartupTime time(); // Store the *nix timestamp of when the bot was started, used when ~uptime is asked for
  
$quit false;
  do 
// As long as we have not quit completely:
  
{
    if (!
$quit)
    {
      
// Check each connection to see if they are connected or not, if they lost connection unexpectedly, they should reconnect, and if they are connected, check their status
      
$r = array(); // Array to store the sockets to check
      
if (!$server1->connected())
        
$server1->connect();
      else
      {
        
$server1->check_status();
        
$r[] = $server1->socket;
      }
      if (!
$server2->connected())
        
$server2->connect();
      else
      {
        
$server2->check_status();
        
$r[] = $server2->socket;
      }
      if (@
stream_select($r$w null$e null1) === false// Try to read from the sockets, close the sockets if nothing could be read
      
{
        
$server1->disconnect();
        
$server2->disconnect();
        
$quit true;
        continue;
      }
      
// Process the data on each server as they come in
      
if (in_array($server1->socket$r) || $server1->istheremore)
        
$server1->process_data();
      if (
in_array($server2->socket$r) || $server2->istheremore)
        
$server2->process_data();
    }
  } while (!
$quit);
  if (
$logging)
    
fclose($log); // Close the log file, if we are logging
?>