<?php

function can_create_file(){
	$file = dirname(__FILE__).'/soft.tmp';
	$fp = @fopen($file, 'wb');
	if($fp === FALSE){
		return false;
	}
	
	if(@fwrite($fp, 'ampps') === FALSE){
		return false;
	}
	
	@fclose($fp);
	
	// Check if the file exists
	if(file_exists($file)){
		@unlink($file);
		return true;
	}
	
	return false;	
}

// Back up the database !!!
function backup_mysql_fn($shost, $suser, $spass, $sdb, $sdbfile){
	//echo $shost.' == '. $suser.' == '. $spass.' == '. $sdb.' == '. $sdbfile;
	
	global $data;
	
	$link = soft_mysql_connect($shost, $suser, $spass);
	
	soft_mysql_query('SET CHARACTER SET utf8mb4', $link);
	
	// Open and create a file handle for sql.
	$handle = fopen($sdbfile,'w');
	
	$s_def = $alter_queries = $sresponse = '';
	$sql_alter = $tables = array();
	
	$ser_ver = PMA_sversion($link);
	$s_def = PMA_exportHeader($sdb, $ser_ver);
	
	fwrite($handle, $s_def);
		
	// We did not create the database ! So just backup the tables required for this database
	if(empty($data['dbcreated']) && !empty($data['softdbtables'])){
		
		$thisdb_tables = $data['softdbtables'];
		$additional_tables = (!empty($data['additional_tables']) ? $data['additional_tables'] : array());
		
		if(!is_array($data['softdbtables'])){
			$thisdb_tables = unserialize($data['softdbtables']);
		}
		
		$thisdb_tables = (!empty($additional_tables) ? array_merge($additional_tables, $thisdb_tables) : $thisdb_tables);
		
		// This is just to remove the ` since we are not getting it in $tables below
		foreach($thisdb_tables as $tk => $tv){
			// There was a bug since Softaculous 4.7.2 that did not save softdbtables for ins causing empty array. Fixed in Softaculous 4.7.7
			if(empty($tv)) continue;
			$_thisdb_tables[trim($tk, '`')] = trim($tv, '`');
		}
	}

	//List Views
	$squery = soft_mysql_query('SHOW TABLE STATUS FROM `' . $sdb . '` WHERE COMMENT = \'VIEW\'', $link);
	
	$views = array();	
	if(soft_mysql_num_rows($squery) > 0){
		while($row = soft_mysql_fetch_row($squery)){
			$views[] = $row[0];
		}
	}
	
	// Sort the views
	usort($views, 'strnatcasecmp');
	
	// List the tables
	$squery = soft_mysql_query('SHOW TABLES FROM `' . $sdb . '`', $link);
	
	while($row = soft_mysql_fetch_row($squery)){
		
		// We do not need to backup this table
		if(!empty($_thisdb_tables) && is_array($_thisdb_tables) && !in_array($row[0], $_thisdb_tables)){
			continue;
		}
		
		if(in_array($row[0], $views)){
			continue;
		}
		
		$tables[] = $row[0];
	}
	
	// Sort the tables
	usort($tables, 'strnatcasecmp');	
	
	foreach($tables as $table => $v){
		
		// Get the table structure(table definition)
		$stable_defn = PMA_getTableDef($sdb, $v, "\n", false, true, $link);
		
		$s_def = $stable_defn['structure']."\n";
		fwrite($handle, $s_def);
		
		// Get the table data(table contents)
		// We have added $handle so that we can write the INSERT queries directly when we get it. 
		// Basically To avoid MEMORY EXHAUST FOR  BIG INSERTS
		PMA_exportData($sdb, $v, "\n", $handle, $link);
		
		// List of alter queries 
		// We have changed this because the OLD method was putting the ALTER queries after CREATE table query which was causing issues.
		if(!empty($stable_defn['alter'])){
			$alter_queries .= $stable_defn['alter'];
		}
		
		
	}
	
	//Save Views
	foreach($views as $view){
		
		$defn = PMA_getViews($sdb, $view, "\n", $link);
		
		$view_def = $defn['structure']."\n";
		fwrite($handle, $view_def);
	}
	
	fwrite($handle, $alter_queries);
	
	//List Triggers/Events/Procedures/Functions	
	//Triggers
	$triggers = PMA_getTriggers($sdb, $link);
	foreach($triggers as $trigger){
		fwrite($handle, "\n".$trigger['drop']."\nDELIMITER //\n");
		fwrite($handle, $trigger['create']."// \nDELIMITER ;\n\n");
	}
	
	//Events
	$events = PMA_getEvents($sdb, $link);
	foreach($events as $event){
		fwrite($handle, "\n".$event['drop']."\nDELIMITER $$ \n-- \n-- Events \n--\n");
		fwrite($handle, $event['create']);
		fwrite($handle, "\n$$ \nDELIMITER ;\n\n");
	}
	
	//Functions
	$functions = PMA_getProceduresOrFunctions($sdb, 'FUNCTION', $link);
	foreach($functions as $function){
		fwrite($handle, "\n".$function['drop']."\nDELIMITER $$ \n-- \n-- Functions \n--\n");
		fwrite($handle, $function['create']);
		fwrite($handle, "\n$$ \nDELIMITER ;\n\n");
	}
	
	//Procedures
	$procedures = PMA_getProceduresOrFunctions($sdb, 'PROCEDURE', $link);
	foreach($procedures as $procedure){
		fwrite($handle, "\n".$procedure['drop']."\nDELIMITER $$ \n-- \n-- Procedures \n--\n");
		fwrite($handle, $procedure['create']);
		fwrite($handle, "\n$$ \nDELIMITER ;\n\n");
	}	
	
	$sresponse = PMA_exportFooter(); // Just to add the finishing lines
	fwrite($handle, $sresponse);
	fclose($handle);
	
	// Just check that file is created or not ??
	if(file_exists($sdbfile)){
		return true;
	}
	
	return false;
	
} //End of database backup

function PMA_getViews($db, $view, $crlf, $link){
	
	$schema_create = $auto_increment = $dump = '';
	$new_crlf = $crlf;
	
	// This is for foreign language characters
	//To read the values from the old DB in UTF8 format
	//soft_mysql_query('SET NAMES "utf8mb4"', $link);
	
	// Complete view dump,
	// Whether to quote view and fields names or not
	soft_mysql_query('SET SQL_QUOTE_SHOW_CREATE = 1', $link);
	
	// Create view structure
	$result = soft_mysql_query('SHOW CREATE VIEW `'.$db.'`.`'.$view.'`', $link);

	// Construct the dump for the view structure
	$dump .=  '--' . $crlf
			. '-- Structure for view ' . '`' . $view.'`' . $crlf
			. '--' . $crlf
			. 'DROP VIEW IF EXISTS `' . $view . '`;' . $crlf . $crlf;
	
	if ($row = soft_mysql_fetch_assoc($result)) {
			
		$create_query = $row['Create View'];
		
		preg_match('/DEFINER=(.*?) SQL/is', $create_query, $matches);
		$create_query = str_replace($matches[1], 'CURRENT_USER', $create_query);

		$schema_create .= $new_crlf . $dump;

		// Convert end of line chars to one that we want (note that MySQL doesn't return query it will accept in all cases)
		if (strpos($create_query, "(\r\n ")) {
			$create_query = str_replace("\r\n", $crlf, $create_query);
		} elseif (strpos($create_query, "(\n ")) {
			$create_query = str_replace("\n", $crlf, $create_query);
		} elseif (strpos($create_query, "(\r ")) {
			$create_query = str_replace("\r", $crlf, $create_query);
		}
		
		$schema_create .= $create_query;
	}
		
	soft_mysql_free_result($result);
    	
	// Dump the structure !!!
	$return['structure'] = $schema_create . ';' . $crlf;
	
	return $return;
}

function PMA_getTriggers($db, $link){
	$query = soft_mysql_query('SHOW TRIGGERS FROM `' . $db . '`', $link);
	$result = array(); //added as empty so don't give warning when data is empty..
		
	while($trigger = soft_mysql_fetch_assoc($query)){
		
		$one_result = array();
		$one_result['name'] = $trigger['Trigger'];
		$one_result['table'] = $trigger['Table'];
		$one_result['action_timing'] = $trigger['Timing'];
		$one_result['event_manipulation'] = $trigger['Event'];
		$one_result['definition'] = $trigger['Statement'];
		$one_result['definer'] = $trigger['Definer'];
		$one_result['full_trigger_name'] = '`'.$trigger['Trigger'].'`';
		$one_result['drop'] = 'DROP TRIGGER IF EXISTS `' . $db .'`.'. $one_result['full_trigger_name'].';';
		$one_result['create'] = 'CREATE TRIGGER '
			. $one_result['full_trigger_name'] . ' '
			. $trigger['Timing'] . ' '
			. $trigger['Event']
			. ' ON ' . '`'. $trigger['Table'].'`'
			. "\n" . ' FOR EACH ROW '
			. $trigger['Statement'] . "\n" . $delimiter . "\n";
			
		$result[] = $one_result;
	}

	// Sort results by name
	$name = array();
	foreach ($result as $value) {
		$name[] = $value['name'];
	}
	
	if(!empty($result)){
		array_multisort($name, SORT_ASC, $result);
	}
	
	return($result);
	
}

function PMA_getEvents($db, $link){
	
	$query = soft_mysql_query('SHOW EVENTS FROM `' . $db . '`', $link);

	$result = array();
	while ($event = soft_mysql_fetch_assoc($query)) {
			$one_result = array();
			$one_result['name'] = $event['Name'];
			$one_result['type'] = $event['Type'];
			$one_result['status'] = $event['Status'];
			$one_result['drop'] = 'DROP EVENT IF EXISTS `' . $db .'`.`'. $one_result['name'].'`;';

			$result[] = $one_result;
	}

	// Doing this outside loop because mysqlnd does not support query unless fetch is completed
	foreach($result as $rk => $value) {
		$result[$rk]['create'] = PMA_getDefinition($db, 'EVENT', $value['name'], $link);
	}
	
	// Sort results by name
	$name = array();
	foreach ($result as $value) {
		$name[] = $value['name'];
	}
	
	if(!empty($result)){
		array_multisort($name, SORT_ASC, $result);
	}

	return $result;
}

/**
 * returns the array of PROCEDURE/FUNCTION names
 *
 * @param string $db    db name
 * @param string $which PROCEDURE | FUNCTION | EVENT
 * @param string $link  connection link to the database
 *
 * @return array names of Procedures/Functions
 */
function PMA_getProceduresOrFunctions($db, $which, $link)
{
	$query = soft_mysql_query('SHOW ' . $which . ' STATUS;', $link);
	$result = array();
	
	while($one_show = soft_mysql_fetch_assoc($query)) {
		if ($one_show['Db'] == $db && $one_show['Type'] == $which) {
			$one_show['drop'] = 'DROP '.$which.' IF EXISTS `' . $db .'`.`'. $one_show['Name'].'`;';
			
			$result[] = $one_show;
		}
	}

	// Doing this outside loop because mysqlnd does not support query unless fetch is completed
	foreach($result as $rk => $value) {
	    $result[$rk]['create'] = PMA_getDefinition($db, $which, $value['Name'], $link);
	}
	
	return $result;
}

/**
 * returns the definition of a specific PROCEDURE, FUNCTION or EVENT
 *
 * @param string $db    db name
 * @param string $which PROCEDURE | FUNCTION | EVENT
 * @param string $name  the procedure|function|event name
 * @param string $link  connection link to the database
 *
 * @return string the definition
 */
function PMA_getDefinition($db, $which, $name, $link)
{
	$returned_field = array(
		'PROCEDURE' => 'Create Procedure',
		'FUNCTION'  => 'Create Function',
		'EVENT'     => 'Create Event'
	);
	$query = soft_mysql_query('SHOW CREATE '.$which.' `'.$db.'`.`'.$name.'`;', $link);
	
	if ($res = soft_mysql_fetch_assoc($query)){
		return($res[$returned_field[$which]]);
	}
	
}

// Internal function to add slashes to row values 
function PMA_sqlAddslashes(&$a_string = '', $is_like = false, $crlf = false, $php_code = false) {

    if ($is_like) {
        $a_string = str_replace('\\', '\\\\\\\\', $a_string);
    } else {
        $a_string = str_replace('\\', '\\\\', $a_string);
    }

    if ($crlf) {
        $a_string = str_replace("\n", '\n', $a_string);
        $a_string = str_replace("\r", '\r', $a_string);
        $a_string = str_replace("\t", '\t', $a_string);
    }

    if ($php_code) {
        $a_string = str_replace('\'', '\\\'', $a_string);
    } else {
        $a_string = str_replace('\'', '\'\'', $a_string);
    }

    return $a_string;
} // end of the 'PMA_sqlAddslashes()' function


// Form the table structure && the alter queries if any !! 
function PMA_getTableDef($db, $table, $crlf, $show_dates = false, $add_semicolon = true, $link) {
	
    global $sql_drop_table, $sql_alter;
    global $sql_constraints;
    global $sql_constraints_query; // just the text of the query
    global $sql_drop_foreign_keys;

    $schema_create = $auto_increment = $sql_constraints = '';
    $new_crlf = $crlf;
	
	// Get the Status of the table so as to produce the auto increment value
	$qresult = soft_mysql_query('SHOW TABLE STATUS FROM `'.$db.'` LIKE \''.$table.'\'', $link);
	
	// Handle auto-increment values
	if (soft_mysql_num_rows($qresult) > 0) {
		
		$tmpres = soft_mysql_fetch_assoc($qresult);
		
		if($tmpres['Comment'] != 'VIEW'){
			// Is auto-increment value is set ??
			if(!empty($tmpres['Auto_increment'])){
				$auto_increment .= ' AUTO_INCREMENT=' . $tmpres['Auto_increment'] . ' ';
			}
		}
	
	}
	// Free resourse
	soft_mysql_free_result($qresult);
	
	//added as empty so don't give warning when data is empty..
	$dump = '';
	
	// Construct the dump for the table structure
	$dump .=  '--' . $crlf
			. '-- Table structure for table ' . '`' . $table.'`' . $crlf
			. '--' . $crlf . $crlf;
		 
    $schema_create .= $new_crlf . $dump;

    // Complete table dump,
    // Whether to quote table and fields names or not
    soft_mysql_query('SET SQL_QUOTE_SHOW_CREATE = 1', $link);
	
	// Create table structure
	$result = soft_mysql_query('SHOW CREATE TABLE `'.$db.'`.`'.$table.'`', $link);
	
    if ($row = soft_mysql_fetch_assoc($result)) {
		
        $create_query = $row['Create Table'];
		unset($row);

        // Convert end of line chars to one that we want (note that MySQL doesn't return query it will accept in all cases)
        if (strpos($create_query, "(\r\n ")) {
            $create_query = str_replace("\r\n", $crlf, $create_query);
        } elseif (strpos($create_query, "(\n ")) {
            $create_query = str_replace("\n", $crlf, $create_query);
        } elseif (strpos($create_query, "(\r ")) {
            $create_query = str_replace("\r", $crlf, $create_query);
        }

        // are there any constraints to cut out?
        if (preg_match('@CONSTRAINT|FOREIGN[\s]+KEY@', $create_query)) {

            // Split the query into lines, so we can easily handle it.
			// We know lines are separated by $crlf (done few lines above).	
            $sql_lines = explode($crlf, $create_query);
			$sql_count = count($sql_lines);

            // Lets find first line with constraints
            for ($i = 0; $i < $sql_count; $i++) {
                if (preg_match('@^[\s]*(CONSTRAINT|FOREIGN[\s]+KEY)@', $sql_lines[$i])) {
                 	break;
                }
            }

            // If we really found a constraint
            if ($i != $sql_count) {
				
                // remove , from the end of create statement
                $sql_lines[$i - 1] = preg_replace('@,$@', '', $sql_lines[$i - 1]);

                // comments for current table
                $sql_constraints .= $crlf
								 . PMA_exportComment()
								 . PMA_exportComment('Constraints for table ' . '`' . $table.'`')
								 . PMA_exportComment();
                
                // Let's do the work
                $sql_constraints_query .= 'ALTER TABLE `'.$table.'`' . $crlf;
                $sql_constraints .= 'ALTER TABLE `'.$table.'`' . $crlf;
                $sql_drop_foreign_keys .= 'ALTER TABLE `'.$table.'` `'.$db.'`' . $crlf;

                $first = TRUE;
                for ($j = $i; $j < $sql_count; $j++) {
                    if (preg_match('@CONSTRAINT|FOREIGN[\s]+KEY@', $sql_lines[$j])) {
                        if (!$first) {
                            $sql_constraints .= $crlf;
                        }
                        if (strpos($sql_lines[$j], 'CONSTRAINT') === FALSE) {
                            $tmp_str = preg_replace('/(FOREIGN[\s]+KEY)/', 'ADD \1', $sql_lines[$j]);
                            $sql_constraints_query .= $tmp_str;
                            $sql_constraints .= $tmp_str;
                        } else {
                            $tmp_str = preg_replace('/(CONSTRAINT)/', 'ADD \1', $sql_lines[$j]);
                            $sql_constraints_query .= $tmp_str;
                            $sql_constraints .= $tmp_str;
                            preg_match('/(CONSTRAINT)([\s])([\S]*)([\s])/', $sql_lines[$j], $matches);
                            if (! $first) {
                                $sql_drop_foreign_keys .= ', ';
                            }
                            $sql_drop_foreign_keys .= 'DROP FOREIGN KEY ' . $matches[3];
                        }
                        $first = FALSE;
                    } else {
                        break;
                    }
                }
                $sql_constraints .= ';' . $crlf;
                $sql_constraints_query .= ';';
				
				// Dump the alter queries!!!
				$return['alter'] = $sql_constraints; 
				
				$create_query = implode($crlf, array_slice($sql_lines, 0, $i)) . $crlf . implode($crlf, array_slice($sql_lines, $j, $sql_count - 1));
				unset($sql_lines);
            }
        }
        $schema_create .= $create_query;
    }

    // remove a possible "AUTO_INCREMENT = value" clause
    // that could be there starting with MySQL 5.0.24
    $schema_create = preg_replace('/AUTO_INCREMENT\s*=\s*([0-9])+/', '', $schema_create);

    $schema_create .= $auto_increment;
		
    soft_mysql_free_result($result);
    	
	// Dump the structure !!!
	$return['structure'] = $schema_create . ($add_semicolon ? ';' . $crlf : '');
	
	return $return;
	 
} // end of the 'PMA_getTableDef()' function

// Internal function to get meta details about the database 
function PMA_DBI_get_fields_meta($sresult) {
    $fields       = array();
    $num_fields   = mysql_num_fields($sresult);
    for ($i = 0; $i < $num_fields; $i++) {
        $field = mysql_fetch_field($sresult, $i);
        $field->flags = mysql_field_flags($sresult, $i);
        $field->orgtable = mysql_field_table($sresult, $i);
        $field->orgname = mysql_field_name($sresult, $i);
        $fields[] = $field;
    }
    return $fields;
}

// Export data - values 
function PMA_exportData($db, $table, $crlf, $handle, $link){
	
    global $current_row;
	$count = $GLOBALS['data']['get_db_export_chunksize'];
	$limit = 0;

	// We have modified this code because we were getting error if inserts were >50000
	$cnt_qry = 'SELECT count(*) FROM `'.$db . '`.`' . $table . '`';
	$cnt_result = soft_mysql_query($cnt_qry, $link, 1);
	$cnt_res = soft_mysql_fetch_row($cnt_result);
	// Free resourse
	soft_mysql_free_result($cnt_result);
	
	$sql_query  = 'SELECT * FROM `'.$db . '`.`' . $table . '` LIMIT 0,'.$count;

    $formatted_table_name = '`' . $table . '`';

    $squery= soft_mysql_query($sql_query, $link, 1);
	
	$fields_cnt = soft_mysql_num_fields($squery);

	// Get field information
	if(extension_loaded('mysqli')){
		$fields_meta    = getFieldsMeta($squery);
	}else{
		$fields_meta    = PMA_DBI_get_fields_meta($squery);
	}
	
	$field_flags    = array();
	for ($j = 0; $j < $fields_cnt; $j++) {
		$field_flags[$j] = soft_mysql_field_flags($squery, $j);
	}

	for ($j = 0; $j < $fields_cnt; $j++) {
		$field_set[$j] = '`'.$fields_meta[$j]->name . '`';
	}

	$sql_command = 'INSERT';
   
	$insert_delayed = '';
	$separator = ',';

	$schema_insert = $sql_command . $insert_delayed .' INTO `' . $table . '` VALUES';
	
	$search       = array("\x00", "\x0a", "\x0d", "\x1a"); //\x08\\x09, not required
	$replace      = array('\0', '\n', '\r', '\Z');
	$current_row  = 0;
	$new_query  = 0;
	$query_length = 0;

	$schema_insert .= $crlf;
	for($i = $cnt_res[0]; $i >= 0; $i--){
		
		// Now if 10000 rows has been processed than select next.
		if($count == 0){
			// Now free the result for preventing memory exhaust
			soft_mysql_free_result($squery);
			$count = $GLOBALS['data']['get_db_export_chunksize'];
			$limit = $limit + $count;
			$sql_query  = 'SELECT * FROM `'.$db . '`.`' . $table . '` LIMIT '.($limit).', '.$count;
			$squery= soft_mysql_query($sql_query, $link, 1);
		}
		
		$row = soft_mysql_fetch_array($squery);
		
		// If we get empty result than break the loop
		if(!$row){
			break;
		}
		
		if ($current_row == 0) {
			$head = PMA_exportComment()
				  . PMA_exportComment('Dumping data for table' . ' ' . $formatted_table_name)
				  . PMA_exportComment()
				  . $crlf;
			fwrite($handle, $head);
		}
		$current_row++;
		
		if ($current_row == 1 || $new_query == 1) {
			fwrite($handle, $schema_insert .'(');
		}else{
			fwrite($handle, ','.$crlf.'(');
		}
		
		$add_comma = 0;
		for ($j = 0; $j < $fields_cnt; $j++) {
			
			$separator = ($add_comma > 0 ? ', ' : '');
			
			// NULL
			if (!isset($row[$j]) || is_null($row[$j])) {
				fwrite($handle, $separator . 'NULL');
			// a number
			// timestamp is numeric on some MySQL 4.1, BLOBs are sometimes numeric
			} elseif ($fields_meta[$j]->numeric && $fields_meta[$j]->type != 'timestamp' 
					&& !$fields_meta[$j]->blob) {
				fwrite($handle, $separator . $row[$j]);
			} elseif ($fields_meta[$j]->type == 'bit') {
				fwrite($handle, $separator . PMA_printableBitValue($row[$j], $fields_meta[$j]->length));
			} else { 
				PMA_sqlAddslashes($row[$j]);
				fwrite($handle, $separator . '\'' . str_replace($search, $replace, $row[$j]) . '\'');				
			} // end if
			
			if (isset($row[$j]) && is_string($row[$j])) {
				$query_length += strlen($row[$j]);
			}
			
			$add_comma++;
			$new_query = 0;
		} // end for
		
		fwrite($handle, ')');
		
		// Stop extended insert after 50K chars and open a new INSERT
		if($query_length > 50000){
			
			// When $1 == 1 it is the last row in the table and if we add ; here it will be added again after end of for
			if($i !== 1){
				$query_buffer = ';' . $crlf;
				fwrite($handle, $query_buffer);
			}
			
			$add_comma = 0;
			$new_query = 1;
			$query_length = 0;
		}
	
		// Decrement till 0 so that next 10000 rows can be selected
		$count--;
		
	}// End of FOR
	
	if ($current_row > 0) {   
		$query_buffer = ';' . $crlf;
		fwrite($handle, $query_buffer);
	}
	
	// Free resourses
	soft_mysql_free_result($squery);
	
	$end_line = (!empty($query_buffer) ? $crlf : '' ). PMA_exportComment('--------------------------------------------------------');
	fwrite($handle, $end_line);
	//return $query_buffer . $end_line;
		
} 

function PMA_exportComment($text = '')
{
	$crlf = "\n";
	$ret = '--' . (empty($text) ? '' : ' ') . $text . $crlf;
	return $ret;
}

function PMA_exportHeader($db, $ser_ver)
{
    $crlf = "\n";  

    $head  =  PMA_exportComment('Softaculous SQL Dump')
           .  PMA_exportComment('http://www.softaculous.com')
           .  PMA_exportComment()
		   .  PMA_exportComment('Host: localhost')
           .  PMA_exportComment('Generation Time: '. date("F j, Y, g:i a") .'')
		   .  PMA_exportComment('Server version: '. $ser_ver .'')
           .  PMA_exportComment('PHP Version' . ': ' . phpversion())
           .  $crlf;

    /* We want exported AUTO_INCREMENT fields to have still same value, do this only for recent MySQL exports */
    $head .=  'SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";' . $crlf;
    
    /* Change timezone if we should export timestamps in UTC */
    $head .= 'SET time_zone = "+00:00";' . $crlf . $crlf;
  
	// by default we use the connection charset
	$set_names = 'utf8mb4';
		
	$head .=  $crlf
		   . '/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;' . $crlf
		   . '/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;' . $crlf
		   . '/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;' . $crlf
		   . '/*!40101 SET NAMES ' . $set_names . ' */;' . $crlf . $crlf;
	
	$head .= PMA_exportComment()
		  . PMA_exportComment('Database: `' . $db . '`')
		  . PMA_exportComment()
		  . $crlf
		  . PMA_exportComment('--------------------------------------------------------');
		  
	// We need this comment in template mode
	if(!empty($GLOBALS['data']['template_name'])){
		$head .= PMA_exportComment('HEADER EXPORTED SOFTACULOUS');
	}

    return $head;

}

function PMA_exportFooter()
{
    $crlf = "\n";
    $foot = '';

	$foot .=  $crlf
	   . '/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;' . $crlf
	   . '/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;' . $crlf
	   . '/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;' . $crlf;
	
	return $foot;
}

function PMA_sversion($link){

	// Get version
	$vres = soft_mysql_query('SELECT VERSION()', $link);
	$version = soft_mysql_fetch_assoc($vres);
	// Free resourse
	soft_mysql_free_result($vres);
	
	// Explode to extract version
	$version = explode('-', $version['VERSION()']);
	return $version[0];
	
}

function PMA_printableBitValue($value, $length){
	// if running on a 64-bit server or the length is safe for decbin()
	if (PHP_INT_SIZE == 8 || $length < 33) {
		$printable = decbin($value);
	} else {
		// FIXME: does not work for the leftmost bit of a 64-bit value
		$i = 0;
		$printable = '';
		while ($value >= pow(2, $i)) {
			++$i;
		}
		if ($i != 0) {
			--$i;
		}

		while ($i >= 0) {
			if ($value - pow(2, $i) < 0) {
				$printable = '0' . $printable;
			} else {
				$printable = '1' . $printable;
				$value = $value - pow(2, $i);
			}
			--$i;
		}
		$printable = strrev($printable);
	}
	$printable = str_pad($printable, $length, '0', STR_PAD_LEFT);
	return $printable;
}

function soft_mysql_connect($host, $user, $pass, $newlink = false){
	
	// php 8.1 throws mysqli_sql_exception if the db/dbuser doesn't exists
	try{
		if(extension_loaded('mysqli')){
			//echo 'mysqli';
			//To handle connection if user passes a custom port along with the host as 127.0.0.1:6446.
			//For testing, use port 127.0.0.1 instead of localhost as 127.0.0.1:6446 http://php.net/manual/en/mysqli.construct.php#112328
			$exh = explode(':', $host);
			if(!empty($exh[1])){
				//In webuzo we connect to MYSQL through mysql.sock e.g localhost:/var/lib/mysql/mysql.sock
				//In case of socket $exh[1] will have the socket path and socket is the 6th parameter
				if(!is_numeric($exh[1])){
					$sconn = @mysqli_connect($exh[0], $user, $pass, '', 3306, $exh[1]);
				}else{
					$sconn = @mysqli_connect($exh[0], $user, $pass, '', $exh[1]);
				}
			}else{
				$sconn = @mysqli_connect($host, $user, $pass);
			}
		}else{
			//echo 'mysql';
			$sconn = @mysql_connect($host, $user, $pass, $newlink);
		}
	}catch(Exception $e){
		return false;
	}
	
	return $sconn;
}

function soft_mysql_select_db($db, $conn){
	
	if(empty($conn)){
		return false;
	}
	
	// php 8.1 throws mysqli_sql_exception if the database doesn't exists
	try{
		if(extension_loaded('mysqli')){
			$return = @mysqli_select_db($conn, $db);
		}else{
			$return = @mysql_select_db($db, $conn);
		}
	}catch(Exception $e){
		return false;
	}
	
	return $return;
}

function soft_mysql_query($query, $conn, $unbuffer_mode = 0){
	
	if(empty($conn) || empty($query)){
		return false;
	}
	
	try{
		if(extension_loaded('mysqlnd') && !empty($unbuffer_mode)){
			$return = @mysqli_query($conn, $query, MYSQLI_USE_RESULT);
		}elseif(extension_loaded('mysqli')){
			$return = @mysqli_query($conn, $query);
		}else{
			$return = @mysql_query($query, $conn);
		}
	}catch(Exception $e){
		return false;
	}
	
	return $return;
}

function soft_mysql_fetch_array($result){
	
	// If $result is not a resource return else it will lead to FATAL error
	if(empty($result)){
		return false;
	}
	
	if(extension_loaded('mysqli')){
		$return = @mysqli_fetch_array($result);
	}else{
		$return = @mysql_fetch_array($result);
	}
	
	return $return;
}

function soft_mysql_fetch_assoc($result){
	
	// If $result is not a resource return else it will lead to FATAL error
	if(empty($result)){
		return false;
	}
	
	if(extension_loaded('mysqli')){
		$return = @mysqli_fetch_assoc($result);
	}else{
		$return = @mysql_fetch_assoc($result);
	}
	
	return $return;
}

function soft_mysql_fetch_row($result){
	
	// If $result is not a resource return else it will lead to FATAL error
	if(empty($result)){
		return false;
	}
	
	if(extension_loaded('mysqli')){
		$return = @mysqli_fetch_row($result);
	}else{
		$return = @mysql_fetch_row($result);
	}
	
	return $return;
}

function soft_mysql_fetch_field($result, $field){
	
	// If $result is not a resource return else it will lead to FATAL error
	if(empty($result)){
		return false;
	}
	
	if(extension_loaded('mysqli')){
		$return = @mysqli_fetch_field($result, $field);
	}else{
		$return = @mysql_fetch_field($result, $field);
	}
	
	return $return;
}


function soft_mysql_field_flags($result, $i){
	
	// If $result is not a resource return else it will lead to FATAL error
	if(empty($result)){
		return false;
	}
	
	if(!extension_loaded('mysqli')){
		return mysql_field_flags($result, $i);
	}
	
	$f = mysqli_fetch_field_direct($result, $i);
	$type = $f->type;
	$charsetnr = $f->charsetnr;
	$f = $f->flags;
	$flags = '';
	if ($f & MYSQLI_UNIQUE_KEY_FLAG) {
		$flags .= 'unique ';
	}
	if ($f & MYSQLI_NUM_FLAG) {
		$flags .= 'num ';
	}
	if ($f & MYSQLI_PART_KEY_FLAG) {
		$flags .= 'part_key ';
	}
	if ($f & MYSQLI_SET_FLAG) {
		$flags .= 'set ';
	}
	if ($f & MYSQLI_TIMESTAMP_FLAG) {
		$flags .= 'timestamp ';
	}
	if ($f & MYSQLI_AUTO_INCREMENT_FLAG) {
		$flags .= 'auto_increment ';
	}
	if ($f & MYSQLI_ENUM_FLAG) {
		$flags .= 'enum ';
	}
	// See http://dev.mysql.com/doc/refman/6.0/en/c-api-datatypes.html:
	// to determine if a string is binary, we should not use MYSQLI_BINARY_FLAG
	// but instead the charsetnr member of the MYSQL_FIELD
	// structure. Watch out: some types like DATE returns 63 in charsetnr
	// so we have to check also the type.
	// Unfortunately there is no equivalent in the mysql extension.
	if (($type == MYSQLI_TYPE_TINY_BLOB || $type == MYSQLI_TYPE_BLOB
		|| $type == MYSQLI_TYPE_MEDIUM_BLOB || $type == MYSQLI_TYPE_LONG_BLOB
		|| $type == MYSQLI_TYPE_VAR_STRING || $type == MYSQLI_TYPE_STRING)
		&& 63 == $charsetnr
	) {
		$flags .= 'binary ';
	}
	if ($f & MYSQLI_ZEROFILL_FLAG) {
		$flags .= 'zerofill ';
	}
	if ($f & MYSQLI_UNSIGNED_FLAG) {
		$flags .= 'unsigned ';
	}
	if ($f & MYSQLI_BLOB_FLAG) {
		$flags .= 'blob ';
	}
	if ($f & MYSQLI_MULTIPLE_KEY_FLAG) {
		$flags .= 'multiple_key ';
	}
	if ($f & MYSQLI_UNIQUE_KEY_FLAG) {
		$flags .= 'unique_key ';
	}
	if ($f & MYSQLI_PRI_KEY_FLAG) {
		$flags .= 'primary_key ';
	}
	if ($f & MYSQLI_NOT_NULL_FLAG) {
		$flags .= 'not_null ';
	}
	return trim($flags);
}


function soft_mysql_num_rows($result){
	
	// If $result is not a resource return else it will lead to FATAL error
	if(empty($result)){
		return false;
	}
	
	// $result->type == 1 when mysqlnd and unbuffered queries i.e. with use result
	// $result->type == 0 when not mysqlnd and buffered queries i.e. without use result
	if(extension_loaded('mysqlnd') && !empty($result->type)){
		$return = (int) (!empty($result));
	}elseif(extension_loaded('mysqli')){
		$return = @mysqli_num_rows($result);
	}else{
		$return = @mysql_num_rows($result);
	}
	
	return $return;
}

function soft_mysql_affected_rows($conn){

	if(empty($conn)){
		return false;
	}

	if(extension_loaded('mysqli')){
		$return = @mysqli_affected_rows($conn);
	}else{
		$return = @mysql_affected_rows($conn);
	}
	
	return $return;
}

function soft_mysql_num_fields($result){
	
	// If $result is not a resource return else it will lead to FATAL error
	if(empty($result)){
		return false;
	}
	
	if(extension_loaded('mysqli')){
		$return = @mysqli_num_fields($result);
	}else{
		$return = @mysql_num_fields($result);
	}
	
	return $return;
}

function soft_mysql_free_result($result){
	
	// If $result is not a mysql result object return else it will lead to FATAL error
	if(empty($result) || !is_object($result)){
		return false;
	}
	
	if(extension_loaded('mysqli')){
		$return = @mysqli_free_result($result);
	}else{
		$return = @mysql_free_result($result);
	}
	
	return $return;
}

function getFieldsMeta($result){
	// Build an associative array for a type look up
	
	if(!defined('MYSQLI_TYPE_VARCHAR')){
		define('MYSQLI_TYPE_VARCHAR', 15);
	}
	
	$typeAr = array();
	$typeAr[MYSQLI_TYPE_DECIMAL]     = 'real';
	$typeAr[MYSQLI_TYPE_NEWDECIMAL]  = 'real';
	$typeAr[MYSQLI_TYPE_BIT]         = 'int';
	$typeAr[MYSQLI_TYPE_TINY]        = 'int';
	$typeAr[MYSQLI_TYPE_SHORT]       = 'int';
	$typeAr[MYSQLI_TYPE_LONG]        = 'int';
	$typeAr[MYSQLI_TYPE_FLOAT]       = 'real';
	$typeAr[MYSQLI_TYPE_DOUBLE]      = 'real';
	$typeAr[MYSQLI_TYPE_NULL]        = 'null';
	$typeAr[MYSQLI_TYPE_TIMESTAMP]   = 'timestamp';
	$typeAr[MYSQLI_TYPE_LONGLONG]    = 'int';
	$typeAr[MYSQLI_TYPE_INT24]       = 'int';
	$typeAr[MYSQLI_TYPE_DATE]        = 'date';
	$typeAr[MYSQLI_TYPE_TIME]        = 'time';
	$typeAr[MYSQLI_TYPE_DATETIME]    = 'datetime';
	$typeAr[MYSQLI_TYPE_YEAR]        = 'year';
	$typeAr[MYSQLI_TYPE_NEWDATE]     = 'date';
	$typeAr[MYSQLI_TYPE_ENUM]        = 'unknown';
	$typeAr[MYSQLI_TYPE_SET]         = 'unknown';
	$typeAr[MYSQLI_TYPE_TINY_BLOB]   = 'blob';
	$typeAr[MYSQLI_TYPE_MEDIUM_BLOB] = 'blob';
	$typeAr[MYSQLI_TYPE_LONG_BLOB]   = 'blob';
	$typeAr[MYSQLI_TYPE_BLOB]        = 'blob';
	$typeAr[MYSQLI_TYPE_VAR_STRING]  = 'string';
	$typeAr[MYSQLI_TYPE_STRING]      = 'string';
	$typeAr[MYSQLI_TYPE_VARCHAR]     = 'string'; // for Drizzle
	// MySQL returns MYSQLI_TYPE_STRING for CHAR
	// and MYSQLI_TYPE_CHAR === MYSQLI_TYPE_TINY
	// so this would override TINYINT and mark all TINYINT as string
	// https://sourceforge.net/p/phpmyadmin/bugs/2205/
	//$typeAr[MYSQLI_TYPE_CHAR]        = 'string';
	$typeAr[MYSQLI_TYPE_GEOMETRY]    = 'geometry';
	$typeAr[MYSQLI_TYPE_BIT]         = 'bit';

	$fields = mysqli_fetch_fields($result);

	// this happens sometimes (seen under MySQL 4.0.25)
	if (!is_array($fields)) {
		return false;
	}

	foreach ($fields as $k => $field) {
		$fields[$k]->_type = $field->type;
		$fields[$k]->type = $typeAr[$field->type];
		$fields[$k]->_flags = $field->flags;
		$fields[$k]->flags = soft_mysql_field_flags($result, $k);

		// Enhance the field objects for mysql-extension compatibilty
		//$flags = explode(' ', $fields[$k]->flags);
		//array_unshift($flags, 'dummy');
		$fields[$k]->multiple_key
			= (int) (bool) ($fields[$k]->_flags & MYSQLI_MULTIPLE_KEY_FLAG);
		$fields[$k]->primary_key
			= (int) (bool) ($fields[$k]->_flags & MYSQLI_PRI_KEY_FLAG);
		$fields[$k]->unique_key
			= (int) (bool) ($fields[$k]->_flags & MYSQLI_UNIQUE_KEY_FLAG);
		$fields[$k]->not_null
			= (int) (bool) ($fields[$k]->_flags & MYSQLI_NOT_NULL_FLAG);
		$fields[$k]->unsigned
			= (int) (bool) ($fields[$k]->_flags & MYSQLI_UNSIGNED_FLAG);
		$fields[$k]->zerofill
			= (int) (bool) ($fields[$k]->_flags & MYSQLI_ZEROFILL_FLAG);
		$fields[$k]->numeric
			= (int) (bool) ($fields[$k]->_flags & MYSQLI_NUM_FLAG);
		$fields[$k]->blob
			= (int) (bool) ($fields[$k]->_flags & MYSQLI_BLOB_FLAG);
	}
	return $fields;
}

define('ARCHIVE_TAR_ATT_SEPARATOR', 90001);
define('ARCHIVE_TAR_END_BLOCK', pack("a512", ''));

class softtar
{

    var $_tarname='';
    var $_compress=false;
    var $_compress_type='none';
    var $_separator=',';
    var $_file=0;
    var $_temp_tarname='';
    var $_ignore_regexp='';
    var $error_object=null;
	
	var $_local_tar=''; // The local file	
	var $_orig_tar=''; // The remote file
	var $remote_fp=''; // The remote file pointer	
	var $remote_fp_filter = NULL;
	var $remote_hctx = NULL;
	var $remote_content_size = 0;
	
    function __construct($p_tarname, $p_compress = null, $handle_remote = false)
    {	
	
		// $tmpdir is mainly used for REMOTE protocols so that we can write locally and append on the remote server
		if(preg_match('/\:\/\//', $p_tarname) && $handle_remote){			
			$tmpdir = $GLOBALS['data']['path'].'/tmp';
			$this->_orig_tar = $p_tarname;
			$p_tarname = $tmpdir.'/'.md5($p_tarname);
			$this->_local_tar = $p_tarname;
			//Required in _gdrive.php
			$GLOBALS['slocal_tar'] = $this->_local_tar;
		}
		
        $this->_compress = false;
        $this->_compress_type = 'none';
        if (($p_compress === null) || ($p_compress == '')) {
            if (@file_exists($p_tarname)) {
                if ($fp = @fopen($p_tarname, "rb")) {
                    // look for gzip magic cookie
                    $data = fread($fp, 2);
                    fclose($fp);
                    if ($data == "\37\213") {
                        $this->_compress = true;
                        $this->_compress_type = 'gz';
                        // No sure it's enought for a magic code ....
                    } elseif ($data == "BZ") {
                        $this->_compress = true;
                        $this->_compress_type = 'bz2';
                    }
                }
            } else {
                // probably a remote file or some file accessible
                // through a stream interface
                if (substr($p_tarname, -2) == 'gz') {
                    $this->_compress = true;
                    $this->_compress_type = 'gz';
                } elseif ((substr($p_tarname, -3) == 'bz2') ||
                          (substr($p_tarname, -2) == 'bz')) {
                    $this->_compress = true;
                    $this->_compress_type = 'bz2';
                }
            }
        } else {
            if (($p_compress === true) || ($p_compress == 'gz')) {
                $this->_compress = true;
                $this->_compress_type = 'gz';
            } else if ($p_compress == 'bz2') {
                $this->_compress = true;
                $this->_compress_type = 'bz2';
            } else {
                $this->_error("Unsupported compression type '$p_compress'\n".
                    "Supported types are 'gz' and 'bz2'.\n");
                return false;
            }
        }
        $this->_tarname = $p_tarname;
        if ($this->_compress) { // assert zlib or bz2 extension support
            if ($this->_compress_type == 'gz')
                $extname = 'zlib';
            else if ($this->_compress_type == 'bz2')
                $extname = 'bz2';

            if (!extension_loaded($extname)) {
                PEAR::loadExtension($extname);
            }
            if (!extension_loaded($extname)) {
                $this->_error("The extension '$extname' couldn't be found.\n".
                    "Please make sure your version of PHP was built ".
                    "with '$extname' support.\n");
                return false;
            }
        }
    }
    // }}}

    // {{{ destructor
    function _softtar()
    {
        $this->_close();
        // ----- Look for a local copy to delete
        if ($this->_temp_tarname != '')
            @unlink($this->_temp_tarname);
		
		// In case of REMOTE
		if(!empty($this->_orig_tar) && $GLOBALS['backup_status'] == BACKUP_COMPLETE){
			unlink($this->_local_tar);
		}
    }
    // }}}
	
	function __destruct(){
		$this->_softtar();
	}

    // {{{ create()
    function create($p_filelist)
    {
        return $this->createModify($p_filelist, '', '');
    }
    // }}}

    // {{{ add()
    function add($p_filelist)
    {
        return $this->addModify($p_filelist, '', '');
    }
    // }}}

    // {{{ extract()
    function extract($p_path='', $p_preserve=false)
    {
        return $this->extractModify($p_path, '', $p_preserve);
    }
    // }}}

    // {{{ listContent()
    function listContent()
    {
        $v_list_detail = array();

        if ($this->_openRead()) {
            if (!$this->_extractList('', $v_list_detail, "list", '', '')) {
                unset($v_list_detail);
                $v_list_detail = 0;
            }
            $this->_close();
        }

        return $v_list_detail;
    }
    // }}}

    // {{{ createModify()
    function createModify($p_filelist, $p_add_dir, $p_remove_dir='')
    {
        $v_result = true;
		
        if (!$this->_openWrite())
            return false;

        if ($p_filelist != '') {
            if (is_array($p_filelist))
                $v_list = $p_filelist;
            elseif (is_string($p_filelist))
                $v_list = explode($this->_separator, $p_filelist);
            else {
                $this->_cleanFile();
                $this->_error('Invalid file list');
                return false;
            }

            $v_result = $this->_addList($v_list, $p_add_dir, $p_remove_dir);
        }

        if ($v_result) {
			// --- write footer only if end file is empty..
			if($GLOBALS['backup_status'] == BACKUP_COMPLETE){ 
				
				if($v_result){
					$this->_writeFooter();
				}
			}
            $this->_close();
        } else
            $this->_cleanFile();

        return $v_result;
    }
    // }}}

    // {{{ addModify()
    function addModify($p_filelist, $p_add_dir, $p_remove_dir='')
    {
        $v_result = true;

        if (!$this->_isArchive())
            $v_result = $this->createModify($p_filelist, $p_add_dir,
                                            $p_remove_dir);
        else {
            if (is_array($p_filelist))
                $v_list = $p_filelist;
            elseif (is_string($p_filelist))
                $v_list = explode($this->_separator, $p_filelist);
            else {
                $this->_error('Invalid file list');
                return false;
            }

            $v_result = $this->_append($v_list, $p_add_dir, $p_remove_dir);
        }

        return $v_result;
    }
    // }}}

    // {{{ addString()
    function addString($p_filename, $p_string)
    {
        $v_result = true;

        if (!$this->_isArchive()) {
            if (!$this->_openWrite()) {
                return false;
            }
            $this->_close();
        }

        if (!$this->_openAppend())
            return false;

        // Need to check the get back to the temporary file ? ....
        $v_result = $this->_addString($p_filename, $p_string);

        $this->_writeFooter();

        $this->_close();

        return $v_result;
    }
    // }}}

    // {{{ extractModify()
    function extractModify($p_path, $p_remove_path, $p_preserve=false)
    {
        $v_result = true;
        $v_list_detail = array();

        if ($v_result = $this->_openRead()) {
            $v_result = $this->_extractList($p_path, $v_list_detail,
                "complete", 0, $p_remove_path, $p_preserve);
            $this->_close();
        }

        return $v_result;
    }
    // }}}

    // {{{ extractInString()
    function extractInString($p_filename)
    {
        if ($this->_openRead()) {
            $v_result = $this->_extractInString($p_filename);
            $this->_close();
        } else {
            $v_result = null;
        }

        return $v_result;
    }
    // }}}

    // {{{ extractList()
    function extractList($p_filelist, $p_path='', $p_remove_path='', $p_preserve=false)
    {
        $v_result = true;
        $v_list_detail = array();

        if (is_array($p_filelist))
            $v_list = $p_filelist;
        elseif (is_string($p_filelist))
            $v_list = explode($this->_separator, $p_filelist);
        else {
            $this->_error('Invalid string list');
            return false;
        }

        if ($v_result = $this->_openRead()) {
            $v_result = $this->_extractList($p_path, $v_list_detail, "partial",
                $v_list, $p_remove_path, $p_preserve);
            $this->_close();
        }

        return $v_result;
    }
    // }}}

    // {{{ setAttribute()
    function setAttribute()
    {
        $v_result = true;

        // ----- Get the number of variable list of arguments
        if (($v_size = func_num_args()) == 0) {
            return true;
        }

        // ----- Get the arguments
        $v_att_list = func_get_args();

        // ----- Read the attributes
        $i=0;
        while ($i<$v_size) {

            // ----- Look for next option
            switch ($v_att_list[$i]) {
                // ----- Look for options that request a string value
                case ARCHIVE_TAR_ATT_SEPARATOR :
                    // ----- Check the number of parameters
                    if (($i+1) >= $v_size) {
                        $this->_error('Invalid number of parameters for '
						              .'attribute ARCHIVE_TAR_ATT_SEPARATOR');
                        return false;
                    }

                    // ----- Get the value
                    $this->_separator = $v_att_list[$i+1];
                    $i++;
                break;

                default :
                    $this->_error('Unknow attribute code '.$v_att_list[$i].'');
                    return false;
            }

            // ----- Next attribute
            $i++;
        }

        return $v_result;
    }
    // }}}

    // {{{ setIgnoreRegexp()
    function setIgnoreRegexp($regexp)
    {
    	$this->_ignore_regexp = $regexp;
    }
    // }}}

    // {{{ setIgnoreList()
    function setIgnoreList($list)
    {
    	$regexp = str_replace(array('#', '.', '^', '$'), array('\#', '\.', '\^', '\$'), $list);
    	$regexp = '#/'.join('$|/', $list).'#';
    	$this->setIgnoreRegexp($regexp);
    }
    // }}}

    // {{{ _error()
    function _error($p_message)
    {
		//we have changed this since PEAR is not used
        //$this->error_object = &$this->raiseError($p_message); 
		trigger_error($p_message, E_USER_WARNING);
    }
    // }}}

    // {{{ _warning()
    function _warning($p_message)
    {
		//we have changed this since PEAR is not used
        //$this->error_object = &$this->raiseError($p_message); 
		trigger_error($p_message, E_USER_NOTICE);
    }
    // }}}

    // {{{ _isArchive()
    function _isArchive($p_filename=null)
    {
        if ($p_filename == null) {
            $p_filename = $this->_tarname;
        }
        clearstatcache();
        return @is_file($p_filename) && !@is_link($p_filename);
    }
    // }}}

    // {{{ _openWrite()
    function _openWrite()
    {
        if ($this->_compress_type == 'gz' && function_exists('gzopen'))
            $this->_file = @gzopen($this->_tarname, "ab9"); //added 'a' for append as 'w' mode truncated the file...
        else if ($this->_compress_type == 'bz2' && function_exists('bzopen'))
            $this->_file = @bzopen($this->_tarname, "w");
        else if ($this->_compress_type == 'none')
            $this->_file = @fopen($this->_tarname, "ab");
        else
            $this->_error('Unknown or missing compression type ('
			              .$this->_compress_type.')');

        if ($this->_file == 0) {
            $this->_error('Unable to open in write mode \''
			              .$this->_tarname.'\'');
            return false;
        }

        return true;
    }
    // }}}

    // {{{ _openRead()
    function _openRead()
    {
        if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') {

          // ----- Look if a local copy need to be done
          if ($this->_temp_tarname == '') {
              $this->_temp_tarname = uniqid('tar').'.tmp';
              if (!$v_file_from = @fopen($this->_tarname, 'rb')) {
                $this->_error('Unable to open in read mode \''
				              .$this->_tarname.'\'');
                $this->_temp_tarname = '';
                return false;
              }
              if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) {
                $this->_error('Unable to open in write mode \''
				              .$this->_temp_tarname.'\'');
                $this->_temp_tarname = '';
                return false;
              }
              while ($v_data = @fread($v_file_from, 1024))
                  @fwrite($v_file_to, $v_data);
              @fclose($v_file_from);
              @fclose($v_file_to);
          }

          // ----- File to open if the local copy
          $v_filename = $this->_temp_tarname;

        } else
          // ----- File to open if the normal Tar file
          $v_filename = $this->_tarname;

        if ($this->_compress_type == 'gz')
            $this->_file = @gzopen($v_filename, "rb");
        else if ($this->_compress_type == 'bz2')
            $this->_file = @bzopen($v_filename, "r");
        else if ($this->_compress_type == 'none')
            $this->_file = @fopen($v_filename, "rb");
        else
            $this->_error('Unknown or missing compression type ('
			              .$this->_compress_type.')');

        if ($this->_file == 0) {
            $this->_error('Unable to open in read mode \''.$v_filename.'\'');
            return false;
        }

        return true;
    }
    // }}}

    // {{{ _openReadWrite()
    function _openReadWrite()
    {
        if ($this->_compress_type == 'gz')
            $this->_file = @gzopen($this->_tarname, "r+b");
        else if ($this->_compress_type == 'bz2') {
            $this->_error('Unable to open bz2 in read/write mode \''
			              .$this->_tarname.'\' (limitation of bz2 extension)');
            return false;
        } else if ($this->_compress_type == 'none')
            $this->_file = @fopen($this->_tarname, "r+b");
        else
            $this->_error('Unknown or missing compression type ('
			              .$this->_compress_type.')');

        if ($this->_file == 0) {
            $this->_error('Unable to open in read/write mode \''
			              .$this->_tarname.'\'');
            return false;
        }

        return true;
    }
    // }}}

    // {{{ _close()
    function _close()
    {
        //if (isset($this->_file)) {
        if (is_resource($this->_file)) {
            if ($this->_compress_type == 'gz')
                @gzclose($this->_file);
            else if ($this->_compress_type == 'bz2')
                @bzclose($this->_file);
            else if ($this->_compress_type == 'none')
                @fclose($this->_file);
            else
                $this->_error('Unknown or missing compression type ('
				              .$this->_compress_type.')');

            $this->_file = 0;
        }

        // ----- Look if a local copy need to be erase
        // Note that it might be interesting to keep the url for a time : ToDo
        if ($this->_temp_tarname != '') {
            @unlink($this->_temp_tarname);
            $this->_temp_tarname = '';
        }

        return true;
    }
    // }}}

    // {{{ _cleanFile()
    function _cleanFile()
    {
        $this->_close();

        // ----- Look for a local copy
        if ($this->_temp_tarname != '') {
            // ----- Remove the local copy but not the remote tarname
            @unlink($this->_temp_tarname);
            $this->_temp_tarname = '';
        } else {
            // ----- Remove the local tarname file
            @unlink($this->_tarname);
        }
        $this->_tarname = '';

        return true;
    }
    // }}}

    // {{{ _writeBlock()
    function _writeBlock($p_binary_data, $p_len=null, $finished = false)
    {
      if (is_resource($this->_file)) {
          if ($p_len === null) {
              if ($this->_compress_type == 'gz')
                  $write = @gzputs($this->_file, $p_binary_data);
              else if ($this->_compress_type == 'bz2')
                  $write = @bzwrite($this->_file, $p_binary_data);
              else if ($this->_compress_type == 'none')
                  $write = @fputs($this->_file, $p_binary_data);
              else
                  $this->_error('Unknown or missing compression type ('
				                .$this->_compress_type.')');
          } else {
              if ($this->_compress_type == 'gz')
                  $write = @gzputs($this->_file, $p_binary_data, $p_len);
              else if ($this->_compress_type == 'bz2')
                  $write = @bzwrite($this->_file, $p_binary_data, $p_len);
              else if ($this->_compress_type == 'none')
                  $write = @fputs($this->_file, $p_binary_data, $p_len);
              else
                  $this->_error('Unknown or missing compression type ('
				                .$this->_compress_type.')');

          }
	  
		  if(empty($write)){
			  $this->_error('Failed to write to the backup file. Please check you have enough disk quota available.');
			  return false;
		  }
		  
		  // If there is anything to handle for remote uploads
		  $this->remote_write_handle($finished);
      }
      return true;
    }
    // }}}
	
	function remote_write_handle($finished = false){
		global $error;
		
		// Do we have a remote file ?
		if(empty($this->_orig_tar)){
			return false;
		}
		
		clearstatcache();
		
		// Now is the size exceeding 1 MB
		if(!$finished && filesize($this->_local_tar) < 2097152){
			return false;
		}
		
		// Open the file pointer if not opened
		if(!is_resource($this->remote_fp)){
			$this->remote_fp = fopen($this->_orig_tar, "ab");
			
			if($this->remote_fp == false){
				$error['fopen_failed'] = 'Unable to open in write mode';
				softdie('fopen_failed');
			}
			
			/* // GZip Header
			fputs($this->remote_fp, "\x1F\x8B\x08\x08".pack("V", time())."\0\xFF");
			
			// Filename
			$oname = str_replace("\0", '', ltrim(basename($this->_orig_tar, '.gz'), '.'));
			fwrite($this->remote_fp, $oname."\0", 1+strlen($oname));
			
			// Create Stream
			$this->remote_fp_filter = stream_filter_append($this->remote_fp, "zlib.deflate", STREAM_FILTER_WRITE, -1);
			$this->remote_hctx = hash_init('crc32b'); */
			
			$this->remote_content_size = 0;			
			if(!empty($GLOBALS['init_pos'])){
				$this->remote_content_size = $GLOBALS['init_pos'];
			}
			
			$GLOBALS['start_pos'] = $this->remote_content_size;
		}
		
		// Close the LOCAL file
		$this->_close();
		
		// Write to remote
		$content = file_get_contents($this->_local_tar);
		$clen = strlen($content);
		
		if(!empty($content)){
			//hash_update($this->remote_hctx, $content); // Update Hash
			fwrite($this->remote_fp, $content, $clen); // Write to the stream
			$this->remote_content_size += $clen; // Update Length
		}
		$content = '';
		
		// Delete Local file
		@unlink($this->_local_tar);
		
		// ReOpen the local tar
		$this->_openWrite();
		
		// If we are done, lets delete this file
		if($finished){
			
			/* // Remove Stream
			stream_filter_remove($this->remote_fp_filter);
			
			// Calculate Hash and write it
			$crc = hash_final($this->remote_hctx, true);
			@fwrite($this->remote_fp, $crc[3].$crc[2].$crc[1].$crc[0], 4);
			
			// Also the size
			@fwrite($this->remote_fp, pack("V", $this->remote_content_size), 4); */
			
			// Close
			@fclose($this->remote_fp);
		}
	}

    // {{{ _readBlock()
    function _readBlock()
    {
      $v_block = null;
      if (is_resource($this->_file)) {
          if ($this->_compress_type == 'gz')
              $v_block = @gzread($this->_file, 512);
          else if ($this->_compress_type == 'bz2')
              $v_block = @bzread($this->_file, 512);
          else if ($this->_compress_type == 'none')
              $v_block = @fread($this->_file, 512);
          else
              $this->_error('Unknown or missing compression type ('
			                .$this->_compress_type.')');
      }
      return $v_block;
    }
    // }}}

    // {{{ _jumpBlock()
    function _jumpBlock($p_len=null)
    {
      if (is_resource($this->_file)) {
          if ($p_len === null)
              $p_len = 1;

          if ($this->_compress_type == 'gz') {
              @gzseek($this->_file, gztell($this->_file)+($p_len*512));
          }
          else if ($this->_compress_type == 'bz2') {
              // ----- Replace missing bztell() and bzseek()
              for ($i=0; $i<$p_len; $i++)
                  $this->_readBlock();
          } else if ($this->_compress_type == 'none')
              @fseek($this->_file, $p_len*512, SEEK_CUR);
          else
              $this->_error('Unknown or missing compression type ('
			                .$this->_compress_type.')');

      }
      return true;
    }
    // }}}

    // {{{ _writeFooter()
    function _writeFooter()
    {
      if (is_resource($this->_file)) {
          // ----- Write the last 0 filled block for end of archive
          $v_binary_data = pack('a1024', '');
		 if(!$this->_writeBlock($v_binary_data, null, true)){
			return false;
		 }
      }
      return true;
    }
    // }}}

    // {{{ _addList()
    function _addList($p_list, $p_add_dir, $p_remove_dir)
    {
	  
      $v_result=true;
      $v_header = array();

      // ----- Remove potential windows directory separator
      $p_add_dir = $this->_translateWinPath($p_add_dir);
      $p_remove_dir = $this->_translateWinPath($p_remove_dir, false);

      if (!$this->_file) {
          $this->_error('Invalid file descriptor');
          return false;
      }

      if (sizeof($p_list) == 0)
          return true;

      foreach ($p_list as $pvk => $v_filename) {
	  
        if (!$v_result) {
            break;
        }
		  
        if(!is_integer($pvk) && empty($v_filename)){
          
          if(preg_match('!^BACKUP_SOFT_FILES!', $pvk)){
            $GLOBALS['doing_soft_files'] = !($GLOBALS['doing_soft_files']);
            continue;
          }
          $GLOBALS['end_file'] = '';
          $GLOBALS['backup_status'] = (defined($pvk) ? constant($pvk) : $pvk);
          continue;
        }
		
        // ----- break the loop once last file is found...
        if(!empty($GLOBALS['end_file'])){
          break;
        }

        // ----- Skip the current tar name
        if ($v_filename == $this->_tarname)
            continue;

        if ($v_filename == '')
            continue;

       	// ----- ignore files and directories matching the ignore regular expression
       	if ($this->_ignore_regexp && preg_match($this->_ignore_regexp, '/'.$v_filename)) {
            $this->_warning("File '$v_filename' ignored");
       	    continue;
       	}

        if (!file_exists($v_filename) && !is_link($v_filename)) {
            $this->_warning("File '$v_filename' does not exist");
            continue;
        }
		
        // ----- Add the file or directory header
        if (!$this->_addFile($v_filename, $v_header, $p_add_dir, $p_remove_dir))
            return false;

        if (@is_dir($v_filename) && !@is_link($v_filename)) {
            if (!($p_hdir = opendir($v_filename))) {
                $this->_warning("Directory '$v_filename' can not be read");
                continue;
            }
			$p_temp_list = array();
            while (false !== ($p_hitem = readdir($p_hdir))) {
                if (($p_hitem != '.') && ($p_hitem != '..')) {
                    if ($v_filename != "."){
					  //Double slashes were added and caused issue when the backup directory is inside the installation directory.
					  $v_filename = cleanpath($v_filename);
                        $p_temp_list[0] = $v_filename.'/'.$p_hitem;
				  }else{
                        $p_temp_list[0] = $p_hitem;
				  }

					// ----- break the loop once last file is found...
					if(!empty($GLOBALS['end_file'])){
						break 2;
					}
							
					$p_skip = 0;
					
					if(!empty($GLOBALS['last_file']) && $GLOBALS['start'] == 0){
						
						foreach($p_temp_list as $_p_path){
							
							$p_skip = 0;
		
							// check last file and skip the files that have been already backed up...
							if(!empty($GLOBALS['last_file']) && $GLOBALS['start'] == 0){
								if(preg_match('#^'.$GLOBALS['last_file'].'$#', $_p_path)){
									$GLOBALS['start'] = 1; // give a jump start once the last backed up file is found..
									$p_skip = 1;
									break;
								}
							}
								
							$_p_path = rtrim($_p_path, '/');
							
							// Check if the current folder is a part of the last_file ? 
							if(!preg_match('/^'.preg_quote($_p_path, '/').'/is', $GLOBALS['last_file'])){
								$p_skip = 1;
								break;
							}
						}
					}
					
					if(empty($p_skip)){
					
						$v_result = $this->_addList($p_temp_list,
													$p_add_dir,
													$p_remove_dir);
						
					}
                }
            }

            unset($p_temp_list);
            unset($p_hdir);
            unset($p_hitem);
        }
      }

      return $v_result;
    }
    // }}}

    // {{{ _addFile()
    function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir)
    {
    
	  //echo $GLOBALS['data']['softdomain'].' '.'Adding.. '.$p_filename."\n";
				
      if (!$this->_file) {
          $this->_error('Invalid file descriptor');
          return false;
      }

      if ($p_filename == '') {
          $this->_error('Invalid file name');
          return false;
      }

      // ----- Calculate the stored filename
      $p_filename = $this->_translateWinPath($p_filename, false);
      $v_stored_filename = $p_filename;
      if (strcmp($p_filename, $p_remove_dir) == 0) {
          return true;
      }
		
	// check last file and skip the files that have been already backed up...
	if(!empty($GLOBALS['last_file']) && $GLOBALS['start'] == 0){
		if(preg_match('#^'.$GLOBALS['last_file'].'$#', $p_filename)){
			$GLOBALS['start'] = 1; // give a jump start once the last backed up file is found..
			$p_skip = 1;
		}
	}
					
	if(!empty($p_skip)){
		//echo $GLOBALS['data']['softdomain'].' '.'Skipping because of file already backed up.. '.$p_filename."\n";
		return true;
	}
	  
	// Match filename to be excluded as provided by script
	  foreach($GLOBALS['settings']['exclude_files'] as $ek => $ev){
		if(empty($GLOBALS['doing_soft_files']) && !empty($ev) && preg_match('#^'.$ev.'#', $p_filename)){
			return true;
		}
	  }
	
      if ($p_remove_dir != '') {
          if (substr($p_remove_dir, -1) != '/')
              $p_remove_dir .= '/';

          if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir)
              $v_stored_filename = substr($p_filename, strlen($p_remove_dir));
      }
      $v_stored_filename = $this->_translateWinPath($v_stored_filename);
      if ($p_add_dir != '') {
          if (substr($p_add_dir, -1) == '/')
              $v_stored_filename = $p_add_dir.$v_stored_filename;
          else
              $v_stored_filename = $p_add_dir.'/'.$v_stored_filename;
      }

      $v_stored_filename = $this->_pathReduction($v_stored_filename);

      if ($this->_isArchive($p_filename)) {
          if (($v_file = @fopen($p_filename, "rb")) == 0) {
              $this->_warning("Unable to open file '".$p_filename
			                  ."' in binary read mode");
              return true;
          }

          if (!$this->_writeHeader($p_filename, $v_stored_filename))
              return false;

          while (($v_buffer = fread($v_file, 512)) != '') {
              $v_binary_data = pack("a512", "$v_buffer");
              if(!$this->_writeBlock($v_binary_data)){
				return false;
			 }
          }

          fclose($v_file);

      } else {
          // ----- Only header for dir
          if (!$this->_writeHeader($p_filename, $v_stored_filename))
              return false;
      }
	  
	  // We can run the scripts for the end time already set
	  if(time() >= $GLOBALS['end']){
	  	$GLOBALS['end_file'] = $p_filename; // set end file so that we know where to start from
	  }
	  
      return true;
    }
    // }}}

    // {{{ _addString()
    function _addString($p_filename, $p_string)
    {
      if (!$this->_file) {
          $this->_error('Invalid file descriptor');
          return false;
      }

      if ($p_filename == '') {
          $this->_error('Invalid file name');
          return false;
      }

      // ----- Calculate the stored filename
      $p_filename = $this->_translateWinPath($p_filename, false);

      if (!$this->_writeHeaderBlock($p_filename, strlen($p_string),
	                                  time(), 384, "", 0, 0))
          return false;

      $i=0;
      while (($v_buffer = substr($p_string, (($i++)*512), 512)) != '') {
          $v_binary_data = pack("a512", $v_buffer);
          if(!$this->_writeBlock($v_binary_data)){
			return false;
		 }
      }

      return true;
    }
    // }}}

    // {{{ _writeHeader()
    function _writeHeader($p_filename, $p_stored_filename)
    {
        if ($p_stored_filename == '')
            $p_stored_filename = $p_filename;
        $v_reduce_filename = $this->_pathReduction($p_stored_filename);
		
		
		//echo $v_reduce_filename." - ";
 
		$v_reduce_filename = str_replace($GLOBALS['replace']['from'], $GLOBALS['replace']['to'], $v_reduce_filename);
		
		//echo $v_reduce_filename."<br />";

        if (strlen($v_reduce_filename) > 99) {
          if (!$this->_writeLongHeader($v_reduce_filename))
            return false;
        }
		 
		 // We need to do this before lstat() because after we write this the filesize of the softperms file will change
		 // We have to write the entries for datadir permissions softdatadir/softperms.txt
		if (isset($GLOBALS['bfh']['datadir_softperms']) && preg_match('/'.preg_quote($GLOBALS['replace']['from']['softdatadir'], '/').'/is', $p_filename)) {
			
			//echo $v_reduce_filename.' - '.trim(substr($v_reduce_filename, 12), '/').'<br />';
			
			fwrite($GLOBALS['bfh']['datadir_softperms'], trim(substr($v_reduce_filename, 12), '/')." ". (substr(sprintf('%o', fileperms($p_filename)), -4)) ."\n");

		// We have to write the entries for wwwdir permissions softwwwdir/softperms.txt
		}elseif (isset($GLOBALS['bfh']['wwwdir_softperms']) && preg_match('/'.preg_quote($GLOBALS['replace']['from']['wwwdir'], '/').'/is', $p_filename)) {
			
			//echo $v_reduce_filename.' - '.trim(substr($v_reduce_filename, 7), '/').'<br />';
			
			fwrite($GLOBALS['bfh']['wwwdir_softperms'], trim(substr($v_reduce_filename, 7), '/')." ". (substr(sprintf('%o', fileperms($p_filename)), -4)) ."\n");

		// We have to write the entries in softperms.txt
		}elseif (isset($GLOBALS['bfh']['softperms']) && preg_match('/'.preg_quote($GLOBALS['replace']['from']['softpath'], '/').'/is', $p_filename)) {
			fwrite($GLOBALS['bfh']['softperms'], trim($v_reduce_filename, '/')." ".(!empty($v_linkname) ? "linkto=".rtrim($v_linkname, '/')." " : ""). (substr(sprintf('%o', fileperms($p_filename)), -4)) ."\n"); 

		}
		
		// To make sure we have the correct data after the file is written above
		clearstatcache();
		
        $v_info = lstat($p_filename);
        $v_uid = sprintf("%07s", DecOct($v_info[4]));
        $v_gid = sprintf("%07s", DecOct($v_info[5]));
        $v_perms = sprintf("%07s", DecOct($v_info['mode'] & 000777));

        $v_mtime = sprintf("%011s", DecOct($v_info['mtime']));

        $v_linkname = '';

        if (@is_link($p_filename)) {
          $v_typeflag = '2';
          $v_linkname = readlink($p_filename);
          $v_size = sprintf("%011s", DecOct(0));
        } elseif (@is_dir($p_filename)) {
          $v_typeflag = "5";
          $v_size = sprintf("%011s", DecOct(0));
        } else {
          $v_typeflag = '0';
          clearstatcache();
          $v_size = sprintf("%011s", DecOct($v_info['size']));
        }
		
        $v_magic = 'ustar ';

        $v_version = ' ';
        
        if (function_exists('posix_getpwuid'))
        {
          $userinfo = posix_getpwuid($v_info[4]);
          $groupinfo = posix_getgrgid($v_info[5]);
          
          $v_uname = $userinfo['name'];
          $v_gname = $groupinfo['name'];
        }
        else
        {
          $v_uname = '';
          $v_gname = '';
        }

        $v_devmajor = '';

        $v_devminor = '';

        $v_prefix = '';
        $v_linkname = ''; // This is empty because we will create symlinks with our restore utility using softperms.txt

        $v_binary_data_first = pack("a100a8a8a8a12a12",
		                            $v_reduce_filename, $v_perms, $v_uid,
									$v_gid, $v_size, $v_mtime);
        $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
		                           $v_typeflag, $v_linkname, $v_magic,
								   $v_version, $v_uname, $v_gname,
								   $v_devmajor, $v_devminor, $v_prefix, '');

        // ----- Calculate the checksum
        $v_checksum = 0;
        // ..... First part of the header
        for ($i=0; $i<148; $i++)
            $v_checksum += ord(substr($v_binary_data_first,$i,1));
        // ..... Ignore the checksum value and replace it by ' ' (space)
        for ($i=148; $i<156; $i++)
            $v_checksum += ord(' ');
        // ..... Last part of the header
        for ($i=156, $j=0; $i<512; $i++, $j++)
            $v_checksum += ord(substr($v_binary_data_last,$j,1));

        // ----- Write the first 148 bytes of the header in the archive
        if(!$this->_writeBlock($v_binary_data_first, 148)){
			return false;
	    }

        // ----- Write the calculated checksum
        $v_checksum = sprintf("%06s ", DecOct($v_checksum));
        $v_binary_data = pack("a8", $v_checksum);
        if(!$this->_writeBlock($v_binary_data, 8)){
			return false;
	    }

        // ----- Write the last 356 bytes of the header in the archive
        if(!$this->_writeBlock($v_binary_data_last, 356)){
			return false;
	    }

        return true;
    }
    // }}}

    // {{{ _writeHeaderBlock()
    function _writeHeaderBlock($p_filename, $p_size, $p_mtime=0, $p_perms=0,
	                           $p_type='', $p_uid=0, $p_gid=0)
    {
        $p_filename = $this->_pathReduction($p_filename);

        if (strlen($p_filename) > 99) {
          if (!$this->_writeLongHeader($p_filename))
            return false;
        }

        if ($p_type == "5") {
          $v_size = sprintf("%011s", DecOct(0));
        } else {
          $v_size = sprintf("%011s", DecOct($p_size));
        }

        $v_uid = sprintf("%07s", DecOct($p_uid));
        $v_gid = sprintf("%07s", DecOct($p_gid));
        $v_perms = sprintf("%07s", DecOct($p_perms & 000777));

        $v_mtime = sprintf("%11s", DecOct($p_mtime));

        $v_linkname = '';

        $v_magic = 'ustar ';

        $v_version = ' ';

        if (function_exists('posix_getpwuid'))
        {
          $userinfo = posix_getpwuid($p_uid);
          $groupinfo = posix_getgrgid($p_gid);
          
          $v_uname = $userinfo['name'];
          $v_gname = $groupinfo['name'];
        }
        else
        {
          $v_uname = '';
          $v_gname = '';
        }
        
        $v_devmajor = '';

        $v_devminor = '';

        $v_prefix = '';

        $v_binary_data_first = pack("a100a8a8a8a12A12",
		                            $p_filename, $v_perms, $v_uid, $v_gid,
									$v_size, $v_mtime);
        $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
		                           $p_type, $v_linkname, $v_magic,
								   $v_version, $v_uname, $v_gname,
								   $v_devmajor, $v_devminor, $v_prefix, '');

        // ----- Calculate the checksum
        $v_checksum = 0;
        // ..... First part of the header
        for ($i=0; $i<148; $i++)
            $v_checksum += ord(substr($v_binary_data_first,$i,1));
        // ..... Ignore the checksum value and replace it by ' ' (space)
        for ($i=148; $i<156; $i++)
            $v_checksum += ord(' ');
        // ..... Last part of the header
        for ($i=156, $j=0; $i<512; $i++, $j++)
            $v_checksum += ord(substr($v_binary_data_last,$j,1));

        // ----- Write the first 148 bytes of the header in the archive
        if(!$this->_writeBlock($v_binary_data_first, 148)){
			return false;
		}

        // ----- Write the calculated checksum
        $v_checksum = sprintf("%06s ", DecOct($v_checksum));
        $v_binary_data = pack("a8", $v_checksum);
        if(!$this->_writeBlock($v_binary_data, 8)){
			return false;
		}

        // ----- Write the last 356 bytes of the header in the archive
        if(!$this->_writeBlock($v_binary_data_last, 356)){
			return false;
		}

        return true;
    }
    // }}}

    // {{{ _writeLongHeader()
    function _writeLongHeader($p_filename)
    {
        $v_size = sprintf("%11s ", DecOct(strlen($p_filename)));

        $v_typeflag = 'L';

        $v_linkname = '';

        $v_magic = '';

        $v_version = '';

        $v_uname = '';

        $v_gname = '';

        $v_devmajor = '';

        $v_devminor = '';

        $v_prefix = '';

        $v_binary_data_first = pack("a100a8a8a8a12a12",
		                            '././@LongLink', 0, 0, 0, $v_size, 0);
        $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
		                           $v_typeflag, $v_linkname, $v_magic,
								   $v_version, $v_uname, $v_gname,
								   $v_devmajor, $v_devminor, $v_prefix, '');

        // ----- Calculate the checksum
        $v_checksum = 0;
        // ..... First part of the header
        for ($i=0; $i<148; $i++)
            $v_checksum += ord(substr($v_binary_data_first,$i,1));
        // ..... Ignore the checksum value and replace it by ' ' (space)
        for ($i=148; $i<156; $i++)
            $v_checksum += ord(' ');
        // ..... Last part of the header
        for ($i=156, $j=0; $i<512; $i++, $j++)
            $v_checksum += ord(substr($v_binary_data_last,$j,1));

        // ----- Write the first 148 bytes of the header in the archive
        if(!$this->_writeBlock($v_binary_data_first, 148)){
			return false;
		}

        // ----- Write the calculated checksum
        $v_checksum = sprintf("%06s ", DecOct($v_checksum));
        $v_binary_data = pack("a8", $v_checksum);
        if(!$this->_writeBlock($v_binary_data, 8)){
			return false;
		}

        // ----- Write the last 356 bytes of the header in the archive
        if(!$this->_writeBlock($v_binary_data_last, 356)){
			return false;
		}

        // ----- Write the filename as content of the block
        $i=0;
        while (($v_buffer = substr($p_filename, (($i++)*512), 512)) != '') {
            $v_binary_data = pack("a512", "$v_buffer");
            if(!$this->_writeBlock($v_binary_data)){
				return false;
			}
        }

        return true;
    }
    // }}}

    // {{{ _readHeader()
    function _readHeader($v_binary_data, &$v_header)
    {
        if (strlen($v_binary_data)==0) {
            $v_header['filename'] = '';
            return true;
        }

        if (strlen($v_binary_data) != 512) {
            $v_header['filename'] = '';
            $this->_error('Invalid block size : '.strlen($v_binary_data));
            return false;
        }

        if (!is_array($v_header)) {
            $v_header = array();
        }
        // ----- Calculate the checksum
        $v_checksum = 0;
        // ..... First part of the header
        for ($i=0; $i<148; $i++)
            $v_checksum+=ord(substr($v_binary_data,$i,1));
        // ..... Ignore the checksum value and replace it by ' ' (space)
        for ($i=148; $i<156; $i++)
            $v_checksum += ord(' ');
        // ..... Last part of the header
        for ($i=156; $i<512; $i++)
           $v_checksum+=ord(substr($v_binary_data,$i,1));

		if (version_compare(PHP_VERSION, "5.5.0-dev") < 0) {
            $fmt = "a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" .
                "a8checksum/a1typeflag/a100link/a6magic/a2version/" .
                "a32uname/a32gname/a8devmajor/a8devminor/a131prefix";
        } else {
            $fmt = "Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/" .
                "Z8checksum/Z1typeflag/Z100link/Z6magic/Z2version/" .
                "Z32uname/Z32gname/Z8devmajor/Z8devminor/Z131prefix";
        }
        $v_data = unpack($fmt, $v_binary_data);
                         
        if (strlen($v_data["prefix"]) > 0) {
            $v_data["filename"] = "$v_data[prefix]/$v_data[filename]";
        }

        // ----- Extract the checksum
        $v_header['checksum'] = OctDec(trim($v_data['checksum']));
        if ($v_header['checksum'] != $v_checksum) {
            $v_header['filename'] = '';

            // ----- Look for last block (empty block)
            if (($v_checksum == 256) && ($v_header['checksum'] == 0))
                return true;

            $this->_error('Invalid checksum for file "'.$v_data['filename']
			              .'" : '.$v_checksum.' calculated, '
						  .$v_header['checksum'].' expected');
            return false;
        }

        // ----- Extract the properties
        $v_header['filename'] = $v_data['filename'];
        if ($this->_maliciousFilename($v_header['filename'])) {
            $this->_error('Malicious .tar detected, file "' . $v_header['filename'] .
                '" will not install in desired directory tree');
            return false;
        }
        $v_header['mode'] = OctDec(trim($v_data['mode']));
        $v_header['uid'] = OctDec(trim($v_data['uid']));
        $v_header['gid'] = OctDec(trim($v_data['gid']));
        $v_header['size'] = OctDec(trim($v_data['size']));
        $v_header['mtime'] = OctDec(trim($v_data['mtime']));
        if (($v_header['typeflag'] = $v_data['typeflag']) == "5") {
          $v_header['size'] = 0;
        }
        $v_header['link'] = trim($v_data['link']);

        return true;
    }
    // }}}

    // {{{ _maliciousFilename()
    function _maliciousFilename($file)
    {
        if (strpos($file, '/../') !== false) {
            return true;
        }
        if (strpos($file, '../') === 0) {
            return true;
        }
        return false;
    }
    // }}}

    // {{{ _readLongHeader()
    function _readLongHeader(&$v_header)
    {
      $v_filename = '';
      $n = floor($v_header['size']/512);
      for ($i=0; $i<$n; $i++) {
        $v_content = $this->_readBlock();
        $v_filename .= $v_content;
      }
      if (($v_header['size'] % 512) != 0) {
        $v_content = $this->_readBlock();
        $v_filename .= trim($v_content);
      }

      // ----- Read the next header
      $v_binary_data = $this->_readBlock();

      if (!$this->_readHeader($v_binary_data, $v_header))
        return false;

      $v_filename = trim($v_filename);
      $v_header['filename'] = $v_filename;
        if ($this->_maliciousFilename($v_filename)) {
            $this->_error('Malicious .tar detected, file "' . $v_filename .
                '" will not install in desired directory tree');
            return false;
      }

      return true;
    }
    // }}}

    // {{{ _extractInString()
    function _extractInString($p_filename)
    {
        $v_result_str = "";

        While (strlen($v_binary_data = $this->_readBlock()) != 0)
        {
          if (!$this->_readHeader($v_binary_data, $v_header))
            return null;

          if ($v_header['filename'] == '')
            continue;

          // ----- Look for long filename
          if ($v_header['typeflag'] == 'L') {
            if (!$this->_readLongHeader($v_header))
              return null;
          }

          if ($v_header['filename'] == $p_filename) {
              if ($v_header['typeflag'] == "5") {
                  $this->_error('Unable to extract in string a directory '
				                .'entry {'.$v_header['filename'].'}');
                  return null;
              } else {
                  $n = floor($v_header['size']/512);
                  for ($i=0; $i<$n; $i++) {
                      $v_result_str .= $this->_readBlock();
                  }
                  if (($v_header['size'] % 512) != 0) {
                      $v_content = $this->_readBlock();
                      $v_result_str .= substr($v_content, 0,
					                          ($v_header['size'] % 512));
                  }
                  return $v_result_str;
              }
          } else {
              $this->_jumpBlock(ceil(($v_header['size']/512)));
          }
        }

        return null;
    }
    // }}}

    // {{{ _extractList()
    function _extractList($p_path, &$p_list_detail, $p_mode,
                          $p_file_list, $p_remove_path, $p_preserve=false)
    {
    $v_result=true;
    $v_nb = 0;
    $v_extract_all = true;
    $v_listing = false;

    $p_path = $this->_translateWinPath($p_path, false);
    if ($p_path == '' || (substr($p_path, 0, 1) != '/'
	    && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':'))) {
      $p_path = "./".$p_path;
    }
    $p_remove_path = $this->_translateWinPath($p_remove_path);

    // ----- Look for path to remove format (should end by /)
    if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/'))
      $p_remove_path .= '/';
    $p_remove_path_size = strlen($p_remove_path);

    switch ($p_mode) {
      case "complete" :
        $v_extract_all = true;
        $v_listing = false;
      break;
      case "partial" :
          $v_extract_all = false;
          $v_listing = false;
      break;
      case "list" :
          $v_extract_all = false;
          $v_listing = true;
      break;
      default :
        $this->_error('Invalid extract mode ('.$p_mode.')');
        return false;
    }

    clearstatcache();

    while (strlen($v_binary_data = $this->_readBlock()) != 0)
    {
      $v_extract_file = FALSE;
      $v_extraction_stopped = 0;

      if (!$this->_readHeader($v_binary_data, $v_header))
        return false;

      if ($v_header['filename'] == '') {
        continue;
      }

      // ----- Look for long filename
      if ($v_header['typeflag'] == 'L') {
        if (!$this->_readLongHeader($v_header))
          return false;
      }

      if ((!$v_extract_all) && (is_array($p_file_list))) {
        // ----- By default no unzip if the file is not found
        $v_extract_file = false;

        for ($i=0; $i<sizeof($p_file_list); $i++) {
          // ----- Look if it is a directory
          if (substr($p_file_list[$i], -1) == '/') {
            // ----- Look if the directory is in the filename path
            if ((strlen($v_header['filename']) > strlen($p_file_list[$i]))
			    && (substr($v_header['filename'], 0, strlen($p_file_list[$i]))
				    == $p_file_list[$i])) {
              $v_extract_file = true;
              break;
            }
          }

          // ----- It is a file, so compare the file names
          elseif ($p_file_list[$i] == $v_header['filename']) {
            $v_extract_file = true;
            break;
          }
        }
      } else {
        $v_extract_file = true;
      }

      // ----- Look if this file need to be extracted
      if (($v_extract_file) && (!$v_listing))
      {
        if (($p_remove_path != '')
            && (substr($v_header['filename'], 0, $p_remove_path_size)
			    == $p_remove_path))
          $v_header['filename'] = substr($v_header['filename'],
		                                 $p_remove_path_size);
        if (($p_path != './') && ($p_path != '/')) {
          while (substr($p_path, -1) == '/')
            $p_path = substr($p_path, 0, strlen($p_path)-1);

          if (substr($v_header['filename'], 0, 1) == '/')
              $v_header['filename'] = $p_path.$v_header['filename'];
          else
            $v_header['filename'] = $p_path.'/'.$v_header['filename'];
        }
        if (file_exists($v_header['filename'])) {
          if (   (@is_dir($v_header['filename']))
		      && ($v_header['typeflag'] == '')) {
            $this->_error('File '.$v_header['filename']
			              .' already exists as a directory');
            return false;
          }
          if (   ($this->_isArchive($v_header['filename']))
		      && ($v_header['typeflag'] == "5")) {
            $this->_error('Directory '.$v_header['filename']
			              .' already exists as a file');
            return false;
          }
          if (!is_writeable($v_header['filename'])) {
			//We cannot use $globals['ofc'] here and after restoring the files we are anyways changing the file's permissions according to the perms file. Therefore, using 0644/0755 directly here shouldn't be an issue.
			if(is_dir($v_header['filename'])){
				$chmod = chmod($v_header['filename'], 0755);
			}else{
				$chmod = chmod($v_header['filename'], 0644);
			}
			if (!is_writeable($v_header['filename'])) {
				$this->_error('File '.$v_header['filename']
			              .' already exists and is write protected');
				return false;
			}
          }
          if (filemtime($v_header['filename']) > $v_header['mtime']) {
            // To be completed : An error or silent no replace ?
          }
        }

        // ----- Check the directory availability and create it if necessary
        elseif (($v_result
		         = $this->_dirCheck(($v_header['typeflag'] == "5"
				                    ?$v_header['filename']
									:dirname($v_header['filename'])))) != 1) {
            $this->_error('Unable to create path for '.$v_header['filename']);
            return false;
        }

        if ($v_extract_file) {
          if ($v_header['typeflag'] == "5") {
            if (!@file_exists($v_header['filename'])) {
                if (!@mkdir($v_header['filename'], 0777)) {
                    $this->_error('Unable to create directory {'
					              .$v_header['filename'].'}');
                    return false;
                }
            }
          } elseif ($v_header['typeflag'] == "2") {
              if (@file_exists($v_header['filename'])) {
                  @unlink($v_header['filename']);
              }
              if (!@symlink($v_header['link'], $v_header['filename'])) {
                  $this->_error('Unable to extract symbolic link {'
                                .$v_header['filename'].'}');
                  return false;
              }
          } else {
              if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) {
                  $this->_error('Error while opening {'.$v_header['filename']
				                .'} in write binary mode');
                  return false;
              } else {
                  $n = floor($v_header['size']/512);
                  for ($i=0; $i<$n; $i++) {
                      $v_content = $this->_readBlock();
                      fwrite($v_dest_file, $v_content, 512);
                  }
            if (($v_header['size'] % 512) != 0) {
              $v_content = $this->_readBlock();
              fwrite($v_dest_file, $v_content, ($v_header['size'] % 512));
            }

            @fclose($v_dest_file);
            
            if ($p_preserve) {
                @chown($v_header['filename'], $v_header['uid']);
                @chgrp($v_header['filename'], $v_header['gid']);
            }

            // ----- Change the file mode, mtime
            @touch($v_header['filename'], $v_header['mtime']);
            if ($v_header['mode'] & 0111) {
                // make file executable, obey umask
                $mode = fileperms($v_header['filename']) | (~umask() & 0111);
                @chmod($v_header['filename'], $mode);
            }
          }

          // ----- Check the file size
          clearstatcache();
          if (!is_file($v_header['filename'])) {
              $this->_error('Extracted file '.$v_header['filename']
                            .'does not exist. Archive may be corrupted.');
              return false;
          }
          
          $filesize = filesize($v_header['filename']);
          if ($filesize != $v_header['size']) {
              $this->_error('Extracted file '.$v_header['filename']
                            .' does not have the correct file size \''
                            .$filesize
                            .'\' ('.$v_header['size']
                            .' expected). Archive may be corrupted.');
              return false;
          }
          }
        } else {
          $this->_jumpBlock(ceil(($v_header['size']/512)));
        }
      } else {
          $this->_jumpBlock(ceil(($v_header['size']/512)));
      }

      if ($v_listing || $v_extract_file || $v_extraction_stopped) {
        // ----- Log extracted files
        if (($v_file_dir = dirname($v_header['filename']))
		    == $v_header['filename'])
          $v_file_dir = '';
        if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == ''))
          $v_file_dir = '/';

        // Only if we are to return the list i.e. in listContent() then we fill full $v_header else we just need the count
        $p_list_detail[$v_nb++] = (!empty($v_listing) ? $v_header : '');
        if (is_array($p_file_list) && (count($p_list_detail) == count($p_file_list))) {
            return true;
        }
      }
    }

        return true;
    }
    // }}}

    // {{{ _openAppend()
    function _openAppend()
    {
        if (filesize($this->_tarname) == 0)
          return $this->_openWrite();

        if ($this->_compress) {
            $this->_close();

            if (!@rename($this->_tarname, $this->_tarname.".tmp")) {
                $this->_error('Error while renaming \''.$this->_tarname
				              .'\' to temporary file \''.$this->_tarname
							  .'.tmp\'');
                return false;
            }

            if ($this->_compress_type == 'gz')
                $v_temp_tar = @gzopen($this->_tarname.".tmp", "rb");
            elseif ($this->_compress_type == 'bz2')
                $v_temp_tar = @bzopen($this->_tarname.".tmp", "r");

            if ($v_temp_tar == 0) {
                $this->_error('Unable to open file \''.$this->_tarname
				              .'.tmp\' in binary read mode');
                @rename($this->_tarname.".tmp", $this->_tarname);
                return false;
            }

            if (!$this->_openWrite()) {
                @rename($this->_tarname.".tmp", $this->_tarname);
                return false;
            }

            if ($this->_compress_type == 'gz') {
                $end_blocks = 0;
                
                while (!@gzeof($v_temp_tar)) {
                    $v_buffer = @gzread($v_temp_tar, 512);
                    if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) {
                        $end_blocks++;
                        // do not copy end blocks, we will re-make them
                        // after appending
                        continue;
                    } elseif ($end_blocks > 0) {
                        for ($i = 0; $i < $end_blocks; $i++) {
                            if(!$this->_writeBlock(ARCHIVE_TAR_END_BLOCK)){
								return false;
						  }
                        }
                        $end_blocks = 0;
                    }
                    $v_binary_data = pack("a512", $v_buffer);
                    if(!$this->_writeBlock($v_binary_data)){
						return false;
				  }
                }

                @gzclose($v_temp_tar);
            }
            elseif ($this->_compress_type == 'bz2') {
                $end_blocks = 0;
                
                while (strlen($v_buffer = @bzread($v_temp_tar, 512)) > 0) {
                    if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) {
                        $end_blocks++;
                        // do not copy end blocks, we will re-make them
                        // after appending
                        continue;
                    } elseif ($end_blocks > 0) {
                        for ($i = 0; $i < $end_blocks; $i++) {
                            if(!$this->_writeBlock(ARCHIVE_TAR_END_BLOCK)){
							return false;
						  }
                        }
                        $end_blocks = 0;
                    }
                    $v_binary_data = pack("a512", $v_buffer);
                    if(!$this->_writeBlock($v_binary_data)){
					return false;
				  }
                }

                @bzclose($v_temp_tar);
            }

            if (!@unlink($this->_tarname.".tmp")) {
                $this->_error('Error while deleting temporary file \''
				              .$this->_tarname.'.tmp\'');
            }

        } else {
            // ----- For not compressed tar, just add files before the last
			//       one or two 512 bytes block
            if (!$this->_openReadWrite())
               return false;

            clearstatcache();
            $v_size = filesize($this->_tarname);

            // We might have zero, one or two end blocks.
            // The standard is two, but we should try to handle
            // other cases.
            fseek($this->_file, $v_size - 1024);
            if (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) {
                fseek($this->_file, $v_size - 1024);
            }
            elseif (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) {
                fseek($this->_file, $v_size - 512);
            }
        }

        return true;
    }
    // }}}

    // {{{ _append()
    function _append($p_filelist, $p_add_dir='', $p_remove_dir='')
    {
        if (!$this->_openAppend())
            return false;

        if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir))
           $this->_writeFooter();

        $this->_close();

        return true;
    }
    // }}}

    // {{{ _dirCheck()
    function _dirCheck($p_dir)
    {
        clearstatcache();
        if ((@is_dir($p_dir)) || ($p_dir == ''))
            return true;

        $p_parent_dir = dirname($p_dir);

        if (($p_parent_dir != $p_dir) &&
            ($p_parent_dir != '') &&
            (!$this->_dirCheck($p_parent_dir)))
             return false;

        if (!@mkdir($p_dir, 0777)) {
            $this->_error("Unable to create directory '$p_dir'");
            return false;
        }

        return true;
    }

    // }}}

    // {{{ _pathReduction()
    function _pathReduction($p_dir)
    {
        $v_result = '';

        // ----- Look for not empty path
        if ($p_dir != '') {
            // ----- Explode path by directory names
            $v_list = explode('/', $p_dir);

            // ----- Study directories from last to first
            for ($i=sizeof($v_list)-1; $i>=0; $i--) {
                // ----- Look for current path
                if ($v_list[$i] == ".") {
                    // ----- Ignore this directory
                    // Should be the first $i=0, but no check is done
                }
                else if ($v_list[$i] == "..") {
                    // ----- Ignore it and ignore the $i-1
                    $i--;
                }
                else if (   ($v_list[$i] == '')
				         && ($i!=(sizeof($v_list)-1))
						 && ($i!=0)) {
                    // ----- Ignore only the double '//' in path,
                    // but not the first and last /
                } else {
                    $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?'/'
					            .$v_result:'');
                }
            }
        }
        
        if (defined('OS_WINDOWS') && OS_WINDOWS) {
            $v_result = strtr($v_result, '\\', '/');
        }
        
        return $v_result;
    }

    // }}}

    // {{{ _translateWinPath()
    function _translateWinPath($p_path, $p_remove_disk_letter=true)
    {
      if (defined('OS_WINDOWS') && OS_WINDOWS) {
          // ----- Look for potential disk letter
          if (   ($p_remove_disk_letter)
		      && (($v_position = strpos($p_path, ':')) != false)) {
              $p_path = substr($p_path, $v_position+1);
          }
          // ----- Change potential windows directory separator
          if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) {
              $p_path = strtr($p_path, '\\', '/');
          }
      }
      return $p_path;
    }
    // }}}
}

error_reporting(E_ALL);

function rmdir_recursive_fn($path){
	
	$path = (substr($path, -1) == '/' || substr($path, -1) == '\\' ? $path : $path.'/');
	
	resetfilelist();
	
	$files = filelist_fn($path, 1, 0, 'all');
	$files = (!is_array($files) ? array() : $files);
	
	//First delete the files only
	foreach($files as $k => $v){
		@chmod($k, 0777);
		if(file_exists($k) && is_file($k) && @filetype($k) == "file"){
			@unlink($k);
		}
	}
	
	@clearstatcache();
	
	$folders = filelist_fn($path, 1, 1, 'all');
	$folders = (!is_array($folders) ? array() : $folders);
	@krsort($folders);

	//Now Delete the FOLDERS
	foreach($folders as $k => $v){
		@chmod($k, 0777);
		if(is_dir($k)){
			@rmdir($k);
		}
	}
	
	@rmdir($path);
	
	@clearstatcache();

}

function filelist_fn($startdir="./", $searchSubdirs=1, $directoriesonly=0, $maxlevel="all", $level=1, $reset = 1) {
   //list the directory/file names that you want to ignore
   $ignoredDirectory[] = ".";
   $ignoredDirectory[] = "..";
   $ignoredDirectory[] = "_vti_cnf";
   global $directorylist;    //initialize global array
   
   if(substr($startdir, -1) != '/'){
		$startdir = $startdir.'/';
	}
   
   if (is_dir($startdir)) {
       if ($dh = opendir($startdir)) {
           while (($file = readdir($dh)) !== false) {
               if (!(array_search($file,$ignoredDirectory) > -1)) {
                 if (@filetype($startdir . $file) == "dir") {
                     
					   //build your directory array however you choose;
                       //add other file details that you want.
					   
                       $directorylist[$startdir . $file]['level'] = $level;
                       $directorylist[$startdir . $file]['dir'] = 1;
                       $directorylist[$startdir . $file]['name'] = $file;
                       $directorylist[$startdir . $file]['path'] = $startdir;
                       if ($searchSubdirs) {
                           if ((($maxlevel) == "all") or ($maxlevel > $level)) {
                               filelist_fn($startdir . $file . "/", $searchSubdirs, $directoriesonly, $maxlevel, ($level + 1), 0);
                           }
                       }
					  
					   
                   } else {
                       if (!$directoriesonly) {
					     
					  //  echo substr(strrchr($file, "."), 1);
                           //if you want to include files; build your file array 
                           //however you choose; add other file details that you want.
                         $directorylist[$startdir . $file]['level'] = $level;
                         $directorylist[$startdir . $file]['dir'] = 0;
                         $directorylist[$startdir . $file]['name'] = $file;
                         $directorylist[$startdir . $file]['path'] = $startdir;
						  
					 
     }}}}
           closedir($dh);
}}

if(!empty($reset)){
	$r = $directorylist;
	$directorylist = array();
	return($r);
}
}

function tar_archive($tarname, $file_list, $handle_remote = false){
	global $globals;
	
	$tar_archive = new softtar($tarname, '', $handle_remote);
	
	$res = $tar_archive->createModify($file_list, '', '');
	
	if(!$res){
		return false;	
	}
	
	return true;
}

function resetfilelist(){
global $directorylist;
	$directorylist = array();
}

function softdie($txt, $l_file = ''){
	
	global $data, $can_write;
	
	$array = array();
	$array['settings'] = $GLOBALS['settings'];
	$array['result'] = $txt;
	$array['data'] = $GLOBALS['data'];
	$array['backup_status'] = $GLOBALS['backup_status'];
	
	// Add last backed up file to the array if the process is still INCOMPLETE
	if(!empty($l_file)){
		$array['l_file'] = $l_file;
		
		if(!empty($GLOBALS['init_data'])){
			$array['init_data'] = $GLOBALS['init_data'];
		}
		
		if(!empty($GLOBALS['start_pos'])){
			$array['init_pos'] = $GLOBALS['start_pos'];
		}
		
		// AWS stuff
		if(!empty($GLOBALS['awss3_part_no'])){
			$array['awss3_part_no'] = $GLOBALS['awss3_part_no'];
		}
		
		// AWS stuff
		if(!empty($GLOBALS['awss3_etags'])){
			$array['awss3_etags'] = $GLOBALS['awss3_etags'];
		}

	}
	
	if($txt == 'DONE'){
		$array['size'] = filesize($GLOBALS['successfile']);
		
		/////Temp solution for non-suphp servers
		if(!$can_write){
			$array['cant_write'] = 1;
		}
	}
	
	// Was there an error ?
	if(!empty($GLOBALS['error'])){
		$array['error'] = $GLOBALS['error'];
	}
	
	echo '<aefer>'.base64_encode(serialize($array)).'</aefer>';die();
}

// Clean the Backup files
function backup_clean($data){
	
	foreach($GLOBALS['bfh'] as $v){
		@fclose($v);
	}
	
	// Delete tmp/ folder only if the process was completed
	if($GLOBALS['backup_status'] == BACKUP_COMPLETE){
		rmdir_recursive_fn($data['path'].'/tmp/'.$data['name']);
	}
	
	if(file_exists($data['path'].'/tmp/'.$data['name'].'/softver.txt')){
		@unlink($data['path'].'/tmp/'.$data['name'].'/softver.txt');
	}
	
	return false;
	
}

// Delete old failed backups
function delete_failed_backups(){
	
	global $data, $tmpdir;
	
	$start_time = time();
	
	$backup_path = $data['path'].'/';
	
	$raw_files = filelist_fn($backup_path, 0);
	// r_print($raw_files);
	
	if(!empty($raw_files)){
	
		//for dot'.' file
		$dotfile_deleted_count = 0;
		$dot_delete_limit = 100; // set max 100
		
		//for 'tmp' folder
		$tmpfile_deleted_count = 0;
		$tmp_delete_limit = 100; // set max 100
		
		$one_hr = time() - 3600; // 1 hour
		
		$delete_time_limit = $start_time + 10;
		
		foreach($raw_files as $raw_k => $raw_v){
			
			if(($dotfile_deleted_count >= $dot_delete_limit) || (time() > $delete_time_limit)){
				break;
			}
			
			if(empty($raw_v['dir']) && strpos($raw_v['name'], '.') === 0 && preg_match('/^\..*\.tar\.gz$/', $raw_v['name'])){
				
				$filetime = filemtime($raw_k);
				
				if($filetime < $one_hr){
					// echo 'deleting - '.$raw_k.'<br />';
					unlink($raw_k);
					$dotfile_deleted_count++;
				}
			}
			
		}
		
		if((time() > $delete_time_limit)){
			return true;
		}
	
		//to delete tmp folder data
		$raw_files_tmp = filelist_fn($backup_path.'tmp/', 0);
		// r_print($raw_files_tmp);
		
		if(!empty($raw_files_tmp)){
			
			foreach($raw_files_tmp as $tmp_k => $tmp_v){
			
				if(($tmpfile_deleted_count >= $tmp_delete_limit) || (time() > $delete_time_limit)){
					break;
				}
				
				if(empty($tmp_v['name'])){
					continue;
				}
				
				$tmp_filetime = filemtime($tmp_k);
					
				if($tmp_filetime < $one_hr){
					// echo 'deleting tmp files - '.$tmp_k.' => '.$tmp_filetime.' <br />';
					if(empty($tmp_v['dir'])){
						unlink($tmp_k);
					}else{
						rmdir_recursive_fn($tmp_k);
					}
					
					$tmpfile_deleted_count++;
				}
				
			}
		}
	}
}

function optGET($name, $default = ''){

global $error;

	//Check the GETED NAME was GETed
	if(isset($_GET[$name])){
	
		return inputsec(htmlizer(trim($_GET[$name])));
		
	}else{
		
		return $default;
	
	}

}

function optREQ($name, $default = ''){

global $error;

	//Check the POSTED NAME was posted
	if(isset($_REQUEST[$name])){
	
		return inputsec(htmlizer(trim($_REQUEST[$name])));
		
	}else{
		
		return $default;
	
	}

}

function inputsec($string){
	
	//get_magic_quotes_gpc is depricated in php 7.4
	if(version_compare(PHP_VERSION, '7.4', '<')){
		if(!get_magic_quotes_gpc()){
		
			$string = addslashes($string);
		
		}else{
		
			$string = stripslashes($string);
			$string = addslashes($string);
		
		}
	}else{
		$string = addslashes($string);
	}
	
	// This is to replace ` which can cause the command to be executed in exec()
	$string = str_replace('`', '\`', $string);
	
	return $string;

}

function htmlizer($string){

global $globals;

	$string = htmlentities($string, ENT_QUOTES, @$globals['charset']);
	
	preg_match_all('/(&amp;#(\d{1,7}|x[0-9a-fA-F]{1,6});)/', $string, $matches);//r_print($matches);
	
	foreach($matches[1] as $mk => $mv){		
		$tmp_m = entity_check($matches[2][$mk]);
		$string = str_replace($matches[1][$mk], $tmp_m, $string);
	}
	
	return $string;
	
}

function entity_check($string){
	
	//Convert Hexadecimal to Decimal
	$num = ((substr($string, 0, 1) === 'x') ? hexdec(substr($string, 1)) : (int) $string);
	
	//Squares and Spaces - return nothing 
	$string = (($num > 0x10FFFF || ($num >= 0xD800 && $num <= 0xDFFF) || $num < 0x20) ? '' : '&#'.$num.';');
	
	return $string;
			
}

function cleanpath($path){
	
	// This was made for Locaweb since they have network paths starting with \\
	/* if(function_exists('override_cleanpath')){
		return override_cleanpath($path);
	} */
	
	$path = str_replace('\\\\', '/', $path);
	$path = str_replace('\\', '/', $path);
	return rtrim($path, '/');
}

function soft_stream_wrapper_register($protocol, $classname){
	
	$protocols = array('dropbox', 'gdrive', 'softftpes', 'softsftp', 'webdav','softaws', 'onedrive');
	
	if(!in_array($protocol, $protocols)){
		return true;
	}
	
	@include_once('_'.$protocol.'.php');
	
	if(!stream_wrapper_register($protocol, $classname)){
		return false;
	}
	
	return true;
}

function soft_preg_replace($pattern, $file, &$var, $valuenum, $stripslashes = ''){	
	preg_match($pattern, $file, $matches);
	if(empty($stripslashes)){
		$var = @trim($matches[$valuenum]);
	}else{
		$var = @stripslashes(trim($matches[$valuenum]));
	}
}

function r_print($array){

	echo '<pre>';
	print_r($array);
	echo '</pre>';

}

@unlink(__FILE__); // More has to be done here ! 

// Do we need to rename the .htaccess back ? 
if(file_exists('soft_htaccess')){
	@rename('soft_htaccess', '.htaccess');
}

// The settings
$settings = unserialize(base64_decode('[[[settings]]]'));
$data = unserialize(base64_decode('[[[data]]]'));
$software = unserialize(base64_decode('[[[software]]]'));
$app = unserialize(base64_decode('[[[app]]]'));

define('APP', $app);

// Check the progress
define('BACKUP_SQL', 1);
define('BACKUP_DIR', 2);
define('BACKUP_DATADIR', 3);
define('BACKUP_WWWDIR', 4);
define('BACKUP_COMPLETE', 5);

$remote_location = unserialize(base64_decode('[[[remote_location]]]'));

global $user, $globals, $theme, $softpanel, $settings, $iscripts, $catwise, $error, $can_write;

$can_write = can_create_file(); // Check if we can write

$name = $data['name'];
//$path = $data['path'];
$tmpdir = $data['path'].'/tmp';

if(!empty($remote_location)){
	$path = $remote_location['full_backup_loc'];		
	$zipfile = $path.'/.'.$name.'.tar';
	$successfile = $path.'/'.$name.'.tar';
	// For OneDrive and AWS
	$GLOBALS['local_dest'] = $data['path'];

	soft_stream_wrapper_register($remote_location['protocol'], $remote_location['protocol']);
}else{
	/////Temp solution for non-suphp servers
	$path = ((!$can_write) ? $tmpdir : $data['path']);
	$zipfile = $path.'/.'.$name.'.tar.gz';
	$successfile = $path.'/'.$name.'.tar.gz';
}

$f_list = array(); // Files/Folder which has to be added to the tar.gz

// We need to stop execution in 25 secs.. We will be called again if the process is incomplete
//Increased the exec time to 500 temporarily as Backup on remote in aefer is creating incorrectly compressed file when the backups executes in loop

// Is custom keep alive time defained ? 
if(!empty($settings['SOFTACULOUS_KEEP_ALIVE'])){
	$keepalive = (int) $settings['SOFTACULOUS_KEEP_ALIVE'];
}

// Set default value
if(empty($keepalive)){
	$keepalive = 25;
}

$GLOBALS['end'] = (int) time() + $keepalive;

// Empty last file everytime as a precaution
$GLOBALS['last_file'] = '';
$GLOBALS['last_file'] = optREQ('last_file');
if(!empty($GLOBALS['last_file'])){
	$GLOBALS['last_file'] = rawurldecode($GLOBALS['last_file']);
}

$GLOBALS['backup_status'] = (int) optREQ('backup_status');

if($GLOBALS['backup_status'] < 1){
	delete_failed_backups();
}

$GLOBALS['sinit_data'] = '';
$GLOBALS['sinit_data'] = optGET('init_data');
if(!empty($GLOBALS['sinit_data'])){
	$GLOBALS['sinit_data'] = base64_decode($GLOBALS['sinit_data']);
}
$GLOBALS['init_data'] = $GLOBALS['sinit_data'];

$GLOBALS['init_pos'] = 0;
$GLOBALS['init_pos'] = (int) optGET('init_pos');
$GLOBALS['start_pos'] = $GLOBALS['init_pos'];

// AWS Stuff
$GLOBALS['awss3_part_no'] = (int) optGET('awss3_part_no');
if(empty($GLOBALS['awss3_part_no'])){
	$GLOBALS['awss3_part_no'] = 1;
}

// AWS Stuff
$GLOBALS['awss3_etags'] = optGET('awss3_etags');
if(!empty($GLOBALS['awss3_etags'])){
	$GLOBALS['awss3_etags'] = unserialize(base64_decode($GLOBALS['awss3_etags']));
}else{
	$GLOBALS['awss3_etags'] = [];
}

$GLOBALS['doing_soft_files'] = false;

//Backup the DATABASE
// If $GLOBALS['last_file'] is not empty that means the database has been already exported and added to tar
if(!empty($data['backup_db']) && !empty($data['softdb']) && $GLOBALS['backup_status'] < BACKUP_SQL){
	
	// Store the progress
	//soft_progress($data['ssk'], 15, __('Backing up the Database'));
	$GLOBALS['progress'][1] = 15;
	
	$dbfile = $tmpdir.'/'.$name.'/softsql.sql';
	
	$f_list['BACKUP_SOFT_FILES_1'] = '';
	$f_list[] = $dbfile;
	$f_list['BACKUP_SQL'] = '';
	$f_list['BACKUP_SOFT_FILES_2'] = '';
	
	$GLOBALS['replace']['from']['softsql'] = $dbfile;
	$GLOBALS['replace']['to']['softsql'] = 'softsql.sql';
	
	$dbuser = (empty($softpanel->backupmysql['user']) ? $data['softdbuser'] : $softpanel->backupmysql['user']);
	$dbpass = (empty($softpanel->backupmysql['pass']) ? $data['softdbpass'] : $softpanel->backupmysql['pass']);
	
	$__settings['softurl'] = 'http://'.$data['softdomain'];
	
	if(!empty($data['mysql_dump']) && function_exists('exec')){
		
		exec('mysqldump -h '.escapeshellarg($data['softdbhost']).' -u '.escapeshellarg($dbuser).' -p'.escapeshellarg($dbpass).' '.escapeshellarg($data['softdb']).' > '.escapeshellarg($dbfile));
	}
	
	if(!file_exists($dbfile) || empty(filesize($dbfile)) ){
		
		$sql_conn = soft_mysql_connect($data['softdbhost'], $dbuser, $dbpass);
			
		if(!$sql_conn){
			$error['mysql_connect'] = 'Cannot connect mysql.';
			softdie('conn');
		}
		
		$sel = soft_mysql_select_db($data['softdb'], $sql_conn);
			
		if(!$sel){
			$error['mysql_sel_db'] = 'Could not select the database';
			softdie('conn');
		}
		
		$host = $data['softdbhost'];
		$user = $data['softdbuser'];
		$pass = $data['softdbpass'];
		$db = $data['softdb'];

		//include_once('mysql_functions.php');
		
		if(!backup_mysql_fn($host, $user, $pass, $db, $dbfile)){
			$error[] = 'Back up was not successful';
			softdie('conn');
		}
	}
	
	if(!file_exists($dbfile)){
		$error['backup_db'] = 'Could not create sql file from database.';
		return backup_clean($data);
	}
}

//Backup the DIRECTORY
if(!empty($data['backup_dir']) && $GLOBALS['backup_status'] < BACKUP_DIR){
		
	// Store the progress
	//soft_progress($data['ssk'], 45, $progress_data);
	$GLOBALS['progress'][2] = 45;
	
	// Save the version
	$GLOBALS['bfh']['softver'] = @fopen($data['path'].'/tmp/'.$data['name'].'/softver.txt','a');
	@fwrite($GLOBALS['bfh']['softver'], $data['ver']);
	
	$GLOBALS['replace']['from']['softver'] = $data['path'].'/tmp/'.$data['name'].'/softver.txt';
	$GLOBALS['replace']['to']['softver'] = 'softver.txt';
		
	if(!empty($data['select_files_backup']) && !empty($data['fileindex'])){
		$_root_filelist = filelist_fn(cleanpath($data['softpath']), 0);
		$root_filelist = array();
		
		// Lets get the full paths in fileindex
		$full_fileindex = array();
		foreach($data['fileindex'] as $sfk => $sfv){
			$full_fileindex[] = trim(cleanpath($data['softpath'])).'/'.$sfv;
		}
		
		foreach($_root_filelist as $rk => $rv){
			$tmp_rk = cleanpath($rk);
			$tmp_rv = $rv;
			// Do we need to exclude the files ? 
			if(!empty($data['select_files_backup']) && !in_array(trim($tmp_rk), $full_fileindex)){
				continue;
			}
			
			$tmp_rv['path'] = cleanpath($rv['path']);
			$root_filelist[$tmp_rk] = $tmp_rv;
		}
		
		$final_filelist = array_keys($root_filelist);
		//r_print($final_filelist);
		
		foreach($final_filelist as $fk => $fv){
			$f_list[] = $fv;
		}
		
	}else{
		// Adding the directory in $f_list to add to tar
		$f_list[] = $data['softpath'].'/';
	}
	
	$f_list['BACKUP_DIR'] = '';
	
	// File Permission
	$GLOBALS['bfh']['softperms'] = @fopen($data['path'].'/tmp/'.$data['name'].'/softperms.txt','a');
	
	$GLOBALS['replace']['from']['softperms'] = $data['path'].'/tmp/'.$data['name'].'/softperms.txt';
	$GLOBALS['replace']['to']['softperms'] = 'softperms.txt';
	
	//Did it open the File Stream
	if(!$GLOBALS['bfh']['softperms']){
		$error[] = 'There were errors while trying to make a file of permissions';
		backup_clean($data);
		softdie('permdir');
	}
	
	// The directory itself
	@fwrite($GLOBALS['bfh']['softperms'], '/ '.@substr(sprintf('%o', fileperms($data['softpath'])), -4)."\n");
	
}

// Backup the datadir as well	
if(!empty($data['backup_datadir']) && !empty($data['softdatadir']) && $GLOBALS['backup_status'] < BACKUP_DATADIR){
	
	// Store the progress
	//soft_progress($data['ssk'], 85, __('Backing up the Data Directory'));	
	$GLOBALS['progress'][3] = 85;
	
	// Adding the directory in $f_list to add to tar
	$f_list[] = $data['softdatadir'].'/';
	$f_list['BACKUP_DATADIR'] = '';
	
	$GLOBALS['replace']['from']['softdatadir'] = $data['softdatadir'].'/';
	$GLOBALS['replace']['to']['softdatadir'] = 'softdatadir/';
	
	// We now create the file for storing file permission of datadir. We create here handler for the file.
	// Data will be written in class file of tar.
	$GLOBALS['bfh']['datadir_softperms'] = fopen($data['path'].'/tmp/'.$data['name'].'/datadir_softperms.txt','a');
	
	$GLOBALS['replace']['from']['datadir_softperms'] = $data['path'].'/tmp/'.$data['name'].'/datadir_softperms.txt';
	$GLOBALS['replace']['to']['datadir_softperms'] = 'softdatadir/softperms.txt';
	
	//Did it open the File Stream
	if(!$GLOBALS['bfh']['datadir_softperms']){
		$error['err_dataperm_file'] = 'There were errors while trying to make a file of permissions of the data directory';
		backup_clean($data);
		softdie('permdatadir');
	}
	
	// The directory itself
	@fwrite($GLOBALS['bfh']['datadir_softperms'], '/ '.@substr(sprintf('%o', fileperms($data['softdatadir'])), -4)."\n");
}


// Backup the Web directory as well	
if(!empty($data['backup_wwwdir']) && !empty($data['wwwdir']) && $GLOBALS['backup_status'] < BACKUP_WWWDIR){
	
	// Store the progress
	//soft_progress($data['ssk'], 90, __('Backing up the Data Directory'));	
	$GLOBALS['progress'][4] = 90;
	
	// Adding the directory in $f_list to add to tar
	$f_list[] = $data['wwwdir'].'/';
	$f_list['BACKUP_WWWDIR'] = '';
	$GLOBALS['replace']['from']['wwwdir'] = $data['wwwdir'].'/';
	$GLOBALS['replace']['to']['wwwdir'] = 'wwwdir/';
	
	// We now create the file for storing file permission of datadir. We create here handler for the file.
	// Data will be written in class file of tar.
	$GLOBALS['bfh']['wwwdir_softperms'] = fopen($data['path'].'/tmp/'.$data['name'].'/wwwdir_softperms.txt','a');
	
	$GLOBALS['replace']['from']['wwwdir_softperms'] = $data['path'].'/tmp/'.$data['name'].'/wwwdir_softperms.txt';
	$GLOBALS['replace']['to']['wwwdir_softperms'] = 'wwwdir/softperms.txt';
	
	//Did it open the File Stream
	if(!$GLOBALS['bfh']['wwwdir_softperms']){
		$error['err_wwwperm_file'] = 'There were errors while trying to make a file of permissions of the WEB directory';
		backup_clean($data);
		softdie('permwebdir');
	}
	
	// The directory itself
	@fwrite($GLOBALS['bfh']['wwwdir_softperms'], '/ '.@substr(sprintf('%o', fileperms($data['wwwdir'])), -4)."\n");
	
}

// This is done at the end to make sure we have added all possible replace paths before the softpath
if(!empty($data['backup_dir'])){
		
	$GLOBALS['replace']['from']['softpath'] = $data['softpath'].'/';
	$GLOBALS['replace']['to']['softpath'] = '';
	
}

$f_list['BACKUP_SOFT_FILES_3'] = '';

// Now we will have to add the permission file to the end os an array of directory list.
if(!empty($GLOBALS['bfh']['softperms'])){
	$f_list[] = $data['path'].'/tmp/'.$data['name'].'/softperms.txt';
}

if(!empty($GLOBALS['bfh']['datadir_softperms'])){
	$f_list[] = $data['path'].'/tmp/'.$data['name'].'/datadir_softperms.txt';
}

if(!empty($GLOBALS['bfh']['wwwdir_softperms'])){
	$f_list[] = $data['path'].'/tmp/'.$data['name'].'/wwwdir_softperms.txt';
}

if(!empty($GLOBALS['bfh']['softver'])){
	$f_list[] = $data['path'].'/tmp/'.$data['name'].'/softver.txt';
}

$f_list['BACKUP_SOFT_FILES_4'] = '';

$f_list['BACKUP_COMPLETE'] = '';

if(empty($error) && !empty($f_list)){
	
	// Set default values
	$GLOBALS['start'] = 0;
	$GLOBALS['end_file'] = '';
	
	if(!tar_archive($zipfile, $f_list, true)){
		backup_clean($data);
		$error['backup_dir'] = 'The backup utility could not back up the files.';
		@unlink($zipfile);
		softdie('failbackup');
	}
}

if(!empty($error)){
	@unlink($zipfile);
	backup_clean($data);
	softdie('failbackup');
}

@print_r($error);

// CHMOD it to something Safe
@chmod($zipfile, 0600);

backup_clean($data);

// Is the backup process INCOMPLETE ? 
if($GLOBALS['backup_status'] != BACKUP_COMPLETE){
	
	// This is to notify that the backup is in progress and change the tmp folder's mtime
	$mtime_file = $data['path'].'/tmp/'.$data['name'].'/'.$data['name'].'.txt';
	file_put_contents($mtime_file, '');
	unlink($mtime_file);
	
	softdie('INCOMPLETE-'.$GLOBALS['progress'][$GLOBALS['backup_status']], $GLOBALS['end_file']);
}

// Rename the ZIP file
@rename($zipfile, $successfile);

softdie('DONE');
