<?php
/**
 * N.U.C.O.K.E. Main central development core
 * © Hsilamot 2014
 * developed by Hidalgo Rionda
 * This Core is designed to be used as a full Apache Alias
 * this means / should redirect to /path/to/script/api.php
 *
 * i'm making this collection of classes and examples to make it simple for people to create
 * advanced PHP sites, since most of us want friendly URL's and to avoid copy/paste of our code
 * i'm trying to create a set of standard libraries which can be used to create powerful sites.
 *
 * this is the first release of NuCoKe collection
 *
 * +----------------------------------------------------------------------+
 * |                       N.U.C.O.K.E. v 2.2.1977                        |
 * |             released under Creative Commons BY-NC-SA 3.0             |
 * |          http://creativecommons.org/licenses/by-nc-sa/3.0/           |
 * +----------------------------------------------------------------------+
 *
 * +----------------------------------------------------------------------+
 * |                              changelog                               |
 * | v1.0.1 (Hsilamot) first release                                      |
 * | v2.0.1649 (Hsilamot) second release                                  |
 * | v2.1.1802 (Hsilamot) third release with &inherit                     |
 * | v2.2.1977 (Hsilamot) fourth release with html themes support         |
 * +----------------------------------------------------------------------+
 *
 * remember ALL SQL sentences must be formed with single quote characters ->'<- NOT double ->"<-
 * THIS IS PRE-BETA, it is not complete yet.
 * Also remember to vaccine all customer SQL sentences
 *
 * @access public
 * @author Hsilamot <php@hsilamot.com>
 * @license http://creativecommons.org/licenses/by-nc-sa/3.0/
 * @version 2.2.1977
 */
if (get_magic_quotes_gpc()) {
	/* this will remove Magic Quotes */
	$process = array(&$_GET, &$_POST, &$_COOKIE, &$_REQUEST);
	while (list($key, $val) = each($process)) {
		foreach ($val as $k => $v) {
			unset($process[$key][$k]);
			if (is_array($v)) {
				$process[$key][stripslashes($k)] = $v;
				$process[] = &$process[$key][stripslashes($k)];
			} else {
				$process[$key][stripslashes($k)] = stripslashes($v);
			}
		}
	}
	unset($process);
}
class NuCoKe {
	/**
	 * database configurations
	 * @access public
	 * @var array data for the database configurations
	 */
	private $db=array();
	/**
	 * main config array containing all the configuration of the class
	 * @access protected
	 * @var array config variables
	 */
	protected $config=array();
	/**
	 * error log
	 * @access public
	 * @var array data for the errors
	 */
	private $errors=array();
	/**
	 * integer containing the http response code
	 * @access public
	 * @var int response code for http request
	 */
	public $http_code=200;
	/**
	 * this array contains the cookies to be sent to the browser, once sent they can be modified but will not be resent
	 * @access private
	 * @var array cookies
	 */
	private $http_cookies=array();
	/**
	 * this array contains the headers to be sent to the browser, once sent they can be modified but will not be resent
	 * @access private
	 * @var array headers to return to the browser
	 */
	private $http_headers=array();
	/**
	 * is class initialized
	 * @access private
	 * @var bool is class initialized
	 */
	private $init=false;
	/**
	 * The actual full URL of the user
	 * @access public
	 * @var string full URL
	 */
	public $url;
	/**
	 * The UserAgent of the class
	 * @access protected
	 * @var array data of the user agent
	 */
	protected $user_agent=array();
	/**
	 * for multiple purpose use, will save and reload
	 * @access protected
	 * @var array multiple purpose use
	 */
	protected $var=array();
	/**
	 * will initialize the class
	 * @access public
	 * @version 1.0.101
	 * @param array $config array containing any extra configuration from the user
	 * @param object $nucoke previously initialized nucoke
	 * @return boolean if success
	 */
	public function __construct($config=array(),&$nucoke=null) {
		if (is_object($nucoke)) {
			$version = $nucoke->version();
			if ($version[2]>1784) {
				$inherit = $nucoke->inherit();
				$this->database = &$inherit['database'];
				$this->db = &$inherit['db'];
				$this->config = &$inherit['config'];
				$this->http_code = &$inherit['http_code'];
				$this->http_headers = &$inherit['http_headers'];
				$this->init = &$inherit['init'];
				$this->url = &$inherit['url'];
				$this->init = &$inherit['init'];
				$this->user_agent = &$inherit['user_agent'];
			}
		}
		if ($this->init==false) {
		/* we set the error handler */
		set_error_handler(array($this, 'error_handler'));
		error_reporting(-1);
		/* we will now set the default configuration */
		$this->config['start'] = microtime(true); /* time we started the class */
		$this->config['errorprint'] = false;
		$this->config['timezone'] = 'America/Mexico_City'; /* timezone we will set the PHP (http://www.php.net/manual/en/timezones.php) */
		$this->config['name'] = 'NuCoKe'; /* this is the version of the user's proyect'*/
		$this->config['version'] = '1.0.0'; /* this is the version of the user's proyect'*/
		$this->config['charset'] = 'UTF-8'; /* the Charset we will be working on */
		$this->config['path'] = getcwd(); /* the Main path we will be working on */
		$this->config['index'] = array(); /* list of indexing pages */
		$this->config['nucoke_db'] = null; /* name of the NuCoKe Database */
		$this->config['nucoke_db_default'] = null; /* name of the default database */
		$this->config['nucoke_var'] = null; /* name of the database for storing global variables */
		$this->config['nucoke_versionize'] = null; /* name of the database for storing versionize hashes */
		$version = $this->version(); $this->user_agent_add('NuCoKe v'.$version[0].'.'.$version[1]); /* will add self to the user agent string */
		/* we will parse any configuration added from user end */
		if (!is_array($config)) { $config = array(); trigger_error('NuCoKe: Unknown configuration type passed on __initialize($config)',E_USER_NOTICE); }
		foreach ($config as $name => $value) {
			switch ($name) {
				case 'errorprint':
					$this->config['errorprint'] = $value; break;
				case 'timezone':
					$this->config['timezone'] = $value; break;
				case 'name':
					$this->config['name'] = $value; break;
				case 'version':
					$this->config['version'] = $value; break;
				case 'charset':
					$this->config['charset'] = $value; break;
				case 'path':
					$this->config['path'] = $value; break;
				case 'db':
					$this->config['nucoke_db'] = $value; break;
				case 'db_default':
					$this->config['nucoke_db_default'] = $value; break;
				case 'var':
					$this->config['nucoke_var'] = $value; break;
				case 'versionize':
					$this->config['nucoke_versionize'] = $value; break;
				default:
					trigger_error('NuCoKe: Unknown configuration name \''.$name.'\'',E_USER_NOTICE); break;
			}
		}
		if (!date_default_timezone_set($this->config['timezone'])) { trigger_error('TimeZone invalid!'); } /* we set the timezone */
		$this->http_header('Date',date(DATE_RFC822,time())); /* time in which the request is being served */
		$this->http_code=200; /* we got a HTTP OK code for now */
		/* we now change the work directory path */
		if (is_dir($this->config['path'])) {
			$chdir = chdir($this->config['path']);
			if (!$chdir) {
				trigger_error('NuCoKe: Could not set working directory to \''.$this->config['path'].'\'',E_USER_ERROR);
			}
		} else {
			trigger_error('NuCoKe: The directory \''.$this->config['path'].'\' does not exist, could not change work directory',E_USER_ERROR);
		}
		/* we check the URL requested from the client */
		if (isset($_SERVER['REQUEST_URI'])) { $this->url = $_SERVER['REQUEST_URI']; } else {
			if (php_sapi_name()=='cli') {
				$this->url = str_replace($this->get_path(),'',$_SERVER['PHP_SELF']);
			} else {
				$this->url = '/';
			}
		}
		$this->init=true;
		}
		return true;
	}
	/**
	 * will destroy the class
	 * @access public
	 * @version 1.0.37
	 * @return boolean if success
	 */
	public function __destruct() {
		/*
		if (count($this->errors)>0) {
			echo '<pre>';
			foreach ($this->errors as $err) {
				echo $err['file'].':'.$err['line'].'#'.$err['number'].' '.$err['string'].chr(13).chr(10);
			}
			echo '</pre>';
		}
		*/
		return true;
	}
	/**
	 * Adds database to list of databases, will replace existing ones
	 * @access public
	 * @version 1.0.66
	 * @param string $name name of the database
	 * @param array $params parameters of the database
	 * @return bool return true if success
	 */
	public function db_add($name,$params) {
		/* we will now prepare the Array for the parameters of the DataBase */
		$prepara = array();
		/* SYSTEM */
		$prepara['object']		= false; /* here we will store the PDO object */
		$prepara['connected']	= false; /* here we will define if the Database is connected or not */
		$prepara['name']		= $name; /* This is the name of the Database */
		$prepara['retry']		= 0; /* The Current Retry */
		$prepara['retry_ts']	= 0; /* The Last Retry in time() (we use this to determine if $between has passed) */
		$prepara['queries']		= 0; /* Number of Queries sent to the Database */
		/* User Options */
		$prepara['persistent']	= false; /* Will the connection be persistent? */
		$prepara['host']		= '127.0.0.1'; /* Host to connect to if $socket is false */
		$prepara['port']		= '3306'; /* Port to connect to if $socket is false */
		$prepara['socket']		= false; /* if this is not false we will try to connect to this UNIX Socket*/
		$prepara['engine']		= 'mysql'; /* Engine to use */
		$prepara['user']		= ''; /* Username of the Database */
		$prepara['pass']		= ''; /* Passworf of the Database */
		$prepara['database']	= ''; /* Name of the Database */
		$prepara['charset']		= 'UTF8'; /* Charset of the database */
		$prepara['prefix']		= ''; /* Prefix of the database (not implemented) */
		/* Misc Options */
		$prepara['retries']		= 1; /* Number of retries before we give up connecting */
		$prepara['wait']		= 0; /* Number of seconds to wait between Retries */
		$prepara['between']		= 5; /* This is useful on daemon, try again to connect after this ammount of time */
		foreach ($prepara as $pname => $value) {
			if (isset($params[$pname])) {
				$prepara[$pname] = $params[$pname];
			}
		}
		$this->db[$name] = $prepara;
		return true;
	}
	/**
	 * This script calls home to mantain an index of the NuCoKe installations
	 * @access public
	 * @version 1.0.1
	 * @return bool return true if success
	 */
	public function call_home() {
		if ($this->gvar('call_home')!=date('Y-m-d')) {
			
		}
		return true;
	}
	/**
	 * Handles errors
	 * @access public
	 * @version 1.0.24
	 * @return bool return true if success
	 */
	public function error_handler($errno,$errstr,$errfile,$errline) {
		if ($this->config['errorprint']) {
			echo $errfile.':'.$errline.'#'.$errno.' '.$errstr.chr(13).chr(10);
			flush();
		}
		return true;
	}
	/**
	 * get remote address user agent if available
	 * @access public
	 * @version 1.0.1
	 * @return string remote address
	 */
	public function get_agent() {
		if (php_sapi_name()=='cli') {
			$agent = '';
			if (isset($_SERVER['USER'])) { $agent .= $_SERVER['USER'].'@'; } else { $agent .= 'unknown@'; }
			if (isset($_SERVER['HOSTNAME'])) { $agent .= $_SERVER['HOSTNAME'].' ('; } else { $agent .= '127.0.0.1 ('; }
			if (isset($_SERVER['SSH_TTY'])) { $agent .= $_SERVER['SSH_TTY'].';'; } else { $agent .= 'unknowntty;'; }
			if (isset($_SERVER['SSH_CONNECTION'])) { $agent .= $_SERVER['SSH_CONNECTION'].')'; } else { $agent .= 'unknown)'; }
			return $agent;
		} else {
			if (isset($_SERVER['HTTP_USER_AGENT'])) {
				return $_SERVER['HTTP_USER_AGENT'];
			} else {
				return 'unknown (unknown)';
			}
		}
		return true;
	}
	/**
	 * get remote address if available, localhost on fail
	 * @access public
	 * @version 1.0.12
	 * @return string remote address
	 */
	public function get_ip() {
		if (php_sapi_name()=='cli') {
			if (isset($_SERVER['SSH_CLIENT'])) {
				$ip = explode(' ',$_SERVER['SSH_CLIENT']);
				$ip = $ip[0];
			} else {
				$ip = '127.0.0.1';
			}
		} else {
			if (isset($_SERVER['REMOTE_ADDR'])) {
				$ip = $_SERVER['REMOTE_ADDR'];
			} else {
				$ip = '127.0.0.1';
			}
		}
		return $ip;
	}
	/**
	 * returns the path value
	 * @access public
	 * @version 1.0.4
	 * @return string return the actual path value
	 */
	public function get_path() {
		return $this->config['path'];
	}
	/**
	 * returns time elapsed since the creation of NuCoKe
	 * @access public
	 * @version 1.0.3
	 * @return string return time elapsed
	 */
	public function get_time_elapsed() {
		return number_format(microtime(true)-$this->config['start'],7);
	}
	/**
	 * returns value of the variable
	 * @access public
	 * @version 1.0.34
	 * @param $variable string Variable Name
	 * @param $value string Variable value (if setting variable)
	 * @return string variable value
	 */
	public function gvar($variable,$value=null) {
		if (!$this->config['nucoke_var']) { return false; }
		$sql = 'SELECT * FROM `'.$this->config['nucoke_var'].'` WHERE `var` = 0x'.bin2hex($variable);
		$var = $this->sql($sql,$this->config['nucoke_db']);
		if (count($var)>0) {
			$var = $var[0];
			if ($value!==null) {
				$sql = 'UPDATE `'.$this->config['nucoke_var'].'` SET `val` = 0x'.bin2hex(serialize($value)).' WHERE `var` = 0x'.bin2hex($var['var']).' LIMIT 1';
				$this->sql($sql,$this->config['nucoke_db']);
				$sql = 'UPDATE `'.$this->config['nucoke_var'].'` SET `write` = (`write`+1) WHERE `var` = 0x'.bin2hex($var['var']).' LIMIT 1';
				$this->sql($sql,$this->config['nucoke_db']);
				return $value;
			} else {
				$sql = 'UPDATE `'.$this->config['nucoke_var'].'` SET `read` = (`read`+1) WHERE `var` = 0x'.bin2hex($var['var']).' LIMIT 1';
				$this->sql($sql,$this->config['nucoke_db']);
				return unserialize($var['val']);
			}
		} else {
			if ($value===false) {
				return false;
			}
			$sql = 'INSERT INTO `'.$this->config['nucoke_var'].'` (`var`,`val`,`write`) VALUES (0x'.bin2hex($variable).',0x'.bin2hex(serialize($value)).',0)';
			$this->sql($sql,$this->config['nucoke_db']);
			return $value;
		}
	}
	/**
	 * will set a cookie, returns false if headers have been sent
	 * @access public
	 * @version 1.0.2
	 * @param string $name name of the cookie
	 * @param string $value value of the cookie
	 * @param string $expires expiration of the cookie in seconds
	 * @param string $path path to set the cookie
	 * @param string $domain domain to set the cookie for
	 * @return bool return true if headers haven't been sent already
	 */
	public function http_cookie($name,$value,$expires=86400,$path=false,$domain=false) {
		if (!headers_sent()) {
			$this->http_cookies[$name] = array('name'	=>	$name,
										'value'	=>	$value,
										'expires'	=>	$expires,
										'path'	=>	$path,
										'domain'	=>	$domain
										);
			return true;
		} else {
			return false;
		}
	}
	/**
	 * will set a header, return false if the headers have been already sent
	 * @access public
	 * @version 1.0.3
	 * @param string $header the name of the header
	 * @param string $value value of the header
	 * @return bool return true if headers haven't been sent already
	 */
	public function http_header($header,$value) {
		if (!headers_sent()) {
			$this->http_headers[$header] = $value;
			return true;
		} else {
			return false;
		}
	}
	/**
	 * Will check the HTTP Host header, if different it will move the client to that host
	 * @access public
	 * @version 1.0.18
	 * @param string $host host that should be sent by the client
	 * @param bool $https if the protocol should be SSL
	 * @param int $port what port to use
	 * @return bool return true if the host is correct
	 */
	public function http_host($host,$https=false,$port=false,$httpsforce=false) {
		$do = false;
		if (strtolower($_SERVER['HTTP_HOST'])!=strtolower($host)) {
			$do = true;
		}
		if (isset($_SERVER['HTTPS'])&&$https==false&&$httpsforce==true) {
			$do = true;
		}
		if (isset($_SERVER['HTTPS'])==false&&$https==true&&$httpsforce==true) {
			$do = true;
		}
		if ($port!==false) {
			if ($_SERVER['SERVER_PORT']!=$port) {
				$do = true;
			}
		}
		if ($do) {
			$redirect  = 'http';
			if ($https) {
				$redirect .= 's';
			}
			$redirect .= '://'.$host;
			if ($port!==false) {
				$redirect .= ':'.$port;
			}
			$redirect .= $_SERVER['REQUEST_URI'];
			$this->redirect($redirect);
		}
		return true;
	}
	/**
	 * will proceed to the set of HTTP headers
	 * @access public
	 * @version 1.0.24
	 * @return bool return true if headers have been sent
	 */
	public function http_set_headers() {
		if (!headers_sent()) {
			switch ($this->http_code) {
				case 200:
					header('HTTP/1.1 200 OK',true,200); header('status: 200 OK',true,200); break;
				case 206:
					header('HTTP/1.1 206 Partial content',true,206); header('status: 206 Partial content',true,206); break;
				case 302:
					header('HTTP/1.1 302 Found',true,302); header('status: 302 Found',true,302); break;
				case 304:
					header('HTTP/1.1 304 Not Modified',true,304); header('status: 304 Not Modified',true,304); break;
				case 401:
					header('HTTP/1.1 401 Unauthorized',true,401); header('status: 401 Unauthorized',true,401); break;
				case 403:
					header('HTTP/1.1 403 Forbidden',true,403); header('status: 403 Forbidden',true,403); break;
				case 404:
					header('HTTP/1.1 404 Not Found',true,404); header('status: 404 Not Found',true,404); break;
				case 405:
					header('HTTP/1.1 405 Method Not Allowed',true,405); header('status: 405 Method Not Allowed',true,405); break;
				default:
					header('HTTP/1.1 200 OK',true,200); header('status: 200 OK',true,200); $this->http_code = 200; break;
			}
			foreach ($this->http_headers as $header => $value) {
				header($header.': '.$value,true,$this->http_code);
			}
			foreach ($this->http_cookies as $cookie => $value) {
				$header = 'Set-Cookie: ';
				$header .= $value['name'];
				$header .= '=';
				$header .= $value['value'];
				$header .= '; ';
				$header .= 'expires='.date(DATE_RFC822,(time()+$value['expires']));
				$header .= ';';
				if (strlen($value['path'])>0) {
					$header .= ' path='.$value['path'].';';
				}
				if (strlen($value['domain'])>0) {
					$header .= ' domain='.$value['domain'].';';
				}
				header($header,false);
			}
			return true;
		} else {
			return false;
		}
	}
	/**
	 * will return all neccesary values to be a inherited NuCoKe class
	 * @access public
	 * @version 1.0.53
	 * @return array values to inherit
	 */
	public function inherit() {
		$return['db'] = &$this->db;
		$return['database'] = &$this->database;
		$return['config'] = &$this->config;
		$return['http_code'] = &$this->http_code;
		$return['http_headers'] = &$this->http_headers;
		$return['init'] = &$this->init;
		$return['url'] = &$this->url;
		$return['user_agent'] = &$this->user_agent;
		return $return;
	}
	/**
	 * will decode a 4-byte binary ip into normal string
	 * @access public
	 * @version 1.0.18
	 * @param binary $ip 4-byte IP
	 * @return string return full decoded ip x.x.x.x format
	 */
	public function ip_decode($ip) {
		$ip_a = ord(substr($ip,0,1));
		$ip_b = ord(substr($ip,1,1));
		$ip_c = ord(substr($ip,2,1));
		$ip_d = ord(substr($ip,3,1));
		return $ip_a.'.'.$ip_b.'.'.$ip_c.'.'.$ip_d;
	}
	/**
	 * will encode a string IP into a hexadecimal string
	 * @access public
	 * @version 1.0.5
	 * @param string $ip normal x.x.x.x ip
	 * @return string return 8 0 to F hexadecimal string
	 */
	public function ip_encode($ip) {
		$ip_exploded = explode('.', $ip);
		$ip_a = dechex($ip_exploded[0]);
		while (strlen($ip_a)<2) { $ip_a = '0'.$ip_a; }
		$ip_b = dechex($ip_exploded[1]);
		while (strlen($ip_b)<2) { $ip_b = '0'.$ip_b; }
		$ip_c = dechex($ip_exploded[2]);
		while (strlen($ip_c)<2) { $ip_c = '0'.$ip_c; }
		$ip_d = dechex($ip_exploded[3]);
		while (strlen($ip_d)<2) { $ip_d = '0'.$ip_d; }
		return $ip_a.$ip_b.$ip_c.$ip_d;
	}
	/**
	 * returns the path value
	 * @access public
	 * @version 1.0.4
	 * @return string return the actual path value
	 */
	public function path_get() {
		return $this->config['path'];
	}
	/**
	 * will stop output and returns normal HTML output to redirect user requested URL
	 * @access public
	 * @version 1.0.63
	 * @param string $url url to redirect to
	 * @return string return HTML to redirect
	 */
	public function redirect($url) {
		if (substr_count($url, '://') == 0) {
			if (substr($url,0,1)!='/') { $url = '/'.$url; }
			if (substr($_SERVER['HTTP_HOST'], 0, 3) == 'ssl') {
				$goto = 'https://'.$_SERVER['HTTP_HOST'].$url;
			}
			else {
				$goto = 'http://'.$_SERVER['HTTP_HOST'].$url;
			}
		}
		else { $goto = $url; }
		header('location: '.$goto,true,302);
		$html = 	'<h1>'.$_SERVER['SERVER_PROTOCOL'].' 302 Found</h1>'.chr(13).chr(10).
				'<p>The page you have requested has been found: '.$_SERVER['SERVER_PROTOCOL'].' (302)</p>'.chr(13).chr(10).
				'<script type="text/javascript">document.location.href="'.$goto.'"</script>'.chr(13).chr(10).
				'<p>if you can see this message please accept our apologies and click next link: <a href="'.$goto.'">'.$goto.'</a></p>';
		$this->http_code=302;
		ob_clean();
		echo $html;
		die();
	}
	/**
	 * will force page to refresh
	 * @access public
	 * @version 1.0.18
	 * @return bool success
	 */
	public function refresh() {
		global $_SERVER;
		$url = $_SERVER['REQUEST_URI'];
		if (substr_count($url, '://') == 0) {
			if (substr($url,0,1)!='/') { $url = '/'.$url; }
			if (substr($_SERVER['HTTP_HOST'], 0, 3) == 'ssl') {
				$goto = 'https://'.$_SERVER['HTTP_HOST'].$url;
			}
			else {
				$goto = 'http://'.$_SERVER['HTTP_HOST'].$url;
			}
		}
		else { $goto = $url; }
		header('Pragma: no-cache');
		header('cache-Control: no-cache, must-revalidate');
		header('Expires: Sat, 1 Jan 2000 00:00:00 GMT');
		header('location: '.$goto,true,302);
		$html = 	'<h1>'.$_SERVER['SERVER_PROTOCOL'].' 302 Found</h1>'.chr(13).chr(10).
				'<p>The page you have requested has been found: '.$_SERVER['SERVER_PROTOCOL'].' (302)</p>'.chr(13).chr(10).
				'<script type="text/javascript">document.location.href="'.$goto.'"</script>'.chr(13).chr(10).
				'<p>if you can see this message please accept our apologies and click next link: <a href="'.$goto.'">'.$goto.'</a></p>';
		$this->http_code=302;
		echo chr(13).chr(10).$html;
		die(); 
	}
	/**
	 * will convert a decimal number to a hexadecimal representation for SQL insertion
	 * @access public
	 * @version 1.0.1
	 * @param string $string representation of the string
	 * @return string with hexadecimal representation
	 */
	public function sql_dechex($value) {
		return '0x'.dechex(intval($value));
	}
	/**
	 * will attempt to connect to the database, unless an attempt was made
	 * recently with no success
	 * @access public
	 * @version 2.0.335
	 * @return bool on success
	 */
	public function sql_connect(&$db) {
		if ($db['retry']<$db['retries']) {
			$db['retry']++;
			$db['retry_ts'] = time();
			$dsn = $db['engine'].':';
			if ($db['socket']!==false) {
				$dsn .= 'unix_socket='.$db['socket'].';';
			} else {
				if ($db['engine']=='dblib') {
					$dsn .= 'host='.$db['host'].':';
					$dsn .= 'port='.$db['port'].';';
				} else {
					$dsn .= 'host='.$db['host'].';';
					$dsn .= 'port='.$db['port'].';';
				}
			}
			if (strlen($db['database'])>0) {
				$dsn .= 'dbname='.$db['database'].';';
			}
			$options = array(PDO::ATTR_PERSISTENT => $db['persistent']);
			try {
				$db['connected'] = true;
				$db['object'] = new PDO($dsn,$db['user'],$db['pass'],$options);
			} catch (Exception $e) {
				$db['object'] = false;
				$db['connected'] = false;
				trigger_error('NuCoKe: Error Connecting to database ('.$db['name'].':'.$db['user'].'@'.$db['host'].') Message: '.$e->getMessage(),E_USER_NOTICE);
			}
			if ($db['connected']) {
				try {
					$db['object']->query('SET SESSION `character_set_results` = \''.$db['charset'].'\'');
				} catch (Exception $e) {
					$db['object'] = false;
					$db['connected'] = false;
					trigger_error('NuCoKe: Unknown error setting charset \''.$db['charset'].'\' ('.$db['name'].':'.$db['user'].'@'.$db['host'].') Message: '.$e->getMessage(),E_USER_NOTICE);
				}
				try {
					$db['object']->query('SET SESSION `character_set_client` = \''.$db['charset'].'\'');
				} catch (Exception $e) {
					$db['object'] = false;
					$db['connected'] = false;
					trigger_error('NuCoKe: Unknown error setting charset \''.$db['charset'].'\' ('.$db['name'].':'.$db['user'].'@'.$db['host'].') Message: '.$e->getMessage(),E_USER_NOTICE);
				}
			}
			if ($db['connected']) {
				$db['retry'] = 0;
				return true;
			} else {
				if ($db['retries']<$db['retry']) {
					trigger_error('NuCoKe: Database Connection not stablished... sleeping for '.$db['wait'].' seconds...',E_USER_NOTICE);
					sleep($db['wait']);
					return $this->sql_connect($db);
				}
			}
		} else {
			$tiempo = time()-$db['retry_ts'];
			if ($tiempo>$db['between']) {
				$db['retry'] = 0;
				return $this->sql_connect($db);
			}
			return false;
		}
		return false;
	}
	/**
	 * closes all sql connections
	 * @access public
	 * @version 1.0.14
	 * @return bool on success
	 */
	public function sql_close() {
		foreach ($this->db as $index => &$db) {
			unset($db['object']);
		}
		return true;
	}
	/**
	 * will $query the $database
	 * @access public
	 * @version 1.0.68
	 * @param string $query query to execute
	 * @param string $database name of database to use
	 * @return array with the result of the query
	 */
	public function sql($query,$database=null) {
		if (strlen($database)<1) { $database=$this->config['nucoke_db_default']; }
		if ($this->db[$database]) {
			$db = &$this->db[$database];
			if (!$db['connected']) {
				$this->sql_connect($db);
			}
			if ($db['connected']) {
				$db['queries']++;
				try {
					$queryresult = $db['object']->query($query);
				} catch (Exception $e) {
					trigger_error('NuCoKe->SQL: '.$e->getMessage(),E_USER_NOTICE);
					if (is_object($db['object'])) {
						$status = $db['object']->getAttribute(PDO::ATTR_SERVER_INFO);
						if ($status===false) {
							$db['connected'] = false;
						}
					} else {
						$db['connected'] = false;
					}
				}
				if ($queryresult) {
					if (substr($query,0,6)!='UPDATE' && substr($query,0,6)!='INSERT' && substr($query,0,7)!='REPLACE' && substr($query,0,6)!='CREATE' && substr($query,0,6)!='DELETE') {
						$result = array();
						foreach ($queryresult as $index => $rows) {
							$row = array();
							foreach ($rows as $number => $rowc) {
								if (!is_int($number)) {
									$row[$number] = $rowc;
								}
							}
							$result[] = $row;
						}
						return $result;
					}
					if (substr($query,0,6)=='INSERT') {
						return $db['object']->lastInsertId();
					}
					foreach ($queryresult as $index => $row) {
						var_dump($row);
					}
					return false;
				} else {
					return false;
				}
			} else {
				trigger_error('NuCoKe->SQL: Unable to execute query: "'.$query.'", database connection failed',E_USER_NOTICE);
				return false;
			}
		} else {
			/* return error of no database defined */
			trigger_error('The database \''.$database.'\' is not configured',512);
			return false;
		}
		return false;
	}
	/**
	 * will convert a text to a hexadecimal representation for SQL insertion
	 * @access public
	 * @version 1.0.1
	 * @param string $string representation of the string
	 * @return string with hexadecimal representation or null if empty
	 */
	public function sql_texthex($string) {
		if (strlen($string)>0) {
			return '0x'.bin2hex($string);
		} else {
			return 'NULL';
		}
	}
	/**
	 * will kill anything that can destroy our precious SQL sentences
	 * @access public
	 * @version 1.0.4
	 * @param string $string part of the query to vaccine
	 * @return string with vaccioned string
	 */
	public function sql_vaccine($string) {
		$end = str_replace(chr(92).chr(39),chr(92).chr(92).chr(39),$string);
		$end = str_replace(chr(39),chr(92).chr(39),$end);
		return $end;
	}
	/**
	 * checks table and create it if does n ot exists, also modifies it into the desired configuration DEPRECEATED
	 * @access public
	 * @version 1.0.23
	 * @param string $table name of the table to check
	 * @param string $dbname name of the database to check on
	 * @return bool if success
	 */
	protected function table($table,$dbname) {
		$result = $this->sql('SHOW TABLES',$dbname);
		$found = false;
		foreach ($result as $tables) {
			foreach ($tables as $tabl) {
				if ($tabl == $table) {
					$found = true;
				}
			}
		}
	}
	/**
	 * returns user agent string
	 * @access public
	 * @version 2.0.56
	 * @return string user agent
	 */
	public function user_agent() {
		$agent = $this->config['name'].' (';
		$ya = false;
		foreach ($this->user_agent as $string) {
			if ($ya) { $agent .= '; '; }
			$agent .= $string;
			$ya = true;
		}
		$agent .= ')';
		return $agent;
	}
	/**
	 * will add an app to the user agent
	 * @access protected
	 * @version 2.0.0
	 * @param string $text text to add to the user agent string
	 * @return bool if added correctly
	 */
	protected function user_agent_add($text) {
		$toadd = str_replace('(',null,$text);
		$toadd = str_replace(')',null,$toadd);
		$toadd = str_replace(';',null,$toadd);
		$this->user_agent[] = $toadd;
		return true;
	}
	/**
	 * uuid verification (RFC 4211)
	 * @param	uuid $uuid the UUID
	 * @version 1.0.0
	 */
	public function uuid_valid($uuid) {
		return preg_match('/^\{?[0-9a-f]{8}\-?[0-9a-f]{4}\-?[0-9a-f]{4}\-?[0-9a-f]{4}\-?[0-9a-f]{12}\}?$/i', $uuid)===1;
	}
	/**
	 * uuid v3 generator (RFC 4211)
	 * @param	uuid $namespace a valid UUID
	 * @param	string $name name to get UUID from
	 * @version 1.0.0
	 */
	public function uuid_v3($namespace,$name) {
		if (!$this->uuid_valid($namespace)) { return false; }
		$namespace_hex = str_replace(array('-','{','}'), '', $namespace);
		$namespace_bin = '';
		for ($i=0;$i<strlen($namespace_hex);$i+=2) {
			$namespace_bin .= chr(hexdec($namespace_hex[$i].$namespace_hex[$i+1]));
		}
		$hash = md5($namespace_bin.$name);
		$uuid = sprintf('%08s-%04s-%04x-%04x-%12s',substr($hash,0,8),substr($hash,8,4),(hexdec(substr($hash,12,4))&0x0fff)|0x3000,(hexdec(substr($hash,16,4))&0x3fff)|0x8000,substr($hash,20,12));
		return $uuid;
	}
	/**
	 * uuid v4 generator (RFC 4211)
	 * @param	uuid $namespace a valid UUID
	 * @param	string $name name to get UUID from
	 * @version 1.0.14
	 */
	public function uuid_v4($namespace,$name) {
		return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',mt_rand(0,0xffff),mt_rand(0,0xffff),mt_rand(0,0xffff),mt_rand(0,0x0fff)|0x4000,mt_rand(0,0x3fff)|0x8000,mt_rand(0,0xffff),mt_rand(0,0xffff),mt_rand(0,0xffff));
	}
	/**
	 * uuid v5 generator (RFC 4211)
	 * @param	uuid $namespace a valid UUID
	 * @param	string $name name to get UUID from
	 * @version 1.0.13
	 */
	public function uuid_v5($namespace,$name) {
		if (!$this->uuid_valid($namespace)) { return false; }
		$namespace_hex = str_replace(array('-','{','}'), '', $namespace);
		$namespace_bin = '';
		for ($i=0;$i<strlen($namespace_hex);$i+=2) {
			$namespace_bin .= chr(hexdec($namespace_hex[$i].$namespace_hex[$i+1]));
		}
		$hash = sha1($namespace_bin.$name);
		$uuid = sprintf('%08s-%04s-%04x-%04x-%12s',substr($hash,0,8),substr($hash,8,4),(hexdec(substr($hash,12,4))&0x0fff)|0x5000,(hexdec(substr($hash,16,4))&0x3fff)|0x8000,substr($hash,20,12));
		return $uuid;
	}
	/**
	 * will return the version of the class in Mayor, Minor and Release
	 * @access public
	 * @version 1.0.1977
	 * @return array ( main, minor, release )
	 */
	public function version() {
		return array( 2, 2, 1977 );
	}
	/**
	 * this function is capable of reading a file, determine the version of the functions inside it
	 * and assign versions, general recommendation is having the file well commented and only functions on it
	 * tested only on the NuCoKe cores, in simple mode the script replaces only the first @version it encounters
	 * @access public
	 * @version 1.0.27
	 * @param string path to the file to check version
	 * @param bool if we should versionize only for the global file
	 * @param bool if we should write version info into the file
	 * @return true if success
	 */
	public function versionize($file,$simple=true,$write=false) {
		if (!is_file($this->get_path().$file)) {
			trigger_error('File does not Exist: '.$file,E_USER_WARNING);
			return false;
		}
		if ($this->config['nucoke_versionize']!=null) {
			$sql = 'SELECT * FROM `'.$this->config['nucoke_versionize'].'` WHERE `file` = '.$this->sql_texthex($file);
			$version = $this->sql($sql);
			if (count($version)>0) {
				$version = unserialize($version[0]['version']);
			} else {
				$version = array();
			}
		}
		else {
			if (!is_file($this->get_path().$file.'.version')) {
				$version = array();
			} else {
				$version = unserialize(file_get_contents($this->get_path().$file.'.version'));
			}
		}
		$leida = $version['sha1'];
		$file_check = file_get_contents($this->get_path().$file);
		if ($simple) {
			$process = $file_check;
			$process = explode(chr(10),$process);
			$continuar = true;
			$found = false;
			$f_version = false;
			$f_version_line = 0;
			foreach ($process as $number => $line) {
				if ($continuar) {
					if (!$found) {
						$search = strpos($process[$number],'/*');
						if ($search!==false) {
							$found = true;
						}
					}
					if ($found) {
						$search = strpos($process[$number],'*/');
						if ($search!==false) { $continuar = false; }
						$search = strpos($process[$number],'@version');
						if ($search!==false) {
							$f_version = substr($process[$number],($search+9));
							$f_version = explode('.',$f_version);
							$f_version[0] = intval($f_version[0]); $f_version[1] = intval($f_version[1]); $f_version[2] = intval($f_version[2]);
							$f_version_line = $number;
							$continua = false;
						}
					}
				}
			}
			if (!isset($version['version'])) {
				if ($f_version) {
					$version['version'] = $f_version;
				} else {
					$version['version'] = array(1,0,0);
				}
				$version['sha1'] = 'INITIALIZED';
			}
			if ($version['sha1'] != sha1($file_check)) {
				$version['version'][2]++;
				$process[$f_version_line] = str_replace($f_version[0].'.'.$f_version[1].'.'.$f_version[2],$version['version'][0].'.'.$version['version'][1].'.'.$version['version'][2],$process[$f_version_line]);
				$version['sha1'] = sha1($file_check);
				if ($write) {
					$file_check = implode(chr(10),$process);
					$version['sha1'] = sha1($file_check);
					file_put_contents($this->get_path().$file, $file_check);
				}
			}
		} else {
			if (!isset($version['version'])) {
				$process = $file_check;
				$search = strpos($process,'function version');
				$process = substr($process,$search);
				$search = strpos($process,'array(');
				$process = substr($process,($search)+6);
				$search = strpos($process,')');
				$process = substr($process,0,$search);
				$process = str_replace(chr(32),null,$process);
				$process = explode(',',$process);
				$version['version'] = $process;
				if (count($version['version'])!=3) {
					$version['version'] = array(1,0,0);
				}
				$version['version'][0] = intval($version['version'][0]);
				$version['version'][1] = intval($version['version'][1]);
				$version['version'][2] = intval($version['version'][2]);
				$version['sha1'] = 'INITIALIZED';
			}
			if ($version['sha1'] != sha1($file_check)) {
				$process = $file_check;
				$process = explode(chr(10),$process);
				foreach ($process as $number => $line) {
					$search = strpos($line,'function');
					if ($search!==false) {
						$resta = 1;
						$search = strpos($process[($number-$resta)],'*/');
						if ($search!==false) {
							$continua = true;
							$f_version = false;
							$f_version_line = 0;
							$limite=5000;
							while ($continua) {
								$search = strpos($process[($number-$resta)],'/*');
								if ($search!==false) { $continua = false; }
								$search = strpos($process[($number-$resta)],'@version');
								if ($search!==false) {
									$f_version = substr($process[($number-$resta)],($search+9));
									$f_version = explode('.',$f_version);
									$f_version_line = ($number-$resta);
									$f_version[0] = intval($f_version[0]); $f_version[1] = intval($f_version[1]); $f_version[2] = intval($f_version[2]);
									$continua = false;
								}
								$resta++;
								$limite--;
								if ($limite<1) {$continua=false;}
							}
							if ($f_version) {
								$continua = true;
								$funcion = 0;
								$brackets = 0;
								$sha1 = '';
								$limite=5000;
								while ($continua) {
									$sha1 .= $process[($number+$funcion)].chr(10);
									$procesa = $process[($number+$funcion)];
									while (strlen($procesa)>0) {
										if (ord(substr($procesa,0,1))==123) { $brackets++; }
										if (ord(substr($procesa,0,1))==125) { $brackets--; }
										$procesa = substr($procesa,1);
									}
									if ($brackets<1) {$continua=false;}
									$limite--;
									if ($limite<1) {$continua=false;}
									$funcion++;
								}
								$name = substr($process[($number)],(strpos($process[($number)],'function')+9));
								$name = substr($name,0,strpos($name,'('));
								$name = 'f_'.$name;
								if (isset($version[$name])) {
									if ($version[$name]['sha1'] != sha1($sha1)) {
										$version[$name]['version'][2]++;
										$process[$f_version_line] = str_replace($f_version[0].'.'.$f_version[1].'.'.$f_version[2],$version[$name]['version'][0].'.'.$version[$name]['version'][1].'.'.$version[$name]['version'][2],$process[$f_version_line]);
									}
								} else {
									$version[$name]['version'] = $f_version;
									$version[$name]['sha1'] = sha1($sha1);
								}
							}
						}
					}
				}
				$version['version'][2]++;
				$version['sha1'] = sha1($file_check);
				if ($write) {
					$file_check = implode(chr(10),$process);
					$process = $file_check;
					$search = strpos($process,'function version');
					$process = substr($process,$search);
					$search = strpos($process,'array(');
					$process = substr($process,($search)+6);
					$search = strpos($process,')');
					$process = substr($process,0,$search);
					$process = str_replace(chr(32),null,$process);
					$process = explode(',',$process);
					$file_check = str_replace($process[0].', '.$process[1].', '.$process[2],$version['version'][0].', '.$version['version'][1].', '.$version['version'][2],$file_check);
					$file_check = str_replace($process[0].'.'.$process[1].'.'.$process[2],$version['version'][0].'.'.$version['version'][1].'.'.$version['version'][2],$file_check);
					$version['sha1'] = sha1($file_check);
					file_put_contents($this->get_path().$file, $file_check);
				}
			}
		}
		if ($leida!=$version['sha1']) {
			if ($this->config['nucoke_versionize']!=null) {
				$sql = 'REPLACE INTO `'.$this->config['nucoke_versionize'].'` (`file`,`version`) VALUES ('.$this->sql_texthex($file).','.$this->sql_texthex(serialize($version)).')';
				$this->sql($sql);
			}
			else {
				file_put_contents($this->get_path().$file.'.version',serialize($version));
			}
		}
	}
}
?>