diff --git a/.idea/MySQLManager.iml b/.idea/MySQLManager.iml new file mode 100644 index 0000000..c956989 --- /dev/null +++ b/.idea/MySQLManager.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml new file mode 100644 index 0000000..c4c9543 --- /dev/null +++ b/.idea/codeStyleSettings.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..e7bedf3 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/deployment.xml b/.idea/deployment.xml new file mode 100644 index 0000000..e9219a5 --- /dev/null +++ b/.idea/deployment.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..281a6c0 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..12dce33 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..78e665f --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/webServers.xml b/.idea/webServers.xml new file mode 100644 index 0000000..c27033c --- /dev/null +++ b/.idea/webServers.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..d25d040 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,939 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + starttun + fingerprintconfirmed + ->localTunnelPort + localtunnelport + GotResponse + Got Response + FingerprintNotMatched + version + FingerprintNotConfirmed + === 4 + + + ->LocalTunnelPort + + + + + + + + + + + + + + + + + + + + + + + + + true + DEFINITION_ORDER + + + + + + + + + + + + + + + + + + + + + + + Data flow issuesJavaScript + + + Error handlingJavaScript + + + JavaScript + + + JavaScript validity issuesJavaScript + + + + + AngularJS + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + project + + + + + + + + + + + + + + + + project + + + true + + + + DIRECTORY + + false + + + + + + + + + + + + + + C:\Users\Chris\AppData\Roaming\Subversion + 125 + + + + + + + + + + + + + + + + + + + + + + + 1469375157576 + + + 1469375157576 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1469396271744 + + + + 1469396271745 + + + 1469653751323 + + + + 1469653751324 + + + 1470516867269 + + + + 1470516867269 + + + 1472941461866 + + + + 1472941461866 + + + 1473028091060 + + + + 1473028091060 + + + 1484688091946 + + + + 1484688091946 + + + 1485387142975 + + + + 1485387142975 + + + 1485905678485 + + + + 1485905678485 + + + 1487796771691 + + + + 1487796771691 + + + 1488494718442 + + + + 1488494718442 + + + 1489264504332 + + + + 1489264504332 + + + 1490224780073 + + + + 1490224780073 + + + 1497471846421 + + + + 1497471846421 + + + 1497473709898 + + + + 1497473709898 + + + 1497808888471 + + + + 1497808888471 + + + 1497808916580 + + + + 1497808916580 + + + 1499114201004 + + + + 1499114201004 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/APIManager.php b/APIManager.php new file mode 100644 index 0000000..a97a09d --- /dev/null +++ b/APIManager.php @@ -0,0 +1,111 @@ +getLogID(); + + $logger->writeToLog("Received API call from " . HelperClass::getIP()); + + + $postArray = decryptPostArray($_POST); + + $logger->writeToLog("Decrypted Post Array"); + $logger->writeToLog(print_r($postArray, true)); + + switch ($postArray['type']) + { + case "GetAPIStatus": + $logger->writeToLog("Getting API Status"); + $helperClass = new HelperClass(); + + $returnArray = $helperClass->returnSuccessArray(null); + $returnArray["Version"] = VERSION; + print json_encode($returnArray); + break; + case "TestDBConnection": + $logger->writeToLog("About to test connection to: " . $postArray["server"]); + $connManager = new ConnectionManager($logID); + print $connManager->testConnection($postArray); + //echo $encryption->encrypt("{\"result\":1,\"message\":\"there was a problem creating the tunnel\"}"); + return; + case "RetrieveDatabasesAndTables": + $logger->writeToLog("About to retrieve databaes and tables"); + $connManager = new ConnectionManager($logID); + print $connManager->retrieveDBsAndTables($postArray); + break; + case "GetDatabases": + $logger->writeToLog("About to retrieve databases"); + $connManager = new ConnectionManager($logID); + print $connManager->getDatabases($postArray); + break; + case "GetTables": + $logger->writeToLog("About to retrieve tables"); + $connManager = new ConnectionManager($logID); + print $connManager->getTableData($postArray); + break; + case "GetServerStatus": + $connManager = new ConnectionManager($logID); + print $connManager->getServerStatus($postArray); + break; + case "ExecuteSQL": + case "ExecuteSQLRepost": + $logger->writeToLog("About to perform SQL Query"); + $queryExecMan = new QueryExecManager($logID); + print $queryExecMan->executeQuery($postArray); + break; + case "GetSelectAllStatementCopy": + case "GetSelectAllStatementEditor": + case "GetInsertStatementCopy": + case "GetInsertStatementEditor": + case "GetUpdateStatementCopy": + case "GetUpdateStatementEditor": + case "GetDeleteStatementCopy": + case "GetDeleteStatementEditor": + case "GetCreateStatementCopy": + case "GetCreateStatementEditor": + $logger->writeToLog("About to retrieve SQL statement"); + $statementManager = new StatementRetrievalManager($logID); + return $statementManager->getSqlStatement($postArray); + default: + $helperClass = new HelperClass(); + $returnArray = $helperClass->returnError("Unknown API Method. Type: '" . $_POST["type"] . "'"); + print json_encode($returnArray); + } + + function decryptPostArray($postArray) + { + require_once 'Encryption.php'; + try + { + $decryptedPostArray = array(); + + $encryption = new Encryption(); + + foreach ($postArray as $key => $value) { + $decryptedPostArray[$encryption->decrypt($key)] = $encryption->decrypt($value); + } + return $decryptedPostArray; + } + catch (Exception $x) + { + $helperClass = new HelperClass(); + $returnArray = $helperClass->returnError("DecryptionFailed"); + $returnArray["version"] = VERSION; + print json_encode($returnArray); + } + + } +?> \ No newline at end of file diff --git a/CommonTasks.php b/CommonTasks.php new file mode 100644 index 0000000..29f1311 --- /dev/null +++ b/CommonTasks.php @@ -0,0 +1,198 @@ + $maxLength) + { + $length = $maxLength; + } + + $i = 0; + while ($i < $length) + { + $char = substr($possible, mt_rand(0, $maxLength-1), 1); + if (!strstr($string, $char)) + { + $string .= $char; + $i++; + } + } + return $string; + } + + /** + * @deprecated since version 1.1 + * Will generate a random password at the specified length. If length + * omitted then it defaults to false + * @param int $length + * @return string + */ + + function generatePassword($length = 6) + { + $password = ""; + $possible = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!£$&*"; + + $maxLength = strlen($possible); + if ($length > $maxLength) + { + $length = $maxLength; + } + + $i = 0; + while ($i < $length) + { + $char = substr($possible, mt_rand(0, $maxLength-1), 1); + if (!strstr($password, $char)) + { + $password .= $char; + $i++; + } + } + return $password; + } + + function generatePasswordOnlyNumbers($length = 6) + { + $password = ""; + $possible = "1234567890"; + + $maxLength = strlen($possible); + if ($length > $maxLength) + { + $length = $maxLength; + } + + $i = 0; + while ($i < $length) + { + $char = substr($possible, mt_rand(0, $maxLength-1), 1); + if (!strstr($password, $char)) + { + $password .= $char; + $i++; + } + } + return $password; + } + + + /** + * Will limit the length of the message that is passed. I.e. to make + * long strings which are being shown in smaller spaces to avoid + * ugly wrapping issues + * @param string $message + * @param int $length + * @return string + */ + function limitString($message, $length) + { + $arr = explode(' ',$message); + $string = ''; + + foreach($arr as $word) { + if(strlen($string) > $length) break; + $string .= ' ' . $word; + } + + return $string; + } + + /** + * Returns a date and time string in the format that MySQL Database wants it + * @return string + */ + function getDBDateTime() + { + $date = date('Y-m-d'); + $t = time(); + $time = date('H:i:s', $t); + + return "$date $time"; + } + + /** + * Returns a long value of an epoch time stamp, this is based on the + * current time + * @return long + */ + function getEpochTimeStamp() + { + $date = new DateTime(); + return date_timestamp_get($date); + } + + /** + * Returns the time in the format of H:i + * @return string + */ + function getTime() + { + date_default_timezone_set('Europe/London'); + $time = date("H:i"); + return $time; + } + + /** + * Returns a date string in a specific format. The format is an optional + * parameter so if it is null it will default with YYYY/mm/dd otherwise + * it will return the date in the specific format + * @param string $format + * @return string + */ + function findDateInSpecificFormat($format=null) + { + date_default_timezone_set('Europe/London'); + $date = getdate(); + $day = $date['mday']; + $month = $date['mon']; + $year = $date['year']; + if ($format == null) + { + return $year . '/' . $month . '/' . $day; + } + else + { + $returnedDate = str_replace('Y', $year, $format); + $returnedDate = str_replace('m', $month, $returnedDate); + $returnedDate = str_replace('d', $day, $returnedDate); + + return $returnedDate; + } + } + } +?> diff --git a/ConfigManager.php b/ConfigManager.php new file mode 100644 index 0000000..216c9d3 --- /dev/null +++ b/ConfigManager.php @@ -0,0 +1,63 @@ +configFile = $configFile; + $this->configArray = parse_ini_file($this->configFile, true); + } + + /** + * Returns the array created when the config file is parsed + * @return array The array of all values and sections from the config file + */ + function getConfigArray() + { + return $this->configArray; + } + + function getConfigArraySection($section) + { + return $this->configArray[$section]; + } + + /** + * Returns the value from a config file from the section and item. If the item + * is not found within the section of the config file, then a default value is returned + * @param String $section The section name within the config file + * @param String $item The item name from within the config file + * @param String $defaultValue The default value that should be returned if the config item was not found in the section + * @return String + * @throws Exception + */ + function getConfigItemValue($section, $item, $defaultValue) + { + //Check to ensure config array is set and not empty + if (!isset($this->configArray) || empty($this->configArray)) + { + throw new Exception("Config array is empty, most likely config file does not exist, or was not able to be read"); + } + //Check if the section exists + if (!array_key_exists($section, $this->configArray)) + { + throw new Exception("Section not found in config file"); + } + //Check if config item exists if not, return default value + if (!isset($this->configArray[$section][$item])) + { + return $defaultValue; + } + else + { + return $this->configArray[$section][$item]; + } + } +} \ No newline at end of file diff --git a/ConnectionManager.php b/ConnectionManager.php new file mode 100644 index 0000000..c8e6e0f --- /dev/null +++ b/ConnectionManager.php @@ -0,0 +1,593 @@ +encryption = new Encryption(); + $this->logger = new Logger($logID); + } + + /** + * Get the server status from the SHOW VARIABLES command + * @param type $postArray The postArray that contains the connection details + */ + function getServerStatus($postArray) + { + $server = $postArray['server']; + $username = $postArray['username']; + $password = $postArray['password']; + $database = $postArray['database']; + $port = $postArray['port']; + + $this->postArray = $postArray; + + $connStatus = $this->connectToDB($server, $username, $password, $database, $port, "-1", $postArray); + + if ($connStatus[RESULT] == ERROR) + { + $this->logger->writeToLog("Faield to get connect to DB. MySQL Error: " . $connStatus[MYSQL_ERROR]); + HelperClass::printResponseInCorrectEncodingAndCloseTunnelIfNeeded($postArray, $connStatus); + //print $this->encryption->encrypt(json_encode($connStatus)); + exit(); //Failed to connect to DB, so stop processing + } + + $query = "SHOW VARIABLES"; + $this->logger->writeToLog("Running Query: $query"); + $result = $this->conn->query($query); + if ($result) + { + $varArray = array(); + while ($myrow = $result->fetch_array()) + { + $varArray[$myrow['Variable_name']] = $myrow['Value']; + } + $varArray[RESULT] = SUCCESS; + $varArray[LOCAL_TUNNEL_PORT] = $connStatus[LOCAL_TUNNEL_PORT]; + $this->logger->writeToLog("Successfully retrieved server status"); + HelperClass::printResponseInCorrectEncodingAndCloseTunnelIfNeeded($postArray, $varArray); + //print $this->encryption->encrypt(json_encode($varArray)); + } + else + { + $status = array(); + $status[RESULT] = ERROR; + $status[MYSQL_ERROR] = mysqli_error($this->conn); + $status[ERROR_NO] = mysqli_errno($this->conn); + $status[QUERY] = $query; + $this->logger->writeToLog("Failed to get server status. MySQL Error: " . $status[MYSQL_ERROR]); + //print $this->encryption->encrypt(json_encode($status)); + HelperClass::printResponseInCorrectEncodingAndCloseTunnelIfNeeded($postArray, $status); + exit(); //MySQL Error so stop processing + } + } + + + public function getTableData($postArray) + { + $server = $postArray['server']; + $username = $postArray['username']; + $password = $postArray['password']; + $database = $postArray['database']; + $port = $postArray['port']; + + $connStatus = $this->connectToDB($server, $username, $password, $database, $port, "-1", $postArray); + if ($connStatus[RESULT] == ERROR) + { + //print $this->encryption->encrypt(json_encode($connStatus)); + $this->logger->writeToLog("Faield to connect to the database. " . $connStatus[MYSQL_ERROR]); + HelperClass::printResponseInCorrectEncodingAndCloseTunnelIfNeeded($postArray, $connStatus); + exit(); //Failed to connect to DB, so stop processing + } + + try + { + $query = "SHOW TABLES FROM $database"; + $this->logger->writeToLog("Getting tables from database: $query"); + $result = $this->conn->query($query); + if ($result) + { + $tables = array(); + $i = 0; + while ($myrow = $result->fetch_array()) + { + $tables[$i]["TableName"] = $myrow["Tables_in_$database"]; + $tables[$i]["TableDescription"] = $this->getTableDescription($myrow["Tables_in_$database"]); + $i++; + } + if (isset($postArray["version"]) && $postArray["version"] >= "2.0.1.0") + { + $returnResult[TUNNEL_STATUS][LOCAL_TUNNEL_PORT] = $connStatus[LOCAL_TUNNEL_PORT]; + $returnResult[RESULT] = SUCCESS; + $returnResult[DATA] = $tables; + $this->logger->writeToLog("Databases successfully retrieved"); + HelperClass::printResponseInCorrectEncodingAndCloseTunnelIfNeeded($postArray, $returnResult); + } + else + { + //$dbAndTables[RESULT] = SUCCESS; + $this->logger->writeToLog("Databases and tables successfully retrieved"); + HelperClass::printResponseInCorrectEncodingAndCloseTunnelIfNeeded($postArray, $returnResult); + } + } + else + { + throw new Exception(mysqli_error($this->conn)); + } + } + catch (Exception $ex) + { + $status = array(); + $status[RESULT] = ERROR; + $status[MYSQL_ERROR] = mysqli_error($this->conn); + $status[ERROR_NO] = mysqli_errno($this->conn); + $status[QUERY] = $query; + $this->logger->writeToLog("Failed to get table data. MySQL Error: " . $ex->getMessage()); + //print $this->encryption->encrypt(json_encode($status)); + HelperClass::printResponseInCorrectEncodingAndCloseTunnelIfNeeded($postArray, $status); + exit(); //MySQL Error so stop processing + } + } + + private function getTableDescription($tableName) + { + $query = "DESC $tableName"; + $this->logger->writeToLog("Getting table description: $query"); + $result = $this->conn->query($query); + if ($result) + { + $data = array(); + $i = 0; + while ($myrow = $result->fetch_array()) + { + $data[$i]["Field"] = $myrow["Field"]; + $data[$i]["Type"] = $myrow["Type"]; + $data[$i]["Null"] = ($myrow["Null"] === "No") ? false : true; + $data[$i]["Default"] = $myrow["Default"]; + $data[$i]["Extra"] = $myrow["Extra"]; + $data[$i]["Key"] = $myrow["Key"]; + $i++; + } + return $data; + } + else + { + throw new Exception(mysqli_error($this->conn)); + } + } + + public function getDatabases($postArray) + { + require_once 'Logger.php'; + $server = $postArray['server']; + $username = $postArray['username']; + $password = $postArray['password']; + $database = $postArray['database']; + $port = $postArray['port']; + + $connStatus = $this->connectToDB($server, $username, $password, $database, $port, "-1", $postArray); + $logger = new Logger(); + if ($connStatus[RESULT] == ERROR || $connStatus[RESULT] == TUNNEL_ERROR) + { + if (isset($postArray["version"]) && $postArray["version"] >= "2.2.0.1") { + //print $this->encryption->encrypt(json_encode($connStatus)); + $this->logger->writeToLog("Faield to connect to the database. " . $connStatus[MYSQL_ERROR]); + HelperClass::printResponseInCorrectEncodingAndCloseTunnelIfNeeded($postArray, $connStatus); + exit(); //Failed to connect to DB, so stop processing + } + } + + $databases = array(); + $query = "SELECT SCHEMA_NAME AS `Database` FROM information_schema.SCHEMATA"; + $result = $this->conn->query($query); + if($result) + { + $this->logger->writeToLog("Found " . mysqli_num_rows($result) . " databases"); + while ($myrow = $result->fetch_array()) + { + $databases[] = $myrow["Database"]; + } + } + else + { + $status = array(); + $status[RESULT] = ERROR; + $status[MYSQL_ERROR] = mysqli_error($this->conn); + $status[ERROR_NO] = mysqli_errno($this->conn); + $status[QUERY] = $query; + $status[LOCAL_TUNNEL_PORT] = $connStatus[LOCAL_TUNNEL_PORT]; + //print $this->encryption->encrypt(json_encode($status)); + $this->logger->writeToLog("Failed to get databases. MySQL Error: " . $status[MYSQL_ERROR]); + HelperClass::printResponseInCorrectEncodingAndCloseTunnelIfNeeded($postArray, $status); + exit(); //MySQL Error so stop processing + } + + if (isset($postArray["version"]) && $postArray["version"] >= "2.0.1.0") + { + $returnResult[TUNNEL_STATUS][LOCAL_TUNNEL_PORT] = $connStatus[LOCAL_TUNNEL_PORT]; + $returnResult[RESULT] = SUCCESS; + $returnResult[DATA] = $databases; + $this->logger->writeToLog("Databases successfully retrieved"); + HelperClass::printResponseInCorrectEncodingAndCloseTunnelIfNeeded($postArray, $returnResult); + } + else + { + //$dbAndTables[RESULT] = SUCCESS; + $this->logger->writeToLog("Databases and tables successfully retrieved"); + HelperClass::printResponseInCorrectEncodingAndCloseTunnelIfNeeded($postArray, $databases); + } + } + + + /** + * Returns a JSON array to retrieve all of the databases that are availabe on the connection + * and calls a function to also get the tables from each database + * @param type $postArray The post array that contains that connection details + * @deprecated This replaced with two separate API calls GetDatabases and GetTables + */ + function retrieveDBsAndTables($postArray) + { + $server = $this->encryption->decrypt($postArray['server']); + $username = $this->encryption->decrypt($postArray['username']); + $password = $this->encryption->decrypt($postArray['password']); + $database = $this->encryption->decrypt($postArray['database']); + $port = $this->encryption->decrypt($postArray['port']); + + $connStatus = $this->connectToDB($server, $username, $password, $database, $port, "-1", $postArray); + if ($connStatus[RESULT] == ERROR) + { + //print $this->encryption->encrypt(json_encode($connStatus)); + $this->logger->writeToLog("Faield to connect to the database. " . $connStatus[MYSQL_ERROR]); + HelperClass::printResponseInCorrectEncodingAndCloseTunnelIfNeeded($postArray, $connStatus); + exit(); //Failed to connect to DB, so stop processing + } + + $dbAndTables = array(); + + if ((!isset($database)) || empty($database)) + { + $query = "SELECT SCHEMA_NAME AS `Database` FROM information_schema.SCHEMATA"; + $result = $this->conn->query($query); + if ($result) + { + $this->logger->writeToLog("Found " . mysqli_num_rows($result) . " database(s)"); + while ($myrow = $result->fetch_array()) + { + $dbAndTables[$myrow['Database']] = $this->getTables($myrow['Database']); + } + } + else + { + $status = array(); + $status[RESULT] = ERROR; + $status[MYSQL_ERROR] = mysqli_error($this->conn); + $status[ERROR_NO] = mysqli_errno($this->conn); + $status[QUERY] = $query; + $status[LOCAL_TUNNEL_PORT] = $connStatus[LOCAL_TUNNEL_PORT]; + //print $this->encryption->encrypt(json_encode($status)); + $this->logger->writeToLog("Failed to get databases. MySQL Error: " . $status[MYSQL_ERROR]); + HelperClass::printResponseInCorrectEncodingAndCloseTunnelIfNeeded($postArray, $status); + exit(); //MySQL Error so stop processing + } + } + else + { + $dbAndTables[$database] = $this->getTables($database); + } + //$dbAndTables[RESULT] = SUCCESS; + if (isset($postArray["version"]) && $postArray["version"] >= "2.0.1.0") + { + $returnResult[TUNNEL_STATUS][LOCAL_TUNNEL_PORT] = $connStatus[LOCAL_TUNNEL_PORT]; + $returnResult[RESULT] = SUCCESS; + $returnResult[DATA] = $dbAndTables; + $this->logger->writeToLog("Databases and tables successfully retrieved"); + HelperClass::printResponseInCorrectEncodingAndCloseTunnelIfNeeded($postArray, $returnResult); + } + else + { + //$dbAndTables[RESULT] = SUCCESS; + $this->logger->writeToLog("Databases and tables successfully retrieved"); + HelperClass::printResponseInCorrectEncodingAndCloseTunnelIfNeeded($postArray, $dbAndTables); + } + //print $this->encryption->encrypt(json_encode($dbAndTables)); + } + + /** + * Tests the passed in the connection details into the post array + * @param type $postArray The post array that contains the connection details + */ + function testConnection($postArray) + { + $tunnelManager = null; + $localTunnelPort = "-1"; + if (isset($postArray["useTunnel"]) && $postArray["useTunnel"] === "1") + { + $tunnelManager = new TunnelManager(); + $tunnelManager->setSSHParametersFromPostArray($postArray); + + $this->logger->writeToLog("Testing connection to database, about to start SSH tunnel"); + $tunnelStatus = $tunnelManager->startTunnel(); + if ($tunnelStatus->result === SUCCESS) + { + if ($postArray["fingerprintConfirmed"] === "false") + { + $status[RESULT] = SUCCESS; + $status[FINGERPRINT] = $tunnelStatus->fingerprint; + HelperClass::printResponseInCorrectEncodingAndCloseTunnelIfNeeded($postArray, $status); + exit(); + } + $this->localTunnelPort = $tunnelStatus->LocalTunnelPort; + $localTunnelPort = $tunnelStatus->LocalTunnelPort; + $this->logger->writeToLog("SSH tunnel was started successfully"); + } + else + { + $this->logger->writeToLog("Failed to start SSH tunnel. Error: " . $tunnelStatus->message); + HelperClass::printResponseInCorrectEncodingAndCloseTunnelIfNeeded($postArray, $tunnelStatus); + exit(); + } + } + + $server = $postArray['server']; + $username = $postArray['username']; + $password = $postArray['password']; + $database = $postArray['database']; + $port = intval($postArray['port']); + + //If we're connecting via a tunnel than connect to localhost + if ($localTunnelPort !== "-1") + { + $server = "localhost"; + } + $status = $this->connectToDB($server, $username, $password, $database, $port, $localTunnelPort, $postArray); + + $this->logger->writeToLog("Test completed, returning response"); + HelperClass::printResponseInCorrectEncodingAndCloseTunnelIfNeeded($postArray, $status); + //print $this->encryption->encrypt(json_encode($status)); + } + + /** + * Attempts a connection to the database and returns the connection state + * @param type $postArray The post array that contains the connection details + * @return type a JSON encoded result of how the connection was done + */ + function connectToDBFromPostArray($postArray) + { + $tunnelManager = null; + $localTunnelPort = "-1"; + if (isset($postArray["useTunnel"]) && $postArray["useTunnel"]) + { + $tunnelManager = new TunnelManager(); + $tunnelManager->setSSHParametersFromPostArray($postArray); + $tunnelResponse = $tunnelManager->startTunnel(); + if(isset($tunnelResponse->LocalTunnelPort)) + { + $localTunnelPort = $tunnelResponse->LocalTunnelPort; + } + else if ($tunnelResponse->result === 4) //Need to amend this - the API Error codes should match with C++ + { + if (isset($postArray["version"]) && $postArray["version"] >= "2.3.0.0") + { + if ($tunnelResponse->message === "FingerprintNotMatched") + { + $status[RESULT] = TUNNEL_ERROR; + $status[FINGERPRINT] = $tunnelResponse->fingerprint; + $status[MESSAGE] = "FingerprintNotMatched"; + return $status; + } + else if ($tunnelResponse->message === "PasswordAuthFailed") + { + $status[RESULT] = TUNNEL_ERROR; + $status[MESSAGE] = $tunnelResponse->message; + return $status; + } + } + } + else //If the local tunnel port not set - then the fingerprint will be returned so it can be confirmed + { + if (isset($postArray["version"]) && $postArray["version"] >= "2.3.0.0") { + $status[RESULT] = TUNNEL_ERROR; + $status[FINGERPRINT] = "FingerprintNotConfirmed"; + return $status; + } + } + } + $server = $postArray['server']; + $username = $postArray['username']; + $password = $postArray['password']; + $database = $postArray['database']; + $port = $postArray['port']; + if ($localTunnelPort === "-1") + { + return $this->connectToDB($server, $username, $password, $database, $port); + } + else + { + return $this->connectToDB($server, $username, $password, $database, $port, $localTunnelPort, $postArray); + } + } + + /** + * Performs the actual MySQL Connection to the server + * @param type $server The MySQL Server to connect to + * @param type $username The username used for the database connection + * @param type $password The password used for the database connection + * @param type $database The database to use, can be null (no default database will be selected + * @param type $port The port number that should used + * @return type Array that details the connection state + */ + function connectToDB($server, $username, $password, $database, $port, $localTunnelPort = "-1", $postArray = null) + { + require_once 'Logger.php'; + $logger = new Logger(); + //Check if the local tunnel port is -1 and then double check + //if the tunnel needs to be created if this function has been called directly + $tunnelManager = null; + $usingTunnel = false; + $logger->writeToLog("LocalTunnelPort: $localTunnelPort"); + //If we're testing the SSH tunnel might already be started, so set usingTunnel to true, and don't start it twice + if (!empty($localTunnelPort) && $localTunnelPort !== "-1") + { + $usingTunnel = true; + } + if (($postArray != null) && isset($postArray["useTunnel"]) && $postArray["useTunnel"] === "1" && $localTunnelPort === "-1") + { + $logger->writeToLog("INSIDE HERE"); + $tunnelManager = new TunnelManager(); + $tunnelManager->setSSHParametersFromPostArray($postArray); + $tunnelResponse = $tunnelManager->startTunnel(); + if ($tunnelResponse->result === 4) //Need to ammend this - the API Error codes should match with C++ + { + if (isset($postArray["version"]) && $postArray["version"] >= "2.3.0.0") { + if ($tunnelResponse->message === "FingerprintNotMatched") { + $status[RESULT] = TUNNEL_ERROR; + $status[FINGERPRINT] = $tunnelResponse->fingerprint; + $status[MESSAGE] = "FingerprintNotMatched"; + return $status; + } else if ($tunnelResponse->message === "PasswordAuthFailed") { + $status[RESULT] = TUNNEL_ERROR; + $status[MESSAGE] = $tunnelResponse->message; + return $status; + } else { + $status[RESULT] = TUNNEL_ERROR; + $status[MESSAGE] = $tunnelResponse->message; + return $status; + } + } + } + $usingTunnel = true; + if ((isset($postArray["version"]) && $postArray["version"] >= "2.3.0.0") && $tunnelManager->getFingerprintConfirmed() === false) + { + //Return the fingerprint + $status[RESULT] = SUCCESS; + $status[FINGERPRINT] = $tunnelResponse->fingerprint; + return $status; + } + else + { + $localTunnelPort = $tunnelResponse->LocalTunnelPort; + } + } + + //If the local tunnel port is blank, then we the port forward wasn't set up, likely because the fingerprint was confirmed + if ((isset($postArray["version"]) && $postArray["version"] >= "2.3.0.0") && $usingTunnel && empty($localTunnelPort)) + { + $status[RESULT] = TUNNEL_ERROR; + $status[MESSAGE] = "FingerprintNotConfirmed"; + return $status; + } + //Connect to the database directly + if ($localTunnelPort === "-1") + { + $this->conn = mysqli_connect($server, $username, $password, null, $port);// or die (HelperClass::printResponseInCorrectEncoding(array(RESULT => ERROR, MYSQL_ERROR => mysqli_connect_error(), ERROR_NO => mysqli_connect_errno()))); + } + //Use the tunnel + else + { + $this->conn = mysqli_connect("127.0.0.1", $username, $password, null, $localTunnelPort); + } + if (!$this->conn) + { + $status[RESULT] = ERROR; + $status[MYSQL_ERROR] = mysqli_connect_error(); + $status[ERROR_NO] = mysqli_connect_errno(); + } + else + { + if (isset($database) && !empty($database)) + { + if (mysqli_select_db($this->conn, $database)) + { + $status[RESULT] = SUCCESS; + } + else + { + $status[RESULT] = ERROR; + $status[MYSQL_ERROR] = mysqli_error($this->conn); + $status[ERROR_NO] = mysqli_errno($this->conn); + } + } + else + { + $status[RESULT] = SUCCESS; + } + } + if ($localTunnelPort !== "-1") + { + $status[LOCAL_TUNNEL_PORT] = $localTunnelPort; + } + return $status; + } + + /** + * Gets the tables from the specified database + * @param type $db The database name where the tables should be retrieved from + * @return type An array detailing the error if the tables couldn't be retrieved, or an array of the tables that were retrieved + */ + function getTables($db) + { + $tables = array(); + $query = "SHOW TABLES FROM $db"; + $result = $this->conn->query($query); + if ($result) + { + while ($myrow = $result->fetch_array()) + { + $tables[] = $myrow["Tables_in_$db"]; + } + return $tables; + } + else + { + $status = array(); + $status[RESULT] = ERROR; + $status[MYSQL_ERROR] = mysqli_error($this->conn); + $status[ERROR_NO] = mysqli_errno($this->conn); + $status[QUERY] = $query; + $status[LOCAL_TUNNEL_PORT] = $this->LocalTunnelPort; + + //print $this->encryption->encrypt(json_encode($status)); + HelperClass::printResponseInCorrectEncodingAndCloseTunnelIfNeeded($this->postArray, $status); + exit(); //MySQL Query Failed + } + return $tables; + } + + /** + * Builds an array of what the error could be + * @param type $error The error message + * @param type $errorCode The error code of the message + * @return type Returns an array detailing the error + */ + function handleError($error, $errorCode) + { + $status[RESULT] = ERROR; + $status[MYSQL_ERROR] = $error; + $status[ERROR_NO] = $errorCode; + + return $status; + } + } + +?> diff --git a/DBHelper.php b/DBHelper.php new file mode 100644 index 0000000..b60d239 --- /dev/null +++ b/DBHelper.php @@ -0,0 +1,239 @@ +mysqlIconnectToDB($server, $username, $password, $port, $database); + } + catch (Exception $e) + { + throw $e; + } + } + + function mysqlIconnectToDB($server, $username, $password, $port, $database) + { + if ($this->dbConnection !== null) + { + return $this->dbConnection; + } + $conn = mysqli_connect($server, $username, $password, null, $port); + if (!$conn) + { + $error = mysqli_connect_error(); + $errorno = mysqli_connect_errno(); + throw new Exception("Unable to connect to database. Error: $error. Error No: $errorno"); + } + + if (isset($database) && !empty($database)) + { + if (!mysqli_select_db($conn, $database)) + { + $error = mysqli_error($conn); + $errorno = mysqli_errno($conn); + throw new Exception("Unable to connect to database. Error: $error. Error No: $errorno"); + } + } + $this->dbConnection = $conn; + return $conn; + } + + function closeMysqlIDBConnection($conn) + { + if ($conn != null) + { + if (mysqli_close($conn) === false) + { + $error = mysql_error(); + $errorno = mysql_errno(); + throw new Exception("Failed to close DB Connection. Error: $error. Error No: $errorno"); + } + else + { + return true; + } + } + } + + /** + * Connects to a database from config array, using ConfigManager class and get the array + * @param array $configArray Array values from the config file created from ConfigManager.php + * @return MySQLConnection A valid MySQL Connection Resouce + * @deprecated since version 1.0.0.5 + */ + function connectToDBFromConfigArray($configArray) + { + $server = $configArray['database']['server']; + $username = $configArray['database']['username']; + $password = $configArray['database']['password']; + $port = $configArray['database']['port']; + $database = null; + if (isset($configArray['database']['database']) && + !empty($configArray['database']['database'])) + { + $database = $configArray['database']['database']; + } + + return $this->connectToDB($server, $username, $password, $port, $database); + } + + /** + * Connects to a database using individual variables for username, password and server etc + * @param String $server The server to connect to + * @param String $username The username to connect to the server + * @param String $password The password to connect to the server + * @param int $port The port to connect to the server + * @param String $database + * @return MySQLConnection Valid MySQL Connection resource + * @throws Exception Thrown when failed to connect to database + */ + function connectToDB($server, $username, $password, $port, $database) + { + $conn = mysql_connect($server, $username, $password, $port); + if ($conn === false) + { + $error = mysql_error(); + $errorno = mysql_errno(); + throw new Exception("Unable to connect to database. Error: $error. Error No: $errorno"); + } + + if (isset($database) && !empty($database)) + { + if (mysql_select_db($database) === false) + { + $error = mysql_error(); + $errorno = mysql_errno(); + throw new Exception("Unable to connect to database. Error: $error. Error No: $errorno"); + } + } + + return $conn; + } + + function startDBTransaction($conn = null) + { + if ($conn == null) + { + if (!mysql_query("START TRANSACTION")) + { + throw new Exception("Failed to start transaction. MySQL Error: " . mysql_error()); + } + } + else + { + if (!$conn->query("START TRANSACTION")) + { + throw new Exception("Failed to start transaction. MySQL Error: " . mysql_error()); + } + } + } + + function finishDBTransaction($transStatus, $conn = null) + { + if ($transStatus == DBTransactionStatus::SUCCESSFUL) + { + if ($conn == null) + { + if (!mysql_query("COMMIT")) + { + throw new Exception("Failed to commit transaction. MySQL Error: " . mysql_error()); + } + } + else + { + if (!$conn->query("COMMIT")) + { + throw new Exception("Failed to commit transaction. MySQL Error: " . mysql_error()); + } + } + } + else if ($transStatus == DBTransactionStatus::FAILURE) + { + if ($conn == null) + { + if (!mysql_query("ROLLBACK")) + { + throw new Exception("Failed to rollback transaction. MySQL Error: " . mysql_error()); + } + } + else + { + if (!$conn->query("ROLLBACK")) + { + throw new Exception("Failed to rollback transaction. MySQL Error: " . mysql_error()); + } + } + } + } + + /** + * Closes a mySQL Database Connection + * @param MySQLConnection $conn A valid MySQL Connection + * @return boolean true on success otherwise throws an exception + * @throws Exception Thrown when failed to close database + * @deprecated since version 1.0.0.5 + */ + function closeDBConnection($conn) + { + if ($conn != null) + { + if (mysql_close($conn) === false) + { + $error = mysql_error(); + $errorno = mysql_errno(); + throw new Exception("Failed to close DB Connection. Error: $error. Error No: $errorno"); + } + else + { + return true; + } + } + } + + /** + * Get the ID that is going to be used next when the record is inserted + * @param String $database The database name that should be looked in + * @param String $table The table name where the id should be found + * @param String $error The memory location of the error, this way error is returned to the calling function + * @return int/String int if query is executed successfully and string for the error message on failure + */ + function getTheNextIDFromDBToUse($database, $table, $error) + { + $query = "SELECT `AUTO_INCREMENT` FROM INFORMATION_SCHEMA.TABLES + WHERE TABLE_SCHEMA = '$database' AND TABLE_NAME = '$table';"; + $result = mysql_query($query); + if ($result) + { + $myrow = mysql_fetch_array($result); + $error = null; + return $myrow['AUTO_INCREMENT']; + } + else + { + $error = mysql_error(); + return -1; + } + } + } + +?> \ No newline at end of file diff --git a/Encryption.php b/Encryption.php new file mode 100644 index 0000000..86f5581 --- /dev/null +++ b/Encryption.php @@ -0,0 +1,81 @@ +cipher = $configManager->getConfigItemValue("encryption", "cipher", null); + if ($this->cipher == null || empty($this->cipher)) + { + throw new Exception("Encryption cipher cannot be null. Ensure that it is specified in a section call encryption in the configuration file"); + } + $this->iv = $configManager->getConfigItemValue("encryption", "iv", null); + if ($this->iv == null || empty($this->iv)) + { + throw new Exception("Encryption iv cannot be null. Ensure that it is specified in a sectioned called encryption in the configuration file"); + } + } + + function resetKeysToDefaultKeys() + { + + } + + function encrypt($data) + { + + //$iv = "ryojvlzmdalyglrj"; + //$key = CIPHERKEY; + + return base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $this->cipher, $this->addpadding($data), MCRYPT_MODE_CBC, $this->iv)); + } + + private function addpadding($string, $blocksize = 16) + { + $len = strlen($string); + $pad = $blocksize - ($len % $blocksize); + $string .= str_repeat(chr($pad), $pad); + + return $string; + } + + private function strippadding($string) + { + $slast = ord(substr($string, -1)); + $slastc = chr($slast); + $pcheck = substr($string, -$slast); + if(@preg_match("/$slastc{".$slast."}/", $string)){ + $string = substr($string, 0, strlen($string)-$slast); + return $string; + } else { + throw new Exception("Strip padding failed. Likely not encrypted"); + } + } + + function decrypt($data) + { + try + { + + $base64Decoded = @base64_decode($data); + $decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $this->cipher, $base64Decoded, MCRYPT_MODE_CBC, $this->iv); + return $this->strippadding($decrypted); + } + catch (Exception $e) + { + throw new Exception("Failed to decrypt"); + } + } + } +?> diff --git a/HelperClass.php b/HelperClass.php new file mode 100644 index 0000000..674ac5b --- /dev/null +++ b/HelperClass.php @@ -0,0 +1,115 @@ +LocalTunnelPort) && + empty($response->LocalTunnelPort) || $response->LocalTunnelPort === "-1") || + $response->LocalTunnelPort === null) + { + unset($response->LocalTunnelPort); + } + + if (isset($postArray["version"]) && $postArray["version"] >= "1.2.0.0") + { + + print $encryption->encrypt(json_encode($response)); + } + else + { + //Temporary test for older application backward compatibility testing + //TODO: REMOVE THE ENCRYPTION + //print json_encode($encryption->decrypt($response)); + if (isset($response->data)) + { + print json_encode($response->data); + } + else + { + print json_encode($response); + } + } + if (isset($response->LocalTunnelPort) && !empty($response->LocalTunnelPort) && $response->LocalTunnelPort !== "-1") + { + $tunnelManager = new TunnelManager(); + $tunnelManager->closeTunnel($response->LocalTunnelPort); + } + /*else if (isset($response[TUNNEL_STATUS]) && $response[TUNNEL_STATUS][LOCAL_TUNNEL_PORT] && $response[TUNNEL_STATUS][LOCAL_TUNNEL_PORT] !== "-1") + { + $tunnelManager = new TunnelManager(); + $tunnelManager->closeTunnel($response[TUNNEL_STATUS][LOCAL_TUNNEL_PORT]); + }*/ + } + + /** + * Return the clients IP address + * @return string + */ + public static function getIP() + { + if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) + { + $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; + } + else + { + $ip = $_SERVER['REMOTE_ADDR']; + } + return $ip; + } + } + +?> diff --git a/Logger.php b/Logger.php new file mode 100644 index 0000000..fd9862b --- /dev/null +++ b/Logger.php @@ -0,0 +1,126 @@ +logID = intval(str_pad((rand(1, 99999)), "0", STR_PAD_LEFT)); + } + else + { + $this->logID = $logID; + } + + $this->configManager = new ConfigManager($this->configFile); + + $this->logHandle = fopen($this->configManager->getConfigItemValue("debug", "debug_log", "api.log"), "a+"); + if (!$this->logHandle) + { + throw new Exception("Unable to open debug log file: Error: " + error_get_last()); + } + } + + /** + * Returns the current Log ID for this script instance + * @return int + */ + public function getLogID() + { + return $this->logID; + } + + /** + * Write a message to the API log file. A new line will automatically be put on the end of the log message + * @param $logMessage The message to be written to the file + */ + public function writeToLog($logMessage) + { + //Get the debug array so we can include the file name and line number of the log message - this will make debugging simple, fingers crossed + $debug = debug_backtrace(); + $currentFile = $debug[0]["file"]; + $lineNumber = $debug[0]["line"]; + $currentDate = date("d/m/Y H:i:s"); + + fwrite($this->logHandle, "$currentDate -" . $this->logID . "-: $currentFile:$lineNumber:\t$logMessage\n"); + fflush($this->logHandle); //Ensure that the log is flushed to the file + + $this->archiveIfRequired(); + } + + private function archiveIfRequired() + { + $fileName = $this->configManager->getConfigItemValue("debug", "debug_log", "api.log"); + $fileSize = filesize( $fileName) / (1024 * 1024); + + $maxFileSize = $this->configManager->getConfigItemValue("debug", "maxFileSizeInMB", 5); + + if ($fileSize > $maxFileSize) + { + //Close the file handle first + fclose($this->logHandle); + rename($fileName, "$fileName" . "_" . date("YmdHis")); + + //Now open a new file + $this->logHandle = fopen($this->configManager->getConfigItemValue("debug", "debug_log", "api.log"), "w+"); + } + + //Now check if the oldest should be removed + $files = scandir("."); + $archivedFiles = array(); + for ($i = 0; $i < count($files); $i++) + { + //If it is the API log and includes an underscore - they'll be the archived logs + if (strpos($files[$i], $fileName) === 0 && strpos($files[$i], "_") > 0) + { + $archivedFiles[filemtime($files[$i])] = $files[$i]; + } + } + + //Sort the array into descending ascending order - the oldest will always be at 0 + ksort($archivedFiles, SORT_ASC); + + $keys = array_keys($archivedFiles); + $maxArchiveCount = $this->configManager->getConfigItemValue("debug", "maxArchiveCount", 3); + + if (count($archivedFiles) > $maxArchiveCount) + { + do + { + unlink($archivedFiles[$keys[0]]); + unset($archivedFiles[$keys[0]]); + $archivedFiles = array_filter($archivedFiles); + } while (count($archivedFiles) > $maxArchiveCount); + } + + } + + public function __destruct() + { + fflush($this->logHandle); + fclose($this->logHandle); + } + +} \ No newline at end of file diff --git a/QueryExecManager.php b/QueryExecManager.php new file mode 100644 index 0000000..516a5f8 --- /dev/null +++ b/QueryExecManager.php @@ -0,0 +1,477 @@ +encryption = new Encryption(); + $this->logger = new Logger($logID); + } + + /** + * Execute a query using the specified post data. The post data will include + * the connectin details along with the query. + * @param $postArray + */ + function executeQuery($postArray) + { + include_once("ConnectionManager.php"); + $connManager = new ConnectionManager(); + $status = $connManager->connectToDBFromPostArray($postArray); + if ($status[RESULT] !== SUCCESS) + { + $this->logger->writeToLog("Failed to connect to database. MySQL Error: " . $status[MYSQL_ERROR]); + HelperClass::printResponseInCorrectEncodingAndCloseTunnelIfNeeded($postArray, $status); + exit(); + } + if ((!isset($postArray["rowsDownloaded"])) || empty($postArray["rowsDownloaded"])) + { + $totalRows = 0; + } + else + { + $totalRows = $postArray["rowsDownloaded"]; + } + $this->logger->writeToLog("$totalRows row(s) has so far been returned"); + $queryLimitedTo = null; + if (stripos($postArray["query"], "SELECT") !== false) + { + //This fixes issues where WHERE parameters add slashes around the surrounding speech marks + //quotes causing an issue with the query + $postArray["query"] = $this->addSlahesToWhereParameters($postArray["query"], $connManager->conn); + if ($postArray["defaultLimit"] < API_RESULTSET_LIMIT) + { + $queryLimitedTo = $postArray["defaultLimit"]; + } + else + { + $queryLimitedTo = API_RESULTSET_LIMIT; + } + } + + if (!isset($postArray["startIndex"])) + { + $startIndex = 0; + } + else + { + $startIndex = $postArray["startIndex"]; + } + + + if (stripos($postArray["query"], "SELECT") !== false) + { + $queryWithUpdatedLimit = $this->getLimitString($startIndex, $postArray["query"], $totalRows, + $queryLimitedTo); + } + else + { + $queryWithUpdatedLimit = $postArray["query"]; + } + $helperClass = new HelperClass(); + if ($status[RESULT] != SUCCESS) + { + HelperClass::printResponseInCorrectEncodingAndCloseTunnelIfNeeded($postArray, $status); + exit(); + } + + $this->logger->writeToLog("Executing Query: $queryWithUpdatedLimit"); + + $startTime = round(microtime(true) * 1000); + $result = $connManager->conn->query($queryWithUpdatedLimit); + $endTime = round(microtime(true) * 1000); + + $timeDifference = $endTime - $startTime; + $timeTaken = (double)$timeDifference / (double)1000; //Time taken in seconds + $this->logger->writeToLog("Query took $timeTaken second(s)"); + + //Set the time taken from what was previously executed + if (isset($postArray[TIME_TAKEN]) && !empty($postArray[TIME_TAKEN])) + { + $timeTaken += $postArray[TIME_TAKEN]; + } + + $returnArray = array(); + if ($result && stripos($postArray["query"], "SELECT") !== false || $result && stripos($postArray["query"], "SHOW") === 0) + { + + $data = array(); + for ($i = 0; $i < $result->field_count; $i++) + { + $fieldInfo = $result->fetch_field(); + //$fieldInfo = mysql_fetch_field($result, $i); + $cols[] = $fieldInfo->name; + } + $data[] = $cols; + + while ($myrow = $result->fetch_row()) + { + $data[] = $myrow; + } + + $rowsReturned = $result->num_rows; + $totalRows += $rowsReturned; + //If the number of rows returned is less than the API + //limit then there is no more data to get. Tell Android + //so that it doesn't attempt to post it. + if ($rowsReturned < API_RESULTSET_LIMIT) + { + $returnArray = $helperClass->returnSuccessArray($data); + $returnArray[MESSAGE] = NO_MORE_ROWS; + $returnArray[START_INDEX] = $startIndex; + $returnArray[ROWS_RETURNED] = $totalRows; + $returnArray[TIME_TAKEN] = $timeTaken; + } + else + { + if (($queryLimitedTo === null) || $totalRows >= $queryLimitedTo) + { + $returnArray = $helperClass->returnSuccessArray($data); + $returnArray[MESSAGE] = NO_MORE_ROWS; + $returnArray[START_INDEX] = $startIndex; + $returnArray[ROWS_RETURNED] = $totalRows; + $returnArray[TIME_TAKEN] = $timeTaken; + } + else + { + $returnArray = $helperClass->returnSuccessArray($data); + $returnArray[MESSAGE] = MORE_ROWS_TO_GET; + $returnArray[START_INDEX] = $startIndex; + $returnArray[ROWS_RETURNED] = $totalRows; + $returnArray[TIME_TAKEN] = $timeTaken; + } + } + $this->logger->writeToLog("Query execution completed successfully"); + $returnArray[LOCAL_TUNNEL_PORT] = $status[LOCAL_TUNNEL_PORT]; + HelperClass::printResponseInCorrectEncodingAndCloseTunnelIfNeeded($postArray, $returnArray); + } + else + { + if ($result) + { + $returnArray = $helperClass->returnSuccessArray($returnArray); + $returnArray[MESSAGE] = NO_RESULT_SET_REQUIRED; + $returnArray[LOCAL_TUNNEL_PORT] = $status[LOCAL_TUNNEL_PORT]; + $returnArray[TIME_TAKEN] = $timeTaken; + HelperClass::printResponseInCorrectEncodingAndCloseTunnelIfNeeded($postArray, $returnArray); + } + else + { + $helper = new HelperClass(); + $error = mysqli_error($connManager->conn); + $errorNo = mysqli_errno($connManager->conn); + $this->logger->writeToLog("Failed to execute MySQL Query: Error Code: $errorNo Message: $error"); + $mysqlErrorArray = $helper->returnMySQLErrorArray($error, $errorNo); + $mysqlErrorArray[LOCAL_TUNNEL_PORT] = $status[LOCAL_TUNNEL_PORT]; + HelperClass::printResponseInCorrectEncodingAndCloseTunnelIfNeeded($postArray, $mysqlErrorArray); + } + } + } + + /** + * This converts the limit string to get portions of the database out + * This prevents a lock up occuring for too long and therefore result in a + * Time out exception within Android. This ensures that if no limit string was + * specified in the query, then add a limit string so it can get a portion + * and post to it again to get the next portion out + * @param int $startIndex The start index of the result set to be returned + * (the first parameter of the limit + * @param string $query The query to be performed + * @return string The query where the limit string has been converted + */ + private function getLimitString(&$startIndex, $query, $totalRows, &$returnLimitParameter) + { + $startIndex = intval($startIndex); + $limitOccurrenceCount = $this->getCountOfLimitsInQuery($query); + $this->logger->writeToLog("Limit Occurence Count: $limitOccurrenceCount"); + //Limit is in the query multiple times, therefore just return the + //query. Likelyhood is this type of query won't be a huge amount + //of data so shouldn't cause Android to throw an timeout exception. + //Therefore just return the original query + if ($limitOccurrenceCount > 1) + { + return $query; + } + //The query does include a limit, therefore, + //ensure that the limit has both a start index and a limit + else + { + if ($limitOccurrenceCount == 1) + { + return $this->convertLimit($query, $startIndex, $totalRows, $returnLimitParameter); + } + //The query doesn't include a limit, therefore, + //just add the limit to the query + else + { + //Get a portion of the data. From the start index + //get a resultset of 10000 rows. Send this back to + //Android and allow Android to respost if necessary + if ($returnLimitParameter < API_RESULTSET_LIMIT) + { + $returnLimitParameter = intval($returnLimitParameter); + $query .= " LIMIT $startIndex, $returnLimitParameter"; + } + else + { + $query .= " LIMIT $startIndex, " . API_RESULTSET_LIMIT; + } + } + } + + + //Get the position of the first + return $query; + } + + private function convertLimit($query, &$startIndex, $totalRowsCurrentlyDownloaded, &$returnLimitParameter) + { + $queryLength = strlen($query); + $startOfLimit = stripos($query, "LIMIT"); + $startOfLimitParameter = $startOfLimit + 6; + + //Get the first parameter of limit, +6 to the start 5 characters in LIMIT + Space + $limitParamater1 = str_replace(",", "", + trim(substr($query, $startOfLimitParameter, stripos($query, " ", $startOfLimitParameter) - $queryLength))); + + $this->logger->writeToLog("Limit Parameter 1: $limitParamater1"); + + $returnLimitParameter = $limitParamater1; + + //Is the next character next to the first paramater, or the 2 character after + //the parameter is a comma, then no need to convert, just return the query + //as it has a start index and a limit + + $queryChar1Index = $startOfLimitParameter + strlen($limitParamater1) - 1; + $queryChar2Index = $startOfLimitParameter + strlen($limitParamater1); + + $this->logger->writeToLog("QueryChar1Index", $queryChar1Index); + $this->logger->writeToLog("QueryChar2Index", $queryChar2Index); + + if ($query[$queryChar1Index] == ',' || $query[$queryChar2Index] == ',') + { + //The limit has a limit index and a start position, find out which index is the comma + //in order to find the second paramater so this can be replaced with the API limit + if ($query[$queryChar1Index] == ",") + { + $startOfLimitParam2 = $queryChar1Index; + } + else + { + $startOfLimitParam2 = $queryChar2Index; + } + $limitParam2 = trim(substr($query, $startOfLimitParam2 + 1, $queryLength - $startOfLimitParam2)); + $startIndex = $limitParamater1; + $returnLimitParameter = $limitParam2; + + $this->logger->writeToLog("Limit Param 2: $limitParam2"); + + $rowCountDifference = $totalRowsCurrentlyDownloaded - $limitParam2; + + if ($rowCountDifference < API_RESULTSET_LIMIT) + { + $newLimit = $rowCountDifference; + } + + if ($limitParam2 > API_RESULTSET_LIMIT) + { + $newLimit = API_RESULTSET_LIMIT; + } + else + { + $newLimit = $limitParam2; + } + $startIndex = intval($startIndex); + $newLimit = intval($newLimit); + $limitParamater1 = intval($limitParamater1); + $limitParam2 = intval($limitParam2); + return str_ireplace("LIMIT $limitParamater1, $limitParam2", "LIMIT $startIndex, $newLimit", $query); + } + else + { + //Get the difference between the current downloaded rows and the users limit parameter 1 + $rowCountDifference = $totalRowsCurrentlyDownloaded - $limitParamater1; + + if ($rowCountDifference < API_RESULTSET_LIMIT) + { + $newLimit = $rowCountDifference; + } + + //If the parameter of the limit is higher than the default API limit + //then set this parameter a the API default. + else + { + if ($limitParamater1 > API_RESULTSET_LIMIT) + { + $newLimit = API_RESULTSET_LIMIT; + } + else + { + $newLimit = $limitParamater1; + } + } + $startIndex = intval($startIndex); + $newLimit = intval($newLimit); + $limitParamater1 = intval($limitParamater1); + $query = str_ireplace("LIMIT $limitParamater1", "LIMIT $startIndex, $newLimit", $query); + return $query; + } + } + + /** + * Gets the number of times LIMIT is in the SQL query. + * If the count is will this will be updated with a subset limit + * to avoid timeouts, if it is greater than 2, then just return the original + * query + * @param string $query The query that should be checked + * @return int The number of times the query contains LIMIT + */ + private function getCountOfLimitsInQuery($query) + { + return substr_count(strtoupper($query), "LIMIT"); + } + + private function addSlahesToWhereParameters($query, $conn) + { + $newQuery = null; + + //Trim the query, make sure there is no blank space either side of the query. + //Check if the last character is a semi colon and remove it. This isn't need and can + //break the query under certain circumstances as the API changes default limits + $query = trim($query); + + $this->logger->writeToLog("Unprocessed Query: $query"); + + if ($query[strlen($query)-1] === ";") + { + $query[strlen($query)-1] = " "; + } + + if (stripos($query, "WHERE") !== false) + { + //First check if the query has already been escaped, it should have done as that's how queries work + //however, remove them, get the WHERE clause and get PHP to escape the parameters values + + $query = str_replace("\\'", "'", $query); + $query = str_replace('\\"', '"', $query); + + //Get the where part of the string + $whereQuery = substr($query, stripos($query, "WHERE")); + + //Remove the Where part of the query + $newQuery = substr($query, 0, stripos($query, "WHERE")); + + $found = array(); + + + //preg_match_all('/\w+\h*=\h*\S+/', $whereQuery, $found); + //preg_match_all('/(?:AND |OR )?\w+\h*=\h*\'([^\n\']*(?:\'\w+[^\']*|[^\n\']+|\'{2})*)\'|(?:AND |OR )?\w+\h* LIKE \h*\'([^\n\']*(?:\'\w+[^\']*|[^\n\']+|\'{2})*)\'|(?:AND |OR )?\w+\h*=\d*/', $whereQuery, $found); + //preg_match_all('/(?:AND |OR )?\w+\h*=\h*\'([^\n\']*(?:\'\w+[^\']*|[^\n\']+|\'{2})*)\'|(?:AND |OR )?\w+\h* LIKE \h*\'([^\n\']*(?:\'\w+[^\']*|[^\n\']+|\'{2})*)\'|(?:AND |OR )?\w+\h* > \d*/', $whereQuery, $found); + preg_match_all('/(?:AND |OR )?\w+\h*=\h*\'([^\n\']*(?:\'\w+[^\']*|[^\n\']+|\'{2})*)\'|(?:AND |OR )?\w+\h* LIKE \h*\'([^\n\']*(?:\'\w+[^\']*|[^\n\']+|\'{2})*)\'|(?:AND |OR )?\w+\h* > \d*|(?:AND |OR )?\w+\h* < \d*|(?:AND |OR )?\w+\h* = \d*|(?:AND |OR )?\w+\h* != \d*|(?:AND |OR )?\w+\h*>\d*|(?:AND |OR )?\w+\h*<\d*|\w+\h*=\d*|(?:AND |OR )?\w+\h*<>\d*|(?:AND |OR )?\w+\h*!=\d*/', $whereQuery, $found); + + //I don't know regex too well, it creates too empty array in $found, so unset index 1 and 2 and + //index 0 contains all the where parameters + if (isset($found[1])) + { + unset($found[1]); + } + if (isset($found[2])) + { + unset($found[2]); + } + + //$found = $found[0]; + + $this->logger->writeToLog("Preg Match Found: " . print_r($found, true)); + + $processedWhere = "WHERE "; + for ($i = 0; $i < count($found[0]); $i++) + { + if (stripos($found[0][$i], "LIKE")) + { + $keyValue = explode("LIKE", $found[0][$i]); + $key = trim($keyValue[0]); + $value = trim($keyValue[1]); + } + else + { + $keyValue = null; + if (strpos($found[0][$i], "=" ) > 0 && strpos($found[0][$i], "!=") === false) + { + $keyValue = explode("=", $found[0][$i]); + $operator = "="; + } + else if (strpos($found[0][$i], "<") > 0 && strpos($found[0][$i], "<>") === false) + { + $keyValue = explode("<", $found[0][$i]); + $operator = "<"; + } + else if (strpos($found[0][$i], ">") > 0 && strpos($found[0][$i], "<>") === false) + { + $keyValue = explode(">", $found[0][$i]); + $operator = ">"; + } + else if (strpos($found[0][$i], "!=") > 0) + { + $keyValue = explode("!=", $found[0][$i]); + $operator = "!="; + } + else if (strpos($found[0][$i], "<>") > 0) + { + $keyValue = explode("<>", $found[0][$i]); + $operator = "<>"; + } + $key = trim($keyValue[0]); + $value = trim($keyValue[1]); + } + //Remove the surrounding quotes, they will get re-added at the end + + //Remove the surrounding quotes, they will get re-added at the end if they exist + if ($value[0] === "'") + { + $value[0] = ""; + $value[strlen($value) - 1] = ""; + $value = trim($value); + $value = mysqli_escape_string($conn, $value); + $processedWhere .= " $key $operator '$value' "; + } + else + { + $value = trim($value); + $value = mysqli_escape_string($conn, $value); + + $processedWhere .= " $key $operator $value "; + } + + if (stripos($found[0][$i], "LIKE")) + { + $processedWhere .= " $key LIKE '$value'"; + } + } + $newQuery .= " $processedWhere"; + return $newQuery; + } + else + { + //There's no where clause so return the original + return $query; + } + } +} + +?> diff --git a/StatementRetrievalManager.php b/StatementRetrievalManager.php new file mode 100644 index 0000000..c9c680c --- /dev/null +++ b/StatementRetrievalManager.php @@ -0,0 +1,251 @@ +encryption = new Encryption(); + $this->logger = new Logger($logID); + } + + /** + * The base function class that retrieves the specified SQL statement + * @param type $postData The post data that includes the connection details and the type of SQL statement that should be retrieved + */ + function getSqlStatement($postData) + { + include_once("ConnectionManager.php"); + $helperClass = new HelperClass(); + $this->connManager = new ConnectionManager(); + $status = $this->connManager->connectToDBFromPostArray($postData); + + if ($status[RESULT] != SUCCESS) + { + //print $this->encryption->encrypt(json_encode($status)); + HelperClass::printResponseInCorrectEncodingAndCloseTunnelIfNeeded($postData, $status); + exit(); + } + + $query = "SHOW FIELDS FROM " . $postData["database"] . "." . $postData["table"]; + $this->logger->writeToLog("Running Query: $query"); + $result = $this->connManager->conn->query($query); + + if ($result) + { + if ($postData['type'] == "GetSelectAllStatementCopy" || + $postData['type'] == "GetSelectAllStatementEditor" + ) + { + $data = $this->getSelectAllStatement($postData, $result); + } + else + { + if ($postData['type'] == "GetInsertStatementCopy" || + $postData['type'] == "GetInsertStatementEditor" + ) + { + $fields = array(); + $i = 0; + while ($myrow = $result->fetch_array()) + { + $fields[$i] = $myrow['Field']; + $i++; + } + $data = $this->getInsertStatement($postData, $fields); + } + else + { + if ($postData['type'] == "GetUpdateStatementCopy" || + $postData['type'] == "GetUpdateStatementEditor" + ) + { + $fields = array(); + $i = 0; + while ($myrow = $result->fetch_array()) + { + $fields[$i] = $myrow['Field']; + $i++; + } + $data = $this->getUpdateStatement($postData, $fields); + } + else + { + if ($postData['type'] == "GetDeleteStatementCopy" + || $postData['type'] == "GetDeleteStatementEditor" + ) + { + $data = $this->getDeleteStatement($postData); + } + else + { + if ($postData['type'] == "GetCreateStatementCopy" + || $postData['type'] == "GetCreateStatementEditor" + ) + { + $data = $this->getCreateStatement($postData); + } + } + } + } + } + $result = $helperClass->returnSuccessArray($data); + $result[LOCAL_TUNNEL_PORT] = $status[LOCAL_TUNNEL_PORT]; + $this->logger->writeToLog("Successfully retrieved SQL statement"); + HelperClass::printResponseInCorrectEncodingAndCloseTunnelIfNeeded($postData, $result); + //print $this->encryption->encrypt(json_encode($helperClass->returnSuccessArray($data))); + } + else + { + $mysqlError = mysqli_error($this->connManager->conn); + $this->logger->writeToLog("Failed to retrieve SQL Statement. MySQL Error: " . $mysqlError); + $returnSqlArray = $helperClass->returnMySQLErrorArray($mysqlError, mysqli_errno($this->connManager->conn)); + $returnSqlArray[LOCAL_TUNNEL_PORT] = $status[LOCAL_TUNNEL_PORT]; + HelperClass::printResponseInCorrectEncodingAndCloseTunnelIfNeeded($postData, $returnSqlArray); + //print $this->encryption->encrypt(json_encode($helperClass->returnMySQLErrorArray(mysqli_error($this->connManager->conn), mysqli_errno($this->connManager->conn)))); + } + } + + /** + * Retrieves the create statement back to the main function call + * @param type $postData The post data containing the connction details + * @return type Will either return the create statement or print JSON an error + */ + private function getCreateStatement($postData) + { + $helperClass = new HelperClass(); + $query = "SHOW CREATE TABLE `" . $postData['database'] . "`.`" . $postData['table'] . "`"; + $this->logger->writeToLog("Running query: $query"); + $result = $this->connManager->conn->query($query); + if ($result) + { + while ($myrow = $result->fetch_array()) + { + return $myrow['Create Table']; + } + } + else + { + $mysqlError = mysqli_error($this->connManager->conn); + $this->logger->writeToLog("Failed to get SQL statement. MySQL Error: $mysqlError"); + print $this->encryption->encrypt(json_encode($helperClass->returnMySQLErrorArray($mysqlError, mysqli_errno($this->connManager->conn)))); + exit(); + } + } + + /** + * Retrieves SQL to perform a select query + * @param type $postData The connection details + * @param type $result Result set of fields retrieved from database + * @return string The SELECT statement query + */ + private function getSelectAllStatement($postData, $result) + { + $line = "SELECT "; + $i = 0; + while ($myrow = $result->fetch_array()) + { + if ($i < mysqli_num_rows($result) -1) + { + $line .= "`" . $postData["table"] . "`.`" . $myrow['Field'] . "`,\n\t"; + } + else + { + $line .= "`" . $postData["table"] . "`.`" . $myrow['Field'] . "`\n"; + } + $i++; + } + $line .= "FROM `" . $postData['database'] . "`.`" . $postData['table'] . "`;"; + + return $line; + } + + /** + * Retrieves the INSERT INTO statement + * @param type $postData The connection details + * @param type $fields The fields that will make sure up the statement + * @return string The SQL statement + */ + private function getInsertStatement($postData, $fields) + { + $line = "INSERT INTO `" . $postData['database'] . "`.`" . $postData['table'] . "`\n("; + + $fieldCount = count($fields); + + for ($i = 0; $i < $fieldCount; $i++) + { + if ($i < $fieldCount - 1) + { + $line .= "`" . $fields[$i] . "`,\n"; + } + else + { + $line .= "`" . $fields[$i] . "`)\n"; + } + } + + $line .= "VALUES\n("; + for ($i = 0; $i < $fieldCount; $i++) + { + if ($i < $fieldCount - 1) + { + $line .= "<" . $fields[$i] . ">,\n"; + } + else + { + $line .= "<" . $fields[$i] . ">);"; + } + } + return $line; + } + + /** + * Returns the update statement + * @param type $postData The connection details + * @param type $fields The fields that will make up the statement + * @return string The update statement + */ + private function getUpdateStatement($postData, $fields) + { + $line = "UPDATE `" . $postData['database'] . "`.`" . $postData['table'] . "`\nSET\n"; + + $fieldCount = count($fields); + + for ($i = 0; $i < $fieldCount; $i++) + { + if ($i < $fieldCount - 1) + { + $line .= "`" . $fields[$i] . "` = <" . $fields[$i] . ">,\n"; + } + else + { + $line .= "`" . $fields[$i] . "` = <" . $fields[$i] . ">\n"; + } + } + $line .= "WHERE `" . $fields[0] . "` = <{expr}>"; + return $line; + } + + /** + * Retrieves the SQL DELETE statement + * @param type $postData The connection details + * @return string The SQL Delete statement + */ + private function getDeleteStatement($postData) + { + $line = "DELETE FROM `" . $postData['database'] . "`.`" . $postData['table'] . "`\n"; + $line .= "WHERE <{where_expression}>"; + return $line; + } +} + +?> diff --git a/TunnelAPITest.php b/TunnelAPITest.php new file mode 100644 index 0000000..d48e2d4 --- /dev/null +++ b/TunnelAPITest.php @@ -0,0 +1,30 @@ + \ No newline at end of file diff --git a/TunnelManager.php b/TunnelManager.php new file mode 100644 index 0000000..b0a755e --- /dev/null +++ b/TunnelManager.php @@ -0,0 +1,242 @@ +configManager = new ConfigManager("mysql.conf"); + $this->encryption = new Encryption(); + } + + public function getFingerprintConfirmed() + { + return $this->fingerprintConfirmed; + } + + public function setSSHParametersFromPostArray($postArray) + { + $this->sshHost = $postArray["sshHost"]; + $this->sshUsername = $postArray["sshUsername"]; + $this->authMethod = $postArray["authMethod"]; + $this->fingerprintConfirmed = $postArray["fingerprintConfirmed"]; + + if ($this->authMethod === "Password" || empty($this->authMethod)) + { + $this->sshPassword = $postArray["sshPassword"]; + + //If the auth method hasn't been set due to being an older app, then set it + if (empty($this->authMethod)) + { + $this->authMethod = "Password"; + } + } + else if ($this->authMethod === "PrivateKey") + { + $this->privateSSHKey = $postArray["sshPrivateKey"]; + if (isset($postArray["certPassphrase"]) && !empty($postArray["certPassphrase"])) + { + $this->certificatePassphrase = $postArray["certPassphrase"]; + } + } + if (isset($postArray["fingerprint"])) + { + $this->fingerprint = $postArray["fingerprint"]; + } + $this->sshPort = $postArray["sshPort"]; + $this->mysqlPort = $postArray["port"]; + $this->mysqlHost = $postArray["server"]; + } + + public function closeTunnel($port) + { + try + { + $socket = $this->returnSocket(); + + /*$xmlGenerator = new XMLGenerator(); + $xmlGenerator->writeStartDocument(); + $xmlGenerator->writeStartElement("CloseTunnel"); + $xmlGenerator->writeElementString("LocalPort", $port); + $xmlGenerator->writeEndElement(); + + $input = $xmlGenerator->returnXmlString();*/ + + $request = new StdClass(); + $request->method = "CloseTunnel"; + $request->localPort = intval($port); + + $input = json_encode($request); + + socket_write($socket, $input, strlen($input)); + socket_close(); + } + catch (Exception $e) + { + $helperClass = new HelperClass(); + $response = $helperClass->returnError("SSHTunnelNotAvailable"); + HelperClass::printResponseInCorrectEncodingAndCloseTunnelIfNeeded(null, $response); + exit(); + } + } + + public function startTunnel() + { + require_once 'Logger.php'; + $logger = new Logger(); + try { + $socket = $this->returnSocket(); + $request = new stdClass(); + $request->method = "CreateTunnel"; + + $request->sshDetails = new stdClass(); + $request->sshDetails->sshUsername = $this->sshUsername; + $request->sshDetails->authMethod = $this->authMethod; + if ($this->authMethod === "Password") { + $request->sshDetails->sshPassword = $this->sshPassword; + } else { + $request->sshDetails->privateSSHKey = $this->privateSSHKey; + $request->sshDetails->certPassphrase = $this->certificatePassphrase; + } + $request->sshDetails->sshPort = intval($this->sshPort); + $request->sshDetails->sshHost = $this->sshHost; + $request->remoteMySQLPort = intval($this->mysqlPort); + $request->mysqlHost = $this->mysqlHost; + $request->fingerprintConfirmed = ($this->fingerprintConfirmed === "true") ? true : false; + if (!empty($this->fingerprint)) + { + $request->fingerprint = $this->fingerprint; + } + + + + $socketData = json_encode($request); + socket_write($socket, $socketData, strlen($socketData)); + + if (isset($request->sshDetails->sshPassword)) + { + for ($i = 0; $i < strlen($request->sshDetails->sshPassword); $i++) + { + $request->sshDetails->sshPassword[$i] = '*'; + } + } + else if (isset($request->sshDetails->privateSSHKey)) + { + for ($i = 0; $i < strlen($request->sshDetails->privateSSHKey); $i++) + { + $request->sshDetails->privateSSHKey[$i] = '*'; + } + + if (isset($request->sshDetails->certPassphrase) && !empty($request->sshDetails->certPassphrase)) + { + for ($i = 0; $i < strlen($request->sshDetails->certPassphrase); $i++) + { + $request->sshDetails->certPassphrase[$i] = '*'; + } + } + } + + $logger->writeToLog("Sending Request: " . json_encode($request)); + + $jsonResponse = json_decode(socket_read($socket, 1024)); + socket_close($socket); + return $jsonResponse; + } + catch (Exception $ex) + { + $helperClass = new HelperClass(); + $response = $helperClass->returnError("SSHTunnelNotAvailable"); + HelperClass::printResponseInCorrectEncodingAndCloseTunnelIfNeeded(null, $response); + exit(); + } + /*try + { + $socket = $this->returnSocket(); + + /*$xmlGenerator = new XMLGenerator(); + $xmlGenerator->writeStartDocument(); + $xmlGenerator->writeStartElement("CreateTunnel"); + $xmlGenerator->writeElementString("SSHUsername", $this->sshUsername); + $xmlGenerator->writeElementString("AuthMethod", $this->authMethod); + if ($this->authMethod === "Password") + { + $xmlGenerator->writeElementString("SSHPassword", $this->sshPassword); + } + else + { + $xmlGenerator->writeElementString("PrivateSSHKey", $this->privateSSHKey); + if (isset($this->certificatePassphrase) && !empty($this->certificatePassphrase)) + { + $xmlGenerator->writeElementString("CertPassphrase", $this-> certificatePassphrase); + } + } + $xmlGenerator->writeElementString("SSHPort", $this->sshPort); + $xmlGenerator->writeElementString("SSHHost", $this->sshHost); + $xmlGenerator->writeElementString("RemoteMySQLPort", $this->mysqlPort); + $xmlGenerator->writeElementString("MySQLHost", $this->mysqlHost); + $xmlGenerator->writeEndElement(); + + $socketData = $xmlGenerator->returnXmlString(); + socket_write($socket, $socketData, strlen($socketData)); + + $xmlResponse = socket_read($socket, 1024); + socket_close($socket); + + $responseArr = new SimpleXMLElement($xmlResponse); + if ($responseArr->Message->__toString() !== null && $responseArr->Status->__toString() !== "Success") + { + throw new Exception($responseArr->Message->__toString()); + } + else + { + $helperClass = new HelperClass(); + $response = $helperClass->returnSuccessArray(null); + $response[LOCAL_TUNNEL_PORT] = $responseArr->LocalTunnelPort->__toString(); + return $response; + } + } + catch (Exception $ex) + { + $helperClass = new HelperClass(); + $response = $helperClass->returnTunnelError($ex->getMessage()); + return $response; + }*/ + } + + private function returnSocket() + { + $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); + if ($socket === false) + { + throw new Exception("Create Socket Failed: " . socket_strerror(socket_last_error())); + } + + $result = socket_connect($socket, "localhost", $this->configManager->getConfigItemValue("general", "TunnelSocket", 500)); + if ($result === false) + { + throw new Exception("Socket Connect Failed: " . socket_strerror(socket_last_error())); + } + + return $socket; + } + } + +?> \ No newline at end of file diff --git a/XMLGenerator.php b/XMLGenerator.php new file mode 100644 index 0000000..472920c --- /dev/null +++ b/XMLGenerator.php @@ -0,0 +1,124 @@ +xmlString = ""; + if ($xmlSettings == null) + { + $this->xmlSettings = new XMLSettings(); + } + else + { + $this->xmlSettings = $xmlSettings; + } + } + + public function writeStartDocument() + { + $this->tabIndex = -1; + $this->xmlString = ""; + $this->xmlString .= $this->xmlSettings->getNewLineChars(); + } + + public function writeStartElement($startElement) + { + $this->parentsNotEnded .= $startElement . "/"; + $this->xmlString .= sprintf("%s<%s>%s", $this->getTabs(), $startElement, $this->xmlSettings->getNewLineChars()); + $this->tabIndex++; + } + + public function writeElementString($attribute, $value) + { + $this->tabIndex++; + $this->xmlString .= sprintf("%s<%s>%s%s>%s", $this->getTabs(), $attribute, $value, $attribute, $this->xmlSettings->getNewLineChars()); + $this->tabIndex--; + } + + public function writeXmlTagWithSingleAttribute($tagName, $attribute, $attributeValue, $value = null) + { + $dictionary = new Dictionary(); + $dictionary->add($attribute, $attributeValue); + + $this->writeXmlTagWithAttributeArray($tagName, $dictionary->getDictionary(), $value); + } + + public function writeXmlTagWithAttributeArray($tagName, $attributeDictionary, $value = null) + { + $this->tabIndex++; + $this->xmlString .= sprintf("%s<%s", $this->getTabs(), $tagName); + foreach ($attributeDictionary as $attribute) + { + $this->xmlString .= sprintf(" %s=\"%s\"", $attribute->getKey(), $attribute->getValue()); + } + if (empty($value)) + { + $this->xmlString .= " />"; + } + else + { + $this->xmlString .= sprintf(">%s%s>", $value, $tagName); + } + $this->xmlString .= $this->xmlSettings->getNewLineChars(); + $this->tabIndex--; + } + + public function writeEndElement() + { + $parents = explode("/", $this->parentsNotEnded); + $parents = array_values(array_filter($parents)); + $lastElement = count($parents)-1; + $this->xmlString .= sprintf("%s%s>%s", $this->getTabs(), $parents[$lastElement], $this->xmlSettings->getNewLineChars()); + $this->parentsNotEnded = str_replace("/" . $parents[$lastElement] . "/", "/", $this->parentsNotEnded); + $this->tabIndex--; + } + + public function addCustomLineToXML($xmlString) + { + $this->xmlString .= $xmlString . $this->xmlSettings->getNewLineChars(); + } + + private function getTabs() + { + $tab = ""; + for ($i = 0; $i < $this->tabIndex; $i++) + { + $tab .= "\t"; + } + return $tab; + } + + public function returnXmlString() + { + return trim($this->xmlString); + } + } + + class XMLSettings + { + private $indent = false; + private $newLineChars = "\n"; + + public function getIndent() + { + return $this->indent; + } + + public function setNewLineChars($newLineChar) + { + $this->newLineChars($newLineChar); + } + + public function getNewLineChars() + { + return $this->newLineChars; + } + } + +?> \ No newline at end of file diff --git a/mysql.conf b/mysql.conf new file mode 100644 index 0000000..54b8c63 --- /dev/null +++ b/mysql.conf @@ -0,0 +1,11 @@ +[general] +TunnelSocket = 500 + +[debug] +debug_log = api.log +maxFileSizeInMB = 50 +maxArchiveCount = 3 + +[encryption] +cipher = +iv = \ No newline at end of file diff --git a/phpinfo.php b/phpinfo.php new file mode 100644 index 0000000..f35a8ef --- /dev/null +++ b/phpinfo.php @@ -0,0 +1,3 @@ +"; + + echo ""; + print_r($output); + echo ""; + +?> \ No newline at end of file
"; + print_r($output); + echo "