<?php

/* Q3 WebRCON : web-based rcon l33t interface. Yes.
 * http://FrenchFragFactory.net/ressources/webrcon/
 * Ozh - ozh@planetozh.com - ozh on #sarl @ Quakenet
 *
 * Version 1.1
 */


/*******************************************************************************************************
 * CUSTOMISE BELOW THIS LINE
 */


/* If, for some weird reason, the script doesn't work, or $_PHP['SELF'] has no value on your server,
 * give the following variable, as a value, the virtual path of this file.
 * Example : http://www.yoursite.com/yourclan/l33t/webrcon.php -> $this_script = "/yourclan/l33t/webrcon.php";
 * If you don't know about $_PHP['SELF'], upload a blank page with the following line, and check it out in your browser
 *                  <?php phpinfo(); ?>                                                                                                         */
$this_script $_PHP['SELF']; 


/* Do you want the player datas (ip, nick, score) to be sortable ?
 * This is r0x0r javascript high lvl pv me, but may work only with MSIE .. :)
 * Set to 1 to use, set to 0 to display regular tables          */
$javascript 1


/* Define your servers.
 *
 * "ip" and "port" : well, pretty obvious. Mandatory.
 * "pass" : the g_password needed for players to join the server, if any. Optional.
 * "rcon" : the rconpassword for admin commands. Mandatory.
 * "cfg" : optional custom config files for each servers.
 *   Can be a list (array of cfg), a single cfg, or nothing
 *   "cfg" => array ("league.cfg", "ctf.cfg", "tdm.cfg")
 *   "cfg" => "justone.cfg"
 *   "cfg" => ""                                                                       */
$servers = array (
    
"w00t" => array (
        
"ip" => "10.11.12.13",
        
"port" => 27960 ,
        
"pass" => "w00t",
        
"rcon" => "l33t",
        
"cfg" => array ("sarl.cfg""ctf.cfg""test.cfg")
    ),

    
"server2" => array (
        
"ip" => "110.110.110.101",
        
"port" => 27960 ,
        
"pass" => "kikoo",
        
"rcon" => "faggot",
        
"cfg" => "server.cfg"
    
),

    
"server3" => array (
        
"ip" => "201.202.203.204",
        
"pass" => "ctb",
        
"port" => 27961 ,
        
"rcon" => "gnigni",
    ),

);


/* Define your default RCON Commands :
 * enclosed by quotes, separated by a comma, between parenthesis, with a trailing semicolon. Ok ? :) */
$rcon_buttons = array (
    
"say tg n00b",
    
"kick all",
    
"kick allbot",
    
"map_restart",
    
"listip",
    
"g_gravity 10",
    
"g_gravity 800"
    
); 


/* Define your CSS style between the line <<<YOURCSS and YOURCSS; */
$yourcss = <<<YOURCSS
<style>
body    {
    BackGround : black;
    Left : 0px;
    Top : 0px;
    Margin-Left : 20px;
    Margin-Right : 20px;
    Margin-Top : 0px;
    Margin-Bottom : 0px;
    Font-Family : Verdana, Arial;
    font-size: 10pt;
    Color : #A0A0A0;
}

.titrepage{
    font-Family : Verdana, Arial;
    font-size: 15pt;
    margin: 15;
    color: #A7A2E2;
    Font-Weight : bold;
    Text-Align : center;
}

td,p    {Font-Family : Verdana, Arial;
    Margin : 10px;
    font-size: 9pt;
    Color : #B0B0C0;
}

#maintable, #maintable td {
    border:1px solid #252548;
}

#maintable td:hover {
    background: #101015;
}

#serverinfo, #serverinfo td {
    border:0px solid white;
}

#serverinfo td:hover, {
    color: white;
    background: #606090;
}

input, select {
    color : #303060;
    background: #A7A2E2;
    font: normal 11px Verdana, Arial;
    border-color : #A7A2E2;
    margin: 1px 1px 1px 1px ;
}

input:hover, select:hover {
    background: #909090;
}

.odd {
    background: #313337;
}

.even {

}

#status, #status th, #status td {
    whitespace: pre;
    text-align: left;
    font-weight: normal;
    font-size: 100%;
    border:0px solid white;
}

#playerdata tr.odd {
  background-color: #313337;
}

#playerdata td.sorted {
  background-color: #606090;
}

#playerdata th.sorted {
  background-color: #606090;
}

#playerdata tr.odd td.sorted {
  background-color: #303060;
}

#playerdata .player, #playerdata .ip {
    cursor:crosshair;
}

#playerdata tr:hover, #playerdata td:hover {
    color: white;
    background: #606090;
}

.status .player:hover {
    color: white;
}

.titre {
    font: bolder 12px Verdana, Arial;
    color: #9090D0;
}

a {
    font: bolder 12px Verdana, Arial;
    color: #9090D0;
    text-decoration: none;
}

a:hover {
    text-decoration: underline;
}
</style>
YOURCSS;


/*
 * STOP EDITING. OK ?
 ******************************************************************************************************/
 
function droplist ($list = array()) {
    global 
$server$this_script;
    print 
"<form name=\"lstsrv\" action=\"$this_script\" method=\"post\">\n";
    print 
"<select name=\"server\">\n";
    print 
"<option value=\"\">Serveurs :</option>\n";
    foreach (
$list as $key => $val) {
        if (
$server == $key) {$selected="selected";} else {$selected='';}
        print 
"<option value=\"$key\" $selected><b>$key</b> ("$val['ip'] . ':' $val['port'] . ' ' $val['pass'] . ")</option>\n";
    }
    if (
$server) {
        
$button="Refresh";
    } else {
        
$button="Select";
    }
    print 
"</select><input type=\"submit\" value=\"$button\"></form>\n";
}


// This will take the server response and makes it in to readable key value pair
function process($data = array()) {
    
$haystack explode("\n"$data3);
    
$stack explode('\\'$haystack[1]);
    for (
$i 1;$i <= count($stack);$i $i 2) {
        
$list[@$stack[$i]] = @$stack[$i 1];
    }
    
$list[] = @$haystack[2];
    foreach (
$list as $key => $dum) {
        if (empty(
$dum) || !isset($dum)) {
            unset(
$list[$key]);
        }
    }
    return 
$list;
}


//This will iterate thru each key value pair and prints a table.
function outputData($list = array()) {
    global 
$ip,$port,$pass;
    echo 
"<table id=\"serverinfo\" border=\"0\" cellspacing=\"1\" cellpadding=\"0\">\n";
    
// $serv = stripcolors ($list['sv_hostname']);
    
$serv $list['sv_hostname'];
    echo 
"<tr class=\"titre\"><td class=\"titre\">Serveur</td><td class=\"titre\" nowrap>$serv</td></tr>\n";
    echo 
"<tr class=\"titre\"><td class=\"titre\">ip:port</td><td class=\"titre\">$ip:$port</td></tr>\n";
    
ksort($list);
    foreach (
$list as $key => $val) {
        if (
$key != "0") {
            if (
$class=="even") {$class="odd";} else {$class="even";}
            echo 
"<tr class=\"$class\"><td class=\"$class\">$key</td><td class=\"$class\">$val</td><tr>\n";
        }
    }
    echo 
"</TABLE>\n";
}

function 
status ($ip$port$timeout 15) {
    
$errno $errstr null;
    
$cmd "˙˙˙˙getstatus\n";
    
$f fsockopen('udp://' $ip$port$errno$errstr$timeout);
    if (!
$f)
        die (
"Unable to connect. Error $errno - $errstr\n");
    
socket_set_timeout ($f10);
    
fwrite ($f$cmd);
    
$data '';
    while (
$d fread ($f10000)) {
        
$data .= $d;
    }
    
fclose ($f);
    
$data stripcolors ($data);
    return 
$data;
}

function 
rcon ($ip$port$rcon_pass$command) {
    
$fp fsockopen("udp://$ip",$port$errno$errstr2);
    
socket_set_timeout($fp,2);

    if (!
$fp)    {
        echo 
"$errstr ($errno)<br>\n";
    } else {
        
$query "\xFF\xFF\xFF\xFFrcon \"" $rcon_pass "\" " $command;
        
fwrite($fp,$query);
    }
    
$data '';
    while (
$d fread ($fp10000)) {
        
$data .= $d;
    }
    
fclose ($fp);
    
$data preg_replace ("/....print\n/"""$data);
    
$data stripcolors ($data);
    return 
$data;
}

function 
stripcolors ($quoi) {
    
$result $quoi;
    
$result preg_replace ("/\^x....../i"""$result); // remove OSP's colors (^Xrrggbb)
    
$result preg_replace ("/\^./"""$result); // remove Q3 colors (^2) or OSP control (^N, ^B etc..)
    
$result preg_replace ("/</""&lt;"$result); // convert < into &lt; for proper display
    
return $result;
}

function 
printheadrow ($list = array()) {
    global 
$javascript;
    print 
"<tr>\n";
    
$index=0;
    foreach (
$list as $elem) {
        if (
$javascript) {
            print 
"<th><a href=\"\" onclick=\"this.blur();return sortTable('playerdata', $index)\" title=\"Click to Sort by $elem\">$elem</a></th>";
        } else {
            print 
"<th>$elem</th>";
        }
        
$index++;
    }
    print 
"</tr>\n";
}

function 
printrow ($list = array(), $class) {
    print 
"<tr class=\"$class\">\n";
    foreach (
$list as $elem) {
        print 
"<td>$elem</td>";
    }
    print 
"</tr>\n";
}

function 
rconstatus ($ip$port$rcon_pass) {
    
$result rcon ($ip$port$rcon_pass"status");
    
$result explode ("\n"$result);

    
# ok, let's deal with the following :
    #
    # map: q3wcp9
    # num score ping name            lastmsg address               qport rate
    # --- ----- ---- --------------- ------- --------------------- ----- -----
    #   1    19   33 l33t^n1ck       33 62.212.106.216:27960   5294 25000

    
print "<table id=\"status\">\n";
    print 
"<thead><tr><th colspan=\"8\">$result[0]</th></tr>\n";
    
printheadrow (array("num""score""ping""name""lastmsg""address""qport""rate"));
    print 
"</thead>\n";
    print 
"<tbody id=\"playerdata\">\n";
    
array_shift($result); // 1st line : map q3wcp9
    
array_shift($result); // 2nd line : col headers
    
array_shift($result); // 3rd line : -- ------ ----
    
array_pop($result);
    
array_pop($result); // two empty lines at the end, go figure.
    
foreach ($result as $line) {
        if (
$class=="odd") {$class "even";} else {$class "odd";}
        
$player $line;
        
preg_match_all("/^\s*(\d+)\s*(\d+)\s*(\d+)(.*?)(\d*)\s*(\S*)\s*(\d*)\s*(\d*)\s*$/"$player$out);
        
// weeeeeeeeee \o/
        
$num $out[1][0];
        
$score $out[2][0];
        
$ping $out[3][0];
        
$name trim($out[4][0]);
        
$name "<span class=\"player\" title=\"Click to kick $name\"  onClick=\"document.forms.rcon.rcon_cmd.value='clientkick $num';document.rcon.rcon_cmd.focus()\">$name</span>";
        
$lastmsg $out[5][0];
        
$address $out[6][0];
        if (
$address != 'bot') {
            
$addressip preg_replace ("/:.*$/"""$address);
            
$address "<span class=\"ip\" title=\"Click to ban IP $addressip\" onClick=\"document.forms.rcon.rcon_cmd.value='addip $addressip';document.rcon.rcon_cmd.focus()\">$address</span>";
        }
        
$qport $out[7][0];
        
$rate $out[8][0];
        
printrow (array ($num$score$ping$name$lastmsg$address$qport$rate), $class);
    }
    print 
"</tbody>\n</table>\n";
}

function 
theterriblejavascript () {
    
// found here : http://www.brainjar.com/dhtml/tablesort/

    
print <<<JAVASCRIPT
    
<script type="text/javascript">//<![CDATA[
function sortTable(id, col) {

  // Get the table section to sort.
  var tblEl = document.getElementById(id);

  // Set up an array of reverse sort flags, if not done already.
  if (tblEl.reverseSort == null)
    tblEl.reverseSort = new Array();

  // If this column was the last one sorted, reverse its sort direction.
  if (col == tblEl.lastColumn)
    tblEl.reverseSort[col] = !tblEl.reverseSort[col];

  // Remember this column as the last one sorted.
  tblEl.lastColumn = col;

  // Set the table display style to "none" - necessary for Netscape 6 
  // browsers.
  var oldDsply = tblEl.style.display;
  tblEl.style.display = "none";

  // Sort the rows based on the content of the specified column using a
  // selection sort.

  var tmpEl;
  var i, j;
  var minVal, minIdx;
  var testVal;
  var cmp;

  for (i = 0; i < tblEl.rows.length - 1; i++) {

    // Assume the current row has the minimum value.
    minIdx = i;
    minVal = getTextValue(tblEl.rows[i].cells[col]);

    // Search the rows that follow the current one for a smaller value.
    for (j = i + 1; j < tblEl.rows.length; j++) {
      testVal = getTextValue(tblEl.rows[j].cells[col]);
      cmp = compareValues(minVal, testVal);
      // Reverse order?
      if (tblEl.reverseSort[col])
        cmp = -cmp;
      // If this row has a smaller value than the current minimum, remember its
      // position and update the current minimum value.
      if (cmp > 0) {
        minIdx = j;
        minVal = testVal;
      }
    }

    // By now, we have the row with the smallest value. Remove it from the
    // table and insert it before the current row.
    if (minIdx > i) {
      tmpEl = tblEl.removeChild(tblEl.rows[minIdx]);
      tblEl.insertBefore(tmpEl, tblEl.rows[i]);
    }
  }

  // Restore the table's display style.
  tblEl.style.display = oldDsply;

  // Make it look pretty.
  makePretty(tblEl, col);
  
  return false;
}

//-----------------------------------------------------------------------------
// Functions to get and compare values during a sort.
//-----------------------------------------------------------------------------

// This code is necessary for browsers that don't reflect the DOM constants
// (like IE).
if (document.ELEMENT_NODE == null) {
  document.ELEMENT_NODE = 1;
  document.TEXT_NODE = 3;
}

function getTextValue(el) {

  var i;
  var s;

  // Find and concatenate the values of all text nodes contained within the
  // element.
  s = "";
  for (i = 0; i < el.childNodes.length; i++)
    if (el.childNodes[i].nodeType == document.TEXT_NODE)
      s += el.childNodes[i].nodeValue;
    else if (el.childNodes[i].nodeType == document.ELEMENT_NODE &&
             el.childNodes[i].tagName == "BR")
      s += " ";
    else
      // Use recursion to get text within sub-elements.
      s += getTextValue(el.childNodes[i]);

  return normalizeString(s);
}

function compareValues(v1, v2) {

  var f1, f2;

  // If the values are numeric, convert them to floats.

  f1 = parseFloat(v1);
  f2 = parseFloat(v2);
  if (!isNaN(f1) && !isNaN(f2)) {
    v1 = f1;
    v2 = f2;
  }

  // Compare the two values.
  if (v1 == v2)
    return 0;
  if (v1 > v2)
    return 1
  return -1;
}

// Regular expressions for normalizing white space.
var whtSpEnds = new RegExp("^\\s*|\\s*$", "g");
var whtSpMult = new RegExp("\\s\\s+", "g");

function normalizeString(s) {

  s = s.replace(whtSpMult, " ");  // Collapse any multiple whites space.
  s = s.replace(whtSpEnds, "");   // Remove leading or trailing white space.

  return s;
}

// Style class names.
var rowClsNm = "odd";
var colClsNm = "sorted";

// Regular expressions for setting class names.
var rowTest = new RegExp(rowClsNm, "gi");
var colTest = new RegExp(colClsNm, "gi");


function makePretty(tblEl, col) {

  var i, j;
  var rowEl, cellEl;

  // Set style classes on each row to alternate their appearance.
  for (i = 0; i < tblEl.rows.length; i++) {
   rowEl = tblEl.rows[i];
   rowEl.className = rowEl.className.replace(rowTest, "");
    if (i % 2 != 0)
      rowEl.className += " " + rowClsNm;
    rowEl.className = normalizeString(rowEl.className);
    // Set style classes on each column (other than the name column) to
    // highlight the one that was sorted.
    for (j = 0; j < tblEl.rows[i].cells.length; j++) {
      cellEl = rowEl.cells[j];
      cellEl.className = cellEl.className.replace(colTest, "");
      if (j == col)
        cellEl.className += " " + colClsNm;
      cellEl.className = normalizeString(cellEl.className);
    }
  }

  // Find the table header and highlight the column that was sorted.
  var el = tblEl.parentNode.tHead;
  rowEl = el.rows[el.rows.length - 1];
  // Set style classes for each column as above.
  for (i = 0; i < rowEl.cells.length; i++) {
    cellEl = rowEl.cells[i];
    cellEl.className = cellEl.className.replace(colTest, "");
    // Highlight the header of the sorted column.
    if (i == col)
      cellEl.className += " " + colClsNm;
      cellEl.className = normalizeString(cellEl.className);
  }
}

//]]></script>
JAVASCRIPT;
}

// let's c'est parti go

print "<html>\n";
print 
"<head>\n";
print 
"<title>Q3 WebRCON</title>\n";
print 
$yourcss;
if (
$javascripttheterriblejavascript();
print 
"</head>\n<body>";
print 
"<p></p>";
print 
"<table align=\"center\" border=\"1\" cellpadding=\"5\" cellspacing=\"0\" id=\"maintable\">\n";
print 
"<tr><td colspan=\"2\">";
print 
"<span class=\"titrepage\">Quake3 WebRCON</span>\n";
print 
"</td><td>";
droplist($servers) ;
print 
"</td></tr>";

if (
$server) {
    
$ip $servers[$server]['ip'];
    
$port $servers[$server]['port'];
    
$pass $servers[$server]['pass'];
    
$rcon $servers[$server]['rcon'];
    
$cfg $servers[$server]['cfg'];

    print 
"<tr><td valign=\"top\" nowrap rowspan=\"2\">\n";

    print 
"<span class=\"titre\">/exec cfg :</span><br>\n";
    print 
"<form name=\"chgcfg\" action=\"$this_script\" method=\"post\">\n";
    print 
"<input type=\"hidden\" name=server value=\"$server\">\n";
    print 
"<input type=\"hidden\" name=action value=\"chgcfg\">\n";
    print 
"<input type=\"hidden\" name=ozh value=\"$server\">\n";

    if (
$action=='chgcfg') {
        
$lastcmd "exec $config";
        
$lastresult rcon ($ip$port$rcon"exec $config");
    }

    if (
$action=='rcon') {
        if (
preg_match("/^kick /i"$rcon_cmd)) {
            
$rcon_cmd=preg_replace("/^kick /""kick \""$rcon_cmd) . "\"";
        } else {
            
//$rcon_cmd="non";
        
}
        
$lastcmd $rcon_cmd;
        
$lastresult rcon ($ip$port$rcon$rcon_cmd);
    }

    if (
is_array($cfg)) {
        foreach (
$cfg as $config) {
            print 
"<p><input type=\"submit\" name=\"config\" value=\"$config\"></p>\n";
        }
    } else {
        if (
$cfg) {
            print 
"<p><input type=\"submit\" name=\"config\" value=\"$cfg\"></p>\n";
        } else {
            print 
"<p>No .cfg</p>";
        }
    }

    print 
"</form>\n";

    print 
"</td><td valign=\"top\" rowspan=\"2\">\n";

    
$data status($ip,$port,10);
    
$list process($data);
    
outputData($list);

    print 
"</td><td valign=\"top\" nowrap>\n";

    
//$result = rcon ($ip, $port, $rcon, "status");
    
print "<span class=\"titre\">/rcon status :</span><br><br>\n";
    
rconstatus ($ip$port$rcon);
    
//print "<pre>$result</pre>";

    
print "</td></tr><tr><td valign=\"bottom\">\n";

    if (
$lastcmd) {
        print 
"<span class=\"titre\">/rcon $lastcmd :</span><br><br>\n";
        echo 
"<pre>$lastresult\n</pre>\n";
    }

    print 
"<form name=\"rcon\" action=\"$this_script\" method=\"post\">\n";
    print 
"<p><span class=\"titre\">/rcon <input type=\"text\" name=\"rcon_cmd\" size=\"25\"><input type=\"submit\" value=\"&laquo;\"></span></p>\n";
    print 
"<input type=\"hidden\" name=\"server\" value=\"$server\">\n";
    print 
"<input type=\"hidden\" name=\"action\" value=\"rcon\">\n";

    foreach (
$rcon_buttons as $button) {
        print 
"<input type=\"button\" value=\"$button\" onClick=\"document.forms.rcon.rcon_cmd.value=this.value;document.forms.rcon.submit()\"> ";
    }
    print 
"</form>\n";
    print 
"\n</td></tr>\n";
}

print 
"</table>\n";

print 
"<script>document.rcon.rcon_cmd.focus()</script>";
print 
"<center><span class=\"titre\"><br><a href=\"http://frenchfragfactory.net/\">&copy; SARL FFS M8</a></span></center>";
print 
"</body></html>";

?>