Forráskód Böngészése

Composer + Container + installation moved to a class + favicon.ico

Visman 8 éve
szülő
commit
0b5e67c682

+ 8 - 0
.gitignore

@@ -0,0 +1,8 @@
+/app/config/main.php
+/app/cache/**/*.php
+/cache/**/*.php
+/img/avatars/*
+!/img/avatars/index.html
+/img/members/*
+!/img/members/.htaccess
+!/img/members/nofile.gif

+ 7 - 0
app/Core/Blank.php

@@ -0,0 +1,7 @@
+<?php
+
+namespace ForkBB\Core;
+
+class Blank
+{
+}

+ 374 - 0
app/Core/DB/mysql.php

@@ -0,0 +1,374 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+namespace ForkBB\Core\DB;
+
+// Make sure we have built in support for MySQL
+if (!function_exists('mysql_connect'))
+	exit('This PHP environment doesn\'t have MySQL support built in. MySQL support is required if you want to use a MySQL database to run this forum. Consult the PHP documentation for further assistance.');
+
+
+class DBLayer
+{
+	var $prefix;
+	var $link_id;
+	var $query_result;
+
+	var $saved_queries = array();
+	var $num_queries = 0;
+
+	var $error_no = false;
+	var $error_msg = 'Unknown';
+
+	var $datatype_transformations = array(
+		'%^SERIAL$%'	=>	'INT(10) UNSIGNED AUTO_INCREMENT'
+	);
+
+
+	function __construct($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect)
+	{
+		$this->prefix = $db_prefix;
+
+		if ($p_connect)
+			$this->link_id = @mysql_pconnect($db_host, $db_username, $db_password);
+		else
+			$this->link_id = @mysql_connect($db_host, $db_username, $db_password);
+
+		if ($this->link_id)
+		{
+			if (!@mysql_select_db($db_name, $this->link_id))
+				error('Unable to select database. MySQL reported: '.mysql_error(), __FILE__, __LINE__);
+		}
+		else
+			error('Unable to connect to MySQL server. MySQL reported: '.mysql_error(), __FILE__, __LINE__);
+
+		// Setup the client-server character set (UTF-8)
+		if (!defined('FORUM_NO_SET_NAMES'))
+			$this->set_names('utf8');
+
+		return $this->link_id;
+	}
+	
+
+	function start_transaction()
+	{
+		return;
+	}
+
+
+	function end_transaction()
+	{
+		return;
+	}
+
+
+	function query($sql, $unbuffered = false)
+	{
+		if (defined('PUN_SHOW_QUERIES'))
+			$q_start = microtime(true);
+
+		if ($unbuffered)
+			$this->query_result = @mysql_unbuffered_query($sql, $this->link_id);
+		else
+			$this->query_result = @mysql_query($sql, $this->link_id);
+
+		if ($this->query_result)
+		{
+			if (defined('PUN_SHOW_QUERIES'))
+				$this->saved_queries[] = array($sql, sprintf('%.5f', microtime(true) - $q_start));
+
+			++$this->num_queries;
+
+			return $this->query_result;
+		}
+		else
+		{
+			if (defined('PUN_SHOW_QUERIES'))
+				$this->saved_queries[] = array($sql, 0);
+
+			$this->error_no = @mysql_errno($this->link_id);
+			$this->error_msg = @mysql_error($this->link_id);
+
+			return false;
+		}
+	}
+
+
+	function result($query_id = 0, $row = 0, $col = 0)
+	{
+		return ($query_id) ? @mysql_result($query_id, $row, $col) : false;
+	}
+
+
+	function fetch_assoc($query_id = 0)
+	{
+		return ($query_id) ? @mysql_fetch_assoc($query_id) : false;
+	}
+
+
+	function fetch_row($query_id = 0)
+	{
+		return ($query_id) ? @mysql_fetch_row($query_id) : false;
+	}
+
+
+	function num_rows($query_id = 0)
+	{
+		return ($query_id) ? @mysql_num_rows($query_id) : false;
+	}
+
+
+	function affected_rows()
+	{
+		return ($this->link_id) ? @mysql_affected_rows($this->link_id) : false;
+	}
+
+
+	function insert_id()
+	{
+		return ($this->link_id) ? @mysql_insert_id($this->link_id) : false;
+	}
+
+
+	function get_num_queries()
+	{
+		return $this->num_queries;
+	}
+
+
+	function get_saved_queries()
+	{
+		return $this->saved_queries;
+	}
+
+
+	function free_result($query_id = false)
+	{
+		return ($query_id) ? @mysql_free_result($query_id) : false;
+	}
+
+
+	function escape($str)
+	{
+		if (is_array($str))
+			return '';
+		else if (function_exists('mysql_real_escape_string'))
+			return mysql_real_escape_string($str, $this->link_id);
+		else
+			return mysql_escape_string($str);
+	}
+
+
+	function error()
+	{
+		$result['error_sql'] = @current(@end($this->saved_queries));
+		$result['error_no'] = $this->error_no;
+		$result['error_msg'] = $this->error_msg;
+
+		return $result;
+	}
+
+
+	function close()
+	{
+		if ($this->link_id)
+		{
+			if (is_resource($this->query_result))
+				@mysql_free_result($this->query_result);
+
+			return @mysql_close($this->link_id);
+		}
+		else
+			return false;
+	}
+
+	function get_names()
+	{
+		$result = $this->query('SHOW VARIABLES LIKE \'character_set_connection\'');
+		return $this->result($result, 0, 1);
+	}
+
+
+	function set_names($names)
+	{
+		return $this->query('SET NAMES \''.$this->escape($names).'\'');
+	}
+
+
+	function get_version()
+	{
+		$result = $this->query('SELECT VERSION()');
+
+		return array(
+			'name'		=> 'MySQL Standard',
+			'version'	=> preg_replace('%^([^-]+).*$%', '\\1', $this->result($result))
+		);
+	}
+
+
+	function table_exists($table_name, $no_prefix = false)
+	{
+		$result = $this->query('SHOW TABLES LIKE \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\'');
+		return $this->num_rows($result) > 0;
+	}
+
+
+	function field_exists($table_name, $field_name, $no_prefix = false)
+	{
+		$result = $this->query('SHOW COLUMNS FROM '.($no_prefix ? '' : $this->prefix).$table_name.' LIKE \''.$this->escape($field_name).'\'');
+		return $this->num_rows($result) > 0;
+	}
+
+
+	function index_exists($table_name, $index_name, $no_prefix = false)
+	{
+		$exists = false;
+
+		$result = $this->query('SHOW INDEX FROM '.($no_prefix ? '' : $this->prefix).$table_name);
+		while ($cur_index = $this->fetch_assoc($result))
+		{
+			if (strtolower($cur_index['Key_name']) == strtolower(($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name))
+			{
+				$exists = true;
+				break;
+			}
+		}
+
+		return $exists;
+	}
+
+
+	function create_table($table_name, $schema, $no_prefix = false)
+	{
+		if ($this->table_exists($table_name, $no_prefix))
+			return true;
+
+		$query = 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$table_name." (\n";
+
+		// Go through every schema element and add it to the query
+		foreach ($schema['FIELDS'] as $field_name => $field_data)
+		{
+			$field_data['datatype'] = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_data['datatype']);
+
+			$query .= $field_name.' '.$field_data['datatype'];
+
+			if (isset($field_data['collation']))
+				$query .= 'CHARACTER SET utf8 COLLATE utf8_'.$field_data['collation'];
+
+			if (!$field_data['allow_null'])
+				$query .= ' NOT NULL';
+
+			if (isset($field_data['default']))
+				$query .= ' DEFAULT '.$field_data['default'];
+
+			$query .= ",\n";
+		}
+
+		// If we have a primary key, add it
+		if (isset($schema['PRIMARY KEY']))
+			$query .= 'PRIMARY KEY ('.implode(',', $schema['PRIMARY KEY']).'),'."\n";
+
+		// Add unique keys
+		if (isset($schema['UNIQUE KEYS']))
+		{
+			foreach ($schema['UNIQUE KEYS'] as $key_name => $key_fields)
+				$query .= 'UNIQUE KEY '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$key_name.'('.implode(',', $key_fields).'),'."\n";
+		}
+
+		// Add indexes
+		if (isset($schema['INDEXES']))
+		{
+			foreach ($schema['INDEXES'] as $index_name => $index_fields)
+				$query .= 'KEY '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name.'('.implode(',', $index_fields).'),'."\n";
+		}
+
+		// We remove the last two characters (a newline and a comma) and add on the ending
+		$query = substr($query, 0, strlen($query) - 2)."\n".') ENGINE = '.(isset($schema['ENGINE']) ? $schema['ENGINE'] : 'MyISAM').' CHARACTER SET utf8';
+
+		return $this->query($query) ? true : false;
+	}
+
+
+	function drop_table($table_name, $no_prefix = false)
+	{
+		if (!$this->table_exists($table_name, $no_prefix))
+			return true;
+
+		return $this->query('DROP TABLE '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+	}
+
+
+	function rename_table($old_table, $new_table, $no_prefix = false)
+	{
+		// If the new table exists and the old one doesn't, then we're happy
+		if ($this->table_exists($new_table, $no_prefix) && !$this->table_exists($old_table, $no_prefix))
+			return true;
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$old_table.' RENAME TO '.($no_prefix ? '' : $this->prefix).$new_table) ? true : false;
+	}
+
+
+	function add_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+	{
+		if ($this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		$field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+
+		if (!is_null($default_value) && !is_int($default_value) && !is_float($default_value))
+			$default_value = '\''.$this->escape($default_value).'\'';
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.$field_name.' '.$field_type.($allow_null ? '' : ' NOT NULL').(!is_null($default_value) ? ' DEFAULT '.$default_value : '').(!is_null($after_field) ? ' AFTER '.$after_field : '')) ? true : false;
+	}
+
+
+	function alter_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+	{
+		if (!$this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		$field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+
+		if (!is_null($default_value) && !is_int($default_value) && !is_float($default_value))
+			$default_value = '\''.$this->escape($default_value).'\'';
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' MODIFY '.$field_name.' '.$field_type.($allow_null ? '' : ' NOT NULL').(!is_null($default_value) ? ' DEFAULT '.$default_value : '').(!is_null($after_field) ? ' AFTER '.$after_field : '')) ? true : false;
+	}
+
+
+	function drop_field($table_name, $field_name, $no_prefix = false)
+	{
+		if (!$this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' DROP '.$field_name) ? true : false;
+	}
+
+
+	function add_index($table_name, $index_name, $index_fields, $unique = false, $no_prefix = false)
+	{
+		if ($this->index_exists($table_name, $index_name, $no_prefix))
+			return true;
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.($unique ? 'UNIQUE ' : '').'INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name.' ('.implode(',', $index_fields).')') ? true : false;
+	}
+
+
+	function drop_index($table_name, $index_name, $no_prefix = false)
+	{
+		if (!$this->index_exists($table_name, $index_name, $no_prefix))
+			return true;
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' DROP INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name) ? true : false;
+	}
+
+	function truncate_table($table_name, $no_prefix = false)
+	{
+		return $this->query('TRUNCATE TABLE '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+	}
+}

+ 388 - 0
app/Core/DB/mysql_innodb.php

@@ -0,0 +1,388 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+namespace ForkBB\Core\DB;
+
+// Make sure we have built in support for MySQL
+if (!function_exists('mysql_connect'))
+	exit('This PHP environment doesn\'t have MySQL support built in. MySQL support is required if you want to use a MySQL database to run this forum. Consult the PHP documentation for further assistance.');
+
+
+class DBLayer
+{
+	var $prefix;
+	var $link_id;
+	var $query_result;
+	var $in_transaction = 0;
+
+	var $saved_queries = array();
+	var $num_queries = 0;
+
+	var $error_no = false;
+	var $error_msg = 'Unknown';
+
+	var $datatype_transformations = array(
+		'%^SERIAL$%'	=>	'INT(10) UNSIGNED AUTO_INCREMENT'
+	);
+
+
+	function __construct($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect)
+	{
+		$this->prefix = $db_prefix;
+
+		if ($p_connect)
+			$this->link_id = @mysql_pconnect($db_host, $db_username, $db_password);
+		else
+			$this->link_id = @mysql_connect($db_host, $db_username, $db_password);
+
+		if ($this->link_id)
+		{
+			if (!@mysql_select_db($db_name, $this->link_id))
+				error('Unable to select database. MySQL reported: '.mysql_error(), __FILE__, __LINE__);
+		}
+		else
+			error('Unable to connect to MySQL server. MySQL reported: '.mysql_error(), __FILE__, __LINE__);
+
+		// Setup the client-server character set (UTF-8)
+		if (!defined('FORUM_NO_SET_NAMES'))
+			$this->set_names('utf8');
+
+		return $this->link_id;
+	}
+	
+	
+	function start_transaction()
+	{
+		++$this->in_transaction;
+
+		mysql_query('START TRANSACTION', $this->link_id);
+		return;
+	}
+
+
+	function end_transaction()
+	{
+		--$this->in_transaction;
+
+		mysql_query('COMMIT', $this->link_id);
+		return;
+	}
+
+
+	function query($sql, $unbuffered = false)
+	{
+		if (defined('PUN_SHOW_QUERIES'))
+			$q_start = microtime(true);
+
+		if ($unbuffered)
+			$this->query_result = @mysql_unbuffered_query($sql, $this->link_id);
+		else
+			$this->query_result = @mysql_query($sql, $this->link_id);
+
+		if ($this->query_result)
+		{
+			if (defined('PUN_SHOW_QUERIES'))
+				$this->saved_queries[] = array($sql, sprintf('%.5f', microtime(true) - $q_start));
+
+			++$this->num_queries;
+
+			return $this->query_result;
+		}
+		else
+		{
+			if (defined('PUN_SHOW_QUERIES'))
+				$this->saved_queries[] = array($sql, 0);
+
+			$this->error_no = @mysql_errno($this->link_id);
+			$this->error_msg = @mysql_error($this->link_id);
+
+			// Rollback transaction
+			if ($this->in_transaction)
+				mysql_query('ROLLBACK', $this->link_id);
+
+			--$this->in_transaction;
+
+			return false;
+		}
+	}
+
+
+	function result($query_id = 0, $row = 0, $col = 0)
+	{
+		return ($query_id) ? @mysql_result($query_id, $row, $col) : false;
+	}
+
+
+	function fetch_assoc($query_id = 0)
+	{
+		return ($query_id) ? @mysql_fetch_assoc($query_id) : false;
+	}
+
+
+	function fetch_row($query_id = 0)
+	{
+		return ($query_id) ? @mysql_fetch_row($query_id) : false;
+	}
+
+
+	function num_rows($query_id = 0)
+	{
+		return ($query_id) ? @mysql_num_rows($query_id) : false;
+	}
+
+
+	function affected_rows()
+	{
+		return ($this->link_id) ? @mysql_affected_rows($this->link_id) : false;
+	}
+
+
+	function insert_id()
+	{
+		return ($this->link_id) ? @mysql_insert_id($this->link_id) : false;
+	}
+
+
+	function get_num_queries()
+	{
+		return $this->num_queries;
+	}
+
+
+	function get_saved_queries()
+	{
+		return $this->saved_queries;
+	}
+
+
+	function free_result($query_id = false)
+	{
+		return ($query_id) ? @mysql_free_result($query_id) : false;
+	}
+
+
+	function escape($str)
+	{
+		if (is_array($str))
+			return '';
+		else if (function_exists('mysql_real_escape_string'))
+			return mysql_real_escape_string($str, $this->link_id);
+		else
+			return mysql_escape_string($str);
+	}
+
+
+	function error()
+	{
+		$result['error_sql'] = @current(@end($this->saved_queries));
+		$result['error_no'] = $this->error_no;
+		$result['error_msg'] = $this->error_msg;
+
+		return $result;
+	}
+
+
+	function close()
+	{
+		if ($this->link_id)
+		{
+			if (is_resource($this->query_result))
+				@mysql_free_result($this->query_result);
+
+			return @mysql_close($this->link_id);
+		}
+		else
+			return false;
+	}
+
+
+	function get_names()
+	{
+		$result = $this->query('SHOW VARIABLES LIKE \'character_set_connection\'');
+		return $this->result($result, 0, 1);
+	}
+
+
+	function set_names($names)
+	{
+		return $this->query('SET NAMES \''.$this->escape($names).'\'');
+	}
+
+
+	function get_version()
+	{
+		$result = $this->query('SELECT VERSION()');
+
+		return array(
+			'name'		=> 'MySQL Standard (InnoDB)',
+			'version'	=> preg_replace('%^([^-]+).*$%', '\\1', $this->result($result))
+		);
+	}
+
+
+	function table_exists($table_name, $no_prefix = false)
+	{
+		$result = $this->query('SHOW TABLES LIKE \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\'');
+		return $this->num_rows($result) > 0;
+	}
+
+
+	function field_exists($table_name, $field_name, $no_prefix = false)
+	{
+		$result = $this->query('SHOW COLUMNS FROM '.($no_prefix ? '' : $this->prefix).$table_name.' LIKE \''.$this->escape($field_name).'\'');
+		return $this->num_rows($result) > 0;
+	}
+
+
+	function index_exists($table_name, $index_name, $no_prefix = false)
+	{
+		$exists = false;
+
+		$result = $this->query('SHOW INDEX FROM '.($no_prefix ? '' : $this->prefix).$table_name);
+		while ($cur_index = $this->fetch_assoc($result))
+		{
+			if (strtolower($cur_index['Key_name']) == strtolower(($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name))
+			{
+				$exists = true;
+				break;
+			}
+		}
+
+		return $exists;
+	}
+
+
+	function create_table($table_name, $schema, $no_prefix = false)
+	{
+		if ($this->table_exists($table_name, $no_prefix))
+			return true;
+
+		$query = 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$table_name." (\n";
+
+		// Go through every schema element and add it to the query
+		foreach ($schema['FIELDS'] as $field_name => $field_data)
+		{
+			$field_data['datatype'] = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_data['datatype']);
+
+			$query .= $field_name.' '.$field_data['datatype'];
+
+			if (isset($field_data['collation']))
+				$query .= 'CHARACTER SET utf8 COLLATE utf8_'.$field_data['collation'];
+
+			if (!$field_data['allow_null'])
+				$query .= ' NOT NULL';
+
+			if (isset($field_data['default']))
+				$query .= ' DEFAULT '.$field_data['default'];
+
+			$query .= ",\n";
+		}
+
+		// If we have a primary key, add it
+		if (isset($schema['PRIMARY KEY']))
+			$query .= 'PRIMARY KEY ('.implode(',', $schema['PRIMARY KEY']).'),'."\n";
+
+		// Add unique keys
+		if (isset($schema['UNIQUE KEYS']))
+		{
+			foreach ($schema['UNIQUE KEYS'] as $key_name => $key_fields)
+				$query .= 'UNIQUE KEY '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$key_name.'('.implode(',', $key_fields).'),'."\n";
+		}
+
+		// Add indexes
+		if (isset($schema['INDEXES']))
+		{
+			foreach ($schema['INDEXES'] as $index_name => $index_fields)
+				$query .= 'KEY '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name.'('.implode(',', $index_fields).'),'."\n";
+		}
+
+		// We remove the last two characters (a newline and a comma) and add on the ending
+		$query = substr($query, 0, strlen($query) - 2)."\n".') ENGINE = '.(isset($schema['ENGINE']) ? $schema['ENGINE'] : 'InnoDB').' CHARACTER SET utf8';
+
+		return $this->query($query) ? true : false;
+	}
+
+
+	function drop_table($table_name, $no_prefix = false)
+	{
+		if (!$this->table_exists($table_name, $no_prefix))
+			return true;
+
+		return $this->query('DROP TABLE '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+	}
+
+
+	function rename_table($old_table, $new_table, $no_prefix = false)
+	{
+		// If the new table exists and the old one doesn't, then we're happy
+		if ($this->table_exists($new_table, $no_prefix) && !$this->table_exists($old_table, $no_prefix))
+			return true;
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$old_table.' RENAME TO '.($no_prefix ? '' : $this->prefix).$new_table) ? true : false;
+	}
+
+
+	function add_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+	{
+		if ($this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		$field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+
+		if (!is_null($default_value) && !is_int($default_value) && !is_float($default_value))
+			$default_value = '\''.$this->escape($default_value).'\'';
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.$field_name.' '.$field_type.($allow_null ? '' : ' NOT NULL').(!is_null($default_value) ? ' DEFAULT '.$default_value : '').(!is_null($after_field) ? ' AFTER '.$after_field : '')) ? true : false;
+	}
+
+
+	function alter_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+	{
+		if (!$this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		$field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+
+		if (!is_null($default_value) && !is_int($default_value) && !is_float($default_value))
+			$default_value = '\''.$this->escape($default_value).'\'';
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' MODIFY '.$field_name.' '.$field_type.($allow_null ? '' : ' NOT NULL').(!is_null($default_value) ? ' DEFAULT '.$default_value : '').(!is_null($after_field) ? ' AFTER '.$after_field : '')) ? true : false;
+	}
+
+
+	function drop_field($table_name, $field_name, $no_prefix = false)
+	{
+		if (!$this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' DROP '.$field_name) ? true : false;
+	}
+
+
+	function add_index($table_name, $index_name, $index_fields, $unique = false, $no_prefix = false)
+	{
+		if ($this->index_exists($table_name, $index_name, $no_prefix))
+			return true;
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.($unique ? 'UNIQUE ' : '').'INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name.' ('.implode(',', $index_fields).')') ? true : false;
+	}
+
+
+	function drop_index($table_name, $index_name, $no_prefix = false)
+	{
+		if (!$this->index_exists($table_name, $index_name, $no_prefix))
+			return true;
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' DROP INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name) ? true : false;
+	}
+
+	function truncate_table($table_name, $no_prefix = false)
+	{
+		return $this->query('TRUNCATE TABLE '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+	}
+}

+ 381 - 0
app/Core/DB/mysqli.php

@@ -0,0 +1,381 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+namespace ForkBB\Core\DB;
+
+// Make sure we have built in support for MySQL
+if (!function_exists('mysqli_connect'))
+	exit('This PHP environment doesn\'t have Improved MySQL (mysqli) support built in. Improved MySQL support is required if you want to use a MySQL 4.1 (or later) database to run this forum. Consult the PHP documentation for further assistance.');
+
+
+class DBLayer
+{
+	var $prefix;
+	var $link_id;
+	var $query_result;
+
+	var $saved_queries = array();
+	var $num_queries = 0;
+
+	var $error_no = false;
+	var $error_msg = 'Unknown';
+
+	var $datatype_transformations = array(
+		'%^SERIAL$%'	=>	'INT(10) UNSIGNED AUTO_INCREMENT'
+	);
+
+
+	function __construct($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect)
+	{
+		$this->prefix = $db_prefix;
+
+		// Was a custom port supplied with $db_host?
+		if (strpos($db_host, ':') !== false)
+			list($db_host, $db_port) = explode(':', $db_host);
+
+		// Persistent connection in MySQLi are only available in PHP 5.3 and later releases
+		$p_connect = $p_connect && version_compare(PHP_VERSION, '5.3.0', '>=') ? 'p:' : '';
+
+		if (isset($db_port))
+			$this->link_id = @mysqli_connect($p_connect.$db_host, $db_username, $db_password, $db_name, $db_port);
+		else
+			$this->link_id = @mysqli_connect($p_connect.$db_host, $db_username, $db_password, $db_name);
+
+		if (!$this->link_id)
+			error('Unable to connect to MySQL and select database. MySQL reported: '.mysqli_connect_error(), __FILE__, __LINE__);
+
+		// Setup the client-server character set (UTF-8)
+		if (!defined('FORUM_NO_SET_NAMES'))
+			$this->set_names('utf8');
+
+		return $this->link_id;
+	}
+	
+	
+	function start_transaction()
+	{
+		return;
+	}
+
+
+	function end_transaction()
+	{
+		return;
+	}
+
+
+	function query($sql, $unbuffered = false)
+	{
+		if (defined('PUN_SHOW_QUERIES'))
+			$q_start = microtime(true);
+
+		$this->query_result = @mysqli_query($this->link_id, $sql);
+
+		if ($this->query_result)
+		{
+			if (defined('PUN_SHOW_QUERIES'))
+				$this->saved_queries[] = array($sql, sprintf('%.5f', microtime(true) - $q_start));
+
+			++$this->num_queries;
+
+			return $this->query_result;
+		}
+		else
+		{
+			if (defined('PUN_SHOW_QUERIES'))
+				$this->saved_queries[] = array($sql, 0);
+
+			$this->error_no = @mysqli_errno($this->link_id);
+			$this->error_msg = @mysqli_error($this->link_id);
+
+			return false;
+		}
+	}
+
+
+	function result($query_id = 0, $row = 0, $col = 0)
+	{
+		if ($query_id)
+		{
+			if ($row !== 0 && @mysqli_data_seek($query_id, $row) === false)
+				return false;
+
+			$cur_row = @mysqli_fetch_row($query_id);
+			if ($cur_row === false)
+				return false;
+
+			return $cur_row[$col];
+		}
+		else
+			return false;
+	}
+
+
+	function fetch_assoc($query_id = 0)
+	{
+		return ($query_id) ? @mysqli_fetch_assoc($query_id) : false;
+	}
+
+
+	function fetch_row($query_id = 0)
+	{
+		return ($query_id) ? @mysqli_fetch_row($query_id) : false;
+	}
+
+
+	function num_rows($query_id = 0)
+	{
+		return ($query_id) ? @mysqli_num_rows($query_id) : false;
+	}
+
+
+	function affected_rows()
+	{
+		return ($this->link_id) ? @mysqli_affected_rows($this->link_id) : false;
+	}
+
+
+	function insert_id()
+	{
+		return ($this->link_id) ? @mysqli_insert_id($this->link_id) : false;
+	}
+
+
+	function get_num_queries()
+	{
+		return $this->num_queries;
+	}
+
+
+	function get_saved_queries()
+	{
+		return $this->saved_queries;
+	}
+
+
+	function free_result($query_id = false)
+	{
+		return ($query_id) ? @mysqli_free_result($query_id) : false;
+	}
+
+
+	function escape($str)
+	{
+		return is_array($str) ? '' : mysqli_real_escape_string($this->link_id, $str);
+	}
+
+
+	function error()
+	{
+		$result['error_sql'] = @current(@end($this->saved_queries));
+		$result['error_no'] = $this->error_no;
+		$result['error_msg'] = $this->error_msg;
+
+		return $result;
+	}
+
+
+	function close()
+	{
+		if ($this->link_id)
+		{
+			if ($this->query_result instanceof mysqli_result)
+				@mysqli_free_result($this->query_result);
+
+			return @mysqli_close($this->link_id);
+		}
+		else
+			return false;
+	}
+
+
+	function get_names()
+	{
+		$result = $this->query('SHOW VARIABLES LIKE \'character_set_connection\'');
+		return $this->result($result, 0, 1);
+	}
+
+
+	function set_names($names)
+	{
+		return $this->query('SET NAMES \''.$this->escape($names).'\'');
+	}
+
+
+	function get_version()
+	{
+		$result = $this->query('SELECT VERSION()');
+
+		return array(
+			'name'		=> 'MySQL Improved',
+			'version'	=> preg_replace('%^([^-]+).*$%', '\\1', $this->result($result))
+		);
+	}
+
+
+	function table_exists($table_name, $no_prefix = false)
+	{
+		$result = $this->query('SHOW TABLES LIKE \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\'');
+		return $this->num_rows($result) > 0;
+	}
+
+
+	function field_exists($table_name, $field_name, $no_prefix = false)
+	{
+		$result = $this->query('SHOW COLUMNS FROM '.($no_prefix ? '' : $this->prefix).$table_name.' LIKE \''.$this->escape($field_name).'\'');
+		return $this->num_rows($result) > 0;
+	}
+
+
+	function index_exists($table_name, $index_name, $no_prefix = false)
+	{
+		$exists = false;
+
+		$result = $this->query('SHOW INDEX FROM '.($no_prefix ? '' : $this->prefix).$table_name);
+		while ($cur_index = $this->fetch_assoc($result))
+		{
+			if (strtolower($cur_index['Key_name']) == strtolower(($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name))
+			{
+				$exists = true;
+				break;
+			}
+		}
+
+		return $exists;
+	}
+
+
+	function create_table($table_name, $schema, $no_prefix = false)
+	{
+		if ($this->table_exists($table_name, $no_prefix))
+			return true;
+
+		$query = 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$table_name." (\n";
+
+		// Go through every schema element and add it to the query
+		foreach ($schema['FIELDS'] as $field_name => $field_data)
+		{
+			$field_data['datatype'] = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_data['datatype']);
+
+			$query .= $field_name.' '.$field_data['datatype'];
+
+			if (isset($field_data['collation']))
+				$query .= 'CHARACTER SET utf8 COLLATE utf8_'.$field_data['collation'];
+
+			if (!$field_data['allow_null'])
+				$query .= ' NOT NULL';
+
+			if (isset($field_data['default']))
+				$query .= ' DEFAULT '.$field_data['default'];
+
+			$query .= ",\n";
+		}
+
+		// If we have a primary key, add it
+		if (isset($schema['PRIMARY KEY']))
+			$query .= 'PRIMARY KEY ('.implode(',', $schema['PRIMARY KEY']).'),'."\n";
+
+		// Add unique keys
+		if (isset($schema['UNIQUE KEYS']))
+		{
+			foreach ($schema['UNIQUE KEYS'] as $key_name => $key_fields)
+				$query .= 'UNIQUE KEY '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$key_name.'('.implode(',', $key_fields).'),'."\n";
+		}
+
+		// Add indexes
+		if (isset($schema['INDEXES']))
+		{
+			foreach ($schema['INDEXES'] as $index_name => $index_fields)
+				$query .= 'KEY '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name.'('.implode(',', $index_fields).'),'."\n";
+		}
+
+		// We remove the last two characters (a newline and a comma) and add on the ending
+		$query = substr($query, 0, strlen($query) - 2)."\n".') ENGINE = '.(isset($schema['ENGINE']) ? $schema['ENGINE'] : 'MyISAM').' CHARACTER SET utf8';
+
+		return $this->query($query) ? true : false;
+	}
+
+
+	function drop_table($table_name, $no_prefix = false)
+	{
+		if (!$this->table_exists($table_name, $no_prefix))
+			return true;
+
+		return $this->query('DROP TABLE '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+	}
+
+
+	function rename_table($old_table, $new_table, $no_prefix = false)
+	{
+		// If the new table exists and the old one doesn't, then we're happy
+		if ($this->table_exists($new_table, $no_prefix) && !$this->table_exists($old_table, $no_prefix))
+			return true;
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$old_table.' RENAME TO '.($no_prefix ? '' : $this->prefix).$new_table) ? true : false;
+	}
+
+
+	function add_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+	{
+		if ($this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		$field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+
+		if (!is_null($default_value) && !is_int($default_value) && !is_float($default_value))
+			$default_value = '\''.$this->escape($default_value).'\'';
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.$field_name.' '.$field_type.($allow_null ? '' : ' NOT NULL').(!is_null($default_value) ? ' DEFAULT '.$default_value : '').(!is_null($after_field) ? ' AFTER '.$after_field : '')) ? true : false;
+	}
+
+
+	function alter_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+	{
+		if (!$this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		$field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+
+		if (!is_null($default_value) && !is_int($default_value) && !is_float($default_value))
+			$default_value = '\''.$this->escape($default_value).'\'';
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' MODIFY '.$field_name.' '.$field_type.($allow_null ? '' : ' NOT NULL').(!is_null($default_value) ? ' DEFAULT '.$default_value : '').(!is_null($after_field) ? ' AFTER '.$after_field : '')) ? true : false;
+	}
+
+
+	function drop_field($table_name, $field_name, $no_prefix = false)
+	{
+		if (!$this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' DROP '.$field_name) ? true : false;
+	}
+
+
+	function add_index($table_name, $index_name, $index_fields, $unique = false, $no_prefix = false)
+	{
+		if ($this->index_exists($table_name, $index_name, $no_prefix))
+			return true;
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.($unique ? 'UNIQUE ' : '').'INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name.' ('.implode(',', $index_fields).')') ? true : false;
+	}
+
+
+	function drop_index($table_name, $index_name, $no_prefix = false)
+	{
+		if (!$this->index_exists($table_name, $index_name, $no_prefix))
+			return true;
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' DROP INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name) ? true : false;
+	}
+
+	function truncate_table($table_name, $no_prefix = false)
+	{
+		return $this->query('TRUNCATE TABLE '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+	}
+}

+ 394 - 0
app/Core/DB/mysqli_innodb.php

@@ -0,0 +1,394 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+namespace ForkBB\Core\DB;
+
+// Make sure we have built in support for MySQL
+if (!function_exists('mysqli_connect'))
+	exit('This PHP environment doesn\'t have Improved MySQL (mysqli) support built in. Improved MySQL support is required if you want to use a MySQL 4.1 (or later) database to run this forum. Consult the PHP documentation for further assistance.');
+
+
+class DBLayer
+{
+	var $prefix;
+	var $link_id;
+	var $query_result;
+
+	var $saved_queries = array();
+	var $num_queries = 0;
+	var $in_transaction = 0;
+
+	var $error_no = false;
+	var $error_msg = 'Unknown';
+
+	var $datatype_transformations = array(
+		'%^SERIAL$%'	=>	'INT(10) UNSIGNED AUTO_INCREMENT'
+	);
+
+
+	function __construct($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect)
+	{
+		$this->prefix = $db_prefix;
+
+		// Was a custom port supplied with $db_host?
+		if (strpos($db_host, ':') !== false)
+			list($db_host, $db_port) = explode(':', $db_host);
+
+		// Persistent connection in MySQLi are only available in PHP 5.3 and later releases
+		$p_connect = $p_connect && version_compare(PHP_VERSION, '5.3.0', '>=') ? 'p:' : '';
+
+		if (isset($db_port))
+			$this->link_id = @mysqli_connect($p_connect.$db_host, $db_username, $db_password, $db_name, $db_port);
+		else
+			$this->link_id = @mysqli_connect($p_connect.$db_host, $db_username, $db_password, $db_name);
+
+		if (!$this->link_id)
+			error('Unable to connect to MySQL and select database. MySQL reported: '.mysqli_connect_error(), __FILE__, __LINE__);
+
+		// Setup the client-server character set (UTF-8)
+		if (!defined('FORUM_NO_SET_NAMES'))
+			$this->set_names('utf8');
+
+		return $this->link_id;
+	}
+
+
+	function start_transaction()
+	{
+		++$this->in_transaction;
+
+		mysqli_query($this->link_id, 'START TRANSACTION');
+		return;
+	}
+
+
+	function end_transaction()
+	{
+		--$this->in_transaction;
+
+		mysqli_query($this->link_id, 'COMMIT');
+		return;
+	}
+
+
+	function query($sql, $unbuffered = false)
+	{
+		if (defined('PUN_SHOW_QUERIES'))
+			$q_start = microtime(true);
+
+		$this->query_result = @mysqli_query($this->link_id, $sql);
+
+		if ($this->query_result)
+		{
+			if (defined('PUN_SHOW_QUERIES'))
+				$this->saved_queries[] = array($sql, sprintf('%.5f', microtime(true) - $q_start));
+
+			++$this->num_queries;
+
+			return $this->query_result;
+		}
+		else
+		{
+			if (defined('PUN_SHOW_QUERIES'))
+				$this->saved_queries[] = array($sql, 0);
+
+			$this->error_no = @mysqli_errno($this->link_id);
+			$this->error_msg = @mysqli_error($this->link_id);
+
+			// Rollback transaction
+			if ($this->in_transaction)
+				mysqli_query($this->link_id, 'ROLLBACK');
+
+			--$this->in_transaction;
+
+			return false;
+		}
+	}
+
+
+	function result($query_id = 0, $row = 0, $col = 0)
+	{
+		if ($query_id)
+		{
+			if ($row !== 0 && @mysqli_data_seek($query_id, $row) === false)
+				return false;
+
+			$cur_row = @mysqli_fetch_row($query_id);
+			if ($cur_row === false)
+				return false;
+
+			return $cur_row[$col];
+		}
+		else
+			return false;
+	}
+
+
+	function fetch_assoc($query_id = 0)
+	{
+		return ($query_id) ? @mysqli_fetch_assoc($query_id) : false;
+	}
+
+
+	function fetch_row($query_id = 0)
+	{
+		return ($query_id) ? @mysqli_fetch_row($query_id) : false;
+	}
+
+
+	function num_rows($query_id = 0)
+	{
+		return ($query_id) ? @mysqli_num_rows($query_id) : false;
+	}
+
+
+	function affected_rows()
+	{
+		return ($this->link_id) ? @mysqli_affected_rows($this->link_id) : false;
+	}
+
+
+	function insert_id()
+	{
+		return ($this->link_id) ? @mysqli_insert_id($this->link_id) : false;
+	}
+
+
+	function get_num_queries()
+	{
+		return $this->num_queries;
+	}
+
+
+	function get_saved_queries()
+	{
+		return $this->saved_queries;
+	}
+
+
+	function free_result($query_id = false)
+	{
+		return ($query_id) ? @mysqli_free_result($query_id) : false;
+	}
+
+
+	function escape($str)
+	{
+		return is_array($str) ? '' : mysqli_real_escape_string($this->link_id, $str);
+	}
+
+
+	function error()
+	{
+		$result['error_sql'] = @current(@end($this->saved_queries));
+		$result['error_no'] = $this->error_no;
+		$result['error_msg'] = $this->error_msg;
+
+		return $result;
+	}
+
+
+	function close()
+	{
+		if ($this->link_id)
+		{
+			if ($this->query_result instanceof mysqli_result)
+				@mysqli_free_result($this->query_result);
+
+			return @mysqli_close($this->link_id);
+		}
+		else
+			return false;
+	}
+
+
+	function get_names()
+	{
+		$result = $this->query('SHOW VARIABLES LIKE \'character_set_connection\'');
+		return $this->result($result, 0, 1);
+	}
+
+
+	function set_names($names)
+	{
+		return $this->query('SET NAMES \''.$this->escape($names).'\'');
+	}
+
+
+	function get_version()
+	{
+		$result = $this->query('SELECT VERSION()');
+
+		return array(
+			'name'		=> 'MySQL Improved (InnoDB)',
+			'version'	=> preg_replace('%^([^-]+).*$%', '\\1', $this->result($result))
+		);
+	}
+
+
+	function table_exists($table_name, $no_prefix = false)
+	{
+		$result = $this->query('SHOW TABLES LIKE \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\'');
+		return $this->num_rows($result) > 0;
+	}
+
+
+	function field_exists($table_name, $field_name, $no_prefix = false)
+	{
+		$result = $this->query('SHOW COLUMNS FROM '.($no_prefix ? '' : $this->prefix).$table_name.' LIKE \''.$this->escape($field_name).'\'');
+		return $this->num_rows($result) > 0;
+	}
+
+
+	function index_exists($table_name, $index_name, $no_prefix = false)
+	{
+		$exists = false;
+
+		$result = $this->query('SHOW INDEX FROM '.($no_prefix ? '' : $this->prefix).$table_name);
+		while ($cur_index = $this->fetch_assoc($result))
+		{
+			if (strtolower($cur_index['Key_name']) == strtolower(($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name))
+			{
+				$exists = true;
+				break;
+			}
+		}
+
+		return $exists;
+	}
+
+
+	function create_table($table_name, $schema, $no_prefix = false)
+	{
+		if ($this->table_exists($table_name, $no_prefix))
+			return true;
+
+		$query = 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$table_name." (\n";
+
+		// Go through every schema element and add it to the query
+		foreach ($schema['FIELDS'] as $field_name => $field_data)
+		{
+			$field_data['datatype'] = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_data['datatype']);
+
+			$query .= $field_name.' '.$field_data['datatype'];
+
+			if (isset($field_data['collation']))
+				$query .= 'CHARACTER SET utf8 COLLATE utf8_'.$field_data['collation'];
+
+			if (!$field_data['allow_null'])
+				$query .= ' NOT NULL';
+
+			if (isset($field_data['default']))
+				$query .= ' DEFAULT '.$field_data['default'];
+
+			$query .= ",\n";
+		}
+
+		// If we have a primary key, add it
+		if (isset($schema['PRIMARY KEY']))
+			$query .= 'PRIMARY KEY ('.implode(',', $schema['PRIMARY KEY']).'),'."\n";
+
+		// Add unique keys
+		if (isset($schema['UNIQUE KEYS']))
+		{
+			foreach ($schema['UNIQUE KEYS'] as $key_name => $key_fields)
+				$query .= 'UNIQUE KEY '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$key_name.'('.implode(',', $key_fields).'),'."\n";
+		}
+
+		// Add indexes
+		if (isset($schema['INDEXES']))
+		{
+			foreach ($schema['INDEXES'] as $index_name => $index_fields)
+				$query .= 'KEY '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name.'('.implode(',', $index_fields).'),'."\n";
+		}
+
+		// We remove the last two characters (a newline and a comma) and add on the ending
+		$query = substr($query, 0, strlen($query) - 2)."\n".') ENGINE = '.(isset($schema['ENGINE']) ? $schema['ENGINE'] : 'InnoDB').' CHARACTER SET utf8';
+
+		return $this->query($query) ? true : false;
+	}
+
+
+	function drop_table($table_name, $no_prefix = false)
+	{
+		if (!$this->table_exists($table_name, $no_prefix))
+			return true;
+
+		return $this->query('DROP TABLE '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+	}
+
+
+	function rename_table($old_table, $new_table, $no_prefix = false)
+	{
+		// If the new table exists and the old one doesn't, then we're happy
+		if ($this->table_exists($new_table, $no_prefix) && !$this->table_exists($old_table, $no_prefix))
+			return true;
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$old_table.' RENAME TO '.($no_prefix ? '' : $this->prefix).$new_table) ? true : false;
+	}
+
+
+	function add_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+	{
+		if ($this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		$field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+
+		if (!is_null($default_value) && !is_int($default_value) && !is_float($default_value))
+			$default_value = '\''.$this->escape($default_value).'\'';
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.$field_name.' '.$field_type.($allow_null ? '' : ' NOT NULL').(!is_null($default_value) ? ' DEFAULT '.$default_value : '').(!is_null($after_field) ? ' AFTER '.$after_field : '')) ? true : false;
+	}
+
+
+	function alter_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+	{
+		if (!$this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		$field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+
+		if (!is_null($default_value) && !is_int($default_value) && !is_float($default_value))
+			$default_value = '\''.$this->escape($default_value).'\'';
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' MODIFY '.$field_name.' '.$field_type.($allow_null ? '' : ' NOT NULL').(!is_null($default_value) ? ' DEFAULT '.$default_value : '').(!is_null($after_field) ? ' AFTER '.$after_field : '')) ? true : false;
+	}
+
+
+	function drop_field($table_name, $field_name, $no_prefix = false)
+	{
+		if (!$this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' DROP '.$field_name) ? true : false;
+	}
+
+
+	function add_index($table_name, $index_name, $index_fields, $unique = false, $no_prefix = false)
+	{
+		if ($this->index_exists($table_name, $index_name, $no_prefix))
+			return true;
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.($unique ? 'UNIQUE ' : '').'INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name.' ('.implode(',', $index_fields).')') ? true : false;
+	}
+
+
+	function drop_index($table_name, $index_name, $no_prefix = false)
+	{
+		if (!$this->index_exists($table_name, $index_name, $no_prefix))
+			return true;
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' DROP INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name) ? true : false;
+	}
+
+	function truncate_table($table_name, $no_prefix = false)
+	{
+		return $this->query('TRUNCATE TABLE '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+	}
+}

+ 438 - 0
app/Core/DB/pgsql.php

@@ -0,0 +1,438 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+namespace ForkBB\Core\DB;
+
+// Make sure we have built in support for PostgreSQL
+if (!function_exists('pg_connect'))
+	exit('This PHP environment doesn\'t have PostgreSQL support built in. PostgreSQL support is required if you want to use a PostgreSQL database to run this forum. Consult the PHP documentation for further assistance.');
+
+
+class DBLayer
+{
+	var $prefix;
+	var $link_id;
+	var $query_result;
+	var $last_query_text = array();
+	var $in_transaction = 0;
+
+	var $saved_queries = array();
+	var $num_queries = 0;
+
+	var $error_no = false;
+	var $error_msg = 'Unknown';
+
+	var $datatype_transformations = array(
+		'%^(TINY|SMALL)INT( )?(\\([0-9]+\\))?( )?(UNSIGNED)?$%i'			=>	'SMALLINT',
+		'%^(MEDIUM)?INT( )?(\\([0-9]+\\))?( )?(UNSIGNED)?$%i'				=>	'INTEGER',
+		'%^BIGINT( )?(\\([0-9]+\\))?( )?(UNSIGNED)?$%i'						=>	'BIGINT',
+		'%^(TINY|MEDIUM|LONG)?TEXT$%i'										=>	'TEXT',
+		'%^DOUBLE( )?(\\([0-9,]+\\))?( )?(UNSIGNED)?$%i'					=>	'DOUBLE PRECISION',
+		'%^FLOAT( )?(\\([0-9]+\\))?( )?(UNSIGNED)?$%i'						=>	'REAL'
+	);
+
+
+	function __construct($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect)
+	{
+		$this->prefix = $db_prefix;
+
+		if ($db_host)
+		{
+			if (strpos($db_host, ':') !== false)
+			{
+				list($db_host, $dbport) = explode(':', $db_host);
+				$connect_str[] = 'host='.$db_host.' port='.$dbport;
+			}
+			else
+				$connect_str[] = 'host='.$db_host;
+		}
+
+		if ($db_name)
+			$connect_str[] = 'dbname='.$db_name;
+
+		if ($db_username)
+			$connect_str[] = 'user='.$db_username;
+
+		if ($db_password)
+			$connect_str[] = 'password='.$db_password;
+
+		if ($p_connect)
+			$this->link_id = @pg_pconnect(implode(' ', $connect_str));
+		else
+			$this->link_id = @pg_connect(implode(' ', $connect_str));
+
+		if (!$this->link_id)
+			error('Unable to connect to PostgreSQL server', __FILE__, __LINE__);
+
+		// Setup the client-server character set (UTF-8)
+		if (!defined('FORUM_NO_SET_NAMES'))
+			$this->set_names('utf8');
+
+		return $this->link_id;
+	}
+	
+
+	function start_transaction()
+	{
+		++$this->in_transaction;
+
+		return (@pg_query($this->link_id, 'BEGIN')) ? true : false;
+	}
+
+
+	function end_transaction()
+	{
+		--$this->in_transaction;
+
+		if (@pg_query($this->link_id, 'COMMIT'))
+			return true;
+		else
+		{
+			@pg_query($this->link_id, 'ROLLBACK');
+			return false;
+		}
+	}
+
+
+	function query($sql, $unbuffered = false) // $unbuffered is ignored since there is no pgsql_unbuffered_query()
+	{
+		if (strrpos($sql, 'LIMIT') !== false)
+			$sql = preg_replace('%LIMIT ([0-9]+),([ 0-9]+)%', 'LIMIT \\2 OFFSET \\1', $sql);
+
+		if (defined('PUN_SHOW_QUERIES'))
+			$q_start = microtime(true);
+
+		@pg_send_query($this->link_id, $sql);
+		$this->query_result = @pg_get_result($this->link_id);
+
+		if (pg_result_status($this->query_result) != PGSQL_FATAL_ERROR)
+		{
+			if (defined('PUN_SHOW_QUERIES'))
+				$this->saved_queries[] = array($sql, sprintf('%.5f', microtime(true) - $q_start));
+
+			++$this->num_queries;
+
+			$this->last_query_text[intval($this->query_result)] = $sql;
+
+			return $this->query_result;
+		}
+		else
+		{
+			if (defined('PUN_SHOW_QUERIES'))
+				$this->saved_queries[] = array($sql, 0);
+
+			$this->error_no = false;
+			$this->error_msg = @pg_result_error($this->query_result);
+
+			if ($this->in_transaction)
+				@pg_query($this->link_id, 'ROLLBACK');
+
+			--$this->in_transaction;
+
+			return false;
+		}
+	}
+
+
+	function result($query_id = 0, $row = 0, $col = 0)
+	{
+		return ($query_id) ? @pg_fetch_result($query_id, $row, $col) : false;
+	}
+
+
+	function fetch_assoc($query_id = 0)
+	{
+		return ($query_id) ? @pg_fetch_assoc($query_id) : false;
+	}
+
+
+	function fetch_row($query_id = 0)
+	{
+		return ($query_id) ? @pg_fetch_row($query_id) : false;
+	}
+
+
+	function num_rows($query_id = 0)
+	{
+		return ($query_id) ? @pg_num_rows($query_id) : false;
+	}
+
+
+	function affected_rows()
+	{
+		return ($this->query_result) ? @pg_affected_rows($this->query_result) : false;
+	}
+
+
+	function insert_id()
+	{
+		$query_id = $this->query_result;
+
+		if ($query_id && $this->last_query_text[intval($query_id)] != '')
+		{
+			if (preg_match('%^INSERT INTO ([a-z0-9\_\-]+)%is', $this->last_query_text[intval($query_id)], $table_name))
+			{
+				// Hack (don't ask)
+				if (substr($table_name[1], -6) == 'groups')
+					$table_name[1] .= '_g';
+
+				$temp_q_id = @pg_query($this->link_id, 'SELECT currval(\''.$table_name[1].'_id_seq\')');
+				return ($temp_q_id) ? intval(@pg_fetch_result($temp_q_id, 0)) : false;
+			}
+		}
+
+		return false;
+	}
+
+
+	function get_num_queries()
+	{
+		return $this->num_queries;
+	}
+
+
+	function get_saved_queries()
+	{
+		return $this->saved_queries;
+	}
+
+
+	function free_result($query_id = false)
+	{
+		if (!$query_id)
+			$query_id = $this->query_result;
+
+		return ($query_id) ? @pg_free_result($query_id) : false;
+	}
+
+
+	function escape($str)
+	{
+		return is_array($str) ? '' : pg_escape_string($str);
+	}
+
+
+	function error()
+	{
+		$result['error_sql'] = @current(@end($this->saved_queries));
+		$result['error_no'] = $this->error_no;
+		$result['error_msg'] = $this->error_msg;
+
+		return $result;
+	}
+
+
+	function close()
+	{
+		if ($this->link_id)
+		{
+			if ($this->in_transaction)
+			{
+				if (defined('PUN_SHOW_QUERIES'))
+					$this->saved_queries[] = array('COMMIT', 0);
+
+				@pg_query($this->link_id, 'COMMIT');
+			}
+
+			if ($this->query_result)
+				@pg_free_result($this->query_result);
+
+			return @pg_close($this->link_id);
+		}
+		else
+			return false;
+	}
+
+
+	function get_names()
+	{
+		$result = $this->query('SHOW client_encoding');
+		return strtolower($this->result($result)); // MySQL returns lowercase so lets be consistent
+	}
+
+
+	function set_names($names)
+	{
+		return $this->query('SET NAMES \''.$this->escape($names).'\'');
+	}
+
+
+	function get_version()
+	{
+		$result = $this->query('SELECT VERSION()');
+
+		return array(
+			'name'		=> 'PostgreSQL',
+			'version'	=> preg_replace('%^[^0-9]+([^\s,-]+).*$%', '\\1', $this->result($result))
+		);
+	}
+
+
+	function table_exists($table_name, $no_prefix = false)
+	{
+		$result = $this->query('SELECT 1 FROM pg_class WHERE relname = \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\'');
+		return $this->num_rows($result) > 0;
+	}
+
+
+	function field_exists($table_name, $field_name, $no_prefix = false)
+	{
+		$result = $this->query('SELECT 1 FROM pg_class c INNER JOIN pg_attribute a ON a.attrelid = c.oid WHERE c.relname = \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\' AND a.attname = \''.$this->escape($field_name).'\'');
+		return $this->num_rows($result) > 0;
+	}
+
+
+	function index_exists($table_name, $index_name, $no_prefix = false)
+	{
+		$result = $this->query('SELECT 1 FROM pg_index i INNER JOIN pg_class c1 ON c1.oid = i.indrelid INNER JOIN pg_class c2 ON c2.oid = i.indexrelid WHERE c1.relname = \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\' AND c2.relname = \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'_'.$this->escape($index_name).'\'');
+		return $this->num_rows($result) > 0;
+	}
+
+
+	function create_table($table_name, $schema, $no_prefix = false)
+	{
+		if ($this->table_exists($table_name, $no_prefix))
+			return true;
+
+		$query = 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$table_name." (\n";
+
+		// Go through every schema element and add it to the query
+		foreach ($schema['FIELDS'] as $field_name => $field_data)
+		{
+			$field_data['datatype'] = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_data['datatype']);
+
+			$query .= $field_name.' '.$field_data['datatype'];
+
+			// The SERIAL datatype is a special case where we don't need to say not null
+			if (!$field_data['allow_null'] && $field_data['datatype'] != 'SERIAL')
+				$query .= ' NOT NULL';
+
+			if (isset($field_data['default']))
+				$query .= ' DEFAULT '.$field_data['default'];
+
+			$query .= ",\n";
+		}
+
+		// If we have a primary key, add it
+		if (isset($schema['PRIMARY KEY']))
+			$query .= 'PRIMARY KEY ('.implode(',', $schema['PRIMARY KEY']).'),'."\n";
+
+		// Add unique keys
+		if (isset($schema['UNIQUE KEYS']))
+		{
+			foreach ($schema['UNIQUE KEYS'] as $key_name => $key_fields)
+				$query .= 'UNIQUE ('.implode(',', $key_fields).'),'."\n";
+		}
+
+		// We remove the last two characters (a newline and a comma) and add on the ending
+		$query = substr($query, 0, strlen($query) - 2)."\n".')';
+
+		$result = $this->query($query) ? true : false;
+
+		// Add indexes
+		if (isset($schema['INDEXES']))
+		{
+			foreach ($schema['INDEXES'] as $index_name => $index_fields)
+				$result &= $this->add_index($table_name, $index_name, $index_fields, false, $no_prefix);
+		}
+
+		return $result;
+	}
+
+
+	function drop_table($table_name, $no_prefix = false)
+	{
+		if (!$this->table_exists($table_name, $no_prefix))
+			return true;
+
+		return $this->query('DROP TABLE '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+	}
+
+
+	function rename_table($old_table, $new_table, $no_prefix = false)
+	{
+		// If the new table exists and the old one doesn't, then we're happy
+		if ($this->table_exists($new_table, $no_prefix) && !$this->table_exists($old_table, $no_prefix))
+			return true;
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$old_table.' RENAME TO '.($no_prefix ? '' : $this->prefix).$new_table) ? true : false;
+	}
+
+
+	function add_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+	{
+		if ($this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		$field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+
+		$result = $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.$field_name.' '.$field_type) ? true : false;
+
+		if (!is_null($default_value))
+		{
+			if (!is_int($default_value) && !is_float($default_value))
+				$default_value = '\''.$this->escape($default_value).'\'';
+
+			$result &= $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ALTER '.$field_name.' SET DEFAULT '.$default_value) ? true : false;
+			$result &= $this->query('UPDATE '.($no_prefix ? '' : $this->prefix).$table_name.' SET '.$field_name.'='.$default_value) ? true : false;
+		}
+
+		if (!$allow_null)
+			$result &= $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ALTER '.$field_name.' SET NOT NULL') ? true : false;
+
+		return $result;
+	}
+
+
+	function alter_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+	{
+		if (!$this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		$field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+
+		$result = $this->add_field($table_name, 'tmp_'.$field_name, $field_type, $allow_null, $default_value, $after_field, $no_prefix);
+		$result &= $this->query('UPDATE '.($no_prefix ? '' : $this->prefix).$table_name.' SET tmp_'.$field_name.' = '.$field_name) ? true : false;
+		$result &= $this->drop_field($table_name, $field_name, $no_prefix);
+		$result &= $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' RENAME COLUMN tmp_'.$field_name.' TO '.$field_name) ? true : false;
+
+		return $result;
+	}
+
+
+	function drop_field($table_name, $field_name, $no_prefix = false)
+	{
+		if (!$this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' DROP '.$field_name) ? true : false;
+	}
+
+
+	function add_index($table_name, $index_name, $index_fields, $unique = false, $no_prefix = false)
+	{
+		if ($this->index_exists($table_name, $index_name, $no_prefix))
+			return true;
+
+		return $this->query('CREATE '.($unique ? 'UNIQUE ' : '').'INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name.' ON '.($no_prefix ? '' : $this->prefix).$table_name.'('.implode(',', $index_fields).')') ? true : false;
+	}
+
+
+	function drop_index($table_name, $index_name, $no_prefix = false)
+	{
+		if (!$this->index_exists($table_name, $index_name, $no_prefix))
+			return true;
+
+		return $this->query('DROP INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name) ? true : false;
+	}
+
+	function truncate_table($table_name, $no_prefix = false)
+	{
+		return $this->query('DELETE FROM '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+	}
+}

+ 597 - 0
app/Core/DB/sqlite.php

@@ -0,0 +1,597 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+namespace ForkBB\Core\DB;
+
+// Make sure we have built in support for SQLite
+if (!function_exists('sqlite_open'))
+	exit('This PHP environment doesn\'t have SQLite support built in. SQLite support is required if you want to use a SQLite database to run this forum. Consult the PHP documentation for further assistance.');
+
+
+class DBLayer
+{
+	var $prefix;
+	var $link_id;
+	var $query_result;
+	var $in_transaction = 0;
+
+	var $saved_queries = array();
+	var $num_queries = 0;
+
+	var $error_no = false;
+	var $error_msg = 'Unknown';
+
+	var $datatype_transformations = array(
+		'%^SERIAL$%'															=>	'INTEGER',
+		'%^(TINY|SMALL|MEDIUM|BIG)?INT( )?(\\([0-9]+\\))?( )?(UNSIGNED)?$%i'	=>	'INTEGER',
+		'%^(TINY|MEDIUM|LONG)?TEXT$%i'											=>	'TEXT'
+	);
+
+
+	function __construct($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect)
+	{
+		// Prepend $db_name with the path to the forum root directory
+		$db_name = PUN_ROOT.$db_name;
+
+		$this->prefix = $db_prefix;
+
+		if (!file_exists($db_name))
+		{
+			@touch($db_name);
+			@chmod($db_name, 0666);
+			if (!file_exists($db_name))
+				error('Unable to create new database \''.$db_name.'\'. Permission denied', __FILE__, __LINE__);
+		}
+
+		if (!is_readable($db_name))
+			error('Unable to open database \''.$db_name.'\' for reading. Permission denied', __FILE__, __LINE__);
+
+		if (!forum_is_writable($db_name))
+			error('Unable to open database \''.$db_name.'\' for writing. Permission denied', __FILE__, __LINE__);
+
+		if ($p_connect)
+			$this->link_id = @sqlite_popen($db_name, 0666, $sqlite_error);
+		else
+			$this->link_id = @sqlite_open($db_name, 0666, $sqlite_error);
+
+		if (!$this->link_id)
+			error('Unable to open database \''.$db_name.'\'. SQLite reported: '.$sqlite_error, __FILE__, __LINE__);
+		else
+			return $this->link_id;
+	}
+	
+	
+	function start_transaction()
+	{
+		++$this->in_transaction;
+
+		return (@sqlite_query($this->link_id, 'BEGIN')) ? true : false;
+	}
+
+
+	function end_transaction()
+	{
+		--$this->in_transaction;
+
+		if (@sqlite_query($this->link_id, 'COMMIT'))
+			return true;
+		else
+		{
+			@sqlite_query($this->link_id, 'ROLLBACK');
+			return false;
+		}
+	}
+
+
+	function query($sql, $unbuffered = false)
+	{
+		if (defined('PUN_SHOW_QUERIES'))
+			$q_start = microtime(true);
+
+		if ($unbuffered)
+			$this->query_result = @sqlite_unbuffered_query($this->link_id, $sql);
+		else
+			$this->query_result = @sqlite_query($this->link_id, $sql);
+
+		if ($this->query_result)
+		{
+			if (defined('PUN_SHOW_QUERIES'))
+				$this->saved_queries[] = array($sql, sprintf('%.5f', microtime(true) - $q_start));
+
+			++$this->num_queries;
+
+			return $this->query_result;
+		}
+		else
+		{
+			if (defined('PUN_SHOW_QUERIES'))
+				$this->saved_queries[] = array($sql, 0);
+
+			$this->error_no = @sqlite_last_error($this->link_id);
+			$this->error_msg = @sqlite_error_string($this->error_no);
+
+			if ($this->in_transaction)
+				@sqlite_query($this->link_id, 'ROLLBACK');
+
+			--$this->in_transaction;
+
+			return false;
+		}
+	}
+
+
+	function result($query_id = 0, $row = 0, $col = 0)
+	{
+		if ($query_id)
+		{
+			if ($row !== 0 && @sqlite_seek($query_id, $row) === false)
+				return false;
+
+			$cur_row = @sqlite_current($query_id);
+			if ($cur_row === false)
+				return false;
+
+			return $cur_row[$col];
+		}
+		else
+			return false;
+	}
+
+
+	function fetch_assoc($query_id = 0)
+	{
+		if ($query_id)
+		{
+			$cur_row = @sqlite_fetch_array($query_id, SQLITE_ASSOC);
+			if ($cur_row)
+			{
+				// Horrible hack to get rid of table names and table aliases from the array keys
+				foreach ($cur_row as $key => $value)
+				{
+					$dot_spot = strpos($key, '.');
+					if ($dot_spot !== false)
+					{
+						unset($cur_row[$key]);
+						$key = substr($key, $dot_spot+1);
+						$cur_row[$key] = $value;
+					}
+				}
+			}
+
+			return $cur_row;
+		}
+		else
+			return false;
+	}
+
+
+	function fetch_row($query_id = 0)
+	{
+		return ($query_id) ? @sqlite_fetch_array($query_id, SQLITE_NUM) : false;
+	}
+
+
+	function num_rows($query_id = 0)
+	{
+		return ($query_id) ? @sqlite_num_rows($query_id) : false;
+	}
+
+
+	function affected_rows()
+	{
+		return ($this->link_id) ? @sqlite_changes($this->link_id) : false;
+	}
+
+
+	function insert_id()
+	{
+		return ($this->link_id) ? @sqlite_last_insert_rowid($this->link_id) : false;
+	}
+
+
+	function get_num_queries()
+	{
+		return $this->num_queries;
+	}
+
+
+	function get_saved_queries()
+	{
+		return $this->saved_queries;
+	}
+
+
+	function free_result($query_id = false)
+	{
+		return true;
+	}
+
+
+	function escape($str)
+	{
+		return is_array($str) ? '' : sqlite_escape_string($str);
+	}
+
+
+	function error()
+	{
+		$result['error_sql'] = @current(@end($this->saved_queries));
+		$result['error_no'] = $this->error_no;
+		$result['error_msg'] = $this->error_msg;
+
+		return $result;
+	}
+
+
+	function close()
+	{
+		if ($this->link_id)
+		{
+			if ($this->in_transaction)
+			{
+				if (defined('PUN_SHOW_QUERIES'))
+					$this->saved_queries[] = array('COMMIT', 0);
+
+				@sqlite_query($this->link_id, 'COMMIT');
+			}
+
+			return @sqlite_close($this->link_id);
+		}
+		else
+			return false;
+	}
+
+
+	function get_names()
+	{
+		return '';
+	}
+
+
+	function set_names($names)
+	{
+		return true;
+	}
+
+
+	function get_version()
+	{
+		return array(
+			'name'		=> 'SQLite',
+			'version'	=> sqlite_libversion()
+		);
+	}
+
+
+	function table_exists($table_name, $no_prefix = false)
+	{
+		$result = $this->query('SELECT 1 FROM sqlite_master WHERE name = \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\' AND type=\'table\'');
+		return $this->num_rows($result) > 0;
+	}
+
+
+	function field_exists($table_name, $field_name, $no_prefix = false)
+	{
+		$result = $this->query('SELECT sql FROM sqlite_master WHERE name = \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\' AND type=\'table\'');
+		if (!$this->num_rows($result))
+			return false;
+
+		return preg_match('%[\r\n]'.preg_quote($field_name, '%').' %', $this->result($result));
+	}
+
+
+	function index_exists($table_name, $index_name, $no_prefix = false)
+	{
+		$result = $this->query('SELECT 1 FROM sqlite_master WHERE tbl_name = \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\' AND name = \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'_'.$this->escape($index_name).'\' AND type=\'index\'');
+		return $this->num_rows($result) > 0;
+	}
+
+
+	function create_table($table_name, $schema, $no_prefix = false)
+	{
+		if ($this->table_exists($table_name, $no_prefix))
+			return true;
+
+		$query = 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$table_name." (\n";
+
+		// Go through every schema element and add it to the query
+		foreach ($schema['FIELDS'] as $field_name => $field_data)
+		{
+			$field_data['datatype'] = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_data['datatype']);
+
+			$query .= $field_name.' '.$field_data['datatype'];
+
+			if (!$field_data['allow_null'])
+				$query .= ' NOT NULL';
+
+			if (isset($field_data['default']))
+				$query .= ' DEFAULT '.$field_data['default'];
+
+			$query .= ",\n";
+		}
+
+		// If we have a primary key, add it
+		if (isset($schema['PRIMARY KEY']))
+			$query .= 'PRIMARY KEY ('.implode(',', $schema['PRIMARY KEY']).'),'."\n";
+
+		// Add unique keys
+		if (isset($schema['UNIQUE KEYS']))
+		{
+			foreach ($schema['UNIQUE KEYS'] as $key_name => $key_fields)
+				$query .= 'UNIQUE ('.implode(',', $key_fields).'),'."\n";
+		}
+
+		// We remove the last two characters (a newline and a comma) and add on the ending
+		$query = substr($query, 0, strlen($query) - 2)."\n".')';
+
+		$result = $this->query($query) ? true : false;
+
+		// Add indexes
+		if (isset($schema['INDEXES']))
+		{
+			foreach ($schema['INDEXES'] as $index_name => $index_fields)
+				$result &= $this->add_index($table_name, $index_name, $index_fields, false, $no_prefix);
+		}
+
+		return $result;
+	}
+
+
+	function drop_table($table_name, $no_prefix = false)
+	{
+		if (!$this->table_exists($table_name, $no_prefix))
+			return true;
+
+		return $this->query('DROP TABLE '.($no_prefix ? '' : $this->prefix).$this->escape($table_name)) ? true : false;
+	}
+
+
+	function rename_table($old_table, $new_table, $no_prefix = false)
+	{
+		// If the old table does not exist
+		if (!$this->table_exists($old_table, $no_prefix))
+			return false;
+		// If the table names are the same
+		else if ($old_table == $new_table)
+			return true;
+		// If the new table already exists
+		else if ($this->table_exists($new_table, $no_prefix))
+			return false;
+
+		$table = $this->get_table_info($old_table, $no_prefix);
+
+		// Create new table
+		$query = str_replace('CREATE TABLE '.($no_prefix ? '' : $this->prefix).$this->escape($old_table).' (', 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$this->escape($new_table).' (', $table['sql']);
+		$result = $this->query($query) ? true : false;
+
+		// Recreate indexes
+		if (!empty($table['indices']))
+		{
+			foreach ($table['indices'] as $cur_index)
+			{
+				$query = str_replace('CREATE INDEX '.($no_prefix ? '' : $this->prefix).$this->escape($old_table), 'CREATE INDEX '.($no_prefix ? '' : $this->prefix).$this->escape($new_table), $cur_index);
+				$query = str_replace('ON '.($no_prefix ? '' : $this->prefix).$this->escape($old_table), 'ON '.($no_prefix ? '' : $this->prefix).$this->escape($new_table), $query);
+				$result &= $this->query($query) ? true : false;
+			}
+		}
+
+		// Copy content across
+		$result &= $this->query('INSERT INTO '.($no_prefix ? '' : $this->prefix).$this->escape($new_table).' SELECT * FROM '.($no_prefix ? '' : $this->prefix).$this->escape($old_table)) ? true : false;
+
+		// Drop the old table if the new one exists
+		if ($this->table_exists($new_table, $no_prefix))
+			$result &= $this->drop_table($old_table, $no_prefix);
+
+		return $result;
+	}
+
+
+	function get_table_info($table_name, $no_prefix = false)
+	{
+		// Grab table info
+		$result = $this->query('SELECT sql FROM sqlite_master WHERE tbl_name = \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\' ORDER BY type DESC') or error('Unable to fetch table information', __FILE__, __LINE__, $this->error());
+		$num_rows = $this->num_rows($result);
+
+		if ($num_rows == 0)
+			return;
+
+		$table = array();
+		$table['indices'] = array();
+		while ($cur_index = $this->fetch_assoc($result))
+		{
+			if (empty($cur_index['sql']))
+				continue;
+
+			if (!isset($table['sql']))
+				$table['sql'] = $cur_index['sql'];
+			else
+				$table['indices'][] = $cur_index['sql'];
+		}
+
+		// Work out the columns in the table currently
+		$table_lines = explode("\n", $table['sql']);
+		$table['columns'] = array();
+		foreach ($table_lines as $table_line)
+		{
+			$table_line = trim($table_line, " \t\n\r,"); // trim spaces, tabs, newlines, and commas
+			if (substr($table_line, 0, 12) == 'CREATE TABLE')
+				continue;
+			else if (substr($table_line, 0, 11) == 'PRIMARY KEY')
+				$table['primary_key'] = $table_line;
+			else if (substr($table_line, 0, 6) == 'UNIQUE')
+				$table['unique'] = $table_line;
+			else if (substr($table_line, 0, strpos($table_line, ' ')) != '')
+				$table['columns'][substr($table_line, 0, strpos($table_line, ' '))] = trim(substr($table_line, strpos($table_line, ' ')));
+		}
+
+		return $table;
+	}
+
+
+	function add_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+	{
+		if ($this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		$table = $this->get_table_info($table_name, $no_prefix);
+
+		// Create temp table
+		$now = time();
+		$tmptable = str_replace('CREATE TABLE '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).' (', 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'_t'.$now.' (', $table['sql']);
+		$result = $this->query($tmptable) ? true : false;
+		$result &= $this->query('INSERT INTO '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'_t'.$now.' SELECT * FROM '.($no_prefix ? '' : $this->prefix).$this->escape($table_name)) ? true : false;
+
+		// Create new table sql
+		$field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+		$query = $field_type;
+
+		if (!$allow_null)
+			$query .= ' NOT NULL';
+
+		if (is_string($default_value))
+			$default_value = '\''.$this->escape($default_value).'\'';
+
+		if (!is_null($default_value))
+			$query .= ' DEFAULT '.$default_value;
+
+		$old_columns = array_keys($table['columns']);
+
+		// Determine the proper offset
+		if (!is_null($after_field))
+			$offset = array_search($after_field, array_keys($table['columns']), true) + 1;
+		else
+			$offset = count($table['columns']);
+
+		// Out of bounds checks
+		if ($offset > count($table['columns']))
+			$offset = count($table['columns']);
+		else if ($offset < 0)
+			$offset = 0;
+
+		if (!is_null($field_name) && $field_name !== '')
+			$table['columns'] = array_merge(array_slice($table['columns'], 0, $offset), array($field_name => $query), array_slice($table['columns'], $offset));
+
+		$new_table = 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).' (';
+
+		foreach ($table['columns'] as $cur_column => $column_details)
+			$new_table .= "\n".$cur_column.' '.$column_details.',';
+
+		if (isset($table['unique']))
+			$new_table .= "\n".$table['unique'].',';
+
+		if (isset($table['primary_key']))
+			$new_table .= "\n".$table['primary_key'].',';
+
+		$new_table = trim($new_table, ',')."\n".');';
+
+		// Drop old table
+		$result &= $this->drop_table($table_name, $no_prefix);
+
+		// Create new table
+		$result &= $this->query($new_table) ? true : false;
+
+		// Recreate indexes
+		if (!empty($table['indices']))
+		{
+			foreach ($table['indices'] as $cur_index)
+				$result &= $this->query($cur_index) ? true : false;
+		}
+
+		// Copy content back
+		$result &= $this->query('INSERT INTO '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).' ('.implode(', ', $old_columns).') SELECT * FROM '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'_t'.$now) ? true : false;
+
+		// Drop temp table
+		$result &= $this->drop_table($table_name.'_t'.$now, $no_prefix);
+
+		return $result;
+	}
+
+
+	function alter_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+	{
+		// Unneeded for SQLite
+		return true;
+	}
+
+
+	function drop_field($table_name, $field_name, $no_prefix = false)
+	{
+		if (!$this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		$table = $this->get_table_info($table_name, $no_prefix);
+
+		// Create temp table
+		$now = time();
+		$tmptable = str_replace('CREATE TABLE '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).' (', 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'_t'.$now.' (', $table['sql']);
+		$result = $this->query($tmptable) ? true : false;
+		$result &= $this->query('INSERT INTO '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'_t'.$now.' SELECT * FROM '.($no_prefix ? '' : $this->prefix).$this->escape($table_name)) ? true : false;
+
+		// Work out the columns we need to keep and the sql for the new table
+		unset($table['columns'][$field_name]);
+		$new_columns = array_keys($table['columns']);
+
+		$new_table = 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).' (';
+
+		foreach ($table['columns'] as $cur_column => $column_details)
+			$new_table .= "\n".$cur_column.' '.$column_details.',';
+
+		if (isset($table['unique']))
+			$new_table .= "\n".$table['unique'].',';
+
+		if (isset($table['primary_key']))
+			$new_table .= "\n".$table['primary_key'].',';
+
+		$new_table = trim($new_table, ',')."\n".');';
+
+		// Drop old table
+		$result &= $this->drop_table($table_name, $no_prefix);
+
+		// Create new table
+		$result &= $this->query($new_table) ? true : false;
+
+		// Recreate indexes
+		if (!empty($table['indices']))
+		{
+			foreach ($table['indices'] as $cur_index)
+				if (!preg_match('%\('.preg_quote($field_name, '%').'\)%', $cur_index))
+					$result &= $this->query($cur_index) ? true : false;
+		}
+
+		// Copy content back
+		$result &= $this->query('INSERT INTO '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).' SELECT '.implode(', ', $new_columns).' FROM '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'_t'.$now) ? true : false;
+
+		// Drop temp table
+		$result &= $this->drop_table($table_name.'_t'.$now, $no_prefix);
+
+		return $result;
+	}
+
+
+	function add_index($table_name, $index_name, $index_fields, $unique = false, $no_prefix = false)
+	{
+		if ($this->index_exists($table_name, $index_name, $no_prefix))
+			return true;
+
+		return $this->query('CREATE '.($unique ? 'UNIQUE ' : '').'INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name.' ON '.($no_prefix ? '' : $this->prefix).$table_name.'('.implode(',', $index_fields).')') ? true : false;
+	}
+
+
+	function drop_index($table_name, $index_name, $no_prefix = false)
+	{
+		if (!$this->index_exists($table_name, $index_name, $no_prefix))
+			return true;
+
+		return $this->query('DROP INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name) ? true : false;
+	}
+
+	function truncate_table($table_name, $no_prefix = false)
+	{
+		return $this->query('DELETE FROM '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+	}
+}

+ 65 - 0
app/Core/DBLoader.php

@@ -0,0 +1,65 @@
+<?php
+
+namespace ForkBB\Core;
+
+class DBLoader
+{
+    protected $host;
+    protected $username;
+    protected $password;
+    protected $name;
+    protected $prefix;
+    protected $connect;
+
+    public function __construct($host, $username, $password, $name, $prefix, $connect)
+    {
+        $this->host = $host;
+        $this->username = $username;
+        $this->password = $password;
+        $this->name = $name;
+        $this->prefix = $prefix;
+        $this->connect = $connect;
+    }
+
+    /**
+     * @param string $type
+     *
+     * @return \ForkBB\Core\DB\DBLayer
+     */
+    public function load($type)
+    {
+        switch ($type)
+        {
+            case 'mysql':
+                require_once __DIR__ . '/DB/mysql.php';
+                break;
+
+            case 'mysql_innodb':
+                require_once __DIR__ . '/DB/mysql_innodb.php';
+                break;
+
+            case 'mysqli':
+                require_once __DIR__ . '/DB/mysqli.php';
+                break;
+
+            case 'mysqli_innodb':
+                require_once __DIR__ . '/DB/mysqli_innodb.php';
+                break;
+
+            case 'pgsql':
+                require_once __DIR__ . '/DB/pgsql.php';
+                break;
+
+            case 'sqlite':
+                require_once __DIR__ . '/DB/sqlite.php';
+                break;
+
+            default:
+                error('\''.$type.'\' is not a valid database type. Please check settings', __FILE__, __LINE__);
+                break;
+        }
+
+        // Create the database adapter object (and open/connect to/select db)
+        return new \ForkBB\Core\DB\DBLayer($this->host, $this->username, $this->password, $this->name, $this->prefix, $this->connect);
+    }
+}

+ 2169 - 0
app/Core/Install.php

@@ -0,0 +1,2169 @@
+<?php
+
+namespace ForkBB\Core;
+
+use R2\DependencyInjection\ContainerInterface;
+use R2\DependencyInjection\ContainerAwareInterface;
+
+class Install implements ContainerAwareInterface
+{
+    /**
+     * @var Request
+     */
+    protected $request;
+
+    /**
+     * Контейнер
+     * @var ContainerInterface
+     */
+    protected $c;
+
+    /**
+     * Инициализация контейнера
+     *
+     * @param ContainerInterface $container
+     */
+    public function setContainer(ContainerInterface $container)
+    {
+        $this->c = $container;
+    }
+
+    /**
+     * @param Request $request
+     */
+    public function __construct($request)
+    {
+        $this->request = $request;
+    }
+
+    protected function generate_config_file($base_url, $db_type, $db_host, $db_name, $db_username, $db_password, $db_prefix, $cookie_prefix)
+    {
+        $config = file_get_contents($this->c->getParameter('DIR_CONFIG') . 'main.dist.php');
+        if (false === $config) {
+            exit('Error open main.dist.php');
+        }
+        $config = str_replace('_BASE_URL_', addslashes($base_url), $config);
+        $config = str_replace('_DB_TYPE_', addslashes($db_type), $config);
+        $config = str_replace('_DB_HOST_', addslashes($db_host), $config);
+        $config = str_replace('_DB_USERNAME_', addslashes($db_username), $config);
+        $config = str_replace('_DB_PASSWORD_', addslashes($db_password), $config);
+        $config = str_replace('_DB_NAME_', addslashes($db_name), $config);
+        $config = str_replace('_DB_PREFIX_', addslashes($db_prefix), $config);
+        $config = str_replace('_COOKIE_PREFIX_', addslashes($cookie_prefix), $config);
+        $config = str_replace('_COOKIE_SALT_', addslashes(random_key(21, false, true)), $config);
+
+        return $config;
+    }
+
+    public function start()
+    {
+
+        /**
+         * Copyright (C) 2008-2012 FluxBB
+         * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+         * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+         */
+
+        // Send the Content-type header in case the web server is setup to send something else
+        header('Content-type: text/html; charset=utf-8');
+
+        // Turn off PHP time limit
+        @set_time_limit(0);
+
+        // If we've been passed a default language, use it
+        $install_lang = $this->request->requestStr('install_lang', 'English');
+
+        // Make sure we got a valid language string
+        $install_lang = preg_replace('%[\.\\/]%', '', $install_lang);
+
+        // If such a language pack doesn't exist, or isn't up-to-date enough to translate this page, default to English
+        if (! file_exists(PUN_ROOT . 'lang/' . $install_lang . '/install.php'))
+            $install_lang = 'English';
+
+        require PUN_ROOT . 'lang/' . $install_lang . '/install.php';
+
+        // If the cache directory is not specified, we use the default setting
+        if (! defined('FORUM_CACHE_DIR'))
+            define('FORUM_CACHE_DIR', PUN_ROOT . 'cache/');
+
+        // Make sure we are running at least MIN_PHP_VERSION
+        if (! function_exists('version_compare') || version_compare(PHP_VERSION, MIN_PHP_VERSION, '<'))
+            exit(sprintf($lang_install['You are running error'], 'PHP', PHP_VERSION, FORUM_VERSION, MIN_PHP_VERSION));
+
+
+        if (null !== $this->request->postBool('generate_config'))
+        {
+            header('Content-Type: text/x-delimtext; name="main.php"');
+            header('Content-disposition: attachment; filename=main.php');
+
+            $base_url = $this->request->postStr('base_url', '');
+            $db_type = $this->request->postStr('db_type', '');
+            $db_host = $this->request->postStr('db_host', '');
+            $db_name = $this->request->postStr('db_name', '');
+            $db_username = $this->request->postStr('db_username', '');
+            $db_password = $this->request->postStr('db_password', '');
+            $db_prefix = $this->request->postStr('db_prefix', '');
+            $cookie_prefix = $this->request->postStr('cookie_prefix', '');
+
+            echo $this->generate_config_file($base_url, $db_type, $db_host, $db_name, $db_username, $db_password, $db_prefix, $cookie_prefix);
+            exit;
+        }
+
+
+        if (null === $this->request->postBool('form_sent'))
+        {
+            // Make an educated guess regarding base_url
+            $base_url  = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https://' : 'http://';    // protocol
+            $base_url .= preg_replace('%:(80|443)$%', '', $_SERVER['HTTP_HOST']);                            // host[:port]
+            $base_url .= str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME']));                            // path
+
+            if (substr($base_url, -1) == '/')
+                $base_url = substr($base_url, 0, -1);
+
+            $db_type = $db_name = $db_username = $db_prefix = $username = $email = '';
+            $db_host = 'localhost';
+            $title = $lang_install['My ForkBB Forum'];
+            $description = '<p><span>'.$lang_install['Description'].'</span></p>';
+            $default_lang = $install_lang;
+            $default_style = 'Air';
+        }
+        else
+        {
+            $db_type = trim($this->request->postStr('req_db_type'));
+            $db_host = trim($this->request->postStr('req_db_host'));
+            $db_name = trim($this->request->postStr('req_db_name'));
+            $db_username = trim($this->request->postStr('db_username'));
+            $db_password = trim($this->request->postStr('db_password'));
+            $db_prefix = trim($this->request->postStr('db_prefix'));
+            $username = trim($this->request->postStr('req_username'));
+            $email = strtolower(trim($this->request->postStr('req_email')));
+            $password1 = trim($this->request->postStr('req_password1'));
+            $password2 = trim($this->request->postStr('req_password2'));
+            $title = trim($this->request->postStr('req_title'));
+            $description = trim($this->request->postStr('desc'));
+            $base_url = trim($this->request->postStr('req_base_url'));
+            $default_lang = trim($this->request->postStr('req_default_lang'));
+            $default_style = trim($this->request->postStr('req_default_style'));
+            $alerts = array();
+
+            // Make sure base_url doesn't end with a slash
+            if (substr($base_url, -1) == '/')
+                $base_url = substr($base_url, 0, -1);
+
+            // Validate username and passwords
+            if (pun_strlen($username) < 2)
+                $alerts[] = $lang_install['Username 1'];
+            else if (pun_strlen($username) > 25) // This usually doesn't happen since the form element only accepts 25 characters
+                $alerts[] = $lang_install['Username 2'];
+            else if (! strcasecmp($username, 'Guest'))
+                $alerts[] = $lang_install['Username 3'];
+            else if (preg_match('%[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}%', $username) || preg_match('%((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))%', $username))
+                $alerts[] = $lang_install['Username 4'];
+            else if ((strpos($username, '[') !== false || strpos($username, ']') !== false) && strpos($username, '\'') !== false && strpos($username, '"') !== false)
+                $alerts[] = $lang_install['Username 5'];
+            else if (preg_match('%(?:\[/?(?:b|u|i|h|colou?r|quote|code|img|url|email|list)\]|\[(?:code|quote|list)=)%i', $username))
+                $alerts[] = $lang_install['Username 6'];
+
+            if (pun_strlen($password1) < 6)
+                $alerts[] = $lang_install['Short password'];
+            else if ($password1 != $password2)
+                $alerts[] = $lang_install['Passwords not match'];
+
+            // Validate email
+            require PUN_ROOT.'include/email.php';
+
+            if (! is_valid_email($email))
+                $alerts[] = $lang_install['Wrong email'];
+
+            if ($title == '')
+                $alerts[] = $lang_install['No board title'];
+
+            $languages = forum_list_langs();
+            if (! in_array($default_lang, $languages))
+                $alerts[] = $lang_install['Error default language'];
+
+            $styles = forum_list_styles();
+            if (! in_array($default_style, $styles))
+                $alerts[] = $lang_install['Error default style'];
+        }
+
+        // Check if the cache directory is writable
+        if (! forum_is_writable(FORUM_CACHE_DIR))
+            $alerts[] = sprintf($lang_install['Alert cache'], FORUM_CACHE_DIR);
+
+        // Check if default avatar directory is writable
+        if (! forum_is_writable(PUN_ROOT . 'img/avatars/'))
+            $alerts[] = sprintf($lang_install['Alert avatar'], PUN_ROOT . 'img/avatars/');
+
+        if (null === $this->request->postBool('form_sent') || ! empty($alerts))
+        {
+            // Determine available database extensions
+            $db_extensions = array();
+            $mysql_innodb = false;
+            if (function_exists('mysqli_connect'))
+            {
+                $db_extensions[] = array('mysqli', 'MySQL Improved');
+                $db_extensions[] = array('mysqli_innodb', 'MySQL Improved (InnoDB)');
+                $mysql_innodb = true;
+            }
+            if (function_exists('mysql_connect'))
+            {
+                $db_extensions[] = array('mysql', 'MySQL Standard');
+                $db_extensions[] = array('mysql_innodb', 'MySQL Standard (InnoDB)');
+                $mysql_innodb = true;
+            }
+            if (function_exists('sqlite_open'))
+                $db_extensions[] = array('sqlite', 'SQLite');
+            if (function_exists('pg_connect'))
+                $db_extensions[] = array('pgsql', 'PostgreSQL');
+
+            if (empty($db_extensions))
+                error($lang_install['No DB extensions']);
+
+            // Fetch a list of installed languages
+            $languages = forum_list_langs();
+
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1" />
+<title><?php echo $lang_install['ForkBB Installation'] ?></title>
+<link rel="stylesheet" type="text/css" href="style/<?php echo $default_style ?>.css" />
+<script type="text/javascript">
+/* <![CDATA[ */
+function process_form(the_form)
+{
+    var required_fields = {
+        "req_db_type": "<?php echo $lang_install['Database type'] ?>",
+        "req_db_host": "<?php echo $lang_install['Database server hostname'] ?>",
+        "req_db_name": "<?php echo $lang_install['Database name'] ?>",
+        "req_username": "<?php echo $lang_install['Administrator username'] ?>",
+        "req_password1": "<?php echo $lang_install['Password'] ?>",
+        "req_password2": "<?php echo $lang_install['Confirm password'] ?>",
+        "req_email": "<?php echo $lang_install['Administrator email'] ?>",
+        "req_title": "<?php echo $lang_install['Board title'] ?>",
+        "req_base_url": "<?php echo $lang_install['Base URL'] ?>"
+    };
+    if (document.all || document.getElementById)
+    {
+        for (var i = 0; i < the_form.length; ++i)
+        {
+            var elem = the_form.elements[i];
+            if (elem.name && required_fields[elem.name] && !elem.value && elem.type && (/^(?:text(?:area)?|password|file)$/i.test(elem.type)))
+            {
+                alert('"' + required_fields[elem.name] + '" <?php echo $lang_install['Required field'] ?>');
+                elem.focus();
+                return false;
+            }
+        }
+    }
+    return true;
+}
+/* ]]> */
+</script>
+</head>
+<body onload="document.getElementById('install').req_db_type.focus();document.getElementById('install').start.disabled=false;" onunload="">
+
+<div id="puninstall" class="pun">
+<div class="top-box"><div><!-- Top Corners --></div></div>
+<div class="punwrap">
+
+<div id="brdheader" class="block">
+    <div class="box">
+        <div id="brdtitle" class="inbox">
+            <h1><span><?php echo $lang_install['ForkBB Installation'] ?></span></h1>
+            <div id="brddesc"><p><?php echo $lang_install['Welcome'] ?></p></div>
+        </div>
+    </div>
+</div>
+
+<div id="brdmain">
+<?php if (count($languages) > 1): ?><div class="blockform">
+    <h2><span><?php echo $lang_install['Choose install language'] ?></span></h2>
+    <div class="box">
+        <form id="install" method="post" action="">
+            <div class="inform">
+                <fieldset>
+                    <legend><?php echo $lang_install['Install language'] ?></legend>
+                    <div class="infldset">
+                        <p><?php echo $lang_install['Choose install language info'] ?></p>
+                        <label><strong><?php echo $lang_install['Install language'] ?></strong>
+                        <br /><select name="install_lang">
+<?php
+
+foreach ($languages as $temp)
+{
+    if ($temp == $install_lang)
+        echo "\t\t\t\t\t".'<option value="'.$temp.'" selected="selected">'.$temp.'</option>'."\n";
+    else
+        echo "\t\t\t\t\t".'<option value="'.$temp.'">'.$temp.'</option>'."\n";
+}
+
+?>
+                        </select>
+                        <br /></label>
+                    </div>
+                </fieldset>
+            </div>
+            <p class="buttons"><input type="submit" name="start" value="<?php echo $lang_install['Change language'] ?>" /></p>
+        </form>
+    </div>
+</div>
+<?php endif; ?>
+
+<div class="blockform">
+    <h2><span><?php echo sprintf($lang_install['Install'], FORUM_VERSION.'.'.FORUM_VER_REVISION) ?></span></h2>
+    <div class="box">
+        <form id="install" method="post" action="" onsubmit="this.start.disabled=true;if(process_form(this)){return true;}else{this.start.disabled=false;return false;}">
+        <div><input type="hidden" name="form_sent" value="1" /><input type="hidden" name="install_lang" value="<?php echo pun_htmlspecialchars($install_lang) ?>" /></div>
+            <div class="inform">
+<?php if (! empty($alerts)): ?>                <div class="forminfo error-info">
+                    <h3><?php echo $lang_install['Errors'] ?></h3>
+                    <ul class="error-list">
+<?php
+
+foreach ($alerts as $cur_alert)
+    echo "\t\t\t\t\t\t".'<li><strong>'.$cur_alert.'</strong></li>'."\n";
+?>
+                    </ul>
+                </div>
+<?php endif; ?>            </div>
+            <div class="inform">
+                <div class="forminfo">
+                    <h3><?php echo $lang_install['Database setup'] ?></h3>
+                    <p><?php echo $lang_install['Info 1'] ?></p>
+                </div>
+                <fieldset>
+                <legend><?php echo $lang_install['Select database'] ?></legend>
+                    <div class="infldset">
+                        <p><?php echo $lang_install['Info 2'] ?></p>
+                        <label class="required"><strong><?php echo $lang_install['Database type'] ?> <span><?php echo $lang_install['Required'] ?></span></strong>
+                        <br /><select name="req_db_type">
+<?php
+
+foreach ($db_extensions as $temp)
+{
+    if ($temp[0] == $db_type)
+        echo "\t\t\t\t\t\t\t".'<option value="'.$temp[0].'" selected="selected">'.$temp[1].'</option>'."\n";
+    else
+        echo "\t\t\t\t\t\t\t".'<option value="'.$temp[0].'">'.$temp[1].'</option>'."\n";
+}
+
+?>
+                        </select>
+                        <br /></label>
+                    </div>
+                </fieldset>
+            </div>
+            <div class="inform">
+                <fieldset>
+                    <legend><?php echo $lang_install['Database hostname'] ?></legend>
+                    <div class="infldset">
+                        <p><?php echo $lang_install['Info 3'] ?></p>
+                        <label class="required"><strong><?php echo $lang_install['Database server hostname'] ?> <span><?php echo $lang_install['Required'] ?></span></strong><br /><input type="text" name="req_db_host" value="<?php echo pun_htmlspecialchars($db_host) ?>" size="50" /><br /></label>
+                    </div>
+                </fieldset>
+            </div>
+            <div class="inform">
+                <fieldset>
+                    <legend><?php echo $lang_install['Database enter name'] ?></legend>
+                    <div class="infldset">
+                        <p><?php echo $lang_install['Info 4'] ?></p>
+                        <label class="required"><strong><?php echo $lang_install['Database name'] ?> <span><?php echo $lang_install['Required'] ?></span></strong><br /><input id="req_db_name" type="text" name="req_db_name" value="<?php echo pun_htmlspecialchars($db_name) ?>" size="30" /><br /></label>
+                    </div>
+                </fieldset>
+            </div>
+            <div class="inform">
+                <fieldset>
+                    <legend><?php echo $lang_install['Database enter informations'] ?></legend>
+                    <div class="infldset">
+                        <p><?php echo $lang_install['Info 5'] ?></p>
+                        <label class="conl"><?php echo $lang_install['Database username'] ?><br /><input type="text" name="db_username" value="<?php echo pun_htmlspecialchars($db_username) ?>" size="30" /><br /></label>
+                        <label class="conl"><?php echo $lang_install['Database password'] ?><br /><input type="password" name="db_password" size="30" /><br /></label>
+                        <div class="clearer"></div>
+                    </div>
+                </fieldset>
+            </div>
+            <div class="inform">
+                <fieldset>
+                    <legend><?php echo $lang_install['Database enter prefix'] ?></legend>
+                    <div class="infldset">
+                        <p><?php echo $lang_install['Info 6'] ?></p>
+                        <label><?php echo $lang_install['Table prefix'] ?><br /><input id="db_prefix" type="text" name="db_prefix" value="<?php echo pun_htmlspecialchars($db_prefix) ?>" size="20" maxlength="30" /><br /></label>
+                    </div>
+                </fieldset>
+            </div>
+            <div class="inform">
+                <div class="forminfo">
+                    <h3><?php echo $lang_install['Administration setup'] ?></h3>
+                    <p><?php echo $lang_install['Info 7'] ?></p>
+                </div>
+                <fieldset>
+                    <legend><?php echo $lang_install['Administration setup'] ?></legend>
+                    <div class="infldset">
+                        <p><?php echo $lang_install['Info 8'] ?></p>
+                        <label class="required"><strong><?php echo $lang_install['Administrator username'] ?> <span><?php echo $lang_install['Required'] ?></span></strong><br /><input type="text" name="req_username" value="<?php echo pun_htmlspecialchars($username) ?>" size="25" maxlength="25" /><br /></label>
+                        <label class="conl required"><strong><?php echo $lang_install['Password'] ?> <span><?php echo $lang_install['Required'] ?></span></strong><br /><input id="req_password1" type="password" name="req_password1" size="16" /><br /></label>
+                        <label class="conl required"><strong><?php echo $lang_install['Confirm password'] ?> <span><?php echo $lang_install['Required'] ?></span></strong><br /><input type="password" name="req_password2" size="16" /><br /></label>
+                        <div class="clearer"></div>
+                        <label class="required"><strong><?php echo $lang_install['Administrator email'] ?> <span><?php echo $lang_install['Required'] ?></span></strong><br /><input id="req_email" type="text" name="req_email" value="<?php echo pun_htmlspecialchars($email) ?>" size="50" maxlength="80" /><br /></label>
+                    </div>
+                </fieldset>
+            </div>
+            <div class="inform">
+                <div class="forminfo">
+                    <h3><?php echo $lang_install['Board setup'] ?></h3>
+                    <p><?php echo $lang_install['Info 11'] ?></p>
+                </div>
+                <fieldset>
+                    <legend><?php echo $lang_install['General information'] ?></legend>
+                    <div class="infldset">
+                        <label class="required"><strong><?php echo $lang_install['Board title'] ?> <span><?php echo $lang_install['Required'] ?></span></strong><br /><input id="req_title" type="text" name="req_title" value="<?php echo pun_htmlspecialchars($title) ?>" size="50" maxlength="255" /><br /></label>
+                        <label><?php echo $lang_install['Board description'] ?><br /><input id="desc" type="text" name="desc" value="<?php echo pun_htmlspecialchars($description) ?>" size="50" maxlength="255" /><br /></label>
+                        <label class="required"><strong><?php echo $lang_install['Base URL'] ?> <span><?php echo $lang_install['Required'] ?></span></strong><br /><input id="req_base_url" type="text" name="req_base_url" value="<?php echo pun_htmlspecialchars($base_url) ?>" size="50" maxlength="100" /><br /></label>
+                    </div>
+                </fieldset>
+            </div>
+            <div class="inform">
+                <fieldset>
+                    <legend><?php echo $lang_install['Appearance'] ?></legend>
+                    <div class="infldset">
+                        <p><?php echo $lang_install['Info 15'] ?></p>
+                        <label class="required"><strong><?php echo $lang_install['Default language'] ?> <span><?php echo $lang_install['Required'] ?></span></strong><br /><select id="req_default_lang" name="req_default_lang">
+<?php
+
+$languages = forum_list_langs();
+foreach ($languages as $temp)
+{
+    if ($temp == $default_lang)
+        echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$temp.'" selected="selected">'.$temp.'</option>'."\n";
+    else
+        echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$temp.'">'.$temp.'</option>'."\n";
+}
+
+?>
+                        </select><br /></label>
+                        <label class="required"><strong><?php echo $lang_install['Default style'] ?> <span><?php echo $lang_install['Required'] ?></span></strong><br /><select id="req_default_style" name="req_default_style">
+<?php
+
+$styles = forum_list_styles();
+foreach ($styles as $temp)
+{
+    if ($temp == $default_style)
+        echo "\t\t\t\t\t\t\t\t\t".'<option value="'.$temp.'" selected="selected">'.str_replace('_', ' ', $temp).'</option>'."\n";
+    else
+        echo "\t\t\t\t\t\t\t\t\t".'<option value="'.$temp.'">'.str_replace('_', ' ', $temp).'</option>'."\n";
+}
+
+?>
+                        </select><br /></label>
+                    </div>
+                </fieldset>
+            </div>
+            <p class="buttons"><input type="submit" name="start" value="<?php echo $lang_install['Start install'] ?>" /></p>
+        </form>
+    </div>
+</div>
+</div>
+
+</div>
+<div class="end-box"><div><!-- Bottom Corners --></div></div>
+</div>
+
+</body>
+</html>
+<?php
+
+        }
+        else
+        {
+            // Validate prefix
+            if (strlen($db_prefix) > 0 && (! preg_match('%^[a-zA-Z]\w*$%', $db_prefix) || strlen($db_prefix) > 40))
+                error(sprintf($lang_install['Table prefix error'], $db->prefix));
+
+            $this->c->setParameter('DB_TYPE', $db_type);
+            $this->c->setParameter('DB_HOST', $db_host);
+            $this->c->setParameter('DB_USERNAME', $db_username);
+            $this->c->setParameter('DB_PASSWORD', $db_password);
+            $this->c->setParameter('DB_NAME', $db_name);
+            $this->c->setParameter('DB_PREFIX', $db_prefix);
+
+            $db = $this->c->get('DB');
+
+            // Do some DB type specific checks
+            switch ($db_type)
+            {
+                case 'mysql':
+                case 'mysqli':
+                case 'mysql_innodb':
+                case 'mysqli_innodb':
+                    $mysql_info = $db->get_version();
+                    if (version_compare($mysql_info['version'], MIN_MYSQL_VERSION, '<'))
+                        error(sprintf($lang_install['You are running error'], 'MySQL', $mysql_info['version'], FORUM_VERSION, MIN_MYSQL_VERSION));
+                    break;
+
+                case 'pgsql':
+                    $pgsql_info = $db->get_version();
+                    if (version_compare($pgsql_info['version'], MIN_PGSQL_VERSION, '<'))
+                        error(sprintf($lang_install['You are running error'], 'PostgreSQL', $pgsql_info['version'], FORUM_VERSION, MIN_PGSQL_VERSION));
+                    break;
+
+                case 'sqlite':
+                    if (strtolower($db_prefix) == 'sqlite_')
+                        error($lang_install['Prefix reserved']);
+                    break;
+            }
+
+
+            // Make sure ForkBB isn't already installed
+            $result = $db->query('SELECT 1 FROM '.$db_prefix.'users WHERE id=1');
+            if ($db->num_rows($result))
+                error(sprintf($lang_install['Existing table error'], $db_prefix, $db_name));
+
+            // Check if InnoDB is available
+            if ($db_type == 'mysql_innodb' || $db_type == 'mysqli_innodb')
+            {
+                $result = $db->query('SELECT SUPPORT FROM INFORMATION_SCHEMA.ENGINES WHERE ENGINE=\'InnoDB\'');
+                $result = $db->result($result);
+                if (! in_array($result, array('YES', 'DEFAULT')))
+                    error($lang_install['InnoDB off']);
+            }
+
+
+            // Start a transaction
+            $db->start_transaction();
+
+
+            // Create all tables
+            $schema = array(
+                'FIELDS'        => array(
+                    'id'            => array(
+                        'datatype'        => 'SERIAL',
+                        'allow_null'    => false
+                    ),
+                    'username'        => array(
+                        'datatype'        => 'VARCHAR(200)',
+                        'allow_null'    => true
+                    ),
+                    'ip'            => array(
+                        'datatype'        => 'VARCHAR(255)',
+                        'allow_null'    => true
+                    ),
+                    'email'            => array(
+                        'datatype'        => 'VARCHAR(80)',
+                        'allow_null'    => true
+                    ),
+                    'message'        => array(
+                        'datatype'        => 'VARCHAR(255)',
+                        'allow_null'    => true
+                    ),
+                    'expire'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => true
+                    ),
+                    'ban_creator'    => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    )
+                ),
+                'PRIMARY KEY'    => array('id'),
+                'INDEXES'        => array(
+                    'username_idx'    => array('username')
+                )
+            );
+
+            if ($db_type == 'mysql' || $db_type == 'mysqli' || $db_type == 'mysql_innodb' || $db_type == 'mysqli_innodb')
+                $schema['INDEXES']['username_idx'] = array('username(25)');
+
+            $db->create_table('bans', $schema) or error('Unable to create bans table', __FILE__, __LINE__, $db->error());
+
+
+            $schema = array(
+                'FIELDS'        => array(
+                    'id'            => array(
+                        'datatype'        => 'SERIAL',
+                        'allow_null'    => false
+                    ),
+                    'cat_name'        => array(
+                        'datatype'        => 'VARCHAR(80)',
+                        'allow_null'    => false,
+                        'default'        => '\'New Category\''
+                    ),
+                    'disp_position'    => array(
+                        'datatype'        => 'INT(10)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    )
+                ),
+                'PRIMARY KEY'    => array('id')
+            );
+
+            $db->create_table('categories', $schema) or error('Unable to create categories table', __FILE__, __LINE__, $db->error());
+
+
+            $schema = array(
+                'FIELDS'        => array(
+                    'id'            => array(
+                        'datatype'        => 'SERIAL',
+                        'allow_null'    => false
+                    ),
+                    'search_for'    => array(
+                        'datatype'        => 'VARCHAR(60)',
+                        'allow_null'    => false,
+                        'default'        => '\'\''
+                    ),
+                    'replace_with'    => array(
+                        'datatype'        => 'VARCHAR(60)',
+                        'allow_null'    => false,
+                        'default'        => '\'\''
+                    )
+                ),
+                'PRIMARY KEY'    => array('id')
+            );
+
+            $db->create_table('censoring', $schema) or error('Unable to create censoring table', __FILE__, __LINE__, $db->error());
+
+
+            $schema = array(
+                'FIELDS'        => array(
+                    'conf_name'        => array(
+                        'datatype'        => 'VARCHAR(255)',
+                        'allow_null'    => false,
+                        'default'        => '\'\''
+                    ),
+                    'conf_value'    => array(
+                        'datatype'        => 'TEXT',
+                        'allow_null'    => true
+                    )
+                ),
+                'PRIMARY KEY'    => array('conf_name')
+            );
+
+            $db->create_table('config', $schema) or error('Unable to create config table', __FILE__, __LINE__, $db->error());
+
+
+            $schema = array(
+                'FIELDS'        => array(
+                    'group_id'        => array(
+                        'datatype'        => 'INT(10)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'forum_id'        => array(
+                        'datatype'        => 'INT(10)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'read_forum'    => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '1'
+                    ),
+                    'post_replies'    => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '1'
+                    ),
+                    'post_topics'    => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '1'
+                    )
+                ),
+                'PRIMARY KEY'    => array('group_id', 'forum_id')
+            );
+
+            $db->create_table('forum_perms', $schema) or error('Unable to create forum_perms table', __FILE__, __LINE__, $db->error());
+
+
+            $schema = array(
+                'FIELDS'        => array(
+                    'id'            => array(
+                        'datatype'        => 'SERIAL',
+                        'allow_null'    => false
+                    ),
+                    'forum_name'    => array(
+                        'datatype'        => 'VARCHAR(80)',
+                        'allow_null'    => false,
+                        'default'        => '\'New forum\''
+                    ),
+                    'forum_desc'    => array(
+                        'datatype'        => 'TEXT',
+                        'allow_null'    => true
+                    ),
+                    'redirect_url'    => array(
+                        'datatype'        => 'VARCHAR(100)',
+                        'allow_null'    => true
+                    ),
+                    'moderators'    => array(
+                        'datatype'        => 'TEXT',
+                        'allow_null'    => true
+                    ),
+                    'num_topics'    => array(
+                        'datatype'        => 'MEDIUMINT(8) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'num_posts'        => array(
+                        'datatype'        => 'MEDIUMINT(8) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'last_post'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => true
+                    ),
+                    'last_post_id'    => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => true
+                    ),
+                    'last_poster'    => array(
+                        'datatype'        => 'VARCHAR(200)',
+                        'allow_null'    => true
+                    ),
+                    'last_topic'    => array( // last topic on index - Visman
+                        'datatype'        => 'VARCHAR(255)',
+                        'allow_null'    => true
+                    ),
+                    'sort_by'        => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'disp_position'    => array(
+                        'datatype'        => 'INT(10)',
+                        'allow_null'    => false,
+                        'default'        =>    '0'
+                    ),
+                    'cat_id'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        =>    '0'
+                    ),
+                    'no_sum_mess'        => array( // no sum - Visman
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'parent_forum_id' => array( // subforums - Visman
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        =>    '0'
+                    )
+                ),
+                'PRIMARY KEY'    => array('id')
+            );
+
+            $db->create_table('forums', $schema) or error('Unable to create forums table', __FILE__, __LINE__, $db->error());
+
+
+            $schema = array(
+                'FIELDS'        => array(
+                    'g_id'                        => array(
+                        'datatype'        => 'SERIAL',
+                        'allow_null'    => false
+                    ),
+                    'g_title'                    => array(
+                        'datatype'        => 'VARCHAR(50)',
+                        'allow_null'    => false,
+                        'default'        => '\'\''
+                    ),
+                    'g_user_title'                => array(
+                        'datatype'        => 'VARCHAR(50)',
+                        'allow_null'    => true
+                    ),
+                    'g_promote_min_posts'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'g_promote_next_group'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'g_moderator'                => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'g_mod_edit_users'            => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'g_mod_rename_users'        => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'g_mod_change_passwords'    => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'g_mod_ban_users'            => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'g_mod_promote_users'            => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'g_read_board'                => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '1'
+                    ),
+                    'g_view_users'                => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '1'
+                    ),
+                    'g_post_replies'            => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '1'
+                    ),
+                    'g_post_topics'                => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '1'
+                    ),
+                    'g_edit_posts'                => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '1'
+                    ),
+                    'g_delete_posts'            => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '1'
+                    ),
+                    'g_delete_topics'            => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '1'
+                    ),
+                    'g_post_links'                => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '1'
+                    ),
+                    'g_set_title'                => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '1'
+                    ),
+                    'g_search'                    => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '1'
+                    ),
+                    'g_search_users'            => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '1'
+                    ),
+                    'g_send_email'                => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '1'
+                    ),
+                    'g_post_flood'                => array(
+                        'datatype'        => 'SMALLINT(6)',
+                        'allow_null'    => false,
+                        'default'        => '30'
+                    ),
+                    'g_search_flood'            => array(
+                        'datatype'        => 'SMALLINT(6)',
+                        'allow_null'    => false,
+                        'default'        => '30'
+                    ),
+                    'g_email_flood'                => array(
+                        'datatype'        => 'SMALLINT(6)',
+                        'allow_null'    => false,
+                        'default'        => '60'
+                    ),
+                    'g_report_flood'            => array(
+                        'datatype'        => 'SMALLINT(6)',
+                        'allow_null'    => false,
+                        'default'        => '60'
+                    ),
+                    'g_deledit_interval' => array( // мод ограничения времени - Visman
+                        'datatype'        => 'INT(10)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'g_pm' => array( // New PMS - Visman
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '1'
+                    ),
+                    'g_pm_limit'                => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '100'
+                    )
+                ),
+                'PRIMARY KEY'    => array('g_id')
+            );
+
+            $db->create_table('groups', $schema) or error('Unable to create groups table', __FILE__, __LINE__, $db->error());
+
+
+            $schema = array(
+                'FIELDS'        => array(
+                    'user_id'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '1'
+                    ),
+                    'ident'            => array(
+                        'datatype'        => 'VARCHAR(200)',
+                        'allow_null'    => false,
+                        'default'        => '\'\''
+                    ),
+                    'logged'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'idle'            => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'last_post'            => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => true
+                    ),
+                    'last_search'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => true
+                    ),
+                    'witt_data'            => array(
+                        'datatype'        => 'VARCHAR(255)',
+                        'allow_null'    => false,
+                        'default'        => '\'\''
+                    )
+                ),
+                'UNIQUE KEYS'    => array(
+                    'user_id_ident_idx'    => array('user_id', 'ident')
+                ),
+                'INDEXES'        => array(
+                    'ident_idx'        => array('ident'),
+                    'logged_idx'    => array('logged')
+                )
+            );
+
+            if ($db_type == 'mysql' || $db_type == 'mysqli' || $db_type == 'mysql_innodb' || $db_type == 'mysqli_innodb')
+            {
+                $schema['UNIQUE KEYS']['user_id_ident_idx'] = array('user_id', 'ident(25)');
+                $schema['INDEXES']['ident_idx'] = array('ident(25)');
+            }
+
+            if ($db_type == 'mysql_innodb' || $db_type == 'mysqli_innodb')
+                $schema['ENGINE'] = 'InnoDB';
+
+            $db->create_table('online', $schema) or error('Unable to create online table', __FILE__, __LINE__, $db->error());
+
+
+            $schema = array(
+                'FIELDS'        => array(
+                    'id'            => array(
+                        'datatype'        => 'SERIAL',
+                        'allow_null'    => false
+                    ),
+                    'poster'        => array(
+                        'datatype'        => 'VARCHAR(200)',
+                        'allow_null'    => false,
+                        'default'        => '\'\''
+                    ),
+                    'poster_id'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '1'
+                    ),
+                    'poster_ip'        => array(
+                        'datatype'        => 'VARCHAR(39)',
+                        'allow_null'    => true
+                    ),
+                    'poster_email'    => array(
+                        'datatype'        => 'VARCHAR(80)',
+                        'allow_null'    => true
+                    ),
+                    'message'        => array(
+                        'datatype'        => 'MEDIUMTEXT',
+                        'allow_null'    => true
+                    ),
+                    'hide_smilies'    => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'edit_post'    => array( // мод ограничения времени - Visman
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'posted'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'edited'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => true
+                    ),
+                    'edited_by'        => array(
+                        'datatype'        => 'VARCHAR(200)',
+                        'allow_null'    => true
+                    ),
+                    'user_agent'        => array( // MOD user agent - Visman
+                        'datatype'        => 'VARCHAR(255)',
+                        'allow_null'    => true
+                    ),
+                    'topic_id'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    )
+                ),
+                'PRIMARY KEY'    => array('id'),
+                'INDEXES'        => array(
+                    'topic_id_idx'    => array('topic_id'),
+                    'multi_idx'        => array('poster_id', 'topic_id')
+                )
+            );
+
+            $db->create_table('posts', $schema) or error('Unable to create posts table', __FILE__, __LINE__, $db->error());
+
+
+            $schema = array(
+                'FIELDS'        => array(
+                    'id'            => array(
+                        'datatype'        => 'SERIAL',
+                        'allow_null'    => false
+                    ),
+                    'post_id'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'topic_id'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'forum_id'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'reported_by'    => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'created'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'message'        => array(
+                        'datatype'        => 'TEXT',
+                        'allow_null'    => true
+                    ),
+                    'zapped'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => true
+                    ),
+                    'zapped_by'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => true
+                    )
+                ),
+                'PRIMARY KEY'    => array('id'),
+                'INDEXES'        => array(
+                    'zapped_idx'    => array('zapped')
+                )
+            );
+
+            $db->create_table('reports', $schema) or error('Unable to create reports table', __FILE__, __LINE__, $db->error());
+
+
+            $schema = array(
+                'FIELDS'        => array(
+                    'id'            => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'ident'            => array(
+                        'datatype'        => 'VARCHAR(200)',
+                        'allow_null'    => false,
+                        'default'        => '\'\''
+                    ),
+                    'search_data'    => array(
+                        'datatype'        => 'MEDIUMTEXT',
+                        'allow_null'    => true
+                    )
+                ),
+                'PRIMARY KEY'    => array('id'),
+                'INDEXES'        => array(
+                    'ident_idx'    => array('ident')
+                )
+            );
+
+            if ($db_type == 'mysql' || $db_type == 'mysqli' || $db_type == 'mysql_innodb' || $db_type == 'mysqli_innodb')
+                $schema['INDEXES']['ident_idx'] = array('ident(8)');
+
+            $db->create_table('search_cache', $schema) or error('Unable to create search_cache table', __FILE__, __LINE__, $db->error());
+
+
+            $schema = array(
+                'FIELDS'        => array(
+                    'post_id'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'word_id'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'subject_match'    => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    )
+                ),
+                'INDEXES'        => array(
+                    'word_id_idx'    => array('word_id'),
+                    'post_id_idx'    => array('post_id')
+                )
+            );
+
+            $db->create_table('search_matches', $schema) or error('Unable to create search_matches table', __FILE__, __LINE__, $db->error());
+
+
+            $schema = array(
+                'FIELDS'        => array(
+                    'id'            => array(
+                        'datatype'        => 'SERIAL',
+                        'allow_null'    => false
+                    ),
+                    'word'            => array(
+                        'datatype'        => 'VARCHAR(20)',
+                        'allow_null'    => false,
+                        'default'        => '\'\'',
+                        'collation'        => 'bin'
+                    )
+                ),
+                'PRIMARY KEY'    => array('word'),
+                'INDEXES'        => array(
+                    'id_idx'    => array('id')
+                )
+            );
+
+            if ($db_type == 'sqlite')
+            {
+                $schema['PRIMARY KEY'] = array('id');
+                $schema['UNIQUE KEYS'] = array('word_idx'    => array('word'));
+            }
+
+            $db->create_table('search_words', $schema) or error('Unable to create search_words table', __FILE__, __LINE__, $db->error());
+
+
+            $schema = array(
+                'FIELDS'        => array(
+                    'user_id'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'topic_id'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    )
+                ),
+                'PRIMARY KEY'    => array('user_id', 'topic_id')
+            );
+
+            $db->create_table('topic_subscriptions', $schema) or error('Unable to create topic subscriptions table', __FILE__, __LINE__, $db->error());
+
+
+            $schema = array(
+                'FIELDS'        => array(
+                    'user_id'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'forum_id'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    )
+                ),
+                'PRIMARY KEY'    => array('user_id', 'forum_id')
+            );
+
+            $db->create_table('forum_subscriptions', $schema) or error('Unable to create forum subscriptions table', __FILE__, __LINE__, $db->error());
+
+
+            $schema = array(
+                'FIELDS'        => array(
+                    'id'            => array(
+                        'datatype'        => 'SERIAL',
+                        'allow_null'    => false
+                    ),
+                    'poster'        => array(
+                        'datatype'        => 'VARCHAR(200)',
+                        'allow_null'    => false,
+                        'default'        => '\'\''
+                    ),
+                    'subject'        => array(
+                        'datatype'        => 'VARCHAR(255)',
+                        'allow_null'    => false,
+                        'default'        => '\'\''
+                    ),
+                    'posted'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'first_post_id'    => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'last_post'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'last_post_id'    => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'last_poster'    => array(
+                        'datatype'        => 'VARCHAR(200)',
+                        'allow_null'    => true
+                    ),
+                    'num_views'        => array(
+                        'datatype'        => 'MEDIUMINT(8) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'num_replies'    => array(
+                        'datatype'        => 'MEDIUMINT(8) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'closed'        => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'sticky'        => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'stick_fp'        => array( // StickFP - Visman
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'moved_to'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => true
+                    ),
+                    'forum_id'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'poll_type'        => array( // POLL - Visman
+                        'datatype'        => 'TINYINT(4)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'poll_time'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'poll_term'        => array(
+                        'datatype'        => 'TINYINT(4)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'poll_kol'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    )
+                ),
+                'PRIMARY KEY'    => array('id'),
+                'INDEXES'        => array(
+                    'forum_id_idx'        => array('forum_id'),
+                    'moved_to_idx'        => array('moved_to'),
+                    'last_post_idx'        => array('last_post'),
+                    'first_post_id_idx'    => array('first_post_id')
+                )
+            );
+
+            $db->create_table('topics', $schema) or error('Unable to create topics table', __FILE__, __LINE__, $db->error());
+
+            // New PMS - Visman
+            $schema = array(
+                'FIELDS'        => array(
+                    'bl_id'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'bl_user_id'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    )
+                ),
+                'INDEXES'        => array(
+                    'bl_id_idx'    => array('bl_id'),
+                    'bl_user_id_idx'    => array('bl_user_id')
+                )
+            );
+
+            $db->create_table('pms_new_block', $schema) or error('Unable to create pms_new_block table', __FILE__, __LINE__, $db->error());
+
+            $schema = array(
+                'FIELDS'        => array(
+                    'id'            => array(
+                        'datatype'        => 'SERIAL',
+                        'allow_null'    => false
+                    ),
+                    'poster'        => array(
+                        'datatype'        => 'VARCHAR(200)',
+                        'allow_null'    => false,
+                        'default'        => '\'\''
+                    ),
+                    'poster_id'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '1'
+                    ),
+                    'poster_ip'        => array(
+                        'datatype'        => 'VARCHAR(39)',
+                        'allow_null'    => true
+                    ),
+                    'message'        => array(
+                        'datatype'        => 'TEXT',
+                        'allow_null'    => true
+                    ),
+                    'hide_smilies'    => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'posted'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'edited'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => true
+                    ),
+                    'edited_by'        => array(
+                        'datatype'        => 'VARCHAR(200)',
+                        'allow_null'    => true
+                    ),
+                    'post_new'        => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '1'
+                    ),
+                    'topic_id'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    )
+                ),
+                'PRIMARY KEY'    => array('id'),
+                'INDEXES'        => array(
+                    'topic_id_idx'    => array('topic_id'),
+                    'multi_idx'        => array('poster_id', 'topic_id')
+                )
+            );
+
+            $db->create_table('pms_new_posts', $schema) or error('Unable to create pms_new_posts table', __FILE__, __LINE__, $db->error());
+
+            $schema = array(
+                'FIELDS'        => array(
+                    'id'            => array(
+                        'datatype'        => 'SERIAL',
+                        'allow_null'    => false
+                    ),
+                    'topic'        => array(
+                        'datatype'        => 'VARCHAR(255)',
+                        'allow_null'    => false,
+                        'default'        => '\'\''
+                    ),
+                    'starter'        => array(
+                        'datatype'        => 'VARCHAR(200)',
+                        'allow_null'    => false,
+                        'default'        => '\'\''
+                    ),
+                    'starter_id'    => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'to_user'        => array(
+                        'datatype'        => 'VARCHAR(200)',
+                        'allow_null'    => false,
+                        'default'        => '\'\''
+                    ),
+                    'to_id'    => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'replies'    => array(
+                        'datatype'        => 'MEDIUMINT(8) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'last_posted'    => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'last_poster'        => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'see_st'    => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'see_to'    => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'topic_st'        => array(
+                        'datatype'        => 'TINYINT(4)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'topic_to'        => array(
+                        'datatype'        => 'TINYINT(4)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                ),
+                'PRIMARY KEY'    => array('id'),
+                'INDEXES'        => array(
+                    'multi_idx_st'        => array('starter_id', 'topic_st'),
+                    'multi_idx_to'        => array('to_id', 'topic_to')
+                )
+            );
+
+            $db->create_table('pms_new_topics', $schema) or error('Unable to create pms_new_topics table', __FILE__, __LINE__, $db->error());
+            // New PMS - Visman
+
+            $schema = array(
+                'FIELDS'        => array(
+                    'id'                => array(
+                        'datatype'        => 'SERIAL',
+                        'allow_null'    => false
+                    ),
+                    'group_id'            => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '3'
+                    ),
+                    'username'            => array(
+                        'datatype'        => 'VARCHAR(200)',
+                        'allow_null'    => false,
+                        'default'        => '\'\''
+                    ),
+                    'password'            => array(
+                        'datatype'        => 'VARCHAR(40)',
+                        'allow_null'    => false,
+                        'default'        => '\'\''
+                    ),
+                    'email'                => array(
+                        'datatype'        => 'VARCHAR(80)',
+                        'allow_null'    => false,
+                        'default'        => '\'\''
+                    ),
+                    'title'                => array(
+                        'datatype'        => 'VARCHAR(50)',
+                        'allow_null'    => true
+                    ),
+                    'realname'            => array(
+                        'datatype'        => 'VARCHAR(40)',
+                        'allow_null'    => true
+                    ),
+                    'url'                => array(
+                        'datatype'        => 'VARCHAR(100)',
+                        'allow_null'    => true
+                    ),
+                    'jabber'            => array(
+                        'datatype'        => 'VARCHAR(80)',
+                        'allow_null'    => true
+                    ),
+                    'icq'                => array(
+                        'datatype'        => 'VARCHAR(12)',
+                        'allow_null'    => true
+                    ),
+                    'msn'                => array(
+                        'datatype'        => 'VARCHAR(80)',
+                        'allow_null'    => true
+                    ),
+                    'aim'                => array(
+                        'datatype'        => 'VARCHAR(30)',
+                        'allow_null'    => true
+                    ),
+                    'yahoo'                => array(
+                        'datatype'        => 'VARCHAR(30)',
+                        'allow_null'    => true
+                    ),
+                    'location'            => array(
+                        'datatype'        => 'VARCHAR(30)',
+                        'allow_null'    => true
+                    ),
+                    'signature'            => array(
+                        'datatype'        => 'TEXT',
+                        'allow_null'    => true
+                    ),
+                    'disp_topics'        => array(
+                        'datatype'        => 'TINYINT(3) UNSIGNED',
+                        'allow_null'    => true
+                    ),
+                    'disp_posts'        => array(
+                        'datatype'        => 'TINYINT(3) UNSIGNED',
+                        'allow_null'    => true
+                    ),
+                    'email_setting'        => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '1'
+                    ),
+                    'notify_with_post'    => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'auto_notify'        => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'show_smilies'        => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '1'
+                    ),
+                    'show_img'            => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '1'
+                    ),
+                    'show_img_sig'        => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '1'
+                    ),
+                    'show_avatars'        => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '1'
+                    ),
+                    'show_sig'            => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '1'
+                    ),
+                    'timezone'            => array(
+                        'datatype'        => 'FLOAT',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'dst'                => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'time_format'        => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'date_format'        => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'language'            => array(
+                        'datatype'        => 'VARCHAR(25)',
+                        'allow_null'    => false,
+                        'default'        => '\''.$db->escape($default_lang).'\''
+                    ),
+                    'style'                => array(
+                        'datatype'        => 'VARCHAR(25)',
+                        'allow_null'    => false,
+                        'default'        => '\''.$db->escape($default_style).'\''
+                    ),
+                    'num_posts'            => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'last_post'            => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => true
+                    ),
+                    'last_search'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => true
+                    ),
+                    'last_email_sent'    => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => true
+                    ),
+                    'last_report_sent'    => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => true
+                    ),
+                    'registered'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'registration_ip'    => array(
+                        'datatype'        => 'VARCHAR(39)',
+                        'allow_null'    => false,
+                        'default'        => '\'0.0.0.0\''
+                    ),
+                    'last_visit'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'admin_note'        => array(
+                        'datatype'        => 'VARCHAR(30)',
+                        'allow_null'    => true
+                    ),
+                    'activate_string'    => array(
+                        'datatype'        => 'VARCHAR(80)',
+                        'allow_null'    => true
+                    ),
+                    'activate_key'        => array(
+                        'datatype'        => 'VARCHAR(8)',
+                        'allow_null'    => true
+                    ),
+                    'messages_enable'        => array( // New PMS - Visman
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '1'
+                    ),
+                    'messages_email'        => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'messages_flag'        => array(
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'messages_new'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'messages_all'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'pmsn_last_post'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => true
+                    ),
+                    'warning_flag'        => array( // MOD warnings - Visman
+                        'datatype'        => 'TINYINT(1)',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'warning_all'        => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'gender'            => array( // поле для пола - Visman
+                        'datatype'        => 'TINYINT(4) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    )
+                ),
+                'PRIMARY KEY'    => array('id'),
+                'UNIQUE KEYS'    => array(
+                    'username_idx'        => array('username')
+                ),
+                'INDEXES'        => array(
+                    'registered_idx'    => array('registered')
+                )
+            );
+
+            if ($db_type == 'mysql' || $db_type == 'mysqli' || $db_type == 'mysql_innodb' || $db_type == 'mysqli_innodb')
+                $schema['UNIQUE KEYS']['username_idx'] = array('username(25)');
+
+            $db->create_table('users', $schema) or error('Unable to create users table', __FILE__, __LINE__, $db->error());
+
+            // Create "smilies" table - Visman
+            $schema = array(
+                'FIELDS' => array(
+                    'id' => array(
+                        'datatype' => 'SERIAL',
+                        'allow_null' => false
+                    ),
+                    'image' => array(
+                        'datatype' => 'VARCHAR(40)',
+                        'allow_null' => false,
+                        'default' => '\'\''
+                    ),
+                    'text' => array(
+                        'datatype' => 'VARCHAR(20)',
+                        'allow_null' => false,
+                        'default' => '\'\''
+                    ),
+                    'disp_position' => array(
+                        'datatype' => 'TINYINT(4) UNSIGNED',
+                        'allow_null' => false,
+                        'default' => '0'
+                    )
+                ),
+                'PRIMARY KEY' => array('id')
+            );
+
+            $db->create_table('smilies', $schema) or error('Unable to create smilies table', __FILE__, __LINE__, $db->error());
+
+            $smilies = array(
+                ':)' => 'smile.png',
+                '=)' => 'smile.png',
+                ':|' => 'neutral.png',
+                '=|' => 'neutral.png',
+                ':(' => 'sad.png',
+                '=(' => 'sad.png',
+                ':D' => 'big_smile.png',
+                '=D' => 'big_smile.png',
+                ':o' => 'yikes.png',
+                ':O' => 'yikes.png',
+                ';)' => 'wink.png',
+                ':/' => 'hmm.png',
+                ':P' => 'tongue.png',
+                ':p' => 'tongue.png',
+                ':lol:' => 'lol.png',
+                ':mad:' => 'mad.png',
+                ':rolleyes:' => 'roll.png',
+                ':cool:' => 'cool.png');
+
+            $i = 0;
+            foreach ($smilies as $text => $img)
+            {
+                $db->query('INSERT INTO '.$db->prefix.'smilies (image, text, disp_position) VALUES(\''.$img.'\', \''.$db->escape($text).'\', '.$i.')') or error('Unable to add smiley', __FILE__, __LINE__, $db->error());
+                $i++;
+            }
+            // Create "smilies" table - Visman
+
+            // Create warnings table - Visman
+            $schema = array(
+                'FIELDS' => array(
+                    'id' => array(
+                        'datatype' => 'SERIAL',
+                        'allow_null' => false
+                    ),
+                    'poster' => array(
+                        'datatype' => 'VARCHAR(200)',
+                        'allow_null' => false,
+                        'default' => '\'\''
+                    ),
+                    'poster_id'    => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'posted'    => array(
+                        'datatype'        => 'INT(10) UNSIGNED',
+                        'allow_null'    => false,
+                        'default'        => '0'
+                    ),
+                    'message'        => array(
+                        'datatype'        => 'TEXT',
+                        'allow_null' => true
+                    )
+                ),
+                'PRIMARY KEY' => array('id')
+            );
+
+            $db->create_table('warnings', $schema) or error('Unable to create warnings table', __FILE__, __LINE__, $db->error());
+            // Create warnings table - Visman
+
+            $schema = array(
+                    'FIELDS'            => array(
+                            'tid'                => array(
+                                    'datatype'        => 'INT(10) UNSIGNED',
+                                    'allow_null'    => false,
+                                    'default'            => '0'
+                            ),
+                            'question'            => array(
+                                    'datatype'        => 'TINYINT(4)',
+                                    'allow_null'    => false,
+                                    'default'            => '0'
+                            ),
+                            'field'            => array(
+                                    'datatype'        => 'TINYINT(4)',
+                                    'allow_null'    => false,
+                                    'default'            => '0'
+                            ),
+                            'choice'            => array(
+                                    'datatype'        => 'VARCHAR(255)',
+                                    'allow_null'    => false,
+                                    'default'            => '\'\''
+                            ),
+                            'votes'                => array(
+                                    'datatype'        => 'INT(10) UNSIGNED',
+                                    'allow_null'    => false,
+                                    'default'            => '0'
+                            )
+
+                    ),
+                    'PRIMARY KEY'        => array('tid', 'question', 'field')
+            );
+            $db->create_table('poll', $schema) or error('Unable to create table poll', __FILE__, __LINE__, $db->error());
+
+            $schema = array(
+                    'FIELDS'            => array(
+                            'tid'                => array(
+                                    'datatype'        => 'INT(10) UNSIGNED',
+                                    'allow_null'    => false
+                            ),
+                            'uid'            => array(
+                                    'datatype'        => 'INT(10) UNSIGNED',
+                                    'allow_null'    => false
+                            ),
+                            'rez'            => array(
+                                    'datatype'        => 'TEXT',
+                                    'allow_null'    => true
+                            )
+                    ),
+                    'PRIMARY KEY'        => array('tid', 'uid')
+            );
+
+            $db->create_table('poll_voted', $schema) or error('Unable to create table poll_voted', __FILE__, __LINE__, $db->error());
+
+
+            $now = time();
+
+            // Insert the four preset groups
+            $db->query('INSERT INTO '.$db->prefix.'groups ('.($db_type != 'pgsql' ? 'g_id, ' : '').'g_title, g_user_title, g_moderator, g_mod_edit_users, g_mod_rename_users, g_mod_change_passwords, g_mod_ban_users, g_read_board, g_view_users, g_post_replies, g_post_topics, g_edit_posts, g_delete_posts, g_delete_topics, g_set_title, g_search, g_search_users, g_send_email, g_post_flood, g_search_flood, g_email_flood, g_report_flood) VALUES('.($db_type != 'pgsql' ? '1, ' : '').'\''.$db->escape($lang_install['Administrators']).'\', \''.$db->escape($lang_install['Administrator']).'\', 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0)') or error('Unable to add group', __FILE__, __LINE__, $db->error());
+
+            $db->query('INSERT INTO '.$db->prefix.'groups ('.($db_type != 'pgsql' ? 'g_id, ' : '').'g_title, g_user_title, g_moderator, g_mod_edit_users, g_mod_rename_users, g_mod_change_passwords, g_mod_ban_users, g_mod_promote_users, g_read_board, g_view_users, g_post_replies, g_post_topics, g_edit_posts, g_delete_posts, g_delete_topics, g_set_title, g_search, g_search_users, g_send_email, g_post_flood, g_search_flood, g_email_flood, g_report_flood) VALUES('.($db_type != 'pgsql' ? '2, ' : '').'\''.$db->escape($lang_install['Moderators']).'\', \''.$db->escape($lang_install['Moderator']).'\', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0)') or error('Unable to add group', __FILE__, __LINE__, $db->error());
+
+            $db->query('INSERT INTO '.$db->prefix.'groups ('.($db_type != 'pgsql' ? 'g_id, ' : '').'g_title, g_user_title, g_moderator, g_mod_edit_users, g_mod_rename_users, g_mod_change_passwords, g_mod_ban_users, g_read_board, g_view_users, g_post_replies, g_post_topics, g_edit_posts, g_delete_posts, g_delete_topics, g_set_title, g_search, g_search_users, g_send_email, g_post_flood, g_search_flood, g_email_flood, g_report_flood) VALUES('.($db_type != 'pgsql' ? '3, ' : '').'\''.$db->escape($lang_install['Guests']).'\', NULL, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 60, 30, 0, 0)') or error('Unable to add group', __FILE__, __LINE__, $db->error());
+
+            $db->query('INSERT INTO '.$db->prefix.'groups ('.($db_type != 'pgsql' ? 'g_id, ' : '').'g_title, g_user_title, g_moderator, g_mod_edit_users, g_mod_rename_users, g_mod_change_passwords, g_mod_ban_users, g_read_board, g_view_users, g_post_replies, g_post_topics, g_edit_posts, g_delete_posts, g_delete_topics, g_set_title, g_search, g_search_users, g_send_email, g_post_flood, g_search_flood, g_email_flood, g_report_flood) VALUES('.($db_type != 'pgsql' ? '4, ' : '').'\''.$db->escape($lang_install['Members']).'\', NULL, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 60, 30, 60, 60)') or error('Unable to add group', __FILE__, __LINE__, $db->error());
+
+            // Insert guest and first admin user
+            $db->query('INSERT INTO '.$db_prefix.'users (group_id, username, password, email) VALUES(3, \''.$db->escape($lang_install['Guest']).'\', \''.$db->escape($lang_install['Guest']).'\', \''.$db->escape($lang_install['Guest']).'\')')
+                or error('Unable to add guest user. Please check your configuration and try again', __FILE__, __LINE__, $db->error());
+
+            $db->query('INSERT INTO '.$db_prefix.'users (group_id, username, password, email, language, style, num_posts, last_post, registered, registration_ip, last_visit) VALUES(1, \''.$db->escape($username).'\', \''.pun_hash($password1).'\', \''.$email.'\', \''.$db->escape($default_lang).'\', \''.$db->escape($default_style).'\', 1, '.$now.', '.$now.', \''.$db->escape(get_remote_address()).'\', '.$now.')')
+                or error('Unable to add administrator user. Please check your configuration and try again', __FILE__, __LINE__, $db->error());
+
+            // New PMS - Visman
+            $db->query('UPDATE '.$db->prefix.'groups SET g_pm_limit=0 WHERE g_id=1') or error('Unable to merge groups', __FILE__, __LINE__, $db->error());
+
+            // Enable/disable avatars depending on file_uploads setting in PHP configuration
+            $avatars = in_array(strtolower(@ini_get('file_uploads')), array('on', 'true', '1')) ? 1 : 0;
+
+            // Insert config data
+            $pun_config = array(
+                'o_cur_version'                => FORUM_VERSION,
+                'o_cur_ver_revision'    => FORUM_VER_REVISION, // номер сборки - Visman
+                'o_database_revision'        => FORUM_DB_REVISION,
+                'o_searchindex_revision'    => FORUM_SI_REVISION,
+                'o_parser_revision'            => FORUM_PARSER_REVISION,
+                'o_board_title'                => $title,
+                'o_board_desc'                => $description,
+                'o_default_timezone'        => 0,
+                'o_time_format'                => 'H:i:s',
+                'o_date_format'                => 'Y-m-d',
+                'o_timeout_visit'            => 1800,
+                'o_timeout_online'            => 300,
+                'o_redirect_delay'            => 1,
+                'o_show_version'            => 0,
+                'o_show_user_info'            => 1,
+                'o_show_post_count'            => 1,
+                'o_signatures'                => 1,
+                'o_smilies'                    => 1,
+                'o_smilies_sig'                => 1,
+                'o_make_links'                => 1,
+                'o_default_lang'            => $default_lang,
+                'o_default_style'            => $default_style,
+                'o_default_user_group'        => 4,
+                'o_topic_review'            => 15,
+                'o_disp_topics_default'        => 30,
+                'o_disp_posts_default'        => 25,
+                'o_indent_num_spaces'        => 4,
+                'o_quote_depth'                => 3,
+                'o_quickpost'                => 1,
+                'o_users_online'            => 1,
+                'o_censoring'                => 0,
+                'o_show_dot'                => 0,
+                'o_topic_views'                => 1,
+                'o_quickjump'                => 1,
+                'o_gzip'                    => 0,
+                'o_additional_navlinks'        => '',
+                'o_report_method'            => 0,
+                'o_regs_report'                => 0,
+                'o_default_email_setting'    => 1,
+                'o_mailing_list'            => $email,
+                'o_avatars'                    => $avatars,
+                'o_avatars_dir'                => 'img/avatars',
+                'o_avatars_width'            => 60,
+                'o_avatars_height'            => 60,
+                'o_avatars_size'            => 10240,
+                'o_search_all_forums'        => 1,
+                'o_base_url'                => $base_url,
+                'o_admin_email'                => $email,
+                'o_webmaster_email'            => $email,
+                'o_forum_subscriptions'        => 1,
+                'o_topic_subscriptions'        => 1,
+                'o_smtp_host'                => NULL,
+                'o_smtp_user'                => NULL,
+                'o_smtp_pass'                => NULL,
+                'o_smtp_ssl'                => 0,
+                'o_regs_allow'                => 1,
+                'o_regs_verify'                => 0,
+                'o_announcement'            => 0,
+                'o_announcement_message'    => $lang_install['Announcement'],
+                'o_rules'                    => 0,
+                'o_rules_message'            => $lang_install['Rules'],
+                'o_maintenance'                => 0,
+                'o_maintenance_message'        => $lang_install['Maintenance message'],
+                'o_default_dst'                => 0,
+                'o_feed_type'                => 2,
+                'o_feed_ttl'                => 0,
+                'p_message_bbcode'            => 1,
+                'p_message_img_tag'            => 1,
+                'p_message_all_caps'        => 1,
+                'p_subject_all_caps'        => 1,
+                'p_sig_all_caps'            => 1,
+                'p_sig_bbcode'                => 1,
+                'p_sig_img_tag'                => 0,
+                'p_sig_length'                => 400,
+                'p_sig_lines'                => 4,
+                'p_allow_banned_email'        => 1,
+                'p_allow_dupe_email'        => 0,
+                'p_force_guest_email'        => 1,
+                'o_pms_enabled'                => 1,                    // New PMS - Visman
+                'o_pms_min_kolvo'            => 0,
+                'o_merge_timeout'            => 86400,        // merge post - Visman
+                'o_board_redirect'        => '',    // для редиректа - Visman
+                'o_board_redirectg'        => 0,
+                'o_poll_enabled'            => 0,    // опросы - Visman
+                'o_poll_max_ques'            => 3,
+                'o_poll_max_field'        => 20,
+                'o_poll_time'                    => 60,
+                'o_poll_term'                    => 3,
+                'o_poll_guest'                => 0,
+                'o_fbox_guest'                => 0,    // Fancybox - Visman
+                'o_fbox_files'                => 'viewtopic.php,search.php,pmsnew.php',
+                'o_coding_forms'            => 1,    // кодирование форм - Visman
+                'o_check_ip'                    => 0,    // проверка ip администрации - Visman
+                'o_crypto_enable'            => 1,    // случайные имена полей форм - Visman
+                'o_crypto_pas'                => random_pass(25),
+                'o_crypto_salt'                => random_pass(13),
+                'o_enable_acaptcha'        => 1, // математическая каптча
+                'st_max_users'                => 1,    // статистика по максимуму юзеров - Visman
+                'st_max_users_time'        => time(),
+            );
+
+            foreach ($pun_config as $conf_name => $conf_value)
+            {
+                $db->query('INSERT INTO '.$db_prefix.'config (conf_name, conf_value) VALUES(\''.$conf_name.'\', '.(is_null($conf_value) ? 'NULL' : '\''.$db->escape($conf_value).'\'').')')
+                    or error('Unable to insert into table '.$db_prefix.'config. Please check your configuration and try again', __FILE__, __LINE__, $db->error());
+            }
+
+            // Insert some other default data
+            $subject = $lang_install['Test post'];
+            $message = $lang_install['Message'];
+
+            $db->query('INSERT INTO '.$db_prefix.'categories (cat_name, disp_position) VALUES(\''.$db->escape($lang_install['Test category']).'\', 1)')
+                or error('Unable to insert into table '.$db_prefix.'categories. Please check your configuration and try again', __FILE__, __LINE__, $db->error());
+
+            $db->query('INSERT INTO '.$db_prefix.'forums (forum_name, forum_desc, num_topics, num_posts, last_post, last_post_id, last_poster, last_topic, disp_position, cat_id) VALUES(\''.$db->escape($lang_install['Test forum']).'\', \''.$db->escape($lang_install['This is just a test forum']).'\', 1, 1, '.$now.', 1, \''.$db->escape($username).'\', \''.$db->escape($subject).'\', 1, 1)') // last topic on index - Visman
+                or error('Unable to insert into table '.$db_prefix.'forums. Please check your configuration and try again', __FILE__, __LINE__, $db->error());
+
+            $db->query('INSERT INTO '.$db_prefix.'topics (poster, subject, posted, first_post_id, last_post, last_post_id, last_poster, forum_id) VALUES(\''.$db->escape($username).'\', \''.$db->escape($subject).'\', '.$now.', 1, '.$now.', 1, \''.$db->escape($username).'\', 1)')
+                or error('Unable to insert into table '.$db_prefix.'topics. Please check your configuration and try again', __FILE__, __LINE__, $db->error());
+
+            $db->query('INSERT INTO '.$db_prefix.'posts (poster, poster_id, poster_ip, message, posted, topic_id) VALUES(\''.$db->escape($username).'\', 2, \''.$db->escape(get_remote_address()).'\', \''.$db->escape($message).'\', '.$now.', 1)')
+                or error('Unable to insert into table '.$db_prefix.'posts. Please check your configuration and try again', __FILE__, __LINE__, $db->error());
+
+            // Index the test post so searching for it works
+            require PUN_ROOT . 'include/search_idx.php';
+            update_search_index('post', 1, $message, $subject);
+
+            $db->end_transaction();
+
+
+            $alerts = array();
+
+            // Check if we disabled uploading avatars because file_uploads was disabled
+            if ($avatars == '0')
+                $alerts[] = $lang_install['Alert upload'];
+
+            // Add some random bytes at the end of the cookie name to prevent collisions
+            $cookie_prefix = 'fork_'.random_key(7, false, true);
+
+            // Generate the main.php file data
+            $config = $this->generate_config_file($base_url, $db_type, $db_host, $db_name, $db_username, $db_password, $db_prefix, $cookie_prefix);
+
+            // Attempt to write main.php and serve it up for download if writing fails
+            $written = false;
+            if (forum_is_writable($this->c->getParameter('DIR_CONFIG')))
+            {
+                $fh = @fopen($this->c->getParameter('DIR_CONFIG') . 'main.php', 'wb');
+                if ($fh)
+                {
+                    fwrite($fh, $config);
+                    fclose($fh);
+
+                    $written = true;
+                }
+            }
+
+
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1" />
+<title><?php echo $lang_install['ForkBB Installation'] ?></title>
+<link rel="stylesheet" type="text/css" href="style/<?php echo $default_style ?>.css" />
+</head>
+<body>
+
+<div id="puninstall" class="pun">
+<div class="top-box"><div><!-- Top Corners --></div></div>
+<div class="punwrap">
+
+<div id="brdheader" class="block">
+    <div class="box">
+        <div id="brdtitle" class="inbox">
+            <h1><span><?php echo $lang_install['ForkBB Installation'] ?></span></h1>
+            <div id="brddesc"><p><?php echo $lang_install['ForkBB has been installed'] ?></p></div>
+        </div>
+    </div>
+</div>
+
+<div id="brdmain">
+
+<div class="blockform">
+    <h2><span><?php echo $lang_install['Final instructions'] ?></span></h2>
+    <div class="box">
+<?php
+
+if (! $written)
+{
+
+?>
+        <form method="post" action="">
+            <div class="inform">
+                <div class="forminfo">
+                    <p><?php echo $lang_install['Info 17'] ?></p>
+                    <p><?php echo $lang_install['Info 18'] ?></p>
+                </div>
+                <input type="hidden" name="generate_config" value="1" />
+                <input type="hidden" name="base_url" value="<?php echo pun_htmlspecialchars($base_url); ?>" />
+                <input type="hidden" name="db_type" value="<?php echo pun_htmlspecialchars($db_type); ?>" />
+                <input type="hidden" name="db_host" value="<?php echo pun_htmlspecialchars($db_host); ?>" />
+                <input type="hidden" name="db_name" value="<?php echo pun_htmlspecialchars($db_name); ?>" />
+                <input type="hidden" name="db_username" value="<?php echo pun_htmlspecialchars($db_username); ?>" />
+                <input type="hidden" name="db_password" value="<?php echo pun_htmlspecialchars($db_password); ?>" />
+                <input type="hidden" name="db_prefix" value="<?php echo pun_htmlspecialchars($db_prefix); ?>" />
+                <input type="hidden" name="cookie_prefix" value="<?php echo pun_htmlspecialchars($cookie_prefix); ?>" />
+
+<?php if (! empty($alerts)): ?>                <div class="forminfo error-info">
+                    <ul class="error-list">
+<?php
+
+foreach ($alerts as $cur_alert)
+    echo "\t\t\t\t\t".'<li>'.$cur_alert.'</li>'."\n";
+
+?>
+                    </ul>
+                </div>
+<?php endif; ?>            </div>
+            <p class="buttons"><input type="submit" value="<?php echo $lang_install['Download main.php file'] ?>" /></p>
+        </form>
+
+<?php
+
+}
+else
+{
+
+?>
+        <div class="fakeform">
+            <div class="inform">
+                <div class="forminfo">
+                    <p><?php echo $lang_install['ForkBB fully installed'] ?></p>
+                </div>
+            </div>
+        </div>
+<?php
+
+}
+
+?>
+    </div>
+</div>
+
+</div>
+
+</div>
+<div class="end-box"><div><!-- Bottom Corners --></div></div>
+</div>
+
+</body>
+</html>
+<?php
+
+        }
+
+        exit;
+    }
+}

+ 202 - 0
app/Core/Request.php

@@ -0,0 +1,202 @@
+<?php
+
+namespace ForkBB\Core;
+
+class Request
+{
+    /**
+     * @param string $key
+     * @param mixed $default
+     *
+     * @return mixed
+     */
+    public function request($key, $default = null)
+    {
+        if (($result = $this->post($key)) === null
+            && ($result = $this->get($key)) === null
+        ) {
+            return $default;
+        }
+        return $result;
+    }
+
+    /**
+     * @param string $key
+     * @param mixed $default
+     *
+     * @return mixed
+     */
+    public function requestStr($key, $default = null)
+    {
+        if (($result = $this->postStr($key)) === null
+            && ($result = $this->getStr($key)) === null
+        ) {
+            return $default;
+        }
+        return $result;
+    }
+
+    /**
+     * @param string $key
+     * @param mixed $default
+     *
+     * @return mixed
+     */
+    public function requestInt($key, $default = null)
+    {
+        if (($result = $this->postInt($key)) === null
+            && ($result = $this->getInt($key)) === null
+        ) {
+            return $default;
+        }
+        return $result;
+    }
+
+    /**
+     * @param string $key
+     * @param mixed $default
+     *
+     * @return mixed
+     */
+    public function requestBool($key, $default = null)
+    {
+        if (($result = $this->postBool($key)) === null
+            && ($result = $this->getBool($key)) === null
+        ) {
+            return $default;
+        }
+        return $result;
+    }
+
+    /**
+     * @param string $key
+     * @param mixed $default
+     *
+     * @return mixed
+     */
+    public function post($key, $default = null)
+    {
+        if (isset($_POST[$key])) {
+            return $this->replBadChars($_POST[$key]);
+        }
+        return $default;
+    }
+
+    /**
+     * @param string $key
+     * @param mixed $default
+     *
+     * @return mixed
+     */
+    public function postStr($key, $default = null)
+    {
+        if (isset($_POST[$key]) && is_string($_POST[$key])) {
+            return $this->replBadChars($_POST[$key]);
+        }
+        return $default;
+    }
+
+    /**
+     * @param string $key
+     * @param mixed $default
+     *
+     * @return mixed
+     */
+    public function postInt($key, $default = null)
+    {
+        if (isset($_POST[$key]) && is_numeric($_POST[$key]) && is_int(0 + $_POST[$key])) {
+            return (int) $_POST[$key];
+        }
+        return $default;
+    }
+
+    /**
+     * @param string $key
+     * @param mixed $default
+     *
+     * @return mixed
+     */
+    public function postBool($key, $default = null)
+    {
+        if (isset($_POST[$key]) && is_string($_POST[$key])) {
+            return (bool) $_POST[$key];
+        }
+        return $default;
+    }
+
+    /**
+     * @param string $key
+     * @param mixed $default
+     *
+     * @return mixed
+     */
+    public function get($key, $default = null)
+    {
+        if (isset($_GET[$key])) {
+            return $this->replBadChars($_GET[$key]);
+        }
+        return $default;
+    }
+
+    /**
+     * @param string $key
+     * @param mixed $default
+     *
+     * @return mixed
+     */
+    public function getStr($key, $default = null)
+    {
+        if (isset($_GET[$key]) && is_string($_GET[$key])) {
+            return $this->replBadChars($_GET[$key]);
+        }
+        return $default;
+    }
+
+    /**
+     * @param string $key
+     * @param mixed $default
+     *
+     * @return mixed
+     */
+    public function getInt($key, $default = null)
+    {
+        if (isset($_GET[$key]) && is_numeric($_GET[$key]) && is_int(0 + $_GET[$key])) {
+            return (int) $_GET[$key];
+        }
+        return $default;
+    }
+
+    /**
+     * @param string $key
+     * @param mixed $default
+     *
+     * @return mixed
+     */
+    public function getBool($key, $default = null)
+    {
+        if (isset($_GET[$key]) && is_string($_GET[$key])) {
+            return (bool) $_GET[$key];
+        }
+        return $default;
+    }
+
+    /**
+     * @param string|array $data
+     *
+     * @return string|array
+     */
+    protected function replBadChars($data)
+    {
+        if (is_array($data)) {
+            return array_map([$this, 'replBadChars'], $data);
+        }
+
+        // slow, small memory
+        //$data = mb_convert_encoding((string) $data, 'UTF-8', 'UTF-8');
+        // fast, large memory
+        $data = htmlspecialchars_decode(htmlspecialchars((string) $data, ENT_SUBSTITUTE, 'UTF-8'));
+
+        // Remove control characters
+        return preg_replace('%[\x00-\x08\x0B-\x0C\x0E-\x1F]%', '', $data);
+    }
+}

+ 51 - 0
app/bootstrap.php

@@ -0,0 +1,51 @@
+<?php
+
+namespace ForkBB;
+
+use R2\DependencyInjection\Container;
+use Exception;
+
+if (! defined('PUN_ROOT'))
+	exit('The constant PUN_ROOT must be defined and point to a valid FluxBB installation root directory.');
+
+// боевой
+#error_reporting(E_ALL);
+#ini_set('display_errors', 0);
+#ini_set('log_errors', 1);
+// разраб
+error_reporting(E_ALL);
+ini_set('display_errors', 1);
+ini_set('log_errors', 1);
+
+mb_language('uni');
+mb_internal_encoding('UTF-8');
+mb_substitute_character(0xFFFD);
+
+// The maximum size of a post, in bytes, since the field is now MEDIUMTEXT this allows ~16MB but lets cap at 1MB...
+if (!defined('PUN_MAX_POSTSIZE'))
+	define('PUN_MAX_POSTSIZE', 1048576);
+
+if (!defined('PUN_SEARCH_MIN_WORD'))
+	define('PUN_SEARCH_MIN_WORD', 3);
+if (!defined('PUN_SEARCH_MAX_WORD'))
+	define('PUN_SEARCH_MAX_WORD', 20);
+
+if (!defined('FORUM_MAX_COOKIE_SIZE'))
+	define('FORUM_MAX_COOKIE_SIZE', 4048);
+
+$loader = require __DIR__ . '/../vendor/autoload.php';
+$loader->setPsr4('ForkBB\\', __DIR__ . '/');
+
+if (file_exists(__DIR__ . '/config/main.php')) {
+    $container = new Container(include __DIR__ . '/config/main.php');
+} elseif (file_exists(__DIR__ . '/config/install.php')) {
+    $container = new Container(include __DIR__ . '/config/install.php');
+} else {
+    throw new Exception('Application is not configured');
+}
+
+define('PUN', 1);
+
+$container->setParameter('DIR_CONFIG', __DIR__ . '/config/');
+//$container->setParameter('DIR_CACHE', __DIR__ . '/cache/');
+$container->get('firstAction');

+ 33 - 0
app/config/install.php

@@ -0,0 +1,33 @@
+<?php
+
+return [
+    'DB_TYPE'     => '_DB_TYPE_',
+    'DB_HOST'     => '_DB_HOST_',
+    'DB_USERNAME' => '_DB_USERNAME_',
+    'DB_PASSWORD' => '_DB_PASSWORD_',
+    'DB_NAME'     => '_DB_NAME_',
+    'DB_PREFIX'   => '_DB_PREFIX_',
+    'P_CONNECT'   => false,
+    'shared' => [
+        'Request' => \ForkBB\Core\Request::class,
+        'DBLoader' => [
+            'class' => \ForkBB\Core\DBLoader::class,
+            'db_host'     => '%DB_HOST%',
+            'db_username' => '%DB_USERNAME%',
+            'db_password' => '%DB_PASSWORD%',
+            'db_name'     => '%DB_NAME%',
+            'db_prefix'   => '%DB_PREFIX%',
+            'p_connect'   => '%P_CONNECT%',
+        ],
+        'DB' => [
+            'factory method' => '@DBLoader:load',
+            'type' => '%DB_TYPE%',
+        ],
+        'Install' => [
+            'class' => \ForkBB\Core\Install::class,
+            'request' => '@Request',
+        ],
+        'firstAction' => '@Install:start',
+    ],
+    'multiple'  => [],
+];

+ 37 - 0
app/config/main.dist.php

@@ -0,0 +1,37 @@
+<?php
+
+return [
+    'BASE_URL'    => '_BASE_URL_',
+    'DB_TYPE'     => '_DB_TYPE_',
+    'DB_HOST'     => '_DB_HOST_',
+    'DB_USERNAME' => '_DB_USERNAME_',
+    'DB_PASSWORD' => '_DB_PASSWORD_',
+    'DB_NAME'     => '_DB_NAME_',
+    'DB_PREFIX'   => '_DB_PREFIX_',
+    'P_CONNECT'   => false,
+    'COOKIE_PREFIX' => '_COOKIE_PREFIX_',
+    'COOKIE_DOMAIN' => '',
+    'COOKIE_PATH'   => '/',
+    'COOKIE_SECURE' => false,
+    'COOKIE_SALT'   => '_COOKIE_SALT_',
+    'ALGO_FOR_HMAC' => 'sha1',
+    'SALT1' => '',
+    'shared' => [
+        'Request' => \ForkBB\Core\Request::class,
+        'DBLoader' => [
+            'class' => \ForkBB\Core\DBLoader::class,
+            'db_host'     => '%DB_HOST%',
+            'db_username' => '%DB_USERNAME%',
+            'db_password' => '%DB_PASSWORD%',
+            'db_name'     => '%DB_NAME%',
+            'db_prefix'   => '%DB_PREFIX%',
+            'p_connect'   => '%P_CONNECT%',
+        ],
+        'DB' => [
+            'factory method' => '@DBLoader:load',
+            'type' => '%DB_TYPE%',
+        ],
+        'firstAction' => \ForkBB\Core\Blank::class,
+    ],
+    'multiple'  => [],
+];

+ 18 - 0
composer.json

@@ -0,0 +1,18 @@
+{
+    "name": "forkbb/forkbb",
+    "description": "The ForkBB is an open source forum application.",
+    "homepage": "https://github.com/forkbb",
+    "type": "project",
+    "license": "GPL 2.0",
+    "authors": [
+        {
+            "name": "Visman",
+            "email": "mio.visman@yandex.ru",
+            "homepage": "https://github.com/MioVisman"
+        }
+    ],
+    "require": {
+        "php": ">=5.6.0",
+        "artoodetoo/container": "dev-master"
+    }
+}

+ 62 - 0
composer.lock

@@ -0,0 +1,62 @@
+{
+    "_readme": [
+        "This file locks the dependencies of your project to a known state",
+        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+        "This file is @generated automatically"
+    ],
+    "hash": "ec3608ec80234fd3cec94b928cfb161c",
+    "content-hash": "624c9d21e02e6a9ad19afc8c1e907875",
+    "packages": [
+        {
+            "name": "artoodetoo/container",
+            "version": "dev-master",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/artoodetoo/container.git",
+                "reference": "1c52096695eb699e2c63b6b84703794d94d0659f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/artoodetoo/container/zipball/1c52096695eb699e2c63b6b84703794d94d0659f",
+                "reference": "1c52096695eb699e2c63b6b84703794d94d0659f",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.6"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "R2\\DependencyInjection\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "artoodetoo"
+                }
+            ],
+            "description": "Yet another service container",
+            "keywords": [
+                "container",
+                "service"
+            ],
+            "time": "2016-12-27 20:57:10"
+        }
+    ],
+    "packages-dev": [],
+    "aliases": [],
+    "minimum-stability": "stable",
+    "stability-flags": {
+        "artoodetoo/container": 20
+    },
+    "prefer-stable": false,
+    "prefer-lowest": false,
+    "platform": {
+        "php": ">=5.6.0"
+    },
+    "platform-dev": []
+}

BIN
favicon.ico


+ 0 - 5
include/addons.php

@@ -6,11 +6,6 @@
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
 
-// Make sure no one attempts to run this script "directly"
-if (!defined('PUN'))
-	exit;
-
-
 /**
  * Class flux_addon_manager
  *

+ 13 - 26
include/common.php

@@ -11,15 +11,17 @@ if (!defined('PUN_ROOT'))
 
 // Define the version and database revision that this code was written for
 define('FORUM_VERSION', '1.5.10');
-
 define('FORUM_VER_REVISION', 75);	// номер сборки - Visman
-
-$page_js = array();
-
 define('FORUM_DB_REVISION', 21);
 define('FORUM_SI_REVISION', 2.1);
 define('FORUM_PARSER_REVISION', 2);
 
+define('MIN_PHP_VERSION', '5.6.0');
+define('MIN_MYSQL_VERSION', '4.1.2');
+define('MIN_PGSQL_VERSION', '7.0.0');
+define('PUN_SEARCH_MIN_WORD', 3);
+define('PUN_SEARCH_MAX_WORD', 20);
+
 // Block prefetch requests
 if (isset($_SERVER['HTTP_X_MOZ']) && $_SERVER['HTTP_X_MOZ'] == 'prefetch')
 {
@@ -34,24 +36,11 @@ if (isset($_SERVER['HTTP_X_MOZ']) && $_SERVER['HTTP_X_MOZ'] == 'prefetch')
 	exit;
 }
 
-// Attempt to load the configuration file config.php
-if (file_exists(PUN_ROOT.'include/config.php'))
-	require PUN_ROOT.'include/config.php';
-
-// If we have the 1.3-legacy constant defined, define the proper 1.4 constant so we don't get an incorrect "need to install" message
-if (defined('FORUM'))
-	define('PUN', FORUM);
-
-// If PUN isn't defined, config.php is missing or corrupt
-if (!defined('PUN'))
-{
-	header('Location: install.php');
-	exit;
-}
-
 // Record the start time (will be used to calculate the generation time for the page)
 $pun_start = microtime(true);
 
+$page_js = array();
+
 // Load the functions script
 require PUN_ROOT.'include/functions.php';
 
@@ -67,16 +56,14 @@ forum_remove_bad_characters();
 // Reverse the effect of register_globals
 forum_unregister_globals();
 
-// The addon manager is responsible for storing the hook listeners and communicating with the addons
-$flux_addons = new flux_addon_manager();
-
-// Make sure PHP reports all errors except E_NOTICE. FluxBB supports E_ALL, but a lot of scripts it may interact with, do not
-//error_reporting(E_ALL ^ E_NOTICE);
-error_reporting(E_ALL);
-
 // Force POSIX locale (to prevent functions such as strtolower() from messing up UTF-8 strings)
 setlocale(LC_CTYPE, 'C');
 
+require PUN_ROOT . 'app/bootstrap.php';
+
+// The addon manager is responsible for storing the hook listeners and communicating with the addons
+$flux_addons = new flux_addon_manager();
+
 // If a cookie name is not specified in config.php, we use the default (pun_cookie)
 if (empty($cookie_name))
 	$cookie_name = 'pun_cookie';

+ 1 - 3
include/functions.php

@@ -1242,9 +1242,7 @@ function random_pass($len)
 //
 function pun_hash($str)
 {
-	global $salt1;
-
-	return sha1($str.$salt1); // соль на пароль - Visman
+	return sha1($str);
 }
 
 

+ 8 - 2
include/search_idx.php

@@ -244,7 +244,10 @@ function strip_bbcode($text)
 //
 function update_search_index($mode, $post_id, $message, $subject = null)
 {
-	global $db_type, $db;
+	global $container;
+
+    $db = $container->get('DB');
+    $db_type = $container->getParameter('DB_TYPE');
 
 	$message = utf8_strtolower($message);
 	$subject = utf8_strtolower($subject);
@@ -358,7 +361,10 @@ function update_search_index($mode, $post_id, $message, $subject = null)
 //
 function strip_search_index($post_ids)
 {
-	global $db_type, $db;
+	global $container;
+
+    $db = $container->get('DB');
+    $db_type = $container->getParameter('DB_TYPE');
 
 	switch ($db_type)
 	{

+ 6 - 8
lang/English/install.php

@@ -8,10 +8,9 @@ $lang_install = array(
 'Choose install language info'	=>	'The language used for this install script. The default language used for the board itself can be set below.',
 'Install language'				=>	'Install language',
 'Change language'				=>	'Change language',
-'Already installed'				=>	'It seems like ForkBB is already installed. You should go <a href="index.php">here</a> instead.',
 'You are running error'			=>	'You are running %1$s version %2$s. ForkBB %3$s requires at least %1$s %4$s to run properly. You must upgrade your %1$s installation before you can continue.',
 'My ForkBB Forum'				=>	'My ForkBB Forum',
-'Description'					=>	'Unfortunately no one can be told what ForkBB is - you have to see it for yourself.',
+'Description'					=>	'Forum powered by open source software - ForkBB.',
 'Username 1'					=>	'Usernames must be at least 2 characters long.',
 'Username 2'					=>	'Usernames must not be more than 25 characters long.',
 'Username 3'					=>	'The username guest is reserved.',
@@ -55,7 +54,7 @@ $lang_install = array(
 'Table prefix'					=>	'Table prefix',
 'Administration setup'			=>	'Administration setup',
 'Info 7'						=>	'Create the very first account on your board.',
-'Info 8'						=>	'Your username should be between 2 and 25 characters long. Your password must be at least 6 characters long. Salt must be at least 10 characters long. Remember that passwords and salt are case-sensitive.',
+'Info 8'						=>	'Your username should be between 2 and 25 characters long. Your password must be at least 6 characters long. Remember that passwords are case-sensitive.',
 'Password'						=>	'Password',
 'Confirm password'				=>	'Confirm password',
 'Board setup'					=>	'Board setup',
@@ -81,7 +80,7 @@ $lang_install = array(
 'Members'						=>	'Members',
 'Announcement'					=>	'Enter your announcement here.',
 'Rules'							=>	'Enter your rules here',
-'Maintenance message'			=>	'The forums are temporarily down for maintenance. Please try again in a few minutes.',
+'Maintenance message'			=>	'The forums are temporarily down for maintenance. Come back later.',
 'Test post'						=>	'Test topic',
 'Message'						=>	'If you are looking at this (which I guess you are), the install of ForkBB appears to have worked! Now log in and head over to the administration control panel to configure your forum.',
 'Test category'					=>	'Test category',
@@ -92,10 +91,9 @@ $lang_install = array(
 'Alert upload'					=>	'<strong>File uploads appear to be disallowed on this server!</strong> If you want users to be able to upload their own avatar images you must enable the file_uploads configuration setting in PHP. Once file uploads have been enabled, avatar uploads can be enabled in Administration/Options/Features.',
 'ForkBB has been installed'		=>	'ForkBB has been installed. To finalize the installation please follow the instructions below.',
 'Final instructions'			=>	'Final instructions',
-'Info 17'						=>	'To finalize the installation, you need to click on the button below to download a file called config.php. You then need to upload this file to directory /include of your ForkBB installation.',
-'Info 18'						=>	'Once you have uploaded config.php, ForkBB will be fully installed! At that point, you may <a href="index.php">go to the forum index</a>.',
-'Download config.php file'		=>	'Download config.php file',
+'Info 17'						=>	'To finalize the installation, you need to click on the button below to download a file called main.php. You then need to upload this file to directory /app/config (near the main.dist.php file) of your ForkBB installation.',
+'Info 18'						=>	'Once you have uploaded main.php, ForkBB will be fully installed! At that point, you may <a href="index.php">go to the forum index</a>.',
+'Download main.php file'		=>	'Download main.php file',
 'ForkBB fully installed'		=>	'ForkBB has been fully installed! You may now <a href="index.php">go to the forum index</a>.',
-'Salt for password' => 'Salt for passwords',
 
 );

+ 19 - 21
lang/Russian/install.php

@@ -4,21 +4,20 @@
 
 $lang_install = array(
 
-'Choose install language'		=>	'Язык для скрипта установки',
+'Choose install language'		=>	'Язык скрипта установки',
 'Choose install language info'	=>	'Язык, на котором будет отображаться информация во время установки форума. Для форума язык по умолчанию можно задать отдельно, ниже.',
 'Install language'				=>	'Язык',
-'Change language'				=>	'Выберите язык',
-'Already installed'				=>	'ForkBB, кажется, уже установлен. <a href="index.php">Перейдите</a> на форум.',
+'Change language'				=>	'Выбрать язык',
 'You are running error'			=>	'Вы используете %1$s версии %2$s. ForkBB %3$s требует как минимум %1$s %4$s для своей работы. Вы должны обновить ваш %1$s, прежде чем продолжить.',
 'My ForkBB Forum'				=>	'Мой ForkBB форум',
-'Description'					=>	'К сожалению ни кто не может описать, что такое ForkBB - вам придется это увидеть.',
-'Username 1'					=>	'Имя пользователя должно быть хотя бы 2 символа длиной.',
-'Username 2'					=>	'Имя пользователя должно быть не более 25 символов.',
+'Description'					=>	'Форум работает на движке с открытым исходным кодом - ForkBB.',
+'Username 1'					=>	'Имя пользователя должно содержать хотя бы 2 символа.',
+'Username 2'					=>	'Имя пользователя должно быть не длиннее 25 символов.',
 'Username 3'					=>	'Имя "гость" зарезервировано.',
 'Username 4'					=>	'Имя пользователя не может быть похоже на IP адрес.',
 'Username 5'					=>	'Имя не может содержать символы \', " или [ и ] одновременно.',
 'Username 6'					=>	'Имя не должно содержать символы форматирования (BBCode) которые используются на форуме.',
-'Short password'				=>	'Пароль должен иметь длину не менее 6 символов.',
+'Short password'				=>	'Пароль должен быть не короче 6 символов.',
 'Passwords not match'			=>	'Пароли не совпадают.',
 'Wrong email'					=>	'Ошибка в email администратора.',
 'No board title'				=>	'Вы забыли ввести заголовок для форума.',
@@ -27,8 +26,8 @@ $lang_install = array(
 'No DB extensions'				=>	'Ваш PHP не поддерживает данный тип базы данных. PHP должен иметь поддержку MySQL, PostgreSQL или SQLite, чтобы установить ForkBB.',
 'Administrator username'		=>	'Имя администратора',
 'Administrator email'			=>	'Email администратора',
-'Board title'					=>	'Заголовок для форума',
-'Base URL'						=>	'URL (без завершающего слэша) вашего форума. Должен быть введен без ошибок.',
+'Board title'					=>	'Заголовок форума',
+'Base URL'						=>	'URL форума, без завершающего слэша. Не ошибитесь при вводе.',
 'Required field'				=>	'обязательное поле в этой форме.',
 'ForkBB Installation'			=>	'Установка ForkBB',
 'Welcome'						=>	'Вы собираетесь установить ForkBB. Для этого заполните ниже представленную форму. Если вы столкнетесь с трудностями в процессе установки, пожалуйста, обратитесь к документации.',
@@ -55,7 +54,7 @@ $lang_install = array(
 'Table prefix'					=>	'Префикс таблиц',
 'Administration setup'			=>	'Настройки администратора',
 'Info 7'						=>	'Создайте первый аккаунт на вашем форуме.',
-'Info 8'						=>	'Длина имени должна быть от 2 до 25 символов. Пароль должен иметь длину не менее 6 символов. Соль должна иметь длину не менее 10 символов. Пароль и соль чувствительны к регистру.',
+'Info 8'						=>	'Длина имени должна быть от 2 до 25 символов. Пароль должен иметь длину не менее 6 символов. Пароль чувствителен к регистру.',
 'Password'						=>	'Пароль',
 'Confirm password'				=>	'Подтверждение пароля',
 'Board setup'					=>	'Настройки форума',
@@ -79,23 +78,22 @@ $lang_install = array(
 'Guests'						=>	'Гости',
 'Guest'							=>	'Гость',
 'Members'						=>	'Пользователи',
-'Announcement'					=>	'Введите свое объвление в этом поле',
-'Rules'							=>	'Введите правила форума в этом поле',
-'Maintenance message'			=>	'Форум временно переведен в режим обслуживания. Пожалуйста, вернитесь через несколько минут.',
+'Announcement'					=>	'Тут можно задать свое объявление',
+'Rules'							=>	'Тут можно указать правила форума',
+'Maintenance message'			=>	'Форум временно переведен в режим обслуживания. Зайдите позже.',
 'Test post'						=>	'Тестовая тема',
-'Message'						=>	'Если вы видите это сообщение, то установка ForkBB закончена и форум работает! Теперь перейдите в админку и сконфигурируйте ваш форум.',
+'Message'						=>	'Если вы видите это сообщение, то установка ForkBB закончена и форум работает! Теперь перейдите в админку (войдите на форум под администратором) и сконфигурируйте форум.',
 'Test category'					=>	'Тестовая категория',
 'Test forum'					=>	'Тестовый раздел',
-'This is just a test forum'		=>	'Этот раздел создан для тестирования',
-'Alert cache'					=>	'<strong>Папка кэша закрыта от записи!</strong> Для правильного функционирования ForkBB директория <em>%s</em> должна быть открыта для записи из PHP. Используйте chmod для установки прав на директорию. Если сомневаетесь, то установите права 0777.',
-'Alert avatar'					=>	'<strong>Папка для аватар закрыта от записи!</strong> Если вы хотите, чтобы пользователи форума использовали аватары, вы должны разрешить запись в директорию <em>%s</em> для PHP. Позже вы можете сменить директорию хранения аватар (смотрите Админка/Опции). Используйте chmod для установки прав на директорию. Если сомневаетесь, то установите права 0777.',
+'This is just a test forum'		=>	'Этот раздел создан при установке форума',
+'Alert cache'					=>	'<strong>Папка кэша заблокирована для записи!</strong> Для правильного функционирования ForkBB директория <em>%s</em> должна быть открыта для записи из PHP. Используйте chmod для установки прав на директорию. Если сомневаетесь, то установите права 0777.',
+'Alert avatar'					=>	'<strong>Папка для аватар заблокирована для записи!</strong> Если вы хотите, чтобы пользователи форума использовали аватары, вы должны разрешить запись в директорию <em>%s</em> для PHP. Позже вы можете сменить директорию хранения аватар (смотрите Админка/Опции). Используйте chmod для установки прав на директорию. Если сомневаетесь, то установите права 0777.',
 'Alert upload'					=>	'<strong>Загрузка файлов, кажется, выключена на этом сервере!</strong> Если вы хотите, чтобы пользователи форума использовали аватары, вы должны разрешить file_uploads в настройках вашего PHP. После разрешения загрузки файлов на сервер, вы можете разрешить использования аватар для пользователей форума (смотрите Админка/Опции).',
 'ForkBB has been installed'		=>	'ForkBB установлен. Для завершения следуйте ниже приведенной инструкции.',
 'Final instructions'			=>	'Заключительная инструкция',
-'Info 17'						=>	'Чтобы завершить установку форума, нажмите кнопку ниже для скачивания файла config.php. После этого запишите этот файл в директорию /include вашего форума.',
-'Info 18'						=>	'Как только вы загрузите config.php на форум, ForkBB будет полностью установлен! После этого <a href="index.php">перейти на главную страницу форума</a>.',
-'Download config.php file'		=>	'Скачать файл config.php',
+'Info 17'						=>	'Чтобы завершить установку форума, нажмите кнопку ниже для скачивания файла main.php. После этого запишите этот файл в директорию /app/config (рядом с файлом main.dist.php) вашего форума.',
+'Info 18'						=>	'Как только вы загрузите main.php на форум, ForkBB будет полностью установлен! После этого <a href="index.php">перейти на главную страницу форума</a>.',
+'Download main.php file'		=>	'Скачать файл main.php',
 'ForkBB fully installed'		=>	'ForkBB полностью установлен! Теперь вы можете <a href="index.php">перейти на главную страницу форума</a>.',
-'Salt for password' => 'Соль для паролей',
 
 );

+ 21 - 0
vendor/artoodetoo/container/LICENSE

@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 110 - 0
vendor/artoodetoo/container/README.md

@@ -0,0 +1,110 @@
+# Container
+Yet another service container
+
+Container **DOES NOT** use reflection and type hint information in any form.
+
+## Features
+Container keep two kind of values: services and parameters.
+
+Service will instantiated when you call call it by name. _Usually_ service is object type, but not nesessary. Mind it as
+"product" of some routine.
+
+Methods: `get()` and `set()`.
+
+Parameters are not instantiating, they just stored and retrieved.
+
+Methods: `getParameter()` and `setParameter()`.
+
+## Install
+
+To install with composer:
+
+```sh
+composer require artoodetoo/container
+```
+
+## Basic Usage
+
+### Configuration
+There are special configuraton sections:
+
+- root used to store basic values, which can be substituted into service definition.
+- `shared` node defines shared services. after it created once it can be retrieved many times.
+- `multiple` node defines new instance created on every get() call
+
+In other sections you can store any kind of information and retrieve it in dot notation (see below).
+
+### Simple service definition
+```php
+use R2\DependancyInjection\Container;
+
+$config = [
+  'shared' => [
+    'view' => R2\Templating\Dirk::class
+  ]
+];
+$c = new Container($config);
+$c->get('view')->render('index');
+```
+### Parameters substitution
+```php
+$config = [
+  'ROOT'   => '\var\www\mysite',
+  'PUBLIC' => '\var\www\mysite\public',
+  'shared' => [
+    'view' => [
+      'class=' => R2\Templating\Dirk::class,
+      'options' => [
+          'views' => '%ROOT%/views',
+          'cache' => '%ROOT%/cache',
+      ],
+      ...
+  ]
+];
+...
+$c->get('view')->render('index');
+```
+### Factory method
+```php
+$config = [
+  'shared' => [
+    'userManager' => App\UserManager::class,
+    'user' => '@userManager:getCurrentUser',
+    ...
+  ]
+]
+...
+echo $c->get('user')->username;
+```
+
+### Parameters and dot notation:
+```php
+$config = [
+  'options' => [
+    'cookie' => [
+      'name' => 'the-cookie',
+      'domain' => '.example.com'
+    ]
+];
+...
+setcookie(
+  $c->getParameter('options.cookie.name'),
+  $value,
+  0,
+  '/',
+  $c->getParameter('options.cookie.domain')
+);
+
+```
+
+### Notes and Limits
+
+Any part of configuration can be read by getParameter, including special sections `shared` and `multiple`.
+
+As for now, substitution patterns work in service production only.
+
+Only parameters from config root can be used in substitution patterns. 
+
+### License
+
+The Container is open-source software, licensed under the [MIT license](http://opensource.org/licenses/MIT)

+ 19 - 0
vendor/artoodetoo/container/composer.json

@@ -0,0 +1,19 @@
+{
+    "name": "artoodetoo/container",
+    "description": "Yet another service container",
+    "keywords": ["service", "container"],
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "artoodetoo"
+        }
+    ],
+    "require": {
+        "php": ">=5.6"
+    },
+    "autoload": {
+        "psr-4": {
+            "R2\\DependencyInjection\\": "src/"
+        }
+    }
+}

+ 170 - 0
vendor/artoodetoo/container/src/Container.php

@@ -0,0 +1,170 @@
+<?php
+
+namespace R2\DependencyInjection;
+
+use InvalidArgumentException;
+use R2\DependencyInjection\ContainerInterface;
+use R2\DependencyInjection\ContainerAwareInterface;
+
+
+/**
+ * Service Container
+ */
+class Container implements ContainerInterface
+{
+    private $instances = [];
+    private $config = [ 'shared' => [], 'multiple'  => [] ];
+
+    public function __construct(array $config = null)
+    {
+        if (!empty($config)) {
+            $this->config($config);
+        }
+    }
+
+    public function config(array $config)
+    {
+        $this->config = array_replace_recursive($this->config, $config);
+    }
+
+    /**
+     * Gets a service.
+     *
+     * @param string $id The service identifier
+     *
+     * @return mixed The associated service
+     */
+    public function get($id)
+    {
+        if (isset($this->instances[$id])) {
+            return $this->instances[$id];
+        }
+        $toShare = false;
+        if (isset($this->config['shared'][$id])) {
+            $toShare = true;
+            $config = (array) $this->config['shared'][$id];
+        } elseif (isset($this->config['multiple'][$id])) {
+            $config = (array) $this->config['multiple'][$id];
+        } else {
+            throw new \InvalidArgumentException('Wrong property name '.$id);
+        }
+        // N.B. "class" is just the first element, regardless of its key
+        $class = array_shift($config);
+        $args = [];
+        // If you want to susbtitute some values in arguments, use non-numeric keys for them
+        foreach ($config as $k => $v) {
+            $args[] = is_numeric($k) ? $v : $this->resolve($v);
+        }
+        // Special case: reference to factory method
+        if ($class{0} == '@' && strpos($class, ':') !== false) {
+            list($factoryName, $methodName) = explode(':', substr($class, 1));
+            $f = [$this->get($factoryName), $methodName]; /** @var string $f suppress IDE warning :( */
+            $service = $f(...$args);
+        } else {
+            $service = new $class(...$args);
+            if ($service instanceof ContainerAwareInterface) {
+                $service->setContainer($this);
+            }
+        }
+        if ($toShare) {
+            $this->instances[$id] = $service;
+        }
+        return $service;
+    }
+
+    /**
+     * Sets a service.
+     * Provides a fluent interface.
+     *
+     * @param string $id      The service identifier
+     * @param mixed  $service The service instance
+     *
+     * @return ContainerInterface Self reference
+     */
+    public function set($id, $service)
+    {
+        $this->instances[$id] = $service;
+        return $this;
+    }
+
+    /**
+     * Gets a parameter.
+     *
+     * @param string $name The parameter name
+     *
+     * @return mixed The parameter value
+     */
+    public function getParameter($name, $default = null)
+    {
+        $segments = explode('.', $name);
+        $ptr =& $this->config;
+        foreach ($segments as $s) {
+            if (!isset($ptr[$s])) {
+                return $default;
+            }
+            $ptr =& $ptr[$s];
+        }
+
+        return $ptr;
+    }
+
+    /**
+     * Sets a parameter.
+     * Provides a fluent interface.
+     *
+     * @param string $name  The parameter name
+     * @param mixed  $value The parameter value
+     *
+     * @return ContainerInterface Self reference
+     */
+    public function setParameter($name, $value)
+    {
+        $segments = explode('.', $name);
+        $n = count($segments);
+        $ptr =& $this->config;
+        foreach ($segments as $s) {
+            if (--$n) {
+                if (!array_key_exists($s, $ptr)) {
+                    $ptr[$s] = [];
+                } elseif (!is_array($ptr[$s])) {
+                    throw new \InvalidArgumentException("Scalar \"{$s}\" in the path \"{$name}\"");
+                }
+                $ptr =& $ptr[$s];
+            } else {
+                $ptr[$s] = $value;
+            }
+        }
+
+        return $this;
+    }
+
+    protected function resolve($value)
+    {
+        $matches = null;
+        if (is_string($value) && strpos($value, '%') !== false) {
+            // whole string substitution can return any type of value
+            if (preg_match('~^%(\w+)%$~', $value, $matches)) {
+                $value = $this->substitute($matches);
+            // partial string substitution casts value to string
+            } else {
+                $value = preg_replace_callback('~%(\w+)%~', [$this, 'substitute'], $value);
+            }
+        } elseif (is_string($value) && isset($value{0}) && $value{0} == '@') {
+            return $this->get(substr($value, 1));
+        } elseif (is_array($value)) {
+            foreach ($value as &$v) {
+                $v = $this->resolve($v);
+            }
+            unset($v);
+        }
+        return $value;
+    }
+
+    protected function substitute($matches)
+    {
+        if (array_key_exists($matches[1], $this->config)) {
+            return $this->config[$matches[1]];
+        }
+        return '';
+    }
+}

+ 16 - 0
vendor/artoodetoo/container/src/ContainerAwareInterface.php

@@ -0,0 +1,16 @@
+<?php
+
+namespace R2\DependencyInjection;
+
+interface ContainerAwareInterface
+{
+    /**
+     * Sets the Container.
+     * Provides a fluent interface.
+     *
+     * @param ContainerInterface $container A ContainerInterface instance
+     *
+     * @return $this
+     */
+    public function setContainer(ContainerInterface $container);
+}

+ 43 - 0
vendor/artoodetoo/container/src/ContainerInterface.php

@@ -0,0 +1,43 @@
+<?php
+
+namespace R2\DependencyInjection;
+
+interface ContainerInterface
+{
+    /**
+     * Gets a service.
+     *
+     * @param string $id The service identifier
+     *
+     * @return mixed The associated service
+     */
+    public function get($id);
+    /**
+     * Sets a service.
+     * Provides a fluent interface.
+     *
+     * @param string $id      The service identifier
+     * @param mixed  $service The service instance
+     *
+     * @return ContainerInterface Self reference
+     */
+    public function set($id, $service);
+    /**
+     * Gets a parameter.
+     *
+     * @param string $name The parameter name
+     *
+     * @return mixed The parameter value
+     */
+    public function getParameter($name);
+    /**
+     * Sets a parameter.
+     * Provides a fluent interface.
+     *
+     * @param string $name  The parameter name
+     * @param mixed  $value The parameter value
+     *
+     * @return ContainerInterface Self reference
+     */
+    public function setParameter($name, $value);
+}

+ 7 - 0
vendor/autoload.php

@@ -0,0 +1,7 @@
+<?php
+
+// autoload.php @generated by Composer
+
+require_once __DIR__ . '/composer/autoload_real.php';
+
+return ComposerAutoloaderInit90ad93c7251d4f60daa9e545879c49e7::getLoader();

+ 415 - 0
vendor/composer/ClassLoader.php

@@ -0,0 +1,415 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ *     Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Autoload;
+
+/**
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
+ *
+ *     $loader = new \Composer\Autoload\ClassLoader();
+ *
+ *     // register classes with namespaces
+ *     $loader->add('Symfony\Component', __DIR__.'/component');
+ *     $loader->add('Symfony',           __DIR__.'/framework');
+ *
+ *     // activate the autoloader
+ *     $loader->register();
+ *
+ *     // to enable searching the include path (eg. for PEAR packages)
+ *     $loader->setUseIncludePath(true);
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * This class is loosely based on the Symfony UniversalClassLoader.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ * @see    http://www.php-fig.org/psr/psr-0/
+ * @see    http://www.php-fig.org/psr/psr-4/
+ */
+class ClassLoader
+{
+    // PSR-4
+    private $prefixLengthsPsr4 = array();
+    private $prefixDirsPsr4 = array();
+    private $fallbackDirsPsr4 = array();
+
+    // PSR-0
+    private $prefixesPsr0 = array();
+    private $fallbackDirsPsr0 = array();
+
+    private $useIncludePath = false;
+    private $classMap = array();
+    private $classMapAuthoritative = false;
+    private $missingClasses = array();
+
+    public function getPrefixes()
+    {
+        if (!empty($this->prefixesPsr0)) {
+            return call_user_func_array('array_merge', $this->prefixesPsr0);
+        }
+
+        return array();
+    }
+
+    public function getPrefixesPsr4()
+    {
+        return $this->prefixDirsPsr4;
+    }
+
+    public function getFallbackDirs()
+    {
+        return $this->fallbackDirsPsr0;
+    }
+
+    public function getFallbackDirsPsr4()
+    {
+        return $this->fallbackDirsPsr4;
+    }
+
+    public function getClassMap()
+    {
+        return $this->classMap;
+    }
+
+    /**
+     * @param array $classMap Class to filename map
+     */
+    public function addClassMap(array $classMap)
+    {
+        if ($this->classMap) {
+            $this->classMap = array_merge($this->classMap, $classMap);
+        } else {
+            $this->classMap = $classMap;
+        }
+    }
+
+    /**
+     * Registers a set of PSR-0 directories for a given prefix, either
+     * appending or prepending to the ones previously set for this prefix.
+     *
+     * @param string       $prefix  The prefix
+     * @param array|string $paths   The PSR-0 root directories
+     * @param bool         $prepend Whether to prepend the directories
+     */
+    public function add($prefix, $paths, $prepend = false)
+    {
+        if (!$prefix) {
+            if ($prepend) {
+                $this->fallbackDirsPsr0 = array_merge(
+                    (array) $paths,
+                    $this->fallbackDirsPsr0
+                );
+            } else {
+                $this->fallbackDirsPsr0 = array_merge(
+                    $this->fallbackDirsPsr0,
+                    (array) $paths
+                );
+            }
+
+            return;
+        }
+
+        $first = $prefix[0];
+        if (!isset($this->prefixesPsr0[$first][$prefix])) {
+            $this->prefixesPsr0[$first][$prefix] = (array) $paths;
+
+            return;
+        }
+        if ($prepend) {
+            $this->prefixesPsr0[$first][$prefix] = array_merge(
+                (array) $paths,
+                $this->prefixesPsr0[$first][$prefix]
+            );
+        } else {
+            $this->prefixesPsr0[$first][$prefix] = array_merge(
+                $this->prefixesPsr0[$first][$prefix],
+                (array) $paths
+            );
+        }
+    }
+
+    /**
+     * Registers a set of PSR-4 directories for a given namespace, either
+     * appending or prepending to the ones previously set for this namespace.
+     *
+     * @param string       $prefix  The prefix/namespace, with trailing '\\'
+     * @param array|string $paths   The PSR-4 base directories
+     * @param bool         $prepend Whether to prepend the directories
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function addPsr4($prefix, $paths, $prepend = false)
+    {
+        if (!$prefix) {
+            // Register directories for the root namespace.
+            if ($prepend) {
+                $this->fallbackDirsPsr4 = array_merge(
+                    (array) $paths,
+                    $this->fallbackDirsPsr4
+                );
+            } else {
+                $this->fallbackDirsPsr4 = array_merge(
+                    $this->fallbackDirsPsr4,
+                    (array) $paths
+                );
+            }
+        } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
+            // Register directories for a new namespace.
+            $length = strlen($prefix);
+            if ('\\' !== $prefix[$length - 1]) {
+                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+            }
+            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+            $this->prefixDirsPsr4[$prefix] = (array) $paths;
+        } elseif ($prepend) {
+            // Prepend directories for an already registered namespace.
+            $this->prefixDirsPsr4[$prefix] = array_merge(
+                (array) $paths,
+                $this->prefixDirsPsr4[$prefix]
+            );
+        } else {
+            // Append directories for an already registered namespace.
+            $this->prefixDirsPsr4[$prefix] = array_merge(
+                $this->prefixDirsPsr4[$prefix],
+                (array) $paths
+            );
+        }
+    }
+
+    /**
+     * Registers a set of PSR-0 directories for a given prefix,
+     * replacing any others previously set for this prefix.
+     *
+     * @param string       $prefix The prefix
+     * @param array|string $paths  The PSR-0 base directories
+     */
+    public function set($prefix, $paths)
+    {
+        if (!$prefix) {
+            $this->fallbackDirsPsr0 = (array) $paths;
+        } else {
+            $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
+        }
+    }
+
+    /**
+     * Registers a set of PSR-4 directories for a given namespace,
+     * replacing any others previously set for this namespace.
+     *
+     * @param string       $prefix The prefix/namespace, with trailing '\\'
+     * @param array|string $paths  The PSR-4 base directories
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function setPsr4($prefix, $paths)
+    {
+        if (!$prefix) {
+            $this->fallbackDirsPsr4 = (array) $paths;
+        } else {
+            $length = strlen($prefix);
+            if ('\\' !== $prefix[$length - 1]) {
+                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+            }
+            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+            $this->prefixDirsPsr4[$prefix] = (array) $paths;
+        }
+    }
+
+    /**
+     * Turns on searching the include path for class files.
+     *
+     * @param bool $useIncludePath
+     */
+    public function setUseIncludePath($useIncludePath)
+    {
+        $this->useIncludePath = $useIncludePath;
+    }
+
+    /**
+     * Can be used to check if the autoloader uses the include path to check
+     * for classes.
+     *
+     * @return bool
+     */
+    public function getUseIncludePath()
+    {
+        return $this->useIncludePath;
+    }
+
+    /**
+     * Turns off searching the prefix and fallback directories for classes
+     * that have not been registered with the class map.
+     *
+     * @param bool $classMapAuthoritative
+     */
+    public function setClassMapAuthoritative($classMapAuthoritative)
+    {
+        $this->classMapAuthoritative = $classMapAuthoritative;
+    }
+
+    /**
+     * Should class lookup fail if not found in the current class map?
+     *
+     * @return bool
+     */
+    public function isClassMapAuthoritative()
+    {
+        return $this->classMapAuthoritative;
+    }
+
+    /**
+     * Registers this instance as an autoloader.
+     *
+     * @param bool $prepend Whether to prepend the autoloader or not
+     */
+    public function register($prepend = false)
+    {
+        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+    }
+
+    /**
+     * Unregisters this instance as an autoloader.
+     */
+    public function unregister()
+    {
+        spl_autoload_unregister(array($this, 'loadClass'));
+    }
+
+    /**
+     * Loads the given class or interface.
+     *
+     * @param  string    $class The name of the class
+     * @return bool|null True if loaded, null otherwise
+     */
+    public function loadClass($class)
+    {
+        if ($file = $this->findFile($class)) {
+            includeFile($file);
+
+            return true;
+        }
+    }
+
+    /**
+     * Finds the path to the file where the class is defined.
+     *
+     * @param string $class The name of the class
+     *
+     * @return string|false The path if found, false otherwise
+     */
+    public function findFile($class)
+    {
+        // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
+        if ('\\' == $class[0]) {
+            $class = substr($class, 1);
+        }
+
+        // class map lookup
+        if (isset($this->classMap[$class])) {
+            return $this->classMap[$class];
+        }
+        if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
+            return false;
+        }
+
+        $file = $this->findFileWithExtension($class, '.php');
+
+        // Search for Hack files if we are running on HHVM
+        if (false === $file && defined('HHVM_VERSION')) {
+            $file = $this->findFileWithExtension($class, '.hh');
+        }
+
+        if (false === $file) {
+            // Remember that this class does not exist.
+            $this->missingClasses[$class] = true;
+        }
+
+        return $file;
+    }
+
+    private function findFileWithExtension($class, $ext)
+    {
+        // PSR-4 lookup
+        $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
+
+        $first = $class[0];
+        if (isset($this->prefixLengthsPsr4[$first])) {
+            foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
+                if (0 === strpos($class, $prefix)) {
+                    foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
+                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
+                            return $file;
+                        }
+                    }
+                }
+            }
+        }
+
+        // PSR-4 fallback dirs
+        foreach ($this->fallbackDirsPsr4 as $dir) {
+            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
+                return $file;
+            }
+        }
+
+        // PSR-0 lookup
+        if (false !== $pos = strrpos($class, '\\')) {
+            // namespaced class name
+            $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
+                . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
+        } else {
+            // PEAR-like class name
+            $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
+        }
+
+        if (isset($this->prefixesPsr0[$first])) {
+            foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
+                if (0 === strpos($class, $prefix)) {
+                    foreach ($dirs as $dir) {
+                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+                            return $file;
+                        }
+                    }
+                }
+            }
+        }
+
+        // PSR-0 fallback dirs
+        foreach ($this->fallbackDirsPsr0 as $dir) {
+            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+                return $file;
+            }
+        }
+
+        // PSR-0 include paths.
+        if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
+            return $file;
+        }
+
+        return false;
+    }
+}
+
+/**
+ * Scope isolated include.
+ *
+ * Prevents access to $this/self from included files.
+ */
+function includeFile($file)
+{
+    include $file;
+}

+ 21 - 0
vendor/composer/LICENSE

@@ -0,0 +1,21 @@
+
+Copyright (c) 2016 Nils Adermann, Jordi Boggiano
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+

+ 9 - 0
vendor/composer/autoload_classmap.php

@@ -0,0 +1,9 @@
+<?php
+
+// autoload_classmap.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+);

+ 9 - 0
vendor/composer/autoload_namespaces.php

@@ -0,0 +1,9 @@
+<?php
+
+// autoload_namespaces.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+);

+ 10 - 0
vendor/composer/autoload_psr4.php

@@ -0,0 +1,10 @@
+<?php
+
+// autoload_psr4.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+    'R2\\DependencyInjection\\' => array($vendorDir . '/artoodetoo/container/src'),
+);

+ 52 - 0
vendor/composer/autoload_real.php

@@ -0,0 +1,52 @@
+<?php
+
+// autoload_real.php @generated by Composer
+
+class ComposerAutoloaderInit90ad93c7251d4f60daa9e545879c49e7
+{
+    private static $loader;
+
+    public static function loadClassLoader($class)
+    {
+        if ('Composer\Autoload\ClassLoader' === $class) {
+            require __DIR__ . '/ClassLoader.php';
+        }
+    }
+
+    public static function getLoader()
+    {
+        if (null !== self::$loader) {
+            return self::$loader;
+        }
+
+        spl_autoload_register(array('ComposerAutoloaderInit90ad93c7251d4f60daa9e545879c49e7', 'loadClassLoader'), true, true);
+        self::$loader = $loader = new \Composer\Autoload\ClassLoader();
+        spl_autoload_unregister(array('ComposerAutoloaderInit90ad93c7251d4f60daa9e545879c49e7', 'loadClassLoader'));
+
+        $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
+        if ($useStaticLoader) {
+            require_once __DIR__ . '/autoload_static.php';
+
+            call_user_func(\Composer\Autoload\ComposerStaticInit90ad93c7251d4f60daa9e545879c49e7::getInitializer($loader));
+        } else {
+            $map = require __DIR__ . '/autoload_namespaces.php';
+            foreach ($map as $namespace => $path) {
+                $loader->set($namespace, $path);
+            }
+
+            $map = require __DIR__ . '/autoload_psr4.php';
+            foreach ($map as $namespace => $path) {
+                $loader->setPsr4($namespace, $path);
+            }
+
+            $classMap = require __DIR__ . '/autoload_classmap.php';
+            if ($classMap) {
+                $loader->addClassMap($classMap);
+            }
+        }
+
+        $loader->register(true);
+
+        return $loader;
+    }
+}

+ 31 - 0
vendor/composer/autoload_static.php

@@ -0,0 +1,31 @@
+<?php
+
+// autoload_static.php @generated by Composer
+
+namespace Composer\Autoload;
+
+class ComposerStaticInit90ad93c7251d4f60daa9e545879c49e7
+{
+    public static $prefixLengthsPsr4 = array (
+        'R' => 
+        array (
+            'R2\\DependencyInjection\\' => 23,
+        ),
+    );
+
+    public static $prefixDirsPsr4 = array (
+        'R2\\DependencyInjection\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/artoodetoo/container/src',
+        ),
+    );
+
+    public static function getInitializer(ClassLoader $loader)
+    {
+        return \Closure::bind(function () use ($loader) {
+            $loader->prefixLengthsPsr4 = ComposerStaticInit90ad93c7251d4f60daa9e545879c49e7::$prefixLengthsPsr4;
+            $loader->prefixDirsPsr4 = ComposerStaticInit90ad93c7251d4f60daa9e545879c49e7::$prefixDirsPsr4;
+
+        }, null, ClassLoader::class);
+    }
+}

+ 43 - 0
vendor/composer/installed.json

@@ -0,0 +1,43 @@
+[
+    {
+        "name": "artoodetoo/container",
+        "version": "dev-master",
+        "version_normalized": "9999999-dev",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/artoodetoo/container.git",
+            "reference": "1c52096695eb699e2c63b6b84703794d94d0659f"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/artoodetoo/container/zipball/1c52096695eb699e2c63b6b84703794d94d0659f",
+            "reference": "1c52096695eb699e2c63b6b84703794d94d0659f",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.6"
+        },
+        "time": "2016-12-27 20:57:10",
+        "type": "library",
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "R2\\DependencyInjection\\": "src/"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "artoodetoo"
+            }
+        ],
+        "description": "Yet another service container",
+        "keywords": [
+            "container",
+            "service"
+        ]
+    }
+]