<?

/**
 * This file copyright (C) 2014 Barry Hunter (sphinx@barryhunter.co.uk)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

/* REQUIREMENTS
* -- Webserver capable of running PHP scripts (eg Apache) 
* -- SphinxSearch server instance, with SphinxQL port enabled

* INSTALLATION
* -- Copy this script to a folder on the webserver
* -- Edit the config to point to server instance
* -- RECOMMENDED - add some sort of Password Protection!
*/

//config... 

$CONF = array();
$CONF['sphinx_host'] = '127.0.0.1';
$CONF['sphinxql_port'] = 9306;

///////////////////////////////////////////////////////////////////////////////////

if (empty($_GET['tab'])) $_GET['tab'] = 'frameset';

if (!empty(
$_GET['index'])) {
    print 
"<b>".htmlentities($_GET['index'])."</b>";
    
$index urlencode($_GET['index']);
    print 
" <a href=\"?tab=sql&amp;index=$index&amp;sql=DESCRIBE+$index\">Describe</a>";
    print 
" <a href=\"?tab=sql&amp;index=$index&amp;sql=SELECT+*+FROM+$index\">Select</a>";
    print 
" <a href=\"?tab=query&amp;index=$index\">Query</a>";
    print 
" <a href=\"?tab=sql&amp;index=$index&amp;sql=SHOW+INDEX+$index+STATUS\">Status</a>"//to check agailable on this version
    
print " <a href=\"?tab=operations&amp;index=$index\">Operations</a>";
    print 
" <a href=\"?tab=keywords&amp;index=$index\">Keywords</a>";
    print 
" <a href=\"?tab=snippets&amp;index=$index\">Snippets</a>";
    print 
"<hr/>";
}


if (
$_GET['tab'] == 'frameset') {
    
?>
    <title>Ultra Minimalist SphinxSearch Admin interface - v0.1</title>
    <frameset cols="200,*">
        <frame src="?tab=indexes" name="sidebar">
        <frame src="?tab=intro" name="main">
    </frameset>
    <?
} elseif ($_GET['tab'] == 'intro') {
    
?>
    <h2>Ultra Minimalist SphinxSearch Admin interface</h2>
    <p><big>NOTE: There is no security, and its possibly to execute arbitary SphinxQL commands against your SphinxSearch instance via this script. It's very important that you take measures to secure access to this program. For example within a password protected directory!</big></p>
    <p>To start choose an option on the left.</p>
    <b>SphinxQL commands that can be executed by this Tool</b>
    <ul>
                <li>SHOW TABLES (the list is shown on the left!)
        <li>SHOW STATUS/VARIABLES (available top left)
        <li>DESCRIBE index (click index in sidebar, then choose Describe Tab)
        <li>SELECT .. FROM index ...  (most features supported, click index, then select Query Tab)
        <li>SHOW PROFILE (tickbox on the Select Tab to automatially run it for a query)
        <li>UPDATE index ... (Run a SELECT, the id column links to a Edit interface to edit Attributes)
        <li>SHOW INDEX index STATUS (choose index then select the Status Tab)
        <LI>CALL KEYWORDS/SNIPPETS (choose index then click the relevent Tab)
        <li>SHOW META (command is altomatically run for all SELECT ... queries)
        <li>FLUSH/OPTIMIZE/TRUNCATE rtindex (available via the Operations Tab)
        <li>ATTACH ... (can select a local index from list on Opertations tab for RT indexes)
    </ul>
    <b>SphinxQL commands NOT supported</b>
    <ul>
        <li>INSERT/REPLACE INTO ...</li>
        <li>SET ... syntax</li>
        <li>SHOW AGENT STATUS</li>
        <li>BEGIN, COMMIT, ROOLBACK
        <li>DELETE FROM ... WHERE id IN(..) <i>but can delete single documents</i>
        <li>UPDATE index .. WHERE condition <i>but can update single documents</i>
        <li>OPTION/WITHIN GROUP ORDER BY clauses of SELECT</li>
        <li>GROUP n BY clause of SELECT</li>
        <li>Multi-queries (running multiple SELECT statements at once) 
        <li>CREATE/DROP FUNCTION</li>
    </ul>
    <?
} elseif ($_GET['tab'] == 'indexes') {
    
//todo, check server version, and if possible show SHOW AGENT STATUS etc
    //todo, add support for SET .... 
    
?>
    <base target="main">
    <ul>
        <li><a href="?tab=sql&sql=SHOW+STATUS">Show Status</a></li>
        <li><a href="?tab=sql&sql=SHOW+TABLES">Show Tables</a></li>
        <li><a href="?tab=sql&sql=SHOW+VARIABLES">Show Variables</a></li>
        <li><a href="?tab=sql"><i>arbitary SphinxQL</i></a></li>
    </ul>
    <hr/>
    <ul>
    <?
                
include "_database.php";
        
$tables getAll("SHOW TABLES");
        foreach (
$tables as $table) {
            print 
"<li>";
            print 
"<a href=\"?tab=sql&amp;index=".urlencode($table['Index'])."&amp;sql=SELECT+*+FROM+".urlencode($table['Index'])."\">".htmlentities($table['Index'])."</a>";
            print 
" <small><i>{$table['Type']}</i></small>";
            
//print " <a href=\"?tab=sql&amp;index=".urlencode($table['Index'])."&amp;sql=DESCRIBE+".urlencode($table['Index'])."\">Q</a>";
            
print "</li>";
        }
        print 
"</ul>";
        
printf("Server version:<br/>&nbsp; %s"mysql_get_server_info());

} elseif (
$_GET['tab'] == 'sql' || $_GET['tab'] == 'query') {
    
//todo - any sort security required here?
    
if (empty($_REQUEST['sql'])) $_REQUEST['sql'] = 'SHOW TABLES';
    if (
is_array($_REQUEST['sql'])) $_REQUEST['sql'] = implode(' ',$_REQUEST['sql']);


    if (
$_GET['tab'] == 'query') {
            
$table $_GET['index'];

            include 
"_database.php";

            
$desc getAssoc("DESCRIBE $table");

        if (empty(
$_POST)) {
            
$_POST = array(
                
'select' => array('id'=>1,'WEIGHT() AS `weight`'=>1),
                
'limit' => '0,20',
            );
            
$default 1;
        }
            print 
"<form method=post>";
            print 
"<table border=1 cellspacing=0 style=\"background-color:#eee\">";
        print 
"<tr><th>Column</th><th>Type</th><th>SELECT</th><th>WHERE</th><th>GROUP</th><th>DISTINCT</th><th>ORDER</th></tr>";
            foreach (
$desc as $field => $type) {
            if (
$type=='field') {
                
$fields[] = $field;
                continue;
            }
                    print 
"<tr><th>$field</th><td>$type</td>";
            
$checked = !empty($_POST['select'][$field]);
            print 
"<td><input type=checkbox name=\"select[$field]\"".($checked?' checked':'')."></td>";
                    if (
$type == 'timestamp' || $type == 'uint' || $type == 'float') {
                            
$size = ($type =='string' || $type == 'mva')?80:10;
                
$value htmlentities($_POST['where'][$field]);
                            print 
"<td><input type=\"text\" name=\"where[$field]\" value=\"$value\" size=$size title=\"examples:\n\n&gt; 12\nBETWEEN 10 AND 20\n=5\"/></td>";
                    } 
//todo json!
            
else {
                print 
"<td></td>";
            }
            
$checked = ($_POST['group'] == $field);
            print 
"<td><input type=radio name=group value=\"$field\"".($checked?' checked':'')."></td>";
            if (
$field != 'id') {
                
$checked = ($_POST['distinct'] == $field);
                print 
"<td><input type=radio name=distinct value=\"$field\"".($checked?' checked':'')."></td>";
            } else print 
"<td></td>";
            
$checked1 in_array("$field ASC",$_POST['order']);
            
$checked2 in_array("$field DESC",$_POST['order']);
            print 
"<td>ASC <input type=checkbox name=\"order[]\" value=\"$field ASC\"".($checked1?' checked':'').">/<input type=checkbox name=\"order[]\" value=\"$field DESC\"".($checked2?' checked':'')."> DESC</td>";
                    print 
"</tr>";
            }
        
$arr = array(
            
'weight' => 'WEIGHT() AS `weight`',
            
'count' => 'COUNT(*) AS `count`',
        );
        foreach (
$arr as $field => $full) {
            
$checked = !empty($_POST['select'][$full]);
            print 
"<tr><th>$field</th><td><td><input type=checkbox name=\"select[$full]\"".($checked?' checked':'')."></td>";
            print 
"<td></td>";
            print 
"<td></td>";
            print 
"<td></td>";
            
$checked1 in_array("`$field` ASC",$_POST['order']);
            
$checked2 in_array("`$field` DESC",$_POST['order']);
            print 
"<td>ASC <input type=checkbox name=\"order[]\" value=\"`$field` ASC\"".($checked1?' checked':'').">/<input type=checkbox name=\"order[]\" value=\"`$field` DESC\"".($checked2?' checked':'')."> DESC</td>";
        }
            print 
"</table>";
        print 
"WHERE MATCH('<input type=text name=q value=\"".htmlentities($_POST['q'])."\">')";
        print 
"Fields = <tt>".implode("</tt>, <td>",$fields)."</tt><br/>";
        print 
"LIMIT <input type=text name=limit size=10 value=\"".htmlentities($_POST['limit'])."\">";
    
//todo - OPTIONS
    //todo within group order by
            
print "<input type=submit value=\"run query\">";
            print 
"</form>";

        if (!empty(
$_POST) && empty($default)) {
            
$sql = array();
            
$sql['columns'] = implode(", ",array_keys($_POST['select']));
            
$sql['tables'] = array($table);
            
$sql['wheres'] = array();
            if (!empty(
$_POST['q'])) {
                
$sql['wheres'][] = "MATCH('".mysql_real_escape_string($_POST['q'])."')";
            }
            if (!empty(
$_POST['where']))
                foreach (
$_POST['where'] as $key => $value)
                    if (!empty(
$value))
                        
$sql['wheres'][] = mysql_real_escape_string("$key $value");

            if (!empty(
$_POST['group']) && $_POST['group'] != 'id') {
                
$sql['group'] = $_POST['group'];
                if (!empty(
$_POST['distinct'])) {
                    
$sql['columns'] .= ", COUNT(DISTINCT {$_POST['distinct']}) AS distinct";
                }
            }
            if (!empty(
$_POST['order'])) {
                 
$sql['order'] = implode(", ",array_values($_POST['order']));
            }

            
$_REQUEST['sql'] = $_POST['sql'] = sqlMakeQuery($sql);
        }

    }

    if (
$_GET['tab'] == 'sql' || $_POST['sql']) {
        
?>
        <form method=POST action="?tab=sql&amp;index=<? echo htmlentities($_GET['index']); ?>">
            <textarea name="sql" rows=5 cols="100" style="width:100%"><? echo htmlentities($_REQUEST['sql']); ?></textarea>
            <input type="submit"> <input type="checkbox" name="profile" >Profile SELECT queries?
        </form>
        <hr/>
        <?
    
}

    if (!empty(
$_GET['sql']) || !empty($_POST['sql'])) { //just to avoid picking up the default above!
        
if (empty($db))
            include 
"_database.php";
        if (!empty(
$_REQUEST['profile'])) {
            
queryExecute("SET profiling=1");
        }

        
$num dump_sql_table($_REQUEST['sql']);

        print 
"Rows = $num<hr>";

        if (
preg_match('/^\s*SELECT /',$_REQUEST['sql'])) {
            
dump_sql_table("SHOW META");
            if (!empty(
$_REQUEST['profile'])) {
                 
dump_sql_table("SHOW PROFILE");
            }
        }
    }

} elseif (
$_GET['tab'] == 'operations') {
    
$table $_GET['index'];

        include 
"_database.php";

    
$tables getAssoc("SHOW TABLES"); //we need them all anyway for select lists!

    
print "Type: <b>{$tables[$table]}</b><hr>";
    if (
$tables[$table] == 'rt') {

        
$sqls = array(
            
'FLUSH RTINDEX '.$table,
            
'OPTIMIZE INDEX '.$table,
            
'TRUNCATE RTINDEX '.$table,
        );
    } else {
        print 
"no operations available for this type";
    }

    foreach (
$sqls as $sql) {

    
?>
        <form method=POST action="?tab=sql&amp;index=<? echo $index?>">
                <textarea name="sql" rows=5 cols="100" style="width:100%"><? echo htmlentities($sql); ?></textarea>
                <input type="submit">
        </form>
    <hr/>
    <?
    
}

    if (
$tables[$table] == 'rt') {
        
?> <form method=POST action="?tab=sql&amp;index=<? echo $index?>"> <?
        
print "ATTACH INDEX <input type=hidden name=\"sql[]\" value=\"ATTACH INDEX\"/>";
        print 
" <select name=\"sql[]\">";
        foreach (
$tables as $key => $value) {
            if (
$value == 'local') {
                print 
"<option value=$key>$key</option>";
            }
        }
        print 
"</select> ";
        print 
"TO RTINDEX ".htmlentities($table)." <input type=hidden name=\"sql[]\" value=\"TO RTINDEX ".htmlentities($table)."\"/>";
        
?> <br/><input type="submit"> </form> <?
    
} else {

    }


} elseif (
$_GET['tab'] == 'keywords') {
        
?>
        <form method=POST action="?tab=keywords&amp;index=<? echo $index?>">
        List of keywords:<br/>
                <textarea name="keywords" rows=3 cols="100" style="width:100%"><? echo htmlentities($_REQUEST['keywords']); ?></textarea>
                <input type="submit"> <input type="checkbox" name="hits" checked>Include Hits?
        </form>
        <hr/>
        <?
    
if (!empty($_REQUEST['keywords'])) {
            include 
"_database.php";

        
$sql "CALL KEYWORDS('".mysql_real_escape_string($_REQUEST['keywords'])."','".mysql_real_escape_string($index)."'";
        if (!empty(
$_REQUEST['hits']))
            
$sql .= ", 1";
        
$sql .= ")";
                
$num dump_sql_table($sql);

                print 
"Rows = $num<hr>";
        print 
htmlentities($sql);
    }

} elseif (
$_GET['tab'] == 'snippets') {
    if (empty(
$_POST)) {
        
$_REQUEST['options'] = "80 AS limit, 1 AS query_mode";
    }
        
?>
        <form method=POST action="?tab=snippets&amp;index=<? echo $index?>">
        Example Document: (CALL SNIPPETS can include multiple documents, but this demo only does one)<br/>
                <textarea name="document" rows=7 cols="100" style="width:100%"><? echo htmlentities($_REQUEST['document']); ?></textarea>
        Query:<br/>
                <textarea name="keywords" rows=2 cols="100" style="width:100%"><? echo htmlentities($_REQUEST['keywords']); ?></textarea>
        Options: (<a href="http://sphinxsearch.com/docs/current.html#api-func-buildexcerpts" target="_docs">Docs</a>) <br/>
                <textarea name="options" rows=2 cols="100" style="width:100%"><? echo htmlentities($_REQUEST['options']); ?></textarea>
                <input type="submit">
        </form>
        <hr/>
        <?
    
if (!empty($_REQUEST['keywords'])) {
            include 
"_database.php";

        
$sql "CALL SNIPPETS(('".mysql_real_escape_string($_REQUEST['document'])."'),'".mysql_real_escape_string($index)."','".mysql_real_escape_string($_REQUEST['keywords'])."'";
        if (!empty(
$_REQUEST['options']))
            
$sql .= ", ".$_REQUEST['options'];
        
$sql .= ")";
                
$num dump_sql_table($sql);

                print 
"Rows = $num<hr>";
        print 
htmlentities($sql);
    }



} elseif (
$_GET['tab'] == 'edit') { //todo reuse for insert?
    
$table $_GET['index'];

        include 
"_database.php";

        
$tables getAssoc("SHOW TABLES"); //we need them all anyway for select lists!
    
$type $tables[$table];

    
$desc getAssoc("DESCRIBE $table");
    
$values getRow("SELECT * FROM $table WHERE id = ".intval($_GET['id']));

    if (!empty(
$_POST)) {
        
//todo
        
print "NOT IMPLEMENTED SORRY!";
    }

    print 
"<form method=post action=\"?tab=sql&amp;index=$index\">";
    print 
"<input type=hidden name=sql VALUE=\"DELETE FROM $index WHERE id = ".intval($_GET['id'])."\">";
    print 
"<input type=submit value=\"DELETE THIS RECORD!\">";
    print 
"</form>";

    print 
"<form method=post>";
    print 
"<table>";

    foreach (
$desc as $field => $type) {
        print 
"<tr><th>$field</th><td>$type</td>";
        if ( 
$type =='string') {
            print 
"<td>".htmlentities($values[$field])."</td>";
        } elseif (
$type == 'timestamp' || $type == 'uint' || $type == 'float' || $type == 'mva') {
            
$size = ($type =='string' || $type == 'mva')?80:10;
            
$value htmlentities($values[$field]);
            print 
"<td><input type=\"text\" name=\"$field\" value=\"$value\" size=$size/></td>";
        } elseif (
$field == 'id') {
            print 
"<td><b>".intval($_GET['id'])."</b></td>";
        }
        print 
"</tr>";
    }

    print 
"</table>";
    if (!empty(
$size)) //at least one text box
        
print "<input type=submit value=\"save changes\">";
    print 
"</form>";
} else {
    die(
"huh?");
}