codiad/components/update/update.php

878 lines
20 KiB
PHP
Executable File

<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
require_once('../../common.php');
require_once('./class.update.php');
$user_settings_file = DATA . "/settings.php";
$projects_file = DATA . "/projects.php";
$users_file = DATA . "/users.php";
//checkSession();
if ( ! checkAccess() ) {
echo "Error, you do not have access to update Codiad.";
exit();
}
if ( ! ( defined( "DBHOST" ) && defined( "DBNAME" ) && defined( "DBUSER" ) && defined( "DBPASS" ) && defined( "DBTYPE" ) ) ) {
?>
<p>
Hello, this update requires new variables in your Codiad config.php<br />
Please place the following code in your config.php with the correct values applying to your databse and then reload this page. <br>
<br>
Please be aware that at the moment, only mysql databases are supported. However, more database support is planned.
</p>
<code>
<pre>
define( "DBHOST", "localhost" );
define( "DBNAME", "database" );
define( "DBUSER", "username" );
define( "DBPASS", "password" );
define( "DBTYPE", "mysql" );
</pre>
</code>
<?php
exit();
}
/**
* Initiate the update class so we do not have to redefine their
* variables.
*/
class updater {
/**
* Telaaedifex Codiad updater
*
* This updater will extract an archive and then update each file
* with file put contents.
*/
/**
* Constants
*/
const DEFAULT_OPTIONS = array(
array(
"name" => "codiad.editor.autocomplete",
"value" => "false",
),
array(
"name" => "codiad.editor.fileManagerTrigger",
"value" => "false",
),
array(
"name" => "codiad.editor.fontSize",
"value" => "14px",
),
array(
"name" => "codiad.editor.highlightLine",
"value" => "true",
),
array(
"name" => "codiad.editor.indentGuides",
"value" => "true",
),
array(
"name" => "codiad.editor.overScroll",
"value" => "0.5",
),
array(
"name" => "codiad.editor.persistentModal",
"value" => "true",
),
array(
"name" => "codiad.editor.printMargin",
"value" => "true",
),
array(
"name" => "codiad.editor.printMarginColumn",
"value" => "80",
),
array(
"name" => "codiad.editor.rightSidebarTrigger",
"value" => "false",
),
array(
"name" => "codiad.editor.softTabs",
"value" => "false",
),
array(
"name" => "codiad.editor.tabSize",
"value" => "4",
),
array(
"name" => "codiad.editor.theme",
"value" => "twilight",
),
array(
"name" => "codiad.editor.wrapMode",
"value" => "false",
),
array(
"name" => "codiad.settings.autosave",
"value" => "true",
),
array(
"name" => "codiad.settings.plugin.sync",
"value" => "true",
),
array(
"name" => "codiad.settings.plugin.sync",
"value" => "true",
),
);
/**
* Properties
*/
public $archive = "";
public $path = "";
public $protocol = "";
public $update = null;
public $username = "";
function __construct() {
$this->update = new Update();
$this->protocol = $this->check_protocol();
$this->archive = $this->update->archive;
$this->path = BASE_PATH;
$this->username = $_SESSION["user"];
/*
//Trigger update
$this->update();*/
}
function backup() {
$backup = "../../backup/";
$source = "../../";
//Add Sessions path if not there.
/**
* Create sessions path.
*/
if ( ! is_dir( $backup ) ) {
mkdir( $backup, 00755 );
}
function copy_backup( $source, $dest ) {
// Check for symlinks
if (is_link($source)) {
return symlink(readlink($source), $dest);
}
// Simple copy for a file
if (is_file($source)) {
return copy($source, $dest);
}
// Make destination directory
if (!is_dir($dest)) {
mkdir($dest);
}
// Loop through the folder
$dir = dir( $source );
while (false !== $entry = $dir->read()) {
// Skip pointers
if ($entry == '.' || $entry == '..' || $entry == 'backup' || $entry == 'codiad-master' || $entry == 'workspace') {
continue;
}
// Deep copy directories
copy_backup("$source/$entry", "$dest/$entry");
}
// Clean up
$dir->close();
}
copy_backup( $source, $backup );
}
function check_protocol() {
if( extension_loaded( 'curl' ) ) {
//Curl is loaded
return "curl";
} elseif( ini_get('allow_url_fopen') ) {
//Remote get file is enabled
return "fopen";
} else {
//None are enabled exit.
return "none";
}
}
function check_update() {
$response = $this->update->getRemoteVersion();
$local_version = $this->update::VERSION;
$remote_version = $response["name"];
$return = "false";
if( $local_version < $remote_version ) {
$return = "true";
}
return( $return );
}
function check_version() {
$local_version = $this->update::VERSION;
$remote_version = $response["name"];
$return = "false";
if( $local_version <= "v.2.9.2" ) {
$return = "convert";
}
return( $return );
}
function convert() {
require_once('../../common.php');
require_once('../sql/class.sql.php');
$user_settings_file = DATA . "/settings.php";
$projects_file = DATA . "/projects.php";
$users_file = DATA . "/users.php";
$sql = new sql();
$connection = $sql->connect();
$sql = "
CREATE TABLE IF NOT EXISTS `options`(
`id` INT(11) NOT NULL,
`name` VARCHAR(255) NOT NULL,
`value` TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS `projects`(
`id` INT(11) NOT NULL,
`name` VARCHAR(255) NOT NULL,
`path` VARCHAR(255) NOT NULL,
`owner` VARCHAR(255) NOT NULL,
`access` TEXT
);
CREATE TABLE IF NOT EXISTS `users`(
`id` INT(11) NOT NULL,
`first_name` VARCHAR(255) DEFAULT NULL,
`last_name` VARCHAR(255) DEFAULT NULL,
`username` VARCHAR(255) NOT NULL,
`password` TEXT NOT NULL,
`email` VARCHAR(255) DEFAULT NULL,
`project` VARCHAR(255) DEFAULT NULL,
`access` VARCHAR(255) NOT NULL,
`groups` TEXT,
`token` TEXT
);
CREATE TABLE IF NOT EXISTS `user_options`(
`id` INT(11) NOT NULL,
`name` VARCHAR(255) NOT NULL,
`username` VARCHAR(255) NOT NULL,
`value` TEXT NOT NULL
);
ALTER TABLE `options` ADD PRIMARY KEY(`id`), ADD UNIQUE KEY `option_name`(`name`);
ALTER TABLE `projects` ADD PRIMARY KEY(`id`), ADD UNIQUE KEY `project_path`(`path`, `owner`);
ALTER TABLE `users` ADD PRIMARY KEY(`id`), ADD UNIQUE KEY `username`(`username`);
ALTER TABLE `user_options` ADD PRIMARY KEY(`id`), ADD UNIQUE KEY `option_name`(`name`, `username`);
ALTER TABLE `options` MODIFY `id` INT(11) NOT NULL AUTO_INCREMENT;
ALTER TABLE `projects` MODIFY `id` INT(11) NOT NULL AUTO_INCREMENT;
ALTER TABLE `users` MODIFY `id` INT(11) NOT NULL AUTO_INCREMENT;
ALTER TABLE `user_options` MODIFY `id` INT(11) NOT NULL AUTO_INCREMENT;
";
if ( $connection->multi_query( $sql ) !== TRUE ) {
$this->restore();
exit( $connection->error );
}
if( file_exists( $user_settings_file ) ) {
unlink( $user_settings_file );
}
if( file_exists( $projects_file ) ) {
$projects = getJSON( 'projects.php' );
foreach( $projects as $project => $data ) {
$owner = 'nobody';
$sql = "INSERT INTO `projects`( `name`, `path`, `owner` ) VALUES ( ?, ?, ? );";
$bind = "sss";
$bind_variables = array( $data["name"], $data["path"], $owner );
$return = sql::sql( $sql, $bind, $bind_variables, formatJSEND( "error", "Error creating project $project." ) );
if( sql::check_sql_error( $return ) ) {
} else {
$this->restore();
exit(formatJSEND( "error", "There was an error adding projects to database." ));
}
}
unlink( $projects_file );
}
if( file_exists( $users_file ) ) {
$users = getJSON( 'users.php' );
foreach( $users as $user ) {
if( $user["username"] === $_SESSION["user"] ) {
$access = "admin";
} else {
$access = "user";
}
$sql = "INSERT INTO `users`( `username`, `password`, `access`, `project` ) VALUES ( ?, PASSWORD( ? ), ?, ? );";
$bind = "ssss";
$bind_variables = array( $user["username"], $user["password"], $access, null );
$return = sql::sql( $sql, $bind, $bind_variables, formatJSEND( "error", "Error that username is already taken." ) );
if( sql::check_sql_error( $return ) ) {
$this->username = $user["username"];
$this->set_default_options();
//echo formatJSEND( "success", array( "username" => $user["username"] ) );
} else {
$this->restore();
exit(formatJSEND( "error", "The Username is Already Taken" ));
}
}
unlink( $users_file );
}
}
function copyr( $source, $dest ) {
// Check for symlinks
if (is_link($source)) {
return symlink(readlink($source), $dest);
}
// Simple copy for a file
if (is_file($source)) {
return copy($source, $dest);
}
// Make destination directory
if (!is_dir($dest)) {
mkdir($dest);
}
// Loop through the folder
$dir = dir( $source );
while (false !== $entry = $dir->read()) {
// Skip pointers
if ($entry == '.' || $entry == '..' || $entry == 'backup' || $entry == 'codiad-master' || $entry == 'workspace' || $entry == 'plugins') {
continue;
}
// Deep copy directories
$this->copyr("$source/$entry", "$dest/$entry");
}
// Clean up
$dir->close();
return true;
}
//////////////////////////////////////////////////////////////////
// Download latest archive
//////////////////////////////////////////////////////////////////
function download() {
switch( $this->protocol ) {
case( "curl" ):
$filepath = $this->path . "/update.zip";
if( file_exists( $filepath ) ) {
unlink( $filepath );
}
$curl = curl_init();
curl_setopt( $curl, CURLOPT_URL, $this->archive );
//curl_setopt($curl, CURLOPT_POSTFIELDS, "");
curl_setopt( $curl, CURLOPT_HEADER, 0 );
curl_setopt( $curl, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt( $curl, CURLOPT_SSL_VERIFYPEER, false );
curl_setopt( $curl, CURLOPT_SSL_VERIFYHOST, 0 );
curl_setopt( $curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.13' );
$raw_file_data = curl_exec( $curl );
curl_close( $curl );
file_put_contents( $filepath, $raw_file_data );
if( filesize( $filepath ) > 0 ) {
return( "true" );
} else {
return( "false" );
}
break;
}
}
function extract() {
if ( ! extension_loaded( 'zip' ) ) {
return "false";
}
$zip = new ZipArchive;
if ( $zip->open( $this->path . "/update.zip" ) === TRUE ) {
$zip->extractTo( $this->path );
$zip->close();
return "true";
} else {
return "false";
}
}
function remove_directory( $path ) {
$files = glob($path . '/*');
foreach ($files as $file) {
is_dir($file) ? $this->remove_directory($file) : unlink($file);
}
if( is_dir( $path ) ) {
rmdir( $path );
}
return;
}
function restore() {
$dest = "../../";
$source = "../../backup/";
$this->copyr( $source, $dest );
$this->remove_directory( $source );
}
public function set_default_options() {
foreach( self::DEFAULT_OPTIONS as $id => $option ) {
$this->update_option( $option["name"], $option["value"], true );
}
}
function update() {
$this->backup();
try {
$sessions = "../../data/sessions";
//Add Sessions path if not there.
/**
* Create sessions path.
*/
if ( ! is_dir( $sessions ) ) {
mkdir( $sessions, 00755 );
}
/**
* If any directories in the array below are still set delete them.
*
*/
$folder_conflictions = array(
$this->path . "/components/autocomplete",
$this->path . "/plugins/auto_save",
$this->path . "/plugins/Codiad-Auto-Save",
$this->path . "/plugins/Codiad-Auto-Save-master",
$this->path . "/plugins/Codiad-CodeSettings",
$this->path . "/plugins/Codiad-CodeSettings-master",
);
foreach( $folder_conflictions as $dir ) {
$this->remove_directory( $dir );
}
/**
* If any files in the array below are still set delete them.
*
*/
$file_conflictions = array(
$this->path . "/.travis.yml",
$this->path . "/codiad-master/.travis.yml",
$this->path . "/.gitignore",
$this->path . "/codiad-master/.gitignore",
$this->path . "/.gitlab-ci.yml",
$this->path . "/codiad-master/.gitlab-ci.yml"
);
foreach( $file_conflictions as $file ) {
if( is_file( $file ) ) {
unlink( $file );
}
}
$src = $this->path . "/codiad-master/";
$src_folder = $this->path . "/codiad-master";
$dest = $this->path . "/";
$this->copyr( $src, $dest );
$this->remove_directory( $src );
$this->convert();
return( "true" );
} catch( Exception $e ) {
$this->restore();
return( $e );
}
}
public function update_option( $option, $value, $user_setting = null ) {
$query = "INSERT INTO user_options ( `name`, `username`, `value` ) VALUES ( ?, ?, ? );";
$bind = "sss";
$bind_variables = array(
$option,
$this->username,
$value,
);
$result = sql::sql( $query, $bind, $bind_variables, formatJSEND( "error", "Error, Could not add user's settings." ) );
if( $result !== true ) {
$query = "UPDATE user_options SET `value`=? WHERE `name`=? AND `username`=?;";
$bind = "sss";
$bind_variables = array(
$value,
$option,
$this->username,
);
$result = sql::sql( $query, $bind, $bind_variables, formatJSEND( "error", "Error, Could not update user's settings." ) );
}
}
function version() {
$return = "";
if( file_exists( $user_settings_file ) || file_exists( $projects_file ) || file_exists( $users_file ) ) {
$return = "true";
}
}
}
if( isset( $_GET["action"] ) && $_GET["action"] !== '' ) {
$updater = new updater();
$action = $_GET["action"];
switch( $action ) {
case( "apply" ):
echo $updater->update();
break;
case( "check_update" ):
echo $updater->check_update();
break;
case( "check_version" ):
echo $updater->check_version();
break;
case( "download" ):
echo $updater->download();
break;
case( "extract" ):
echo $updater->extract();
break;
case( "update" ):
echo $updater->update();
break;
}
exit();
}
?>
<!DOCTYPE HTML>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Codiad Update</title>
<style>
html {
}
body{
background-color: #1a1a1a;
color: #fff;
font: normal 13px 'Ubuntu', sans-serif;
height: 100%;
overflow: hidden;
text-align: center;
width: 100%;
}
.title {
color: #666;
display: block;
font-weight: 500;
margin: 10px;
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.6);
}
#convert {
display: none;
}
#progress {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
</style>
<script src="../../js/jquery-1.7.2.min.js"></script>
<script>
const codiad = {};
codiad.update = {
progress: null,
init: function() {
this.progress = document.getElementById( "progress" );
this.update();
},
apply: function() {
return jQuery.ajax({
url: "update.php",
type: "GET",
dataType: 'html',
data: {
action: 'apply',
},
success: function( result ) {
return result;
},
error: function( jqXHR, textStatus, errorThrown ) {
console.log( 'jqXHR:' );
console.log( jqXHR );
console.log( 'textStatus:' );
console.log( textStatus);
console.log( 'errorThrown:' );
console.log( errorThrown );
return null;
}
});
},
check_update: function() {
this.progress.innerText = "Checking for update ... ";
return jQuery.ajax({
url: "update.php",
type: "GET",
dataType: 'html',
data: {
action: 'check_update',
},
success: function( result ) {
return result;
},
error: function( jqXHR, textStatus, errorThrown ) {
console.log( 'jqXHR:' );
console.log( jqXHR );
console.log( 'textStatus:' );
console.log( textStatus);
console.log( 'errorThrown:' );
console.log( errorThrown );
return null;
}
});
},
download: function() {
return jQuery.ajax({
url: "update.php",
type: "GET",
dataType: 'html',
data: {
action: 'download',
},
success: function( result ) {
return result;
},
error: function( jqXHR, textStatus, errorThrown ) {
console.log( 'jqXHR:' );
console.log( jqXHR );
console.log( 'textStatus:' );
console.log( textStatus);
console.log( 'errorThrown:' );
console.log( errorThrown );
return null;
}
});
},
extract: function() {
return jQuery.ajax({
url: "update.php",
type: "GET",
dataType: 'html',
data: {
action: 'extract',
},
success: function( result ) {
return result;
},
error: function( jqXHR, textStatus, errorThrown ) {
console.log( 'jqXHR:' );
console.log( jqXHR );
console.log( 'textStatus:' );
console.log( textStatus);
console.log( 'errorThrown:' );
console.log( errorThrown );
return null;
}
});
},
update: async function() {
let result = await this.check_update();
console.log( result );
if( result === "true" ) {
progress.innerText = "An update was found. Downloading update.";
let download = await this.download();
if( download !== "true" ) {
console.log( download );
progress.innerText = "Error downloading update.";
return;
}
progress.innerText = "Extracting update.";
let extract = await this.extract();
if( extract !== "true" ) {
console.log( extract );
progress.innerText = "Error extracting update.";
return;
}
progress.innerText = "Applying update.";
let apply = await this.apply();
if( apply !== "true" ) {
console.log( apply );
progress.innerText = "Error applying update.";
return;
}
progress.innerText = "Update Finished.";
} else if( result === "false" ) {
progress.innerText = "No update was found ...";
} else {
progress.innerText = "Error, checking for updates failed.";
}
},
};
</script>
</head>
<body>
<h1 class="title" style="text-align: center;">
Tela Codiad Updater
</h1>
<div>
<p>Do not leave this page until the process has finished.</p>
<p id="progress"></p>
</div>
<script>
codiad.update.init();
</script>
</body>
</html>