<?

// 24.11.2006

set_time_limit (0);
error_reporting(E_ERROR | E_WARNING | E_PARSE);

include("config.php");

include("logparser.php");
@include("users_pro.php");
if(!class_exists("users")) {
   include("users.php");
}
include("recorder.php");
include("replayer.php");



$listensock = stream_socket_server("tcp://".ETTVIP.":".CONTROLPORT, $errno, $errstr);
if (!$listensock) {
   die("$errstr ($errno)<br />\n");
}

demolist();

$controls = array();
$controlinfo = array();

$procs = array();
$pipes = array();
$pipes['stdout'] = array();
$pipetoproc = array();
$linebuffer = array();
$timers = array();
$lastlog = array();
$adminips = array();
$ettvusers = array();
$controlips = array();

for($num = 1; defined('REP_'.$num.'_SLOTS'); $num++) {
   $task = new replayer(constant('REP_'.$num.'_SLOTS'), constant('REP_'.$num.'_PORT'), constant('REP_'.$num.'_PRIVATE'), 0,constant('REP_'.$num.'_CONFIG'));
   $procs[$task->procid] =& $task;
   unset($task);
}

@rmdir(REPDIR."/play/etpro/demos");
@unlink(REPDIR."/play/etpro/demos");
symlink(DEMODIR, REPDIR."/play/etpro/demos");

while(1) {
   $read = array_merge(array($listensock), $controls, $pipes['stdout']);
   $nexttimer = key($timers);
   if(!$nexttimer) {
      $nexttimer = NULL;
   }
   else {
      $nexttimer = $nexttimer - time();
      if($nexttimer < 0) {
         $nexttimer = 0;
      }
   }

   if(false === @stream_select($read, $write = NULL, $except = NULL, $nexttimer)) {
      if($select_failed > 5) {
         die("Failed to sleep, shutting down\n");
      }
      else {
         debug("Event 45 that shouldnt happen - please report this");
         sleep(1);
         $select_failed++;
      }
   }
   else {
      $select_failed = 0;
   }

   if(count($read) == 0) { // select timed out
      foreach($timers AS $time => $timer) {
         if($time <= time()) {
            foreach($timer AS $cmd) {
               eval($cmd);
            }
            unset($timers[$time]);
         }
         else {
            break;
         }
      }
   }
   else {
      foreach($read AS $stream) {
         $streamid = intval($stream);
         if($stream == $listensock) {
            $thissocket = stream_socket_accept($listensock, 120, $ip);
            list($ip) = explode(":", $ip);
            $sockid = intval($thissocket);
            $controls[$sockid] = $thissocket;
            $controlinfo[$sockid]['auth'] = false;
            $controlinfo[$sockid]['ip'] = $ip;
         }
         elseif(in_array($stream, $controls)) {
            $data = fread($stream, 8192);
            if(strlen($data) == 0) {
               fclose($stream);
               unset($controls[$streamid]);
               unset($linebuffer[$streamid]);
               unset($controlinfo[$streamid]);
            }
            else {
               @$linebuffer[$streamid] .= $data;
               $lines = explode("\n", $linebuffer[$streamid]);
               $linebuffer[$streamid] = array_pop($lines);
               $ip = $controlinfo[$streamid]['ip'];
               foreach($lines AS $command) {
                  $command = trim($command);
                  if(!$controlinfo[$streamid]['auth']) {
                     if(!trustedip($ip)) {
                        @fclose($stream);
                        debug("Login attempt from untrusted $ip - rejected\n");
                        unset($controls[$streamid]);
                        unset($controlinfo[$streamid]);
                        unset($linebuffer[$streamid]);
                     }
                     elseif($command != CONTROLPASS || CONTROLPASS == "") {
                        @fclose($stream);
                        debug("Wrong password from $ip - rejected\n");
                        unset($controls[$streamid]);
                        unset($controlinfo[$streamid]);
                        unset($linebuffer[$streamid]);
                     }
                     else {
                        $controlinfo[$streamid]['auth'] = true;
                        $time = $controlips[$ip];
                        if(time() - $time > 900) {
                           debug("Control socket accepted from $ip\n");
                        }
                        fputs($stream, "111 OK - Welcome\n\r");
                        $controlips[$ip] = time();
                        foreach($controlips AS $ip => $time) {
                           if($time < time() - 900) {
                              unset($controlips[$ip]);
                           }
                        }
                     }
                  }
                  else {
                     // *** Start valid commands
                     @list($first, $second, $third) = $pieces = explode(" ", $command);
                     if($first == "connect") {
                        $reccount = 0;
                        foreach($procs AS &$proc) {
                           if($proc->type == "recorder") {
                              $repcount++;
                           }
                        }
                        unset($proc);
                        if(defined('MAXREC') && constant('MAXREC') <= $reccount) {
                           fputs($stream, "472 ".constant('MAXREC')." - You are not allowed to spawn more than ".constant('MAXREC')." recorders.\n\r");
                        }
                        else {
                           $task = new recorder($second, $third, $pieces[3], $pieces[4]);
                           $task->reportsocket = $stream;
                           $procs[$task->procid] =& $task;
                           fputs($stream, "200 $task->num - Started recorder id: $task->num\n\r");
                           unset($task);
                        }
                     }
                     elseif($first == "hopserver") {
                        $second = intval($second);
                        $procid = findrecorder($second);
                        if($procid) {
                           $procs[$procid]->hopserver($pieces[2], $pieces[3], $pieces[4]);
                           $procs[$procid]->reportsocket = $stream;
                           fputs($stream, "205 $second - Told recorder $second to switch servers\n\r");
                        }
                        else {
                           fputs($stream, "400 $second - Recorder with id $second not running\n\r");
                        }
                     }
                     elseif($first == "broadcast") {
                        $repcount = 0;
                        $repslots = 0;
                        foreach($procs AS &$proc) {
                           if($proc->type == "replayer") {
                              $repcount++;
                              $repslots += $proc->slots;
                           }
                        }
                        unset($proc);
                        if(defined('MAXREP') && constant('MAXREP') <= $repcount) {
                           fputs($stream, "470 ".constant('MAXREP')." - You are not allowed to spawn more than ".constant('MAXREP')." broadcast servers.\n\r");
                        }
                        elseif(defined('MAXREPSLOT') && constant('MAXREPSLOT') < $repslots + $second) {
                           fputs($stream, "471 ".constant('MAXREPSLOT')." - You are not allowed to spawn more than ".constant('MAXREPSLOT')." broadcast slots.\n\r");
                        }
                        else {
                           $task = new replayer($second, $third, $pieces[3], $pieces[4], $pieces[5]);
                           $task->reportsocket = $stream;
                           $procs[$task->procid] =& $task;
                           fputs($stream, "200 $task->num - Started replayer with id $task->num\n\r");
                           unset($task);
                        }
                     }
                     elseif($first == "killrec") {
                        $second = intval($second);
                        $procid = findrecorder($second);
                        if($procid) {
                           $procs[$procid]->shutdown();
                           fputs($stream, "101 $second - Recorder with id $second was shut down\n\r");
                        }
                        else {
                           fputs($stream, "400 $second - Recorder with id $second not running\n\r");
                        }
                     }
                     elseif($first == "killrep") {
                        $second = intval($second);
                        $procid = findreplayer($second);
                        if($procid) {
                           $procs[$procid]->shutdown();
                           fputs($stream, "101 $second - Replayer with id $second was shut down\n\r");
                        }
                        else {
                           fputs($stream, "400 $second - Replayer with id $second not running\n\r");
                        }
                     }
                     elseif($first == "killproc") {
                        $procid = intval($second);
                        if(isset($procs[$procid])) {
                           $procs[$procid]->shutdown();
                           fputs($stream, "176 $procid - Proc with id $procid was shut down\n\r");
                        }
                        else {
                           fputs($stream, "400 $procid - Proc with id $procid not running\n\r");
                        }
                     }
                     elseif($first == "record") {
                        $second = intval($second);
                        $procid = findrecorder($second);
                        if($procid) {
                           $procs[$procid]->record();
                           fputs($stream, "102 $second - Recorder $second enabled record\n\r");
                        }
                        else {
                           fputs($stream, "400 $second - Recorder with id $second not running\n\r");
                        }
                     }
                     elseif($first == "stoprecord") {
                        $second = intval($second);
                        $procid = findrecorder($second);
                        if($procid) {
                           $procs[$procid]->stoprecord();
                           fputs($stream, "103 $second - Recorder $second disabled record\n\r");
                        }
                        else {
                           fputs($stream, "400 $second - Recorder with id $second not running\n\r");
                        }
                     }
                     elseif($first == "recordmode") {
                        $second = intval($second);
                        $procid = findrecorder($second);
                        if($procid) {
                           if($procs[$procid]->recordmode($third)) {
                              fputs($stream, "104 $second $third - Set recordmode \"$third\" for recorder $second\n\r");
                           }
                           else {
                              fputs($stream, "401 $second $third - Recorder $second did not understand mode \"$third\"\n\r");
                           }
                        }
                        else {
                           fputs($stream, "400 $second $third - Recorder with id $second not running\n\r");
                        }
                     }
                     elseif($first == "gamename") {
                        $second = intval($second);
                        $procid = findrecorder($second);
                        if($procid) {
                           list(,,$name) = explode(" ", $command, 3);
                           $procs[$procid]->setgamename($name);
                           fputs($stream, "105 $second \"$name\" - Set gamename \"$name\" for recorder $second\n\r");
                        }
                        else {
                           fputs($stream, "400 $second $third - Recorder with id $second not running\n\r");
                        }
                     }
                     elseif($first == "autodisc") {
                        $second = intval($second);
                        $procid = findrecorder($second);
                        if($procid) {
                           $third = intval($third);
                           $procs[$procid]->autodisc = $third;
                           fputs($stream, "128 $second $third - Broadcaster $second set autodisconnect after 10min of inactivity to $third\n\r");
                        }
                        else {
                           fputs($stream, "400 $second $third - Recorder with id $second not running\n\r");
                        }
                     }
                     elseif($first == "readynum") {
                        $second = intval($second);
                        $procid = findrecorder($second);
                        if($procid) {
                           $third = intval($third);
                           $procs[$procid]->readynum($third);
                           fputs($stream, "129 $second $third - Broadcaster $second set amount of ready players to start the match to $third\n\r");
                        }
                        else {
                           fputs($stream, "400 $second $third - Recorder with id $second not running\n\r");
                        }
                     }
                     elseif($first == "livecast") {
                        $second = intval($second);
                        $procid = findrecorder($second);
                        if($procid) {
                           $third = intval($third);
                           $procs[$procid]->isliveon($third);
                           fputs($stream, "106 $second $third - Broadcaster $third will play recordings from $second (if running)\n\r");
                        }
                        else {
                           fputs($stream, "400 $second $third - Recorder with id $second not running\n\r");
                        }
                     }
                     elseif($first == "dellive") {
                        $second = intval($second);
                        $procid = findrecorder($second);
                        if($procid) {
                           $third = intval($third);
                           if(isset($procs[$procid]->broadcasters[$third])) {
                              unset($procs[$procid]->broadcasters[$third]);
                              fputs($stream, "126 $second $third - Broadcaster $third will not be notified of broadcasts by $second\n\r");
                           }
                           else {
                              fputs($stream, "413 $second $third - Broadcaster $third was not planning to broadcast after $second\n\r");
                           }
                        }
                        else {
                           fputs($stream, "400 $second $third - Recorder with id $second not running\n\r");
                        }
                     }
                     elseif($first == "quit_rly") {
                        fputs($stream, "210 - Shutting down ...\n\r");
                        foreach($procs AS &$proc) {
                           $proc->shutdown();
                        }
                        unset($proc);
                        sleep(2);
                        unset($procs);
                        fputs($stream, "112 - Bye\n\r");
                        die();
                     }
                     elseif($first == "reloaddemos") {
                        fputs($stream, "107 - Reloading demolist from disk\n\r");
                        demolist();
                     }
                     elseif($first == "demolist") {
                        global $demolist;
                        foreach($demolist AS $id => $demo) {
                           fputs($stream, "308 ".str_pad($id, 4)." | ".str_pad($demo['dir'], 20)." | ".str_pad($demo['name'], 30)." | ".str_pad(count($demo['demos']), 2)."\n\r");
                        }
                        fputs($stream, "109 - End of list\n\r");
                     }
                     elseif($first == "viewerlist") {
                        $second = intval($second);
                        $procid = findreplayer($second);
                        if($procid) {
                           foreach($procs[$procid]->viewers AS $id => $viewer) {
                              fputs($stream, "313 $id | $viewer[nick] | $viewer[ip] | $viewer[uid] | $viewer[visits] | $viewer[muted] | $viewer[spamcount]\n\r");
                           }
                           fputs($stream, "109 - End of list\n\r");
                        }
                        else {
                           fputs($stream, "401 $second - Replayer with id $second not running\n\r");
                        }
                     }
                     elseif($first == "playgame") {
                        $second = intval($second);
                        $third = intval($third);
                        $procid = findreplayer($second);
                        if($procid) {
                           if(isset($demolist[$third])) {
                              fputs($stream, "117 $second $third - Replayer $second playing gameid $third\n\r");
                              $procs[$procid]->replaygame($third);
                           }
                           else {
                              fputs($stream, "403 $third - Gameid $third not available\n\r");
                           }
                        }
                        else {
                           fputs($stream, "401 $second - Replayer with id $second not running\n\r");
                        }
                     }
                     elseif($first == "autoreplaynum") {
                        $second = intval($second);
                        $third = intval($third);
                        $procid = findreplayer($second);
                        if($procid) {
                           fputs($stream, "118 $second $third - Replayer $second is now automatically replaying the $third last games\n\r");
                           $procs[$procid]->autoreplay = $third;
                        }
                        else {
                           fputs($stream, "401 $second - Replayer with id $second not running\n\r");
                        }
                     }
                     elseif($first == "nextmap") {
                        $second = intval($second);
                        $procid = findreplayer($second);
                        if($procid) {
                           fputs($stream, "119 $second - Replayer $second playing next map (game)\n\r");
                           $procs[$procid]->nextmap();
                           $procs[$procid]->calldemo();

                        }
                        else {
                           fputs($stream, "401 $second - Replayer with id $second not running\n\r");
                        }
                     }
                     elseif($first == "addqueue") {
                        $second = intval($second);
                        $third = intval($third);
                        $procid = findreplayer($second);
                        if($procid) {
                           fputs($stream, "141 $second $third - Replayer $second added gameid $third to playqueue\n\r");
                           $procs[$procid]->addqueue($third);
                        }
                        else {
                           fputs($stream, "401 $second - Replayer with id $second not running\n\r");
                        }
                     }
                     elseif($first == "delqueue") {
                        $second = intval($second);
                        $procid = findreplayer($second);
                        if($procid) {
                           fputs($stream, "142 $second - Replayer $second cleared replayqueue\n\r");
                           $procs[$procid]->delqueue();
                        }
                        else {
                           fputs($stream, "401 $second - Replayer with id $second not running\n\r");
                        }
                     }
                     elseif($first == "repeatqueue") {
                        $second = intval($second);
                        $third = intval($third);
                        $procid = findreplayer($second);
                        if($procid) {
                           fputs($stream, "143 $second $third - Replayer $second set replay queue to $third\n\r");
                           $procs[$procid]->repeatqueue($third);
                        }
                        else {
                           fputs($stream, "401 $second - Replayer with id $second not running\n\r");
                        }
                     }
                     elseif($first == "listqueue") {
                        $second = intval($second);
                        $procid = findreplayer($second);
                        if($procid) {
                           foreach($procs[$procid]->playqueue AS $gid) {
                              if(isset($demolist[$gid])) {
                                 $name = $demolist[$gid]['name'];
                                 $dir = $demolist[$gid]['dir'];
                              }
                              else {
                                 $name = "Not Found";
                                 $dir = "";
                              }
                              fputs($stream, "314 $gid | $dir | $name\n\r");
                           }
                           if($procs[$procid]->queuerepeat) {
                              fputs($stream, "120 - Repeat is on\n\r");
                           }
                           else {
                              fputs($stream, "121 - Repeat is off\n\r");
                           }
                        }
                        else {
                           fputs($stream, "401 $second - Replayer with id $second not running\n\r");
                        }
                     }
                     elseif($first == "adminip") {
                        if($third == "") $third = "<unknown>";
                        $adminips[$second] = time();
                        arsort($adminips, SORT_NUMERIC);
                        while(count($adminips) > 5) {
                           array_pop($adminips);
                        }
                        debug("Remote authentificated admin: $third with ip $second");
                        fputs($stream, "134 $second $third- Added ip $second to admin access list for $third\n\r");
                     }
                     elseif($first == "reclist") {
                        foreach($procs AS &$proc) {
                           if($proc->type == "recorder") {
                              if($proc->demolinked) {
                                 if($proc->recording) {
                                    $recm = "recording";
                                 }
                                 else {
                                    $recm = "paused";
                                 }
                              }
                              else {
                                 $recm = "stopped";
                              }
                              fputs($stream, "322 $proc->procid | $proc->num | $proc->source | $proc->gamename | ".implode(",", array_keys($proc->broadcasters))." | $proc->map | $proc->recordmode | $proc->game | $proc->warmup  | $proc->round | $recm | ".intval($proc->connected)." | $proc->numplayers | $proc->readyplayers | $proc->playerstartnum | ".intval($proc->defunct)." | $proc->autodisc | $proc->readynum\n\r");
                           }
                        }
                        unset($proc);
                        fputs($stream, "109 - End of list\n\r");
                     }
                     elseif($first == "replist") {
                        foreach($procs AS &$proc) {
                           if($proc->type == "replayer") {
                              fputs($stream, "323 $proc->procid | $proc->num | $proc->source | $proc->gamename | $proc->ip:$proc->port | $proc->liverec | $proc->map | $proc->game | $proc->warmup | $proc->round | ".count($proc->viewers)." | ".intval($proc->islive)." | $proc->slots | $proc->private | $proc->config | ".intval($proc->defunct)." | $proc->autoreplay | $proc->replaydelay | ".intval($proc->queuerepeat)."\n\r");
                           }
                        }
                        unset($proc);
                        fputs($stream, "109 - End of list\n\r");
                     }
                     elseif($first == "debug") {
                        $second = intval($second);
                        $procid = findreplayer($second);
                        $controlinfo[$streamid]['debug'] = $second;
                        fputs($stream, "119 $second - Debug for this connection set to $second\n\r");
                     }
                     else {
                        fputs($stream, "450 $first - Command unknown\n\r");
                     }
                  }
               }
            }
         }
         elseif(in_array($stream, $pipes['stdout'])) {
            $pid = $pipetoproc[$streamid];
            $data = fread($stream, 8192);
            if(strlen($data) == 0) {
               unset($linebuffer[$streamid]);
               debug("Procid $pid lost its task\n");
               if($procs[$pid]->defunct) {
                  unset($procs[$pid]);
               }
               else {
                  $procs[$pid]->restart();
               }
            }
            else {
               @$linebuffer[$streamid] .= $data;
               $lines = explode("\n", $linebuffer[$streamid]);
               $linebuffer[$streamid] = array_pop($lines);
               if(isset($procs[$pid])) {
                  foreach($lines AS $line) { // Handle lines from procs
                     $procs[$pid]->log($line);
                  }
               }
               else {
                  debug("Event 87 that shouldnt happen - please report this");
               }
            }
         }
         else {
            debug("ERROR: Unexpected event");
         }
      }
   }
}




function findrecorder($id) {
   global $procs;
   $recid = false;
   foreach($procs AS &$proc) {
      if($proc->num == $id && $proc->type == "recorder") {
         $recid = $proc->procid;
         break;
      }
   }
   unset($proc);
   return $recid;
}

function findreplayer($id) {
   global $procs;
   $recid = false;
   foreach($procs AS &$proc) {
      if($proc->num == $id && $proc->type == "replayer") {
         $recid = $proc->procid;
         break;
      }
   }
   unset($proc);
   return $recid;
}


function demolist() {
   global $demolist;
   if($handle = opendir(DEMODIR)) {
     while(false !== ($file = readdir($handle))) {
        if(!is_file(DEMODIR."/".$file) && $file != "." && $file != "..")  {
           $games[] = $file;
        }
     }
     closedir($handle);
   }
   sort($games);
   $demolist = array();
   foreach($games AS $game) {
      $demos = array();
      if($handle2 = opendir(DEMODIR."/".$game)) {
        while(false !== ($file = readdir($handle2))) {
           if(is_file(DEMODIR."/".$game."/".$file) && substr($file, -6, 4) == ".tv_")  {
              $demos[] = substr($file, 0, -6);
           }
        }
        closedir($handle2);
      }
      @list($name) = @file(DEMODIR."/".$game."/name.txt");
      $name = trim($name);
      if(!$name) $name = "Unknown";
      if(count($demos) > 0) {
         $demolist[] = array("dir" => $game, "name" => $name, "demos" => $demos);
      }
   }
}

function addtimer($time, $cmd) {
   global $timers;
   $timers[$time][] = $cmd;
   ksort($timers);
   reset($timers);
   // debug("Adding timer \"$cmd\" for $time\n");
}



function debug($line) {
   global $lastlog, $controls, $controlinfo;
   $line = trim($line);
   list($usec) = explode(" ", microtime());
   $usec = substr($usec,1,3);
   echo(date("d.m.y H:i:s").$usec." - ".$line."\n");
   foreach($controls AS $id => $cc) {
      if($controlinfo[$id]['debug']) {
         fputs($cc, "700 - $line\n\r");
      }
   }
}

function trustedip($ip) {
   $subject = explode(".", $ip);
   for($num = 1; defined('TRUSTIP'.$num); $num++) {
      $trusted = explode(".", constant('TRUSTIP'.$num));
      $trustedbytes = 0;
      for($byte = 0; $byte <= 3; $byte++) {
         if($subject[$byte] == $trusted[$byte] || $trusted[$byte] == 255) {
            $trustedbytes++;
         }
      }
      if($trustedbytes == 4) {
         break;
      }
   }
   if($trustedbytes == 4) {
      return true;
   }
   else {
      return false;
   }
}