[Xfce4-commits] <forum:master> Update to Fluxbb 1.4.3.

Nick Schermer noreply at xfce.org
Thu Jan 27 19:42:01 CET 2011


Updating branch refs/heads/master
         to d5804c4df60a0e3ca9407356d39ee0a0c6812d75 (commit)
       from 67c47615e4ce0e5daf1cdae568b0f292478475e3 (commit)

commit d5804c4df60a0e3ca9407356d39ee0a0c6812d75
Author: Nick Schermer <nick at xfce.org>
Date:   Thu Jan 27 19:35:32 2011 +0100

    Update to Fluxbb 1.4.3.

 admin_bans.php                                     |    2 +-
 admin_categories.php                               |   35 +--
 admin_censoring.php                                |    2 +-
 admin_forums.php                                   |    5 +-
 admin_groups.php                                   |   11 +-
 admin_index.php                                    |   30 +-
 admin_loader.php                                   |    2 +-
 admin_maintenance.php                              |    2 +-
 admin_options.php                                  |   53 +++-
 admin_permissions.php                              |    2 +-
 admin_prune.php                                    |    2 +-
 admin_ranks.php                                    |    2 +-
 admin_reports.php                                  |    4 +-
 admin_users.php                                    |    8 +-
 db_update.php                                      |  321 +++++++++++++++-----
 delete.php                                         |   10 +-
 edit.php                                           |   19 +-
 extern.php                                         |   78 +++---
 footer.php                                         |   70 +----
 header.php                                         |   89 ++++--
 help.php                                           |   19 +-
 include/cache.php                                  |   68 +++--
 include/common.php                                 |   20 +-
 include/common_admin.php                           |    2 +-
 include/dblayer/mysql.php                          |   12 +-
 include/dblayer/mysql_innodb.php                   |   12 +-
 include/dblayer/mysqli.php                         |   12 +-
 include/dblayer/mysqli_innodb.php                  |   12 +-
 include/dblayer/pgsql.php                          |   10 +
 include/dblayer/sqlite.php                         |   29 ++
 include/functions.php                              |  104 +++++--
 include/parser.php                                 |   82 ++----
 index.php                                          |    2 +-
 lang/English/admin_bans.php                        |    2 +-
 lang/English/admin_options.php                     |   10 +-
 lang/English/admin_reports.php                     |    2 +-
 lang/English/common.php                            |   12 +-
 lang/English/delete.php                            |    2 +-
 lang/English/forum.php                             |    5 +-
 lang/English/help.php                              |    1 +
 lang/English/mail_templates/new_topic.tpl          |   11 +
 .../{new_reply_full.tpl => new_topic_full.tpl}     |    6 +-
 lang/English/misc.php                              |    6 +-
 lang/English/post.php                              |    1 +
 lang/English/profile.php                           |    2 +
 lang/English/search.php                            |    3 +-
 lang/English/update.php                            |   77 +++++
 login.php                                          |   27 ++-
 misc.php                                           |  116 ++++++--
 moderate.php                                       |   10 +-
 post.php                                           |  148 ++++++++--
 profile.php                                        |  111 ++++---
 register.php                                       |   14 +-
 search.php                                         |   70 +++--
 style/Air.css                                      |   22 ++-
 style/Cobalt.css                                   |   10 +-
 style/Earth.css                                    |   20 ++-
 style/Fire.css                                     |   20 ++-
 style/Lithium.css                                  |   10 +-
 style/Mercury.css                                  |   10 +-
 style/Oxygen.css                                   |   10 +-
 style/Radium.css                                   |   10 +-
 style/Sulfur.css                                   |   10 +-
 style/Technetium.css                               |    9 +-
 userlist.php                                       |    2 +-
 viewforum.php                                      |   31 ++-
 viewtopic.php                                      |   45 ++--
 67 files changed, 1380 insertions(+), 596 deletions(-)

diff --git a/admin_bans.php b/admin_bans.php
index 644dcff..726ce0a 100644
--- a/admin_bans.php
+++ b/admin_bans.php
@@ -9,7 +9,7 @@
 // Tell header.php to use the admin template
 define('PUN_ADMIN_CONSOLE', 1);
 
-define('PUN_ROOT', './');
+define('PUN_ROOT', dirname(__FILE__).'/');
 require PUN_ROOT.'include/common.php';
 require PUN_ROOT.'include/common_admin.php';
 
diff --git a/admin_categories.php b/admin_categories.php
index 7d4129e..a0f6a1e 100644
--- a/admin_categories.php
+++ b/admin_categories.php
@@ -9,7 +9,7 @@
 // Tell header.php to use the admin template
 define('PUN_ADMIN_CONSOLE', 1);
 
-define('PUN_ROOT', './');
+define('PUN_ROOT', dirname(__FILE__).'/');
 require PUN_ROOT.'include/common.php';
 require PUN_ROOT.'include/common_admin.php';
 
@@ -126,23 +126,22 @@ else if (isset($_POST['update'])) // Change position and name of the categories
 {
 	confirm_referrer('admin_categories.php');
 
-	$cat_order = array_map('trim', $_POST['cat_order']);
-	$cat_name = array_map('pun_trim', $_POST['cat_name']);
-
-	$result = $db->query('SELECT id, disp_position FROM '.$db->prefix.'categories ORDER BY disp_position') or error('Unable to fetch category list', __FILE__, __LINE__, $db->error());
-	$num_cats = $db->num_rows($result);
+	$categories = $_POST['cat'];
+	if (empty($categories))
+		message($lang_common['Bad request']);
 
-	for ($i = 0; $i < $num_cats; ++$i)
+	foreach ($categories as $cat_id => $cur_cat)
 	{
-		if ($cat_name[$i] == '')
+		$cur_cat['name'] = pun_trim($cur_cat['name']);
+		$cur_cat['order'] = trim($cur_cat['order']);
+
+		if ($cur_cat['name'] == '')
 			message($lang_admin_categories['Must enter name message']);
 
-		if ($cat_order[$i] == '' || preg_match('/[^0-9]/', $cat_order[$i]))
+		if ($cur_cat['order'] == '' || preg_match('/[^0-9]/', $cur_cat['order']))
 			message($lang_admin_categories['Must enter integer message']);
 
-		list($cat_id, $position) = $db->fetch_row($result);
-
-		$db->query('UPDATE '.$db->prefix.'categories SET cat_name=\''.$db->escape($cat_name[$i]).'\', disp_position='.$cat_order[$i].' WHERE id='.$cat_id) or error('Unable to update category', __FILE__, __LINE__, $db->error());
+		$db->query('UPDATE '.$db->prefix.'categories SET cat_name=\''.$db->escape($cur_cat['name']).'\', disp_position='.$cur_cat['order'].' WHERE id='.intval($cat_id)) or error('Unable to update category', __FILE__, __LINE__, $db->error());
 	}
 
 	// Regenerate the quick jump cache
@@ -159,7 +158,7 @@ $result = $db->query('SELECT id, cat_name, disp_position FROM '.$db->prefix.'cat
 $num_cats = $db->num_rows($result);
 
 for ($i = 0; $i < $num_cats; ++$i)
-	$cat_list[] = $db->fetch_row($result);
+	$cat_list[] = $db->fetch_assoc($result);
 
 $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Categories']);
 define('PUN_ACTIVE_PAGE', 'admin');
@@ -205,8 +204,8 @@ generate_admin_menu('categories');
 										<select name="cat_to_delete" tabindex="3">
 <?php
 
-	foreach ($cat_list as $category)
-		echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$category[0].'">'.pun_htmlspecialchars($category[1]).'</option>'."\n";
+	foreach ($cat_list as $cur_cat)
+		echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$cur_cat['id'].'">'.pun_htmlspecialchars($cur_cat['cat_name']).'</option>'."\n";
 
 ?>
 										</select>
@@ -238,13 +237,13 @@ generate_admin_menu('categories');
 							<tbody>
 <?php
 
-	foreach ($cat_list as $i => $category)
+	foreach ($cat_list as $cur_cat)
 	{
 
 ?>
 								<tr>
-									<td class="tcl"><input type="text" name="cat_name[<?php echo $i ?>]" value="<?php echo pun_htmlspecialchars($category[1]) ?>" size="35" maxlength="80" /></td>
-									<td><input type="text" name="cat_order[<?php echo $i ?>]" value="<?php echo $category[2] ?>" size="3" maxlength="3" /></td>
+									<td class="tcl"><input type="text" name="cat[<?php echo $cur_cat['id'] ?>][name]" value="<?php echo pun_htmlspecialchars($cur_cat['cat_name']) ?>" size="35" maxlength="80" /></td>
+									<td><input type="text" name="cat[<?php echo $cur_cat['id'] ?>][order]" value="<?php echo $cur_cat['disp_position'] ?>" size="3" maxlength="3" /></td>
 								</tr>
 <?php
 
diff --git a/admin_censoring.php b/admin_censoring.php
index 69282b1..b63cd58 100644
--- a/admin_censoring.php
+++ b/admin_censoring.php
@@ -9,7 +9,7 @@
 // Tell header.php to use the admin template
 define('PUN_ADMIN_CONSOLE', 1);
 
-define('PUN_ROOT', './');
+define('PUN_ROOT', dirname(__FILE__).'/');
 require PUN_ROOT.'include/common.php';
 require PUN_ROOT.'include/common_admin.php';
 
diff --git a/admin_forums.php b/admin_forums.php
index 1d1c476..c361c3d 100644
--- a/admin_forums.php
+++ b/admin_forums.php
@@ -9,7 +9,7 @@
 // Tell header.php to use the admin template
 define('PUN_ADMIN_CONSOLE', 1);
 
-define('PUN_ROOT', './');
+define('PUN_ROOT', dirname(__FILE__).'/');
 require PUN_ROOT.'include/common.php';
 require PUN_ROOT.'include/common_admin.php';
 
@@ -72,6 +72,9 @@ else if (isset($_GET['del_forum']))
 		$db->query('DELETE FROM '.$db->prefix.'forums WHERE id='.$forum_id) or error('Unable to delete forum', __FILE__, __LINE__, $db->error());
 		$db->query('DELETE FROM '.$db->prefix.'forum_perms WHERE forum_id='.$forum_id) or error('Unable to delete group forum permissions', __FILE__, __LINE__, $db->error());
 
+		// Delete any subscriptions for this forum
+		$db->query('DELETE FROM '.$db->prefix.'forum_subscriptions WHERE forum_id='.$forum_id) or error('Unable to delete subscriptions', __FILE__, __LINE__, $db->error());
+
 		// Regenerate the quick jump cache
 		if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
 			require PUN_ROOT.'include/cache.php';
diff --git a/admin_groups.php b/admin_groups.php
index aba8e82..6785e13 100644
--- a/admin_groups.php
+++ b/admin_groups.php
@@ -9,7 +9,7 @@
 // Tell header.php to use the admin template
 define('PUN_ADMIN_CONSOLE', 1);
 
-define('PUN_ROOT', './');
+define('PUN_ROOT', dirname(__FILE__).'/');
 require PUN_ROOT.'include/common.php';
 require PUN_ROOT.'include/common_admin.php';
 
@@ -296,7 +296,8 @@ else if (isset($_POST['add_edit_group']))
 	if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
 		require PUN_ROOT.'include/cache.php';
 
-	generate_quickjump_cache();
+	$group_id = $_POST['mode'] == 'add' ? $new_group_id : intval($_POST['group_id']);
+	generate_quickjump_cache($group_id);
 
 	if ($_POST['mode'] == 'edit')
 		redirect('admin_groups.php', $lang_admin_groups['Group edited redirect']);
@@ -364,12 +365,6 @@ else if (isset($_GET['del_group']))
 			$db->query('DELETE FROM '.$db->prefix.'groups WHERE g_id='.$group_id) or error('Unable to delete group', __FILE__, __LINE__, $db->error());
 			$db->query('DELETE FROM '.$db->prefix.'forum_perms WHERE group_id='.$group_id) or error('Unable to delete group forum permissions', __FILE__, __LINE__, $db->error());
 
-			// Regenerate the quick jump cache
-			if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
-				require PUN_ROOT.'include/cache.php';
-
-			generate_quickjump_cache();
-
 			redirect('admin_groups.php', $lang_admin_groups['Group removed redirect']);
 		}
 		else
diff --git a/admin_index.php b/admin_index.php
index 70c31ad..6406a6b 100644
--- a/admin_index.php
+++ b/admin_index.php
@@ -9,7 +9,7 @@
 // Tell header.php to use the admin template
 define('PUN_ADMIN_CONSOLE', 1);
 
-define('PUN_ROOT', './');
+define('PUN_ROOT', dirname(__FILE__).'/');
 require PUN_ROOT.'include/common.php';
 require PUN_ROOT.'include/common_admin.php';
 
@@ -126,18 +126,18 @@ generate_admin_menu('index');
 		<h2><span><?php echo $lang_admin_index['Forum admin head'] ?></span></h2>
 		<div id="adintro" class="box">
 			<div class="inbox">
-				<p>
-					<?php echo $lang_admin_index['Welcome to admin'] ?><br /><br />
-					 - <?php echo $lang_admin_index['Welcome 1'] ?><br />
-					 - <?php echo $lang_admin_index['Welcome 2'] ?><br />
-					 - <?php echo $lang_admin_index['Welcome 3'] ?><br />
-					 - <?php echo $lang_admin_index['Welcome 4'] ?><br />
-					 - <?php echo $lang_admin_index['Welcome 5'] ?><br />
-					 - <?php echo $lang_admin_index['Welcome 6'] ?><br />
-					 - <?php echo $lang_admin_index['Welcome 7'] ?><br />
-					 - <?php echo $lang_admin_index['Welcome 8'] ?><br />
-					 - <?php echo $lang_admin_index['Welcome 9'] ?>
-				</p>
+				<p><?php echo $lang_admin_index['Welcome to admin'] ?></p>
+				<ul>
+					<li><span><?php echo $lang_admin_index['Welcome 1'] ?></span></li>
+					<li><span><?php echo $lang_admin_index['Welcome 2'] ?></span></li>
+					<li><span><?php echo $lang_admin_index['Welcome 3'] ?></span></li>
+					<li><span><?php echo $lang_admin_index['Welcome 4'] ?></span></li>
+					<li><span><?php echo $lang_admin_index['Welcome 5'] ?></span></li>
+					<li><span><?php echo $lang_admin_index['Welcome 6'] ?></span></li>
+					<li><span><?php echo $lang_admin_index['Welcome 7'] ?></span></li>
+					<li><span><?php echo $lang_admin_index['Welcome 8'] ?></span></li>
+					<li><span><?php echo $lang_admin_index['Welcome 9'] ?></span></li>
+				</ul>
 			</div>
 		</div>
 
@@ -147,7 +147,7 @@ generate_admin_menu('index');
 				<dl>
 					<dt><?php echo $lang_admin_index['FluxBB version label'] ?></dt>
 					<dd>
-						<?php printf($lang_admin_index['FluxBB version data'], $pun_config['o_cur_version'], '<a href="admin_index.php?action=check_upgrade">'.$lang_admin_index['Check for upgrade'].'</a>') ?><br />
+						<?php printf($lang_admin_index['FluxBB version data'], $pun_config['o_cur_version'], '<a href="admin_index.php?action=check_upgrade">'.$lang_admin_index['Check for upgrade'].'</a>') ?>
 					</dd>
 					<dt><?php echo $lang_admin_index['Server load label'] ?></dt>
 					<dd>
@@ -164,7 +164,7 @@ generate_admin_menu('index');
 						<?php echo implode(' ', $db->get_version())."\n" ?>
 <?php if (isset($total_records) && isset($total_size)): ?>						<br /><?php printf($lang_admin_index['Database data rows'], forum_number_format($total_records)) ?>
 						<br /><?php printf($lang_admin_index['Database data size'], $total_size) ?>
-<?php endif; endif; ?>					</dd>
+<?php endif; ?>					</dd><?php endif; ?>
 				</dl>
 			</div>
 		</div>
diff --git a/admin_loader.php b/admin_loader.php
index 760f967..c89b745 100644
--- a/admin_loader.php
+++ b/admin_loader.php
@@ -9,7 +9,7 @@
 // Tell header.php to use the admin template
 define('PUN_ADMIN_CONSOLE', 1);
 
-define('PUN_ROOT', './');
+define('PUN_ROOT', dirname(__FILE__).'/');
 require PUN_ROOT.'include/common.php';
 require PUN_ROOT.'include/common_admin.php';
 
diff --git a/admin_maintenance.php b/admin_maintenance.php
index dc3a125..9ccd8b1 100644
--- a/admin_maintenance.php
+++ b/admin_maintenance.php
@@ -11,7 +11,7 @@ define('PUN_ADMIN_CONSOLE', 1);
 // Tell common.php that we don't want output buffering
 define('PUN_DISABLE_BUFFERING', 1);
 
-define('PUN_ROOT', './');
+define('PUN_ROOT', dirname(__FILE__).'/');
 require PUN_ROOT.'include/common.php';
 require PUN_ROOT.'include/common_admin.php';
 
diff --git a/admin_options.php b/admin_options.php
index 6296d32..abb92ee 100644
--- a/admin_options.php
+++ b/admin_options.php
@@ -9,7 +9,7 @@
 // Tell header.php to use the admin template
 define('PUN_ADMIN_CONSOLE', 1);
 
-define('PUN_ROOT', './');
+define('PUN_ROOT', dirname(__FILE__).'/');
 require PUN_ROOT.'include/common.php';
 require PUN_ROOT.'include/common_admin.php';
 
@@ -22,9 +22,7 @@ require PUN_ROOT.'lang/'.$admin_language.'/admin_options.php';
 
 if (isset($_POST['form_sent']))
 {
-	// Custom referrer check (so we can output a custom error message)
-	if (!preg_match('#^'.preg_quote(str_replace('www.', '', $pun_config['o_base_url']).'/admin_options.php', '#').'#i', str_replace('www.', '', (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : ''))))
-		message($lang_admin_options['Bad HTTP Referer message']);
+	confirm_referrer('admin_options.php', $lang_admin_options['Bad HTTP Referer message']);
 
 	$form = array(
 		'board_title'			=> pun_trim($_POST['form']['board_title']),
@@ -32,8 +30,8 @@ if (isset($_POST['form_sent']))
 		'base_url'				=> pun_trim($_POST['form']['base_url']),
 		'default_timezone'		=> floatval($_POST['form']['default_timezone']),
 		'default_dst'			=> $_POST['form']['default_dst'] != '1' ? '0' : '1',
-		'default_lang'			=> preg_replace('#[\.\\\/]#', '', pun_trim($_POST['form']['default_lang'])),
-		'default_style'			=> preg_replace('#[\.\\\/]#', '', pun_trim($_POST['form']['default_style'])),
+		'default_lang'			=> pun_trim($_POST['form']['default_lang']),
+		'default_style'			=> pun_trim($_POST['form']['default_style']),
 		'time_format'			=> pun_trim($_POST['form']['time_format']),
 		'date_format'			=> pun_trim($_POST['form']['date_format']),
 		'timeout_visit'			=> intval($_POST['form']['timeout_visit']),
@@ -71,10 +69,10 @@ if (isset($_POST['form_sent']))
 		'avatars_size'			=> intval($_POST['form']['avatars_size']),
 		'admin_email'			=> strtolower(pun_trim($_POST['form']['admin_email'])),
 		'webmaster_email'		=> strtolower(pun_trim($_POST['form']['webmaster_email'])),
-		'subscriptions'			=> $_POST['form']['subscriptions'] != '1' ? '0' : '1',
+		'forum_subscriptions'	=> $_POST['form']['forum_subscriptions'] != '1' ? '0' : '1',
+		'topic_subscriptions'	=> $_POST['form']['topic_subscriptions'] != '1' ? '0' : '1',
 		'smtp_host'				=> pun_trim($_POST['form']['smtp_host']),
 		'smtp_user'				=> pun_trim($_POST['form']['smtp_user']),
-		'smtp_pass'				=> pun_trim($_POST['form']['smtp_pass']),
 		'smtp_ssl'				=> $_POST['form']['smtp_ssl'] != '1' ? '0' : '1',
 		'regs_allow'			=> $_POST['form']['regs_allow'] != '1' ? '0' : '1',
 		'regs_verify'			=> $_POST['form']['regs_verify'] != '1' ? '0' : '1',
@@ -95,9 +93,12 @@ if (isset($_POST['form_sent']))
 	if (substr($form['base_url'], -1) == '/')
 		$form['base_url'] = substr($form['base_url'], 0, -1);
 
-	if (!file_exists(PUN_ROOT.'lang/'.$form['default_lang'].'/common.php'))
+	$languages = forum_list_langs();
+	if (!in_array($form['default_lang'], $languages))
 		message($lang_common['Bad request']);
-	if (!file_exists(PUN_ROOT.'style/'.$form['default_style'].'.css'))
+
+	$styles = forum_list_styles();
+	if (!in_array($form['default_style'], $styles))
 		message($lang_common['Bad request']);
 
 	if ($form['time_format'] == '')
@@ -125,6 +126,18 @@ if (isset($_POST['form_sent']))
 	if ($form['additional_navlinks'] != '')
 		$form['additional_navlinks'] = pun_trim(pun_linebreaks($form['additional_navlinks']));
 
+	// Change or enter a SMTP password
+	if (isset($_POST['form']['smtp_change_pass']))
+	{
+		$smtp_pass1 = isset($_POST['form']['smtp_pass1']) ? pun_trim($_POST['form']['smtp_pass1']) : '';
+		$smtp_pass2 = isset($_POST['form']['smtp_pass2']) ? pun_trim($_POST['form']['smtp_pass2']) : '';
+
+		if ($smtp_pass1 == $smtp_pass2)
+			$form['smtp_pass'] = $smtp_pass1;
+		else
+			message($lang_admin_options['SMTP passwords did not match']);
+	}
+
 	if ($form['announcement_message'] != '')
 		$form['announcement_message'] = pun_linebreaks($form['announcement_message']);
 	else
@@ -230,7 +243,7 @@ generate_admin_menu('options');
 								<tr>
 									<th scope="row"><?php echo $lang_admin_options['Base URL label'] ?></th>
 									<td>
-										<input type="text" name="form[base_url]" size="50" maxlength="100" value="<?php echo $pun_config['o_base_url'] ?>" />
+										<input type="text" name="form[base_url]" size="50" maxlength="100" value="<?php echo pun_htmlspecialchars($pun_config['o_base_url']) ?>" />
 										<span><?php echo $lang_admin_options['Base URL help'] ?></span>
 									</td>
 								</tr>
@@ -651,10 +664,17 @@ generate_admin_menu('options');
 									</td>
 								</tr>
 								<tr>
-									<th scope="row"><?php echo $lang_admin_options['Subscriptions label'] ?></th>
+									<th scope="row"><?php echo $lang_admin_options['Forum subscriptions label'] ?></th>
+									<td>
+										<input type="radio" name="form[forum_subscriptions]" value="1"<?php if ($pun_config['o_forum_subscriptions'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[forum_subscriptions]" value="0"<?php if ($pun_config['o_forum_subscriptions'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_options['Forum subscriptions help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Topic subscriptions label'] ?></th>
 									<td>
-										<input type="radio" name="form[subscriptions]" value="1"<?php if ($pun_config['o_subscriptions'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[subscriptions]" value="0"<?php if ($pun_config['o_subscriptions'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
-										<span><?php echo $lang_admin_options['Subscriptions help'] ?></span>
+										<input type="radio" name="form[topic_subscriptions]" value="1"<?php if ($pun_config['o_topic_subscriptions'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[topic_subscriptions]" value="0"<?php if ($pun_config['o_topic_subscriptions'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_options['Topic subscriptions help'] ?></span>
 									</td>
 								</tr>
 								<tr>
@@ -674,7 +694,10 @@ generate_admin_menu('options');
 								<tr>
 									<th scope="row"><?php echo $lang_admin_options['SMTP password label'] ?></th>
 									<td>
-										<input type="text" name="form[smtp_pass]" size="25" maxlength="50" value="<?php echo pun_htmlspecialchars($pun_config['o_smtp_pass']) ?>" />
+										<span><input type="checkbox" name="form[smtp_change_pass]" value="1" />  <?php echo $lang_admin_options['SMTP change password help'] ?></span>
+<?php $smtp_pass = !empty($pun_config['o_smtp_pass']) ? random_key(pun_strlen($pun_config['o_smtp_pass']), true) : ''; ?>
+										<input type="password" name="form[smtp_pass1]" size="25" maxlength="50" value="<?php echo $smtp_pass ?>" />
+										<input type="password" name="form[smtp_pass2]" size="25" maxlength="50" value="<?php echo $smtp_pass ?>" />
 										<span><?php echo $lang_admin_options['SMTP password help'] ?></span>
 									</td>
 								</tr>
diff --git a/admin_permissions.php b/admin_permissions.php
index 18fef6c..a73a3b3 100644
--- a/admin_permissions.php
+++ b/admin_permissions.php
@@ -9,7 +9,7 @@
 // Tell header.php to use the admin template
 define('PUN_ADMIN_CONSOLE', 1);
 
-define('PUN_ROOT', './');
+define('PUN_ROOT', dirname(__FILE__).'/');
 require PUN_ROOT.'include/common.php';
 require PUN_ROOT.'include/common_admin.php';
 
diff --git a/admin_prune.php b/admin_prune.php
index d2ba043..8b53d69 100644
--- a/admin_prune.php
+++ b/admin_prune.php
@@ -9,7 +9,7 @@
 // Tell header.php to use the admin template
 define('PUN_ADMIN_CONSOLE', 1);
 
-define('PUN_ROOT', './');
+define('PUN_ROOT', dirname(__FILE__).'/');
 require PUN_ROOT.'include/common.php';
 require PUN_ROOT.'include/common_admin.php';
 
diff --git a/admin_ranks.php b/admin_ranks.php
index c832d95..13b5bad 100644
--- a/admin_ranks.php
+++ b/admin_ranks.php
@@ -9,7 +9,7 @@
 // Tell header.php to use the admin template
 define('PUN_ADMIN_CONSOLE', 1);
 
-define('PUN_ROOT', './');
+define('PUN_ROOT', dirname(__FILE__).'/');
 require PUN_ROOT.'include/common.php';
 require PUN_ROOT.'include/common_admin.php';
 
diff --git a/admin_reports.php b/admin_reports.php
index 634b848..3675f8c 100644
--- a/admin_reports.php
+++ b/admin_reports.php
@@ -9,7 +9,7 @@
 // Tell header.php to use the admin template
 define('PUN_ADMIN_CONSOLE', 1);
 
-define('PUN_ROOT', './');
+define('PUN_ROOT', dirname(__FILE__).'/');
 require PUN_ROOT.'include/common.php';
 require PUN_ROOT.'include/common_admin.php';
 
@@ -173,4 +173,4 @@ else
 </div>
 <?php
 
-require PUN_ROOT.'footer.php';
\ No newline at end of file
+require PUN_ROOT.'footer.php';
diff --git a/admin_users.php b/admin_users.php
index 9552d17..89d5b00 100644
--- a/admin_users.php
+++ b/admin_users.php
@@ -9,7 +9,7 @@
 // Tell header.php to use the admin template
 define('PUN_ADMIN_CONSOLE', 1);
 
-define('PUN_ROOT', './');
+define('PUN_ROOT', dirname(__FILE__).'/');
 require PUN_ROOT.'include/common.php';
 require PUN_ROOT.'include/common_admin.php';
 
@@ -194,7 +194,7 @@ if (isset($_GET['show_users']))
 			{
 				$user_title = get_title($user_data);
 
-				$actions = '<a href="admin_users.php?ip_stats='.$user_data['id'].'">'.$lang_admin_users['Results view IP link'].'</a> | <a href="search.php?action=show_user&user_id='.$user_data['id'].'">'.$lang_admin_users['Results show posts link'].'</a>';
+				$actions = '<a href="admin_users.php?ip_stats='.$user_data['id'].'">'.$lang_admin_users['Results view IP link'].'</a> | <a href="search.php?action=show_user_posts&user_id='.$user_data['id'].'">'.$lang_admin_users['Results show posts link'].'</a>';
 
 ?>
 				<tr>
@@ -406,7 +406,7 @@ else if (isset($_GET['find_user']))
 			if (($user_data['g_id'] == '' || $user_data['g_id'] == PUN_UNVERIFIED) && $user_title != $lang_common['Banned'])
 				$user_title = '<span class="warntext">'.$lang_admin_users['Not verified'].'</span>';
 
-			$actions = '<a href="admin_users.php?ip_stats='.$user_data['id'].'">'.$lang_admin_users['Results view IP link'].'</a> | <a href="search.php?action=show_user&user_id='.$user_data['id'].'">'.$lang_admin_users['Results show posts link'].'</a>';
+			$actions = '<a href="admin_users.php?ip_stats='.$user_data['id'].'">'.$lang_admin_users['Results view IP link'].'</a> | <a href="search.php?action=show_user_posts&user_id='.$user_data['id'].'">'.$lang_admin_users['Results show posts link'].'</a>';
 
 ?>
 				<tr>
@@ -453,7 +453,7 @@ else if (isset($_GET['find_user']))
 else
 {
 	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Users']);
-	$focus_element = array('find_user', 'username');
+	$focus_element = array('find_user', 'form[username]');
 	define('PUN_ACTIVE_PAGE', 'admin');
 	require PUN_ROOT.'header.php';
 
diff --git a/db_update.php b/db_update.php
index e7d8eba..0856f2a 100644
--- a/db_update.php
+++ b/db_update.php
@@ -7,9 +7,9 @@
  */
 
 // The FluxBB version this script updates to
-define('UPDATE_TO', '1.4.2');
+define('UPDATE_TO', '1.4.3');
 
-define('UPDATE_TO_DB_REVISION', 8);
+define('UPDATE_TO_DB_REVISION', 10);
 define('UPDATE_TO_SI_REVISION', 1);
 define('UPDATE_TO_PARSER_REVISION', 1);
 
@@ -34,7 +34,7 @@ define('FORUM_NO_SET_NAMES', 1);
 if (!function_exists('version_compare') || version_compare(PHP_VERSION, MIN_PHP_VERSION, '<'))
 	exit('You are running PHP version '.PHP_VERSION.'. FluxBB '.UPDATE_TO.' requires at least PHP '.MIN_PHP_VERSION.' to run properly. You must upgrade your PHP installation before you can continue.');
 
-define('PUN_ROOT', './');
+define('PUN_ROOT', dirname(__FILE__).'/');
 
 // Attempt to load the configuration file config.php
 if (file_exists(PUN_ROOT.'config.php'))
@@ -44,9 +44,12 @@ if (file_exists(PUN_ROOT.'config.php'))
 if (defined('FORUM'))
 	define('PUN', FORUM);
 
-// If PUN isn't defined, config.php is missing or corrupt or we are outside the root directory
+// If PUN isn't defined, config.php is missing or corrupt
 if (!defined('PUN'))
-	exit('This file must be run from the forum root directory.');
+{
+	header('Location: install.php');
+	exit;
+}
 
 // Enable debug mode
 if (!defined('PUN_DEBUG'))
@@ -115,12 +118,24 @@ $old_connection_charset = defined('FORUM_DEFAULT_CHARSET') ? FORUM_DEFAULT_CHARS
 // Set the connection to UTF-8 now
 $db->set_names('utf8');
 
+// Get the forum config
+$result = $db->query('SELECT * FROM '.$db->prefix.'config') or error('Unable to fetch config.', __FILE__, __LINE__, $db->error());
+while ($cur_config_item = $db->fetch_row($result))
+	$pun_config[$cur_config_item[0]] = $cur_config_item[1];
+
+// Load language file
+$default_lang = $pun_config['o_default_lang'];
+
+if (!file_exists(PUN_ROOT.'lang/'.$default_lang.'/update.php'))
+	$default_lang = 'English';
+
+require PUN_ROOT.'lang/'.$default_lang.'/update.php';
+
 // Check current version
-$result = $db->query('SELECT conf_value FROM '.$db->prefix.'config WHERE conf_name=\'o_cur_version\'') or error('Unable to fetch version info.', __FILE__, __LINE__, $db->error());
-$cur_version = $db->result($result);
+$cur_version = $pun_config['o_cur_version'];
 
 if (version_compare($cur_version, '1.2', '<'))
-	exit('Version mismatch. The database \''.$db_name.'\' doesn\'t seem to be running a FluxBB database schema supported by this update script.');
+	error(sprintf($lang_update['Version mismatch error'], $db_name));
 
 // Do some DB type specific checks
 $mysql = false;
@@ -132,7 +147,7 @@ switch ($db_type)
 	case 'mysqli_innodb':
 		$mysql_info = $db->get_version();
 		if (version_compare($mysql_info['version'], MIN_MYSQL_VERSION, '<'))
-			error('You are running MySQL version '.$mysql_info['version'].'. FluxBB '.UPDATE_TO.' requires at least MySQL '.MIN_MYSQL_VERSION.' to run properly. You must upgrade your MySQL installation before you can continue.');
+			error(sprintf($lang_update['You are running error'], 'MySQL', $mysql_info['version'], UPDATE_TO, MIN_MYSQL_VERSION));
 
 		$mysql = true;
 		break;
@@ -140,22 +155,17 @@ switch ($db_type)
 	case 'pgsql':
 		$pgsql_info = $db->get_version();
 		if (version_compare($pgsql_info['version'], MIN_PGSQL_VERSION, '<'))
-			error('You are running PostgreSQL version '.$pgsql_info['version'].'. FluxBB '.UPDATE_TO.' requires at least PostgreSQL '.MIN_PGSQL_VERSION.' to run properly. You must upgrade your PostgreSQL installation before you can continue.');
+			error(sprintf($lang_update['You are running error'], 'PostgreSQL', $pgsql_info['version'], UPDATE_TO, MIN_PGSQL_VERSION));
 
 		break;
 }
 
-// Get the forum config
-$result = $db->query('SELECT * FROM '.$db->prefix.'config') or error('Unable to fetch config.', __FILE__, __LINE__, $db->error());
-while ($cur_config_item = $db->fetch_row($result))
-	$pun_config[$cur_config_item[0]] = $cur_config_item[1];
-
-// Check the database revision and the current version
+// Check the database, search index and parser revision and the current version
 if (isset($pun_config['o_database_revision']) && $pun_config['o_database_revision'] >= UPDATE_TO_DB_REVISION &&
 		isset($pun_config['o_searchindex_revision']) && $pun_config['o_searchindex_revision'] >= UPDATE_TO_SI_REVISION &&
 		isset($pun_config['o_parser_revision']) && $pun_config['o_parser_revision'] >= UPDATE_TO_PARSER_REVISION &&
 		version_compare($pun_config['o_cur_version'], UPDATE_TO, '>='))
-	exit('Your database is already as up-to-date as this script can make it.');
+	error($lang_update['No update error']);
 
 $default_style = $pun_config['o_default_style'];
 if (!file_exists(PUN_ROOT.'style/'.$default_style.'.css'))
@@ -444,15 +454,14 @@ header('Content-type: text/html; charset=utf-8');
 while (@ob_end_clean());
 
 
-$stage = isset($_GET['stage']) ? $_GET['stage'] : '';
-$old_charset = isset($_GET['req_old_charset']) ? str_replace('ISO8859', 'ISO-8859', strtoupper($_GET['req_old_charset'])) : 'ISO-8859-1';
-$start_at = isset($_GET['start_at']) ? intval($_GET['start_at']) : 0;
+$stage = isset($_REQUEST['stage']) ? $_REQUEST['stage'] : '';
+$old_charset = isset($_REQUEST['req_old_charset']) ? str_replace('ISO8859', 'ISO-8859', strtoupper($_REQUEST['req_old_charset'])) : 'ISO-8859-1';
+$start_at = isset($_REQUEST['start_at']) ? intval($_REQUEST['start_at']) : 0;
 $query_str = '';
 
-switch ($stage)
+// Show form
+if (empty($stage))
 {
-	// Show form
-	case '':
 
 ?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
@@ -460,69 +469,118 @@ switch ($stage)
 <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" />
-<title>FluxBB Database Update</title>
+<title><?php echo $lang_update['Update'] ?></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 element_names = {
+		"req_db_pass": "<?php echo $lang_update['Database password'] ?>",
+		"req_old_charset": "<?php echo $lang_update['Current character set label'] ?>"
+	};
+	if (document.all || document.getElementById)
+	{
+		for (var i = 0; i < the_form.length; ++i)
+		{
+			var elem = the_form.elements[i];
+			if (elem.name && (/^req_/.test(elem.name)))
+			{
+				if (!elem.value && elem.type && (/^(?:text(?:area)?|password|file)$/i.test(elem.type)))
+				{
+					alert('"' + element_names[elem.name] + '" <?php echo $lang_update['Required field'] ?>');
+					elem.focus();
+					return false;
+				}
+			}
+		}
+	}
+	return true;
+}
+/* ]]> */
+</script>
 </head>
-<body>
+<body onload="document.getElementById('install').req_db_type.focus();document.getElementById('install').start.disabled=false;">
 
 <div id="pundb_update" 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_update['Update'] ?></span></h1>
+			<div id="brddesc"><p><?php echo $lang_update['Update message'] ?></p><p><strong><?php echo $lang_update['Note']; ?></strong> <?php echo $lang_update['Members message']; ?></p></div>
+		</div>
+	</div>
+</div>
+
+<div id="brdmain">
 <div class="blockform">
-	<h2><span>FluxBB Update</span></h2>
+	<h2><span><?php echo $lang_update['Update'] ?></span></h2>
 	<div class="box">
-		<form method="get" action="<?php echo pun_htmlspecialchars($_SERVER['REQUEST_URI']) ?>" onsubmit="this.start.disabled=true">
-		<input type="hidden" name="stage" value="start" />
+		<form method="post" action="db_update.php" onsubmit="this.start.disabled=true;if(process_form(this)){return true;}else{this.start.disabled=false;return false;}">
+			<input type="hidden" name="stage" value="start" />
+			<div class="inform">
+				<fieldset>
+				<legend><?php echo $lang_update['Administrator only'] ?></legend>
+					<div class="infldset">
+						<p><?php echo $lang_update['Database password info'] ?></p>
+						<p><strong><?php echo $lang_update['Note']; ?></strong> <?php echo $lang_update['Database password note'] ?></p>
+						<label class="required"><strong><?php echo $lang_update['Database password'] ?> <span><?php echo $lang_update['Required'] ?></span></strong><br /><input type="password" id="req_db_pass" name="req_db_pass" /><br /></label>
+					</div>
+				</fieldset>
+			</div>
 			<div class="inform">
 				<div class="forminfo">
-					<p style="font-size: 1.1em">This script will update your forum database. The update procedure might take anything from a second to hours depending on the speed of the server and the size of the forum database. Don't forget to make a backup of the database before continuing.</p>
-					<p style="font-size: 1.1em">Did you read the update instructions in the documentation? If not, start there.</p>
+					<p><?php echo $lang_update['Intro 1'] ?></p>
+					<p><?php echo $lang_update['Intro 2'] ?></p>
 <?php
 
-if (strpos($cur_version, '1.2') === 0)
-{
-	if (!function_exists('iconv') && !function_exists('mb_convert_encoding'))
+	if (strpos($cur_version, '1.2') === 0)
 	{
+		if (!function_exists('iconv') && !function_exists('mb_convert_encoding'))
+		{
 
 ?>
-					<p style="font-size: 1.1em"><strong>IMPORTANT!</strong> FluxBB has detected that this PHP environment does not have support for the encoding mechanisms required to do UTF-8 conversion from character sets other than ISO-8859-1. What this means is that if the current character set is not ISO-8859-1, FluxBB won't be able to convert your forum database to UTF-8 and you will have to do it manually. Instructions for doing manual charset conversion can be found in the update instructions.</p>
+					<p><?php echo $lang_update['No charset conversion'] ?></p>
 <?php
 
-	}
+		}
 
 ?>
 				</div>
 			</div>
 			<div class="inform">
 				<div class="forminfo">
-					<p style="font-size: 1.1em"><strong>Enable conversion:</strong> When enabled this update script will, after it has made the required structural changes to the database, convert all text in the database from the current character set to UTF-8. This conversion is required if you're upgrading from version 1.2.</p>
-					<p style="font-size: 1.1em"><strong>Current character set:</strong> If the primary language in your forum is English, you can leave this at the default value. However, if your forum is non-English, you should enter the character set of the primary language pack used in the forum. <i>Getting this wrong can corrupt your database so don't just guess!</i> Note: This is required even if the old database is UTF-8.</p>
+					<p><?php echo $lang_update['Enable conversion'] ?></p>
+					<p><?php echo $lang_update['Current character set'] ?></p>
 				</div>
 				<fieldset>
-					<legend>Charset conversion</legend>
+					<legend><?php echo $lang_update['Charset conversion'] ?></legend>
 					<div class="infldset">
 						<div class="rbox">
-							<label><input type="checkbox" name="convert_charset" value="1" checked="checked" /><strong>Enable conversion</strong> (perform database charset conversion).<br /></label>
+							<label><input type="checkbox" name="convert_charset" value="1" checked="checked" /><?php echo $lang_update['Enable conversion label'] ?><br /></label>
 						</div>
 						<label>
-							<strong>Current character set</strong><br />Accept default for English forums otherwise the character set of the primary language pack.<br />
+							<strong><?php echo $lang_update['Current character set label'] ?></strong><br /><?php echo $lang_update['Current character set info'] ?><br />
 							<input type="text" name="req_old_charset" size="12" maxlength="20" value="<?php echo $old_charset ?>" /><br />
 						</label>
 					</div>
 				</fieldset>
 <?php
 
-}
-else
-	echo "\t\t\t\t".'</div>'."\n";
+	}
+	else
+		echo "\t\t\t\t".'</div>'."\n";
 
 ?>
 			</div>
-			<p class="buttons"><input type="submit" name="start" value="Start update" /></p>
+			<p class="buttons"><input type="submit" name="start" value="<?php echo $lang_update['Start update'] ?>" /></p>
 		</form>
 	</div>
 </div>
+</div>
 
 </div>
 <div class="end-box"><div><!-- Bottom Corners --></div></div>
@@ -532,9 +590,66 @@ else
 </html>
 <?php
 
-		break;
+	$db->end_transaction();
+	$db->close();
+	exit;
+
+}
+
+// Read the lock file
+$lock = file_exists(FORUM_CACHE_DIR.'db_update.lock') ? trim(file_get_contents(FORUM_CACHE_DIR.'db_update.lock')) : false;
+$lock_error = false;
+
+// Generate or fetch the UID - this confirms we have a valid admin
+if (isset($_POST['req_db_pass']))
+{
+	$req_db_pass = strtolower(trim($_POST['req_db_pass']));
+
+	switch ($db_type)
+	{
+		// For SQLite we compare against the database file name, since the password is left blank
+		case 'sqlite':
+			if ($req_db_pass != strtolower($db_name))
+				error(sprintf($lang_update['Invalid file error'], 'config.php'));
+
+			break;
+		// For everything else, check the password matches
+		default:
+			if ($req_db_pass != strtolower($db_password))
+				error(sprintf($lang_update['Invalid password error'], 'config.php'));
+
+			break;
+	}
+
+	// Generate a unique id to identify this session, only if this is a valid session
+	$uid = pun_hash($req_db_pass.'|'.uniqid(rand(), true));
+	if ($lock) // We already have a lock file
+		$lock_error = true;
+	else // Create the lock file
+	{
+		$fh = @fopen(FORUM_CACHE_DIR.'db_update.lock', 'wb');
+		if (!$fh)
+			error(sprintf($lang_update['Unable to lock error'], 'cache'));
+
+		fwrite($fh, $uid);
+		fclose($fh);
+	}
+}
+else if (isset($_GET['uid']))
+{
+	$uid = trim($_GET['uid']);
+	if (!$lock || $lock != $uid) // The lock doesn't exist or doesn't match the given UID
+		$lock_error = true;
+}
+else
+	error($lang_update['No password error']);
 
+// If there is an error with the lock file
+if ($lock_error)
+	error(sprintf($lang_update['Script runs error'], FORUM_CACHE_DIR.'db_update.lock'));
 
+switch ($stage)
+{
 	// Start by updating the database structure
 	case 'start':
 		$query_str = '?stage=preparse_posts';
@@ -961,6 +1076,39 @@ else
 			$db->create_table('search_words', $schema);
 		}
 
+		// Rename the subscription table
+		$db->rename_table('subscriptions', 'topic_subscriptions');
+
+		// if we don't have the forum_subscriptions table, create it
+		if (!$db->table_exists('forum_subscriptions'))
+		{
+			$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());
+		}
+
+		// Insert new config option o_forum_subscriptions
+		if (!array_key_exists('o_forum_subscriptions', $pun_config))
+			$db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_forum_subscriptions\', \'1\')') or error('Unable to insert config value \'o_forum_subscriptions\'', __FILE__, __LINE__, $db->error());
+
+		// Rename config option o_subscriptions to o_topic_subscriptions
+		if (!array_key_exists('o_topic_subscriptions', $pun_config))
+			$db->query('UPDATE '.$db->prefix.'config SET conf_name=\'o_topic_subscriptions\' WHERE conf_name=\'o_subscriptions\'') or error('Unable to rename config value \'o_subscriptions\'', __FILE__, __LINE__, $db->error());
+
 		// Change the default style if the old doesn't exist anymore
 		if ($pun_config['o_default_style'] != $default_style)
 			$db->query('UPDATE '.$db->prefix.'config SET conf_value = \''.$db->escape($default_style).'\' WHERE conf_name = \'o_default_style\'') or error('Unable to update default style config', __FILE__, __LINE__, $db->error());
@@ -978,7 +1126,9 @@ else
 
 		function _conv_bans($cur_item, $old_charset)
 		{
-			echo 'Converting ban '.$cur_item['id'].' …<br />'."\n";
+			global $lang_update;
+
+			echo sprintf($lang_update['Converting item'], $lang_update['ban'], $cur_item['id']).'<br />'."\n";
 
 			convert_to_utf8($cur_item['username'], $old_charset);
 			convert_to_utf8($cur_item['message'], $old_charset);
@@ -998,7 +1148,7 @@ else
 	case 'conv_categories':
 		$query_str = '?stage=conv_censors&req_old_charset='.$old_charset;
 
-		echo 'Converting categories …'."<br />\n";
+		echo sprintf($lang_update['Converting'], $lang_update['categories']).'<br />'."\n";
 
 		function _conv_categories($cur_item, $old_charset)
 		{
@@ -1016,7 +1166,7 @@ else
 	case 'conv_censors':
 		$query_str = '?stage=conv_config&req_old_charset='.$old_charset;
 
-		echo 'Converting censor words …'."<br />\n";
+		echo sprintf($lang_update['Converting'], $lang_update['censor words']).'<br />'."\n";
 
 		function _conv_censoring($cur_item, $old_charset)
 		{
@@ -1035,7 +1185,7 @@ else
 	case 'conv_config':
 		$query_str = '?stage=conv_forums&req_old_charset='.$old_charset;
 
-		echo 'Converting configuration …'."<br />\n";
+		echo sprintf($lang_update['Converting'], $lang_update['configuration']).'<br />'."\n";
 
 		function _conv_config($cur_item, $old_charset)
 		{
@@ -1053,7 +1203,7 @@ else
 	case 'conv_forums':
 		$query_str = '?stage=conv_perms&req_old_charset='.$old_charset;
 
-		echo 'Converting forums …'."<br />\n";
+		echo sprintf($lang_update['Converting'], $lang_update['forums']).'<br />'."\n";
 
 		function _conv_forums($cur_item, $old_charset)
 		{
@@ -1092,7 +1242,7 @@ else
 	case 'conv_groups':
 		$query_str = '?stage=conv_online&req_old_charset='.$old_charset;
 
-		echo 'Converting groups …'."<br />\n";
+		echo sprintf($lang_update['Converting'], $lang_update['groups']).'<br />'."\n";
 
 		function _conv_groups($cur_item, $old_charset)
 		{
@@ -1125,7 +1275,9 @@ else
 
 		function _conv_posts($cur_item, $old_charset)
 		{
-			echo 'Converting post '.$cur_item['id'].' …<br />'."\n";
+			global $lang_update;
+
+			echo sprintf($lang_update['Converting item'], $lang_update['post'], $cur_item['id']).'<br />'."\n";
 
 			convert_to_utf8($cur_item['poster'], $old_charset);
 			convert_to_utf8($cur_item['message'], $old_charset);
@@ -1146,7 +1298,7 @@ else
 	case 'conv_ranks':
 		$query_str = '?stage=conv_reports&req_old_charset='.$old_charset;
 
-		echo 'Converting ranks …'."<br />\n";
+		echo sprintf($lang_update['Converting'], $lang_update['ranks']).'<br />'."\n";
 
 		function _conv_ranks($cur_item, $old_charset)
 		{
@@ -1166,7 +1318,9 @@ else
 
 		function _conv_reports($cur_item, $old_charset)
 		{
-			echo 'Converting report '.$cur_item['id'].' …<br />'."\n";
+			global $lang_update;
+
+			echo sprintf($lang_update['Converting item'], $lang_update['report'], $cur_item['id']).'<br />'."\n";
 
 			convert_to_utf8($cur_item['message'], $old_charset);
 
@@ -1236,7 +1390,9 @@ else
 	case 'conv_subscriptions':
 		$query_str = '?stage=conv_topics&req_old_charset='.$old_charset;
 
-		alter_table_utf8($db->prefix.'subscriptions');
+		// By this stage we should have already renamed the subscription table
+		alter_table_utf8($db->prefix.'topic_subscriptions');
+		alter_table_utf8($db->prefix.'forum_subscriptions'); // This should actually already be utf8, but for consistency...
 
 		break;
 
@@ -1247,7 +1403,9 @@ else
 
 		function _conv_topics($cur_item, $old_charset)
 		{
-			echo 'Converting topic '.$cur_item['id'].' …<br />'."\n";
+			global $lang_update;
+
+			echo sprintf($lang_update['Converting item'], $lang_update['topic'], $cur_item['id']).'<br />'."\n";
 
 			convert_to_utf8($cur_item['poster'], $old_charset);
 			convert_to_utf8($cur_item['subject'], $old_charset);
@@ -1270,7 +1428,9 @@ else
 
 		function _conv_users($cur_item, $old_charset)
 		{
-			echo 'Converting user '.$cur_item['id'].' …<br />'."\n";
+			global $lang_update;
+
+			echo sprintf($lang_update['Converting item'], $lang_update['user'], $cur_item['id']).'<br />'."\n";
 
 			convert_to_utf8($cur_item['username'], $old_charset);
 			convert_to_utf8($cur_item['title'], $old_charset);
@@ -1317,24 +1477,24 @@ else
 				$username = pun_trim($_POST['dupe_users'][$id]);
 
 				if (pun_strlen($username) < 2)
-					$errors[$id][] = 'Usernames must be at least 2 characters long. Please choose another (longer) username.';
+					$errors[$id][] = $lang_update['Username too short error'];
 				else if (pun_strlen($username) > 25) // This usually doesn't happen since the form element only accepts 25 characters
-					$errors[$id][] = 'Usernames must not be more than 25 characters long. Please choose another (shorter) username.';
+					$errors[$id][] = $lang_update['Username too long error'];
 				else if (!strcasecmp($username, 'Guest'))
-					$errors[$id][] = 'The username guest is reserved. Please choose another username.';
+					$errors[$id][] = $lang_update['Username Guest reserved error'];
 				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))
-					$errors[$id][] = 'Usernames may not be in the form of an IP address. Please choose another username.';
+					$errors[$id][] = $lang_update['Username IP format error'];
 				else if ((strpos($username, '[') !== false || strpos($username, ']') !== false) && strpos($username, '\'') !== false && strpos($username, '"') !== false)
-					$errors[$id][] = 'Usernames may not contain all the characters \', " and [ or ] at once. Please choose another username.';
+					$errors[$id][] = $lang_update['Username bad characters error'];
 				else if (preg_match('/(?:\[\/?(?:b|u|s|ins|del|em|i|h|colou?r|quote|code|img|url|email|list|\*)\]|\[(?:img|url|quote|list)=)/i', $username))
-					$errors[$id][] = 'Usernames may not contain any of the text formatting tags (BBCode) that the forum uses. Please choose another username.';
+					$errors[$id][] = $lang_update['Username BBCode error'];
 
 				$result = $db->query('SELECT username FROM '.$db->prefix.'users WHERE (UPPER(username)=UPPER(\''.$db->escape($username).'\') OR UPPER(username)=UPPER(\''.$db->escape(preg_replace('/[^\w]/', '', $username)).'\')) AND id>1') or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
 
 				if ($db->num_rows($result))
 				{
 					$busy = $db->result($result);
-					$errors[$id][] = 'Someone is already registered with the username '.pun_htmlspecialchars($busy).'. The username you entered is too similar. The username must differ from that by at least one alphanumerical character (a-z or 0-9). Please choose a different username.';
+					$errors[$id][] = sprintf($lang_update['Username duplicate error'], pun_htmlspecialchars($busy));
 				}
 
 				if (empty($errors[$id]))
@@ -1396,7 +1556,7 @@ else
 					$mail_message = trim(substr($mail_tpl, $first_crlf));
 
 					$mail_subject = str_replace('<board_title>', $pun_config['o_board_title'], $mail_subject);
-					$mail_message = str_replace('<base_url>', $pun_config['o_base_url'].'/', $mail_message);
+					$mail_message = str_replace('<base_url>', get_base_url().'/', $mail_message);
 					$mail_message = str_replace('<old_username>', $old_username, $mail_message);
 					$mail_message = str_replace('<new_username>', $username, $mail_message);
 					$mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'].' Mailer', $mail_message);
@@ -1418,7 +1578,7 @@ else
 <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" />
-<title>FluxBB Database Update</title>
+<title><?php echo $lang_update['Update'] ?></title>
 <link rel="stylesheet" type="text/css" href="style/<?php echo $default_style ?>.css" />
 </head>
 <body>
@@ -1428,14 +1588,14 @@ else
 <div class="punwrap">
 
 <div class="blockform">
-	<h2><span>Error converting users</span></h2>
+	<h2><span><?php echo $lang_update['Error converting users'] ?></span></h2>
 	<div class="box">
 		<form method="post" action="db_update.php?stage=conv_users_dupe">
 			<input type="hidden" name="form_sent" value="1" />
 			<div class="inform">
 				<div class="forminfo">
-						<p style="font-size: 1.1em">There was an error converting some users. This can occur when converting from FluxBB v1.2 if multiple users have registered with very similar usernames, for example "bob" and "böb".</p>
-						<p style="font-size: 1.1em">Below is a list of users who failed to convert. Please choose a new username for each user. Users who are renamed will automatically be sent an email alerting them of the change.</p>
+						<p style="font-size: 1.1em"><?php echo $lang_update['Error info 1'] ?></p>
+						<p style="font-size: 1.1em"><?php echo $lang_update['Error info 2'] ?></p>
 				</div>
 			</div>
 <?php
@@ -1448,11 +1608,11 @@ else
 				<fieldset>
 					<legend><?php echo pun_htmlspecialchars($cur_user['username']); ?></legend>
 					<div class="infldset">
-						<label class="required"><strong>New username <span>(required)</span></strong><br /><input type="text" name="<?php echo 'dupe_users['.$id.']'; ?>" value="<?php if (isset($_POST['dupe_users'][$id])) echo pun_htmlspecialchars($_POST['dupe_users'][$id]); ?>" size="25" maxlength="25" /><br /></label>
+						<label class="required"><strong><?php echo $lang_update['New username'] ?> <span><?php echo $lang_update['required'] ?></span></strong><br /><input type="text" name="<?php echo 'dupe_users['.$id.']'; ?>" value="<?php if (isset($_POST['dupe_users'][$id])) echo pun_htmlspecialchars($_POST['dupe_users'][$id]); ?>" size="25" maxlength="25" /><br /></label>
 					</div>
 				</fieldset>
 <?php if (!empty($errors[$id])): ?>				<div class="forminfo error-info">
-					<h3>The following errors need to be corrected:</h3>
+					<h3><?php echo $lang_update['Correct errors'] ?></h3>
 					<ul class="error-list">
 <?php
 
@@ -1467,7 +1627,7 @@ foreach ($errors[$id] as $cur_error)
 			}
 
 ?>
-			<p class="buttons"><input type="submit" name="rename" value="Rename users" /></p>
+			<p class="buttons"><input type="submit" name="rename" value="<?php echo $lang_update['Rename users'] ?>" /></p>
 		</form>
 	</div>
 </div>
@@ -1502,7 +1662,7 @@ foreach ($errors[$id] as $cur_error)
 		$end_at = 0;
 		while ($cur_item = $db->fetch_assoc($result))
 		{
-			echo 'Preparsing post '.$cur_item['id'].' …<br />'."\n";
+			echo sprintf($lang_update['Preparsing item'], $lang_update['post'], $cur_item['id']).'<br />'."\n";
 			$db->query('UPDATE '.$db->prefix.'posts SET message = \''.$db->escape(preparse_bbcode($cur_item['message'], $temp)).'\' WHERE id = '.$cur_item['id']) or error('Unable to update post', __FILE__, __LINE__, $db->error());
 
 			$end_at = $cur_item['id'];
@@ -1537,7 +1697,7 @@ foreach ($errors[$id] as $cur_error)
 		$end_at = 0;
 		while ($cur_item = $db->fetch_assoc($result))
 		{
-			echo 'Preparsing signature '.$cur_item['id'].' …<br />'."\n";
+			echo sprintf($lang_update['Preparsing item'], $lang_update['signature'], $cur_item['id']).'<br />'."\n";
 			$db->query('UPDATE '.$db->prefix.'users SET signature = \''.$db->escape(preparse_bbcode($cur_item['signature'], $temp, true)).'\' WHERE id = '.$cur_item['id']) or error('Unable to update user', __FILE__, __LINE__, $db->error());
 
 			$end_at = $cur_item['id'];
@@ -1593,7 +1753,7 @@ foreach ($errors[$id] as $cur_error)
 		$end_at = 0;
 		while ($cur_item = $db->fetch_assoc($result))
 		{
-			echo 'Rebuilding index for post '.$cur_item['id'].' …<br />'."\n";
+			echo sprintf($lang_update['Rebuilding index item'], $lang_update['post'], $cur_item['id']).'<br />'."\n";
 
 			if ($cur_item['id'] == $cur_item['first_post_id'])
 				update_search_index('post', $cur_item['id'], $cur_item['message'], $cur_item['subject']);
@@ -1646,13 +1806,16 @@ foreach ($errors[$id] as $cur_error)
 		// Empty the PHP cache
 		forum_clear_cache();
 
+		// Delete the update lock file
+		@unlink(FORUM_CACHE_DIR.'db_update.lock');
+
 ?>
 <!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" />
-<title>FluxBB Database Update</title>
+<title><?php echo $lang_update['Update'] ?></title>
 <link rel="stylesheet" type="text/css" href="style/<?php echo $default_style ?>.css" />
 </head>
 <body>
@@ -1662,12 +1825,12 @@ foreach ($errors[$id] as $cur_error)
 <div class="punwrap">
 
 <div class="blockform">
-	<h2><span>FluxBB Update</span></h2>
+	<h2><span><?php echo $lang_update['Update'] ?></span></h2>
 	<div class="box">
 		<div class="fakeform">
 			<div class="inform">
 				<div class="forminfo">
-					<p style="font-size: 1.1em">Your forum database was successfully updated. You may now <a href="<?php echo PUN_ROOT ?>index.php">go to the forum index</a>.</p>
+					<p style="font-size: 1.1em"><?php printf($lang_update['Successfully updated'], sprintf('<a href="index.php">%s</a>', $lang_update['go to index'])) ?></p>
 				</div>
 			</div>
 		</div>
@@ -1689,4 +1852,4 @@ $db->end_transaction();
 $db->close();
 
 if ($query_str != '')
-	exit('<script type="text/javascript">window.location="db_update.php'.$query_str.'"</script><noscript>JavaScript seems to be disabled. <a href="db_update.php'.$query_str.'">Click here to continue</a>.</noscript>');
+	exit('<script type="text/javascript">window.location="db_update.php'.$query_str.'&uid='.$uid.'"</script><noscript>'.sprintf($lang_update['JavaScript disabled'], sprintf('<a href="db_update.php'.$query_str.'&uid='.$uid.'">%s</a>', $lang_update['Click here to continue'])).'</noscript>');
diff --git a/delete.php b/delete.php
index f810b53..bdd1270 100644
--- a/delete.php
+++ b/delete.php
@@ -6,7 +6,7 @@
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
 
-define('PUN_ROOT', './');
+define('PUN_ROOT', dirname(__FILE__).'/');
 require PUN_ROOT.'include/common.php';
 
 
@@ -67,7 +67,11 @@ if (isset($_POST['delete']))
 		delete_post($id, $cur_post['tid']);
 		update_forum($cur_post['fid']);
 
-		redirect('viewtopic.php?id='.$cur_post['tid'], $lang_delete['Post del redirect']);
+		// Redirect towards the previous post
+		$result = $db->query('SELECT id FROM '.$db->prefix.'posts WHERE topic_id='.$cur_post['tid'].' AND id < '.$id.' ORDER BY id DESC LIMIT 1') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+		$post_id = $db->result($result);
+
+		redirect('viewtopic.php?pid='.$post_id.'#p'.$post_id, $lang_delete['Post del redirect']);
 	}
 }
 
@@ -108,7 +112,7 @@ $cur_post['message'] = parse_message($cur_post['message'], $cur_post['hide_smili
 
 <div id="postreview">
 	<div class="blockpost">
-		<div class="box<?php echo ($post_count % 2 == 0) ? ' roweven' : ' rowodd' ?>">
+		<div class="box">
 			<div class="inbox">
 				<div class="postbody">
 					<div class="postleft">
diff --git a/edit.php b/edit.php
index 62b29e2..3c5f4d4 100644
--- a/edit.php
+++ b/edit.php
@@ -6,7 +6,7 @@
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
 
-define('PUN_ROOT', './');
+define('PUN_ROOT', dirname(__FILE__).'/');
 require PUN_ROOT.'include/common.php';
 
 
@@ -19,7 +19,7 @@ if ($id < 1)
 	message($lang_common['Bad request']);
 
 // Fetch some info about the post, the topic and the forum
-$result = $db->query('SELECT f.id AS fid, f.forum_name, f.moderators, f.redirect_url, fp.post_replies, fp.post_topics, t.id AS tid, t.subject, t.posted, t.first_post_id, t.closed, p.poster, p.poster_id, p.message, p.hide_smilies FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'topics AS t ON t.id=p.topic_id INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND p.id='.$id) or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+$result = $db->query('SELECT f.id AS fid, f.forum_name, f.moderators, f.redirect_url, fp.post_replies, fp.post_topics, t.id AS tid, t.subject, t.posted, t.first_post_id, t.sticky, t.closed, p.poster, p.poster_id, p.message, p.hide_smilies FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'topics AS t ON t.id=p.topic_id INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND p.id='.$id) or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
 if (!$db->num_rows($result))
 	message($lang_common['Bad request']);
 
@@ -89,18 +89,21 @@ if (isset($_POST['form_sent']))
 		$errors[] = $lang_post['No message'];
 
 	$hide_smilies = isset($_POST['hide_smilies']) ? '1' : '0';
+	$stick_topic = isset($_POST['stick_topic']) ? '1' : '0';
+	if (!$is_admmod)
+		$stick_topic = $cur_post['sticky'];
 
 	// Did everything go according to plan?
 	if (empty($errors) && !isset($_POST['preview']))
 	{
-		$edited_sql = (!isset($_POST['silent']) || !$is_admmod) ? $edited_sql = ', edited='.time().', edited_by=\''.$db->escape($pun_user['username']).'\'' : '';
+		$edited_sql = (!isset($_POST['silent']) || !$is_admmod) ? ', edited='.time().', edited_by=\''.$db->escape($pun_user['username']).'\'' : '';
 
 		require PUN_ROOT.'include/search_idx.php';
 
 		if ($can_edit_subject)
 		{
 			// Update the topic and any redirect topics
-			$db->query('UPDATE '.$db->prefix.'topics SET subject=\''.$db->escape($subject).'\' WHERE id='.$cur_post['tid'].' OR moved_to='.$cur_post['tid']) or error('Unable to update topic', __FILE__, __LINE__, $db->error());
+			$db->query('UPDATE '.$db->prefix.'topics SET subject=\''.$db->escape($subject).'\', sticky='.$stick_topic.' WHERE id='.$cur_post['tid'].' OR moved_to='.$cur_post['tid']) or error('Unable to update topic', __FILE__, __LINE__, $db->error());
 
 			// We changed the subject, so we need to take that into account when we update the search words
 			update_search_index('edit', $id, $message, $subject);
@@ -212,6 +215,14 @@ else if (isset($_POST['preview']))
 <?php
 
 $checkboxes = array();
+if ($can_edit_subject && $is_admmod)
+{
+	if (isset($_POST['stick_topic']) || $cur_post['sticky'] == '1')
+		$checkboxes[] = '<label><input type="checkbox" name="stick_topic" value="1" checked="checked" tabindex="'.($cur_index++).'" />'.$lang_common['Stick topic'].'<br /></label>';
+	else
+		$checkboxes[] = '<label><input type="checkbox" name="stick_topic" value="1" tabindex="'.($cur_index++).'" />'.$lang_common['Stick topic'].'<br /></label>';
+}
+
 if ($pun_config['o_smilies'] == '1')
 {
 	if (isset($_POST['hide_smilies']) || $cur_post['hide_smilies'] == '1')
diff --git a/extern.php b/extern.php
index e99f995..7c52fda 100644
--- a/extern.php
+++ b/extern.php
@@ -26,38 +26,38 @@
   display posts) and type (output as HTML or RSS). The only
   mandatory variable is action. Possible/default values are:
 
-    action: feed - show most recent topics/posts (HTML or RSS)
-            online - show users online (HTML)
-            online_full - as above, but includes a full list (HTML)
-            stats - show board statistics (HTML)
+	action: feed - show most recent topics/posts (HTML or RSS)
+			online - show users online (HTML)
+			online_full - as above, but includes a full list (HTML)
+			stats - show board statistics (HTML)
 
-    type:   rss - output as RSS 2.0
-            atom - output as Atom 1.0
-            xml - output as XML
-            html - output as HTML (<li>'s)
+	type:   rss - output as RSS 2.0
+			atom - output as Atom 1.0
+			xml - output as XML
+			html - output as HTML (<li>'s)
 
-    fid:    One or more forum IDs (comma-separated). If ignored,
-            topics from all readable forums will be pulled.
+	fid:    One or more forum IDs (comma-separated). If ignored,
+			topics from all readable forums will be pulled.
 
-    nfid:   One or more forum IDs (comma-separated) that are to be
-            excluded. E.g. the ID of a a test forum.
+	nfid:   One or more forum IDs (comma-separated) that are to be
+			excluded. E.g. the ID of a a test forum.
 
-    tid:    A topic ID from which to show posts. If a tid is supplied,
-            fid and nfid are ignored.
+	tid:    A topic ID from which to show posts. If a tid is supplied,
+			fid and nfid are ignored.
 
-    show:   Any integer value between 1 and 50. The default is 15.
+	show:   Any integer value between 1 and 50. The default is 15.
 
-    order:  last_post - show topics ordered by when they were last
-                        posted in, giving information about the reply.
-            posted - show topics ordered by when they were first
-                     posted, giving information about the original post.
+	order:  last_post - show topics ordered by when they were last
+						posted in, giving information about the reply.
+			posted - show topics ordered by when they were first
+					 posted, giving information about the original post.
 
 -----------------------------------------------------------------------------*/
 
 define('PUN_QUIET_VISIT', 1);
 
 if (!defined('PUN_ROOT'))
-	define('PUN_ROOT', './');
+	define('PUN_ROOT', dirname(__FILE__).'/');
 require PUN_ROOT.'include/common.php';
 
 // The length at which topic subjects will be truncated (for HTML output)
@@ -122,7 +122,7 @@ function output_rss($feed)
 	echo '<rss version="2.0">'."\n";
 	echo "\t".'<channel>'."\n";
 	echo "\t\t".'<title><![CDATA['.escape_cdata($feed['title']).']]></title>'."\n";
-	echo "\t\t".'<link>'.$feed['link'].'</link>'."\n";
+	echo "\t\t".'<link>'.pun_htmlspecialchars($feed['link']).'</link>'."\n";
 	echo "\t\t".'<description><![CDATA['.escape_cdata($feed['description']).']]></description>'."\n";
 	echo "\t\t".'<lastBuildDate>'.gmdate('r', count($feed['items']) ? $feed['items'][0]['pubdate'] : time()).'</lastBuildDate>'."\n";
 
@@ -135,7 +135,7 @@ function output_rss($feed)
 	{
 		echo "\t\t".'<item>'."\n";
 		echo "\t\t\t".'<title><![CDATA['.escape_cdata($item['title']).']]></title>'."\n";
-		echo "\t\t\t".'<link>'.$item['link'].'</link>'."\n";
+		echo "\t\t\t".'<link>'.pun_htmlspecialchars($item['link']).'</link>'."\n";
 		echo "\t\t\t".'<description><![CDATA['.escape_cdata($item['description']).']]></description>'."\n";
 		echo "\t\t\t".'<author><![CDATA['.(isset($item['author']['email']) ? escape_cdata($item['author']['email']) : 'dummy at example.com').' ('.escape_cdata($item['author']['name']).')]]></author>'."\n";
 		echo "\t\t\t".'<pubDate>'.gmdate('r', $item['pubdate']).'</pubDate>'."\n";
@@ -167,7 +167,7 @@ function output_atom($feed)
 
 	echo "\t".'<title type="html"><![CDATA['.escape_cdata($feed['title']).']]></title>'."\n";
 	echo "\t".'<link rel="self" href="'.pun_htmlspecialchars(get_current_url()).'"/>'."\n";
-	echo "\t".'<link href="'.$feed['link'].'"/>'."\n";
+	echo "\t".'<link href="'.pun_htmlspecialchars($feed['link']).'"/>'."\n";
 	echo "\t".'<updated>'.gmdate('Y-m-d\TH:i:s\Z', count($feed['items']) ? $feed['items'][0]['pubdate'] : time()).'</updated>'."\n";
 
 	if ($pun_config['o_show_version'] == '1')
@@ -175,7 +175,7 @@ function output_atom($feed)
 	else
 		echo "\t".'<generator>FluxBB</generator>'."\n";
 
-	echo "\t".'<id>'.$feed['link'].'</id>'."\n";
+	echo "\t".'<id>'.pun_htmlspecialchars($feed['link']).'</id>'."\n";
 
 	$content_tag = ($feed['type'] == 'posts') ? 'content' : 'summary';
 
@@ -183,7 +183,7 @@ function output_atom($feed)
 	{
 		echo "\t".'<entry>'."\n";
 		echo "\t\t".'<title type="html"><![CDATA['.escape_cdata($item['title']).']]></title>'."\n";
-		echo "\t\t".'<link rel="alternate" href="'.$item['link'].'"/>'."\n";
+		echo "\t\t".'<link rel="alternate" href="'.pun_htmlspecialchars($item['link']).'"/>'."\n";
 		echo "\t\t".'<'.$content_tag.' type="html"><![CDATA['.escape_cdata($item['description']).']]></'.$content_tag.'>'."\n";
 		echo "\t\t".'<author>'."\n";
 		echo "\t\t\t".'<name><![CDATA['.escape_cdata($item['author']['name']).']]></name>'."\n";
@@ -192,12 +192,12 @@ function output_atom($feed)
 			echo "\t\t\t".'<email><![CDATA['.escape_cdata($item['author']['email']).']]></email>'."\n";
 
 		if (isset($item['author']['uri']))
-			echo "\t\t\t".'<uri>'.$item['author']['uri'].'</uri>'."\n";
+			echo "\t\t\t".'<uri>'.pun_htmlspecialchars($item['author']['uri']).'</uri>'."\n";
 
 		echo "\t\t".'</author>'."\n";
 		echo "\t\t".'<updated>'.gmdate('Y-m-d\TH:i:s\Z', $item['pubdate']).'</updated>'."\n";
 
-		echo "\t\t".'<id>'.$item['link'].'</id>'."\n";
+		echo "\t\t".'<id>'.pun_htmlspecialchars($item['link']).'</id>'."\n";
 		echo "\t".'</entry>'."\n";
 	}
 
@@ -220,7 +220,7 @@ function output_xml($feed)
 
 	echo '<?xml version="1.0" encoding="utf-8"?>'."\n";
 	echo '<source>'."\n";
-	echo "\t".'<url>'.$feed['link'].'</url>'."\n";
+	echo "\t".'<url>'.pun_htmlspecialchars($feed['link']).'</url>'."\n";
 
 	$forum_tag = ($feed['type'] == 'posts') ? 'post' : 'topic';
 
@@ -229,7 +229,7 @@ function output_xml($feed)
 		echo "\t".'<'.$forum_tag.' id="'.$item['id'].'">'."\n";
 
 		echo "\t\t".'<title><![CDATA['.escape_cdata($item['title']).']]></title>'."\n";
-		echo "\t\t".'<link>'.$item['link'].'</link>'."\n";
+		echo "\t\t".'<link>'.pun_htmlspecialchars($item['link']).'</link>'."\n";
 		echo "\t\t".'<content><![CDATA['.escape_cdata($item['description']).']]></content>'."\n";
 		echo "\t\t".'<author>'."\n";
 		echo "\t\t\t".'<name><![CDATA['.escape_cdata($item['author']['name']).']]></name>'."\n";
@@ -238,7 +238,7 @@ function output_xml($feed)
 			echo "\t\t\t".'<email><![CDATA['.escape_cdata($item['author']['email']).']]></email>'."\n";
 
 		if (isset($item['author']['uri']))
-			echo "\t\t\t".'<uri>'.$item['author']['uri'].'</uri>'."\n";
+			echo "\t\t\t".'<uri>'.pun_htmlspecialchars($item['author']['uri']).'</uri>'."\n";
 
 		echo "\t\t".'</author>'."\n";
 		echo "\t\t".'<posted>'.gmdate('r', $item['pubdate']).'</posted>'."\n";
@@ -269,7 +269,7 @@ function output_html($feed)
 		else
 			$subject_truncated = pun_htmlspecialchars($item['title']);
 
-		echo '<li><a href="'.$item['link'].'" title="'.pun_htmlspecialchars($item['title']).'">'.$subject_truncated.'</a></li>'."\n";
+		echo '<li><a href="'.pun_htmlspecialchars($item['link']).'" title="'.pun_htmlspecialchars($item['title']).'">'.$subject_truncated.'</a></li>'."\n";
 	}
 }
 
@@ -308,7 +308,7 @@ if ($action == 'feed')
 		// Setup the feed
 		$feed = array(
 			'title' 		=>	$pun_config['o_board_title'].$lang_common['Title separator'].$cur_topic['subject'],
-			'link'			=>	$pun_config['o_base_url'].'/viewtopic.php?id='.$tid,
+			'link'			=>	get_base_url(true).'/viewtopic.php?id='.$tid,
 			'description'		=>	sprintf($lang_common['RSS description topic'], $cur_topic['subject']),
 			'items'			=>	array(),
 			'type'			=>	'posts'
@@ -323,7 +323,7 @@ if ($action == 'feed')
 			$item = array(
 				'id'			=>	$cur_post['id'],
 				'title'			=>	$cur_topic['first_post_id'] == $cur_post['id'] ? $cur_topic['subject'] : $lang_common['RSS reply'].$cur_topic['subject'],
-				'link'			=>	$pun_config['o_base_url'].'/viewtopic.php?pid='.$cur_post['id'].'#p'.$cur_post['id'],
+				'link'			=>	get_base_url(true).'/viewtopic.php?pid='.$cur_post['id'].'#p'.$cur_post['id'],
 				'description'		=>	$cur_post['message'],
 				'author'		=>	array(
 					'name'	=> $cur_post['poster'],
@@ -336,7 +336,7 @@ if ($action == 'feed')
 				if ($cur_post['email_setting'] == '0' && !$pun_user['is_guest'])
 					$item['author']['email'] = $cur_post['email'];
 
-				$item['author']['uri'] = $pun_config['o_base_url'].'/profile.php?id='.$cur_post['poster_id'];
+				$item['author']['uri'] = get_base_url(true).'/profile.php?id='.$cur_post['poster_id'];
 			}
 			else if ($cur_post['poster_email'] != '' && !$pun_user['is_guest'])
 				$item['author']['email'] = $cur_post['poster_email'];
@@ -384,7 +384,7 @@ if ($action == 'feed')
 		// Setup the feed
 		$feed = array(
 			'title' 		=>	$pun_config['o_board_title'].$forum_name,
-			'link'			=>	$pun_config['o_base_url'].'/index.php',
+			'link'			=>	get_base_url(true).'/index.php',
 			'description'	=>	sprintf($lang_common['RSS description'], $pun_config['o_board_title']),
 			'items'			=>	array(),
 			'type'			=>	'topics'
@@ -402,7 +402,7 @@ if ($action == 'feed')
 			$item = array(
 				'id'			=>	$cur_topic['id'],
 				'title'			=>	$cur_topic['subject'],
-				'link'			=>	$pun_config['o_base_url'].($order_posted ? '/viewtopic.php?id='.$cur_topic['id'] : '/viewtopic.php?id='.$cur_topic['id'].'&action=new'),
+				'link'			=>	get_base_url(true).'/viewtopic.php?id='.$cur_topic['id'].($order_posted ? '' : '&action=new'),
 				'description'	=>	$cur_topic['message'],
 				'author'		=>	array(
 					'name'	=> $order_posted ? $cur_topic['poster'] : $cur_topic['last_poster']
@@ -415,7 +415,7 @@ if ($action == 'feed')
 				if ($cur_topic['email_setting'] == '0' && !$pun_user['is_guest'])
 					$item['author']['email'] = $cur_topic['email'];
 
-				$item['author']['uri'] = $pun_config['o_base_url'].'/profile.php?id='.$cur_topic['poster_id'];
+				$item['author']['uri'] = get_base_url(true).'/profile.php?id='.$cur_topic['poster_id'];
 			}
 			else if ($cur_topic['poster_email'] != '' && !$pun_user['is_guest'])
 				$item['author']['email'] = $cur_topic['poster_email'];
@@ -446,7 +446,7 @@ else if ($action == 'online' || $action == 'online_full')
 	{
 		if ($pun_user_online['user_id'] > 1)
 		{
-			$users[] = ($pun_user['g_view_users'] == '1') ? '<a href="'.$pun_config['o_base_url'].'/profile.php?id='.$pun_user_online['user_id'].'">'.pun_htmlspecialchars($pun_user_online['ident']).'</a>' : pun_htmlspecialchars($pun_user_online['ident']);
+			$users[] = ($pun_user['g_view_users'] == '1') ? '<a href="'.pun_htmlspecialchars(get_base_url(true)).'/profile.php?id='.$pun_user_online['user_id'].'">'.pun_htmlspecialchars($pun_user_online['ident']).'</a>' : pun_htmlspecialchars($pun_user_online['ident']);
 			++$num_users;
 		}
 		else
@@ -492,7 +492,7 @@ else if ($action == 'stats')
 	header('Pragma: public');
 
 	echo sprintf($lang_index['No of users'], forum_number_format($stats['total_users'])).'<br />'."\n";
-	echo sprintf($lang_index['Newest user'], (($pun_user['g_view_users'] == '1') ? '<a href="'.$pun_config['o_base_url'].'/profile.php?id='.$stats['last_user']['id'].'">'.pun_htmlspecialchars($stats['last_user']['username']).'</a>' : pun_htmlspecialchars($stats['last_user']['username']))).'<br />'."\n";
+	echo sprintf($lang_index['Newest user'], (($pun_user['g_view_users'] == '1') ? '<a href="'.pun_htmlspecialchars(get_base_url(true)).'/profile.php?id='.$stats['last_user']['id'].'">'.pun_htmlspecialchars($stats['last_user']['username']).'</a>' : pun_htmlspecialchars($stats['last_user']['username']))).'<br />'."\n";
 	echo sprintf($lang_index['No of topics'], forum_number_format($stats['total_topics'])).'<br />'."\n";
 	echo sprintf($lang_index['No of posts'], forum_number_format($stats['total_posts'])).'<br />'."\n";
 
diff --git a/footer.php b/footer.php
index 8f18a74..24d7d8e 100644
--- a/footer.php
+++ b/footer.php
@@ -63,72 +63,34 @@ if (isset($footer_style) && ($footer_style == 'viewforum' || $footer_style == 'v
 		<div id="brdfooternav" class="inbox">
 <?php
 
-// If no footer style has been specified, we use the default (only copyright/debug info)
-$footer_style = isset($footer_style) ? $footer_style : NULL;
+echo "\t\t\t".'<div class="conl">'."\n";
 
-if ($footer_style == 'index' || $footer_style == 'search')
+// Display the "Jump to" drop list
+if ($pun_config['o_quickjump'] == '1')
 {
-	echo "\t\t\t".'<div class="conl">'."\n";
+	// Load cached quick jump
+	if (file_exists(FORUM_CACHE_DIR.'cache_quickjump_'.$pun_user['g_id'].'.php'))
+		include FORUM_CACHE_DIR.'cache_quickjump_'.$pun_user['g_id'].'.php';
 
-	if (!$pun_user['is_guest'] && $pun_user['g_search'] == '1')
+	if (!defined('PUN_QJ_LOADED'))
 	{
-		echo "\t\t\t\t".'<dl id="searchlinks">'."\n";
-		echo "\t\t\t\t\t".'<dt><strong>'.$lang_common['Search links'].'</strong></dt>'."\n";
-
-		echo "\t\t\t\t\t".'<dd><span><a href="search.php?action=show_24h">'.$lang_common['Show recent posts'].'</a></span></dd>'."\n";
-		echo "\t\t\t\t\t".'<dd><span><a href="search.php?action=show_unanswered">'.$lang_common['Show unanswered posts'].'</a></span></dd>'."\n";
-
-		if ($pun_config['o_subscriptions'] == '1')
-			echo "\t\t\t\t\t".'<dd><span><a href="search.php?action=show_subscriptions">'.$lang_common['Show subscriptions'].'</a></span></dd>'."\n";
-
-		echo "\t\t\t\t\t".'<dd><span><a href="search.php?action=show_user&user_id='.$pun_user['id'].'">'.$lang_common['Show your posts'].'</a></span></dd>'."\n";
-
-		echo "\t\t\t\t".'</dl>'."\n";
-	}
-	else
-	{
-		if ($pun_user['g_search'] == '1')
-		{
-			echo "\t\t\t\t".'<dl id="searchlinks">'."\n";
-			echo "\t\t\t\t\t".'<dt><strong>'.$lang_common['Search links'].'</strong></dt>'."\n";
-
-			echo "\t\t\t\t\t".'<dd><span><a href="search.php?action=show_24h">'.$lang_common['Show recent posts'].'</a></span></dd>'."\n";
-			echo "\t\t\t\t\t".'<dd><span><a href="search.php?action=show_unanswered">'.$lang_common['Show unanswered posts'].'</a></span></dd>'."\n";
+		if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+			require PUN_ROOT.'include/cache.php';
 
-			echo "\t\t\t\t".'</dl>'."\n";
-		}
+		generate_quickjump_cache($pun_user['g_id']);
+		require FORUM_CACHE_DIR.'cache_quickjump_'.$pun_user['g_id'].'.php';
 	}
-
-	echo "\t\t\t".'</div>'."\n";
 }
-else if ($footer_style == 'viewforum' || $footer_style == 'viewtopic')
-{
-	echo "\t\t\t".'<div class="conl">'."\n";
 
-	// Display the "Jump to" drop list
-	if ($pun_config['o_quickjump'] == '1')
-	{
-		// Load cached quick jump
-		if (file_exists(FORUM_CACHE_DIR.'cache_quickjump_'.$pun_user['g_id'].'.php'))
-			include FORUM_CACHE_DIR.'cache_quickjump_'.$pun_user['g_id'].'.php';
-
-		if (!defined('PUN_QJ_LOADED'))
-		{
-			if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
-				require PUN_ROOT.'include/cache.php';
-
-			generate_quickjump_cache($pun_user['g_id']);
-			require FORUM_CACHE_DIR.'cache_quickjump_'.$pun_user['g_id'].'.php';
-		}
-	}
-
-	echo "\t\t\t".'</div>'."\n";
-}
+echo "\t\t\t".'</div>'."\n";
 
 ?>
 			<div class="conr">
 <?php
 
+// If no footer style has been specified, we use the default (only copyright/debug info)
+$footer_style = isset($footer_style) ? $footer_style : NULL;
+
 if ($footer_style == 'index')
 {
 	if ($pun_config['o_feed_type'] == '1')
@@ -141,7 +103,7 @@ else if ($footer_style == 'viewforum')
 	if ($pun_config['o_feed_type'] == '1')
 		echo "\t\t\t\t".'<p id="feedlinks"><span class="rss"><a href="extern.php?action=feed&fid='.$forum_id.'&type=rss">'.$lang_common['RSS forum feed'].'</a></span></p>'."\n";
 	else if ($pun_config['o_feed_type'] == '2')
-		echo "\t\t\t\t".'<p id="feedlinks" class="actions"><span class="atom"><a href="extern.php?action=feed&fid='.$forum_id.'&type=atom">'.$lang_common['Atom forum feed'].'</a></span></p>'."\n";
+		echo "\t\t\t\t".'<p id="feedlinks"><span class="atom"><a href="extern.php?action=feed&fid='.$forum_id.'&type=atom">'.$lang_common['Atom forum feed'].'</a></span></p>'."\n";
 }
 else if ($footer_style == 'viewtopic')
 {
diff --git a/header.php b/header.php
index 78335e1..70a4e66 100644
--- a/header.php
+++ b/header.php
@@ -104,33 +104,34 @@ if (isset($required_fields))
 /* <![CDATA[ */
 function process_form(the_form)
 {
-	var element_names = new Object()
+	var element_names = {
 <?php
-
-	// Output a JavaScript array with localised field names
+	// Output a JavaScript object with localised field names
+	$tpl_temp = count($required_fields);
 	foreach ($required_fields as $elem_orig => $elem_trans)
-		echo "\t".'element_names["'.$elem_orig.'"] = "'.addslashes(str_replace(' ', ' ', $elem_trans)).'"'."\n";
-
+	{
+		echo "\t\t\"".$elem_orig.'": "'.addslashes(str_replace(' ', ' ', $elem_trans));
+		if (--$tpl_temp) echo "\",\n";
+		else echo "\"\n\t};\n";
+	}
 ?>
-
 	if (document.all || document.getElementById)
 	{
 		for (var i = 0; i < the_form.length; ++i)
 		{
-			var elem = the_form.elements[i]
-			if (elem.name && elem.name.substring(0, 4) == "req_")
+			var elem = the_form.elements[i];
+			if (elem.name && (/^req_/.test(elem.name)))
 			{
-				if (elem.type && (elem.type=="text" || elem.type=="textarea" || elem.type=="password" || elem.type=="file") && elem.value=='')
+				if (!elem.value && elem.type && (/^(?:text(?:area)?|password|file)$/i.test(elem.type)))
 				{
-					alert("\"" + element_names[elem.name] + "\" <?php echo $lang_common['required field'] ?>")
-					elem.focus()
-					return false
+					alert('"' + element_names[elem.name] + '" <?php echo $lang_common['required field'] ?>');
+					elem.focus();
+					return false;
 				}
 			}
 		}
 	}
-
-	return true
+	return true;
 }
 /* ]]> */
 </script>
@@ -153,8 +154,8 @@ ob_end_clean();
 // START SUBST - <body>
 if (isset($focus_element))
 {
-	$tpl_main = str_replace('<body onload="', '<body onload="document.getElementById(\''.$focus_element[0].'\').'.$focus_element[1].'.focus();', $tpl_main);
-	$tpl_main = str_replace('<body>', '<body onload="document.getElementById(\''.$focus_element[0].'\').'.$focus_element[1].'.focus()">', $tpl_main);
+	$tpl_main = str_replace('<body onload="', '<body onload="document.getElementById(\''.$focus_element[0].'\').elements[\''.$focus_element[1].'\'].focus();', $tpl_main);
+	$tpl_main = str_replace('<body>', '<body onload="document.getElementById(\''.$focus_element[0].'\').elements[\''.$focus_element[1].'\'].focus()">', $tpl_main);
 }
 // END SUBST - <body>
 
@@ -165,7 +166,7 @@ $tpl_main = str_replace('<pun_page>', htmlspecialchars(basename($_SERVER['PHP_SE
 
 
 // START SUBST - <pun_title>
-$tpl_main = str_replace('<pun_title>', '<h1><span>'.pun_htmlspecialchars($pun_config['o_board_title']).'</span></h1>', $tpl_main);
+$tpl_main = str_replace('<pun_title>', '<h1><a href="index.php">'.pun_htmlspecialchars($pun_config['o_board_title']).'</a></h1>', $tpl_main);
 // END SUBST - <pun_title>
 
 
@@ -180,11 +181,14 @@ $tpl_main = str_replace('<pun_navlinks>','<div id="brdmenu" class="inbox">'."\n\
 
 
 // START SUBST - <pun_status>
+$page_statusinfo = $page_quicklinks = array();
+
 if ($pun_user['is_guest'])
-	$tpl_temp = '<div id="brdwelcome" class="inbox">'."\n\t\t\t".'<p>'.$lang_common['Not logged in'].'</p>'."\n\t\t".'</div>';
+	$page_statusinfo = '<p>'.$lang_common['Not logged in'].'</p>';
 else
 {
-	$tpl_temp = '<div id="brdwelcome" class="inbox">'."\n\t\t\t".'<ul class="conl">'."\n\t\t\t\t".'<li><span>'.$lang_common['Logged in as'].' <strong>'.pun_htmlspecialchars($pun_user['username']).'</strong></span></li>'."\n\t\t\t\t".'<li><span>'.sprintf($lang_common['Last visit'], format_time($pun_user['last_visit'])).'</span></li>';
+	$page_statusinfo[] = '<li><span>'.$lang_common['Logged in as'].' <strong>'.pun_htmlspecialchars($pun_user['username']).'</strong></span></li>';
+	$page_statusinfo[] = '<li><span>'.sprintf($lang_common['Last visit'], format_time($pun_user['last_visit'])).'</span></li>';
 
 	if ($pun_user['is_admmod'])
 	{
@@ -193,20 +197,51 @@ else
 			$result_header = $db->query('SELECT 1 FROM '.$db->prefix.'reports WHERE zapped IS NULL') or error('Unable to fetch reports info', __FILE__, __LINE__, $db->error());
 
 			if ($db->result($result_header))
-				$tpl_temp .= "\n\t\t\t\t".'<li class="reportlink"><span><strong><a href="admin_reports.php">'.$lang_common['New reports'].'</a></strong></span></li>';
+				$page_statusinfo[] = '<li class="reportlink"><span><strong><a href="admin_reports.php">'.$lang_common['New reports'].'</a></strong></span></li>';
 		}
 
 		if ($pun_config['o_maintenance'] == '1')
-			$tpl_temp .= "\n\t\t\t\t".'<li class="maintenancelink"><span><strong><a href="admin_options.php#maintenance">'.$lang_common['Maintenance mode enabled'].'</a></strong></span></li>';
+			$page_statusinfo[] = '<li class="maintenancelink"><span><strong><a href="admin_options.php#maintenance">'.$lang_common['Maintenance mode enabled'].'</a></strong></span></li>';
 	}
 
-	if (in_array(basename($_SERVER['PHP_SELF']), array('index.php', 'search.php')))
-		$tpl_temp .= "\n\t\t\t".'</ul>'."\n\t\t\t".'<ul class="conr">'.($pun_user['g_search'] == '1' ? "\n\t\t\t\t".'<li><span><a href="search.php?action=show_new">'.$lang_common['Show new posts'].'</a></span></li>' : '')."\n\t\t\t\t".'<li><span><a href="misc.php?action=markread">'.$lang_common['Mark all as read'].'</a></span></li>'."\n\t\t\t".'</ul>'."\n\t\t\t".'<div class="clearer"></div>'."\n\t\t".'</div>';
-	else if (basename($_SERVER['PHP_SELF']) == 'viewforum.php')
-		$tpl_temp .= "\n\t\t\t".'</ul>'."\n\t\t\t".'<ul class="conr">'."\n\t\t\t\t".'<li><span><a href="misc.php?action=markforumread&fid='.$id.'">'.$lang_common['Mark forum read'].'</a></span></li>'."\n\t\t\t".'</ul>'."\n\t\t\t".'<div class="clearer"></div>'."\n\t\t".'</div>';
-	else
-		$tpl_temp .= "\n\t\t\t".'</ul>'."\n\t\t\t".'<div class="clearer"></div>'."\n\t\t".'</div>';
+	$script_name = basename($_SERVER['PHP_SELF']);
+	if ($script_name == 'index.php')
+		$page_quicklinks[] = '<a href="misc.php?action=markread">'.$lang_common['Mark all as read'].'</a>';
+
+	if ($pun_user['g_read_board'] == '1' && $pun_user['g_search'] == '1')
+		$page_quicklinks[] = '<a href="search.php?action=show_new" title="'.$lang_common['Show new posts'].'">'.$lang_common['New posts'].'</a>';
+}
+
+// Quick searches
+if ($pun_user['g_read_board'] == '1' && $pun_user['g_search'] == '1')
+{
+	$page_quicklinks[] = '<a href="search.php?action=show_recent" title="'.$lang_common['Show active topics'].'">'.$lang_common['Active topics'].'</a>';
+	$page_quicklinks[] = '<a href="search.php?action=show_unanswered" title="'.$lang_common['Show unanswered topics'].'">'.$lang_common['Unanswered topics'].'</a>';
+}
+
+
+// Generate all that jazz
+$tpl_temp = '<div id="brdwelcome" class="inbox">'."\n\t\t\t";
+
+// The status information
+if (is_array($page_statusinfo))
+{
+	$tpl_temp .= "\n\t\t\t".'<ul class="conl">';
+	$tpl_temp .= "\n\t\t\t\t".implode("\n\t\t\t\t", $page_statusinfo);
+	$tpl_temp .= "\n\t\t\t".'</ul>';
 }
+else
+	$tpl_temp .= "\n\t\t\t".$page_statusinfo;
+
+// Generate quicklinks
+if (count($page_quicklinks))
+{
+	$tpl_temp .= "\n\t\t\t".'<ul class="conr">';
+	$tpl_temp .= "\n\t\t\t\t".'<li><span>'.implode('</span></li>'."\n\t\t\t\t".'<li><span>', $page_quicklinks).'</span></li>';
+	$tpl_temp .= "\n\t\t\t".'</ul>'."\n\t\t\t".'<div class="clearer"></div>';
+}
+
+$tpl_temp .= "\n\t\t".'</div>';
 
 $tpl_main = str_replace('<pun_status>', $tpl_temp, $tpl_main);
 // END SUBST - <pun_status>
diff --git a/help.php b/help.php
index 1d828fb..e046273 100644
--- a/help.php
+++ b/help.php
@@ -9,7 +9,7 @@
 // Tell header.php to use the help template
 define('PUN_HELP', 1);
 
-define('PUN_ROOT', './');
+define('PUN_ROOT', dirname(__FILE__).'/');
 require PUN_ROOT.'include/common.php';
 
 
@@ -39,7 +39,7 @@ require PUN_ROOT.'header.php';
 		<p><?php echo $lang_help['Text style info'] ?></p>
 		<p><code>[b]<?php echo $lang_help['Bold text'] ?>[/b]</code> <?php echo $lang_help['produces'] ?> <samp><strong><?php echo $lang_help['Bold text'] ?></strong></samp></p>
 		<p><code>[u]<?php echo $lang_help['Underlined text'] ?>[/u]</code> <?php echo $lang_help['produces'] ?> <samp><span class="bbu"><?php echo $lang_help['Underlined text'] ?></span></samp></p>
-		<p><code>[i]<?php echo $lang_help['Italic text'] ?>[/i]</code> <?php echo $lang_help['produces'] ?> <samp><i><?php echo $lang_help['Italic text'] ?></i></samp></p>
+		<p><code>[i]<?php echo $lang_help['Italic text'] ?>[/i]</code> <?php echo $lang_help['produces'] ?> <samp><em><?php echo $lang_help['Italic text'] ?></em></samp></p>
 		<p><code>[s]<?php echo $lang_help['Strike-through text'] ?>[/s]</code> <?php echo $lang_help['produces'] ?> <samp><span class="bbs"><?php echo $lang_help['Strike-through text'] ?></span></samp></p>
 		<p><code>[del]<?php echo $lang_help['Deleted text'] ?>[/del]</code> <?php echo $lang_help['produces'] ?> <samp><del><?php echo $lang_help['Deleted text'] ?></del></samp></p>
 		<p><code>[ins]<?php echo $lang_help['Inserted text'] ?>[/ins]</code> <?php echo $lang_help['produces'] ?> <samp><ins><?php echo $lang_help['Inserted text'] ?></ins></samp></p>
@@ -53,14 +53,14 @@ require PUN_ROOT.'header.php';
 <div class="box">
 	<div class="inbox">
 		<p><?php echo $lang_help['Links info'] ?></p>
-		<p><code>[url=<?php echo $pun_config['o_base_url'].'/' ?>]<?php echo pun_htmlspecialchars($pun_config['o_board_title']) ?>[/url]</code> <?php echo $lang_help['produces'] ?> <samp><a href="<?php echo $pun_config['o_base_url'].'/' ?>"><?php echo pun_htmlspecialchars($pun_config['o_board_title']) ?></a></samp></p>
-		<p><code>[url]<?php echo $pun_config['o_base_url'].'/' ?>[/url]</code> <?php echo $lang_help['produces'] ?> <samp><a href="<?php echo $pun_config['o_base_url'] ?>"><?php echo $pun_config['o_base_url'].'/' ?></a></samp></p>
+		<p><code>[url=<?php echo pun_htmlspecialchars(get_base_url(true).'/') ?>]<?php echo pun_htmlspecialchars($pun_config['o_board_title']) ?>[/url]</code> <?php echo $lang_help['produces'] ?> <samp><a href="<?php echo pun_htmlspecialchars(get_base_url(true).'/') ?>"><?php echo pun_htmlspecialchars($pun_config['o_board_title']) ?></a></samp></p>
+		<p><code>[url]<?php echo pun_htmlspecialchars(get_base_url(true).'/') ?>[/url]</code> <?php echo $lang_help['produces'] ?> <samp><a href="<?php echo pun_htmlspecialchars(get_base_url(true).'/') ?>"><?php echo pun_htmlspecialchars(get_base_url(true).'/') ?></a></samp></p>
 		<p><code>[email]myname at mydomain.com[/email]</code> <?php echo $lang_help['produces'] ?> <samp><a href="mailto:myname at mydomain.com">myname at mydomain.com</a></samp></p>
 		<p><code>[email=myname at mydomain.com]<?php echo $lang_help['My email address'] ?>[/email]</code> <?php echo $lang_help['produces'] ?> <samp><a href="mailto:myname at mydomain.com"><?php echo $lang_help['My email address'] ?></a></samp></p>
 	</div>
 	<div class="inbox">
 		<p><a name="img"></a><?php echo $lang_help['Images info'] ?></p>
-		<p><code>[img=FluxBB bbcode test]<?php echo $pun_config['o_base_url'].'/' ?>img/test.png[/img]</code> <?php echo $lang_help['produces'] ?> <samp><img src="<?php echo $pun_config['o_base_url'].'/' ?>img/test.png" alt="FluxBB bbcode test" /></samp></p>
+		<p><code>[img=<?php echo $lang_help['FluxBB bbcode test'] ?>]<?php echo pun_htmlspecialchars(get_base_url(true)) ?>/img/test.png[/img]</code> <?php echo $lang_help['produces'] ?> <samp><img src="<?php echo pun_htmlspecialchars(get_base_url(true)) ?>/img/test.png" alt="<?php echo $lang_help['FluxBB bbcode test'] ?>" /></samp></p>
 	</div>
 </div>
 <h2><span><?php echo $lang_help['Quotes'] ?></span></h2>
@@ -99,17 +99,17 @@ require PUN_ROOT.'header.php';
 		<p><code>[list][*]<?php echo $lang_help['List text 1'] ?>[/*][*]<?php echo $lang_help['List text 2'] ?>[/*][*]<?php echo $lang_help['List text 3'] ?>[/*][/list]</code>
 		<br /><span><?php echo $lang_help['produces list'] ?></span></p>
 		<div class="postmsg">
-			<ul><li><?php echo $lang_help['List text 1'] ?></li><li><?php echo $lang_help['List text 2'] ?></li><li><?php echo $lang_help['List text 3'] ?></li></ul>
+			<ul><li><p><?php echo $lang_help['List text 1'] ?></p></li><li><p><?php echo $lang_help['List text 2'] ?></p></li><li><p><?php echo $lang_help['List text 3'] ?></p></li></ul>
 		</div>
 		<p><code>[list=1][*]<?php echo $lang_help['List text 1'] ?>[/*][*]<?php echo $lang_help['List text 2'] ?>[/*][*]<?php echo $lang_help['List text 3'] ?>[/*][/list]</code>
 		<br /><span><?php echo $lang_help['produces decimal list'] ?></span></p>
 		<div class="postmsg">
-			<ol class="decimal"><li><?php echo $lang_help['List text 1'] ?></li><li><?php echo $lang_help['List text 2'] ?></li><li><?php echo $lang_help['List text 3'] ?></li></ol>
+			<ol class="decimal"><li><p><?php echo $lang_help['List text 1'] ?></p></li><li><p><?php echo $lang_help['List text 2'] ?></p></li><li><p><?php echo $lang_help['List text 3'] ?></p></li></ol>
 		</div>
 		<p><code>[list=a][*]<?php echo $lang_help['List text 1'] ?>[/*][*]<?php echo $lang_help['List text 2'] ?>[/*][*]<?php echo $lang_help['List text 3'] ?>[/*][/list]</code>
 		<br /><span><?php echo $lang_help['produces alpha list'] ?></span></p>
 		<div class="postmsg">
-			<ol class="alpha"><li><?php echo $lang_help['List text 1'] ?></li><li><?php echo $lang_help['List text 2'] ?></li><li><?php echo $lang_help['List text 3'] ?></li></ol>
+			<ol class="alpha"><li><p><?php echo $lang_help['List text 1'] ?></p></li><li><p><?php echo $lang_help['List text 2'] ?></p></li><li><p><?php echo $lang_help['List text 3'] ?></p></li></ol>
 		</div>
 	</div>
 </div>
@@ -135,7 +135,8 @@ foreach ($smilies as $smiley_text => $smiley_img)
 	$smiley_groups[$smiley_img][] = $smiley_text;
 
 foreach ($smiley_groups as $smiley_img => $smiley_texts)
-	echo "\t\t".'<p><code>'.implode('</code> '.$lang_common['and'].' <code>', $smiley_texts).'</code> <span>'.$lang_help['produces'].'</span> <samp><img src="'.$pun_config['o_base_url'].'/img/smilies/'.$smiley_img.'" width="15" height="15" alt="'.$smiley_texts[0].'" /></samp></p>'."\n";
+	echo "\t\t".'<p><code>'.implode('</code> '.$lang_common['and'].' <code>', $smiley_texts).'</code> <span>'.$lang_help['produces'].'</span> <samp><img src="'.pun_htmlspecialchars(get_base_url(true)).'/img/smilies/'.$smiley_img.'" width="15" height="15" alt="'.$smiley_texts[0].'" /></samp></p>'."\n";
+
 ?>
 	</div>
 </div>
diff --git a/include/cache.php b/include/cache.php
index b1077a4..7db17ff 100644
--- a/include/cache.php
+++ b/include/cache.php
@@ -33,7 +33,7 @@ function generate_config_cache()
 	fclose($fh);
 
 	if (function_exists('apc_delete_file'))
-		apc_delete_file(FORUM_CACHE_DIR.'cache_config.php');
+		@apc_delete_file(FORUM_CACHE_DIR.'cache_config.php');
 }
 
 
@@ -61,7 +61,7 @@ function generate_bans_cache()
 	fclose($fh);
 
 	if (function_exists('apc_delete_file'))
-		apc_delete_file(FORUM_CACHE_DIR.'cache_bans.php');
+		@apc_delete_file(FORUM_CACHE_DIR.'cache_bans.php');
 }
 
 
@@ -89,7 +89,7 @@ function generate_ranks_cache()
 	fclose($fh);
 
 	if (function_exists('apc_delete_file'))
-		apc_delete_file(FORUM_CACHE_DIR.'cache_ranks.php');
+		@apc_delete_file(FORUM_CACHE_DIR.'cache_ranks.php');
 }
 
 
@@ -100,57 +100,71 @@ function generate_quickjump_cache($group_id = false)
 {
 	global $db, $lang_common, $pun_user;
 
+	$groups = array();
+
 	// If a group_id was supplied, we generate the quick jump cache for that group only
 	if ($group_id !== false)
-		$groups[0] = $group_id;
+	{
+		// Is this group even allowed to read forums?
+		$result = $db->query('SELECT g_read_board FROM '.$db->prefix.'groups WHERE g_id='.$group_id) or error('Unable to fetch user group read permission', __FILE__, __LINE__, $db->error());
+		$read_board = $db->result($result);
+
+		$groups[$group_id] = $read_board;
+	}
 	else
 	{
-		// A group_id was now supplied, so we generate the quick jump cache for all groups
-		$result = $db->query('SELECT g_id FROM '.$db->prefix.'groups') or error('Unable to fetch user group list', __FILE__, __LINE__, $db->error());
+		// A group_id was not supplied, so we generate the quick jump cache for all groups
+		$result = $db->query('SELECT g_id, g_read_board FROM '.$db->prefix.'groups') or error('Unable to fetch user group list', __FILE__, __LINE__, $db->error());
 		$num_groups = $db->num_rows($result);
 
-		for ($i = 0; $i < $num_groups; ++$i)
-			$groups[] = $db->result($result, $i);
+		while ($row = $db->fetch_row($result))
+			$groups[$row[0]] = $row[1];
 	}
 
 	// Loop through the groups in $groups and output the cache for each of them
-	foreach ($groups as $group_id)
+	foreach ($groups as $group_id => $read_board)
 	{
 		// Output quick jump as PHP code
 		$fh = @fopen(FORUM_CACHE_DIR.'cache_quickjump_'.$group_id.'.php', 'wb');
 		if (!$fh)
 			error('Unable to write quick jump cache file to cache directory. Please make sure PHP has write access to the directory \'cache\'', __FILE__, __LINE__);
 
-		$output = '<?php'."\n\n".'if (!defined(\'PUN\')) exit;'."\n".'define(\'PUN_QJ_LOADED\', 1);'."\n\n".'?>';
-		$output .= "\t\t\t\t".'<form id="qjump" method="get" action="viewforum.php">'."\n\t\t\t\t\t".'<div><label><span><?php echo $lang_common[\'Jump to\'] ?>'.'<br /></span>'."\n\t\t\t\t\t".'<select name="id" onchange="window.location=(\'viewforum.php?id=\'+this.options[this.selectedIndex].value)">'."\n";
-
-
-		$result = $db->query('SELECT c.id AS cid, c.cat_name, f.id AS fid, f.forum_name, f.redirect_url FROM '.$db->prefix.'categories AS c INNER JOIN '.$db->prefix.'forums AS f ON c.id=f.cat_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$group_id.') WHERE fp.read_forum IS NULL OR fp.read_forum=1 ORDER BY c.disp_position, c.id, f.disp_position', true) or error('Unable to fetch category/forum list', __FILE__, __LINE__, $db->error());
+		$output = '<?php'."\n\n".'if (!defined(\'PUN\')) exit;'."\n".'define(\'PUN_QJ_LOADED\', 1);'."\n".'$forum_id = isset($forum_id) ? $forum_id : 0;'."\n\n".'?>';
 
-		$cur_category = 0;
-		while ($cur_forum = $db->fetch_assoc($result))
+		if ($read_board == '1')
 		{
-			if ($cur_forum['cid'] != $cur_category) // A new category since last iteration?
+			$result = $db->query('SELECT c.id AS cid, c.cat_name, f.id AS fid, f.forum_name, f.redirect_url FROM '.$db->prefix.'categories AS c INNER JOIN '.$db->prefix.'forums AS f ON c.id=f.cat_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$group_id.') WHERE fp.read_forum IS NULL OR fp.read_forum=1 ORDER BY c.disp_position, c.id, f.disp_position', true) or error('Unable to fetch category/forum list', __FILE__, __LINE__, $db->error());
+
+			if ($db->num_rows($result))
 			{
-				if ($cur_category)
-					$output .= "\t\t\t\t\t\t".'</optgroup>'."\n";
+				$output .= "\t\t\t\t".'<form id="qjump" method="get" action="viewforum.php">'."\n\t\t\t\t\t".'<div><label><span><?php echo $lang_common[\'Jump to\'] ?>'.'<br /></span>'."\n\t\t\t\t\t".'<select name="id" onchange="window.location=(\'viewforum.php?id=\'+this.options[this.selectedIndex].value)">'."\n";
 
-				$output .= "\t\t\t\t\t\t".'<optgroup label="'.pun_htmlspecialchars($cur_forum['cat_name']).'">'."\n";
-				$cur_category = $cur_forum['cid'];
-			}
+				$cur_category = 0;
+				while ($cur_forum = $db->fetch_assoc($result))
+				{
+					if ($cur_forum['cid'] != $cur_category) // A new category since last iteration?
+					{
+						if ($cur_category)
+							$output .= "\t\t\t\t\t\t".'</optgroup>'."\n";
 
-			$redirect_tag = ($cur_forum['redirect_url'] != '') ? ' >>>' : '';
-			$output .= "\t\t\t\t\t\t\t".'<option value="'.$cur_forum['fid'].'"<?php echo ($forum_id == '.$cur_forum['fid'].') ? \' selected="selected"\' : \'\' ?>>'.pun_htmlspecialchars($cur_forum['forum_name']).$redirect_tag.'</option>'."\n";
-		}
+						$output .= "\t\t\t\t\t\t".'<optgroup label="'.pun_htmlspecialchars($cur_forum['cat_name']).'">'."\n";
+						$cur_category = $cur_forum['cid'];
+					}
 
-		$output .= "\t\t\t\t\t\t".'</optgroup>'."\n\t\t\t\t\t".'</select>'."\n\t\t\t\t\t".'<input type="submit" value="<?php echo $lang_common[\'Go\'] ?>" accesskey="g" />'."\n\t\t\t\t\t".'</label></div>'."\n\t\t\t\t".'</form>'."\n";
+					$redirect_tag = ($cur_forum['redirect_url'] != '') ? ' >>>' : '';
+					$output .= "\t\t\t\t\t\t\t".'<option value="'.$cur_forum['fid'].'"<?php echo ($forum_id == '.$cur_forum['fid'].') ? \' selected="selected"\' : \'\' ?>>'.pun_htmlspecialchars($cur_forum['forum_name']).$redirect_tag.'</option>'."\n";
+				}
+
+				$output .= "\t\t\t\t\t\t".'</optgroup>'."\n\t\t\t\t\t".'</select>'."\n\t\t\t\t\t".'<input type="submit" value="<?php echo $lang_common[\'Go\'] ?>" accesskey="g" />'."\n\t\t\t\t\t".'</label></div>'."\n\t\t\t\t".'</form>'."\n";
+			}
+		}
 
 		fwrite($fh, $output);
 
 		fclose($fh);
 
 		if (function_exists('apc_delete_file'))
-			apc_delete_file(FORUM_CACHE_DIR.'cache_quickjump_'.$group_id.'.php');
+			@apc_delete_file(FORUM_CACHE_DIR.'cache_quickjump_'.$group_id.'.php');
 	}
 }
 
diff --git a/include/common.php b/include/common.php
index 6d5f8c7..d5caa89 100644
--- a/include/common.php
+++ b/include/common.php
@@ -10,9 +10,9 @@ if (!defined('PUN_ROOT'))
 	exit('The constant PUN_ROOT must be defined and point to a valid FluxBB installation root directory.');
 
 // Define the version and database revision that this code was written for
-define('FORUM_VERSION', '1.4.2');
+define('FORUM_VERSION', '1.4.3');
 
-define('FORUM_DB_REVISION', 8);
+define('FORUM_DB_REVISION', 10);
 define('FORUM_SI_REVISION', 1);
 define('FORUM_PARSER_REVISION', 1);
 
@@ -38,10 +38,6 @@ if (file_exists(PUN_ROOT.'config.php'))
 if (defined('FORUM'))
 	define('PUN', FORUM);
 
-// If PUN isn't defined, config.php is missing or corrupt
-if (!defined('PUN'))
-	exit('The file \'config.php\' doesn\'t exist or is corrupt. Please run <a href="install.php">install.php</a> to install FluxBB first.');
-
 // Load the functions script
 require PUN_ROOT.'include/functions.php';
 
@@ -54,6 +50,13 @@ forum_remove_bad_characters();
 // Reverse the effect of register_globals
 forum_unregister_globals();
 
+// 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 = get_microtime();
 
@@ -120,7 +123,10 @@ if (!isset($pun_config['o_database_revision']) || $pun_config['o_database_revisi
 		!isset($pun_config['o_searchindex_revision']) || $pun_config['o_searchindex_revision'] < FORUM_SI_REVISION ||
 		!isset($pun_config['o_parser_revision']) || $pun_config['o_parser_revision'] < FORUM_PARSER_REVISION ||
 		version_compare($pun_config['o_cur_version'], FORUM_VERSION, '<'))
-	exit('Your FluxBB database is out-of-date and must be upgraded in order to continue. Please run <a href="'.PUN_ROOT.'db_update.php">db_update.php</a> in order to complete the upgrade process.');
+	{
+		header('Location: db_update.php');
+		exit;
+	}
 
 // Enable output buffering
 if (!defined('PUN_DISABLE_BUFFERING'))
diff --git a/include/common_admin.php b/include/common_admin.php
index acf0b4c..240f9f6 100644
--- a/include/common_admin.php
+++ b/include/common_admin.php
@@ -121,7 +121,7 @@ function prune($forum_id, $prune_sticky, $prune_date)
 			// Delete topics
 			$db->query('DELETE FROM '.$db->prefix.'topics WHERE id IN('.$topic_ids.')') or error('Unable to prune topics', __FILE__, __LINE__, $db->error());
 			// Delete subscriptions
-			$db->query('DELETE FROM '.$db->prefix.'subscriptions WHERE topic_id IN('.$topic_ids.')') or error('Unable to prune subscriptions', __FILE__, __LINE__, $db->error());
+			$db->query('DELETE FROM '.$db->prefix.'topic_subscriptions WHERE topic_id IN('.$topic_ids.')') or error('Unable to prune subscriptions', __FILE__, __LINE__, $db->error());
 			// Delete posts
 			$db->query('DELETE FROM '.$db->prefix.'posts WHERE id IN('.$post_ids.')') or error('Unable to prune posts', __FILE__, __LINE__, $db->error());
 
diff --git a/include/dblayer/mysql.php b/include/dblayer/mysql.php
index 1ef5108..e5a41dc 100644
--- a/include/dblayer/mysql.php
+++ b/include/dblayer/mysql.php
@@ -230,7 +230,7 @@ class DBLayer
 		$result = $this->query('SHOW INDEX FROM '.($no_prefix ? '' : $this->prefix).$table_name);
 		while ($cur_index = $this->fetch_assoc($result))
 		{
-			if ($cur_index['Key_name'] == ($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name)
+			if (strtolower($cur_index['Key_name']) == strtolower(($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name))
 			{
 				$exists = true;
 				break;
@@ -301,6 +301,16 @@ class DBLayer
 	}
 
 
+	function rename_table($old_table, $new_table, $no_prefix = false)
+	{
+		// If there 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))
diff --git a/include/dblayer/mysql_innodb.php b/include/dblayer/mysql_innodb.php
index a8588a9..1b4d352 100644
--- a/include/dblayer/mysql_innodb.php
+++ b/include/dblayer/mysql_innodb.php
@@ -244,7 +244,7 @@ class DBLayer
 		$result = $this->query('SHOW INDEX FROM '.($no_prefix ? '' : $this->prefix).$table_name);
 		while ($cur_index = $this->fetch_assoc($result))
 		{
-			if ($cur_index['Key_name'] == ($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name)
+			if (strtolower($cur_index['Key_name']) == strtolower(($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name))
 			{
 				$exists = true;
 				break;
@@ -315,6 +315,16 @@ class DBLayer
 	}
 
 
+	function rename_table($old_table, $new_table, $no_prefix = false)
+	{
+		// If there 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))
diff --git a/include/dblayer/mysqli.php b/include/dblayer/mysqli.php
index 9959705..a23065a 100644
--- a/include/dblayer/mysqli.php
+++ b/include/dblayer/mysqli.php
@@ -237,7 +237,7 @@ class DBLayer
 		$result = $this->query('SHOW INDEX FROM '.($no_prefix ? '' : $this->prefix).$table_name);
 		while ($cur_index = $this->fetch_assoc($result))
 		{
-			if ($cur_index['Key_name'] == ($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name)
+			if (strtolower($cur_index['Key_name']) == strtolower(($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name))
 			{
 				$exists = true;
 				break;
@@ -308,6 +308,16 @@ class DBLayer
 	}
 
 
+	function rename_table($old_table, $new_table, $no_prefix = false)
+	{
+		// If there 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))
diff --git a/include/dblayer/mysqli_innodb.php b/include/dblayer/mysqli_innodb.php
index 4591946..b209c82 100644
--- a/include/dblayer/mysqli_innodb.php
+++ b/include/dblayer/mysqli_innodb.php
@@ -250,7 +250,7 @@ class DBLayer
 		$result = $this->query('SHOW INDEX FROM '.($no_prefix ? '' : $this->prefix).$table_name);
 		while ($cur_index = $this->fetch_assoc($result))
 		{
-			if ($cur_index['Key_name'] == ($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name)
+			if (strtolower($cur_index['Key_name']) == strtolower(($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name))
 			{
 				$exists = true;
 				break;
@@ -321,6 +321,16 @@ class DBLayer
 	}
 
 
+	function rename_table($old_table, $new_table, $no_prefix = false)
+	{
+		// If there 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))
diff --git a/include/dblayer/pgsql.php b/include/dblayer/pgsql.php
index 9743c6e..23f751d 100644
--- a/include/dblayer/pgsql.php
+++ b/include/dblayer/pgsql.php
@@ -352,6 +352,16 @@ class DBLayer
 	}
 
 
+	function rename_table($old_table, $new_table, $no_prefix = false)
+	{
+		// If there 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))
diff --git a/include/dblayer/sqlite.php b/include/dblayer/sqlite.php
index d337a8c..0a1820b 100644
--- a/include/dblayer/sqlite.php
+++ b/include/dblayer/sqlite.php
@@ -349,6 +349,35 @@ class DBLayer
 	}
 
 
+	function rename_table($old_name, $new_name, $no_prefix = false)
+	{
+		// If there 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;
+
+		$table = $this->get_table_info($old_name, $no_prefix);
+
+		// Create new table
+		$newtable = str_replace('CREATE TABLE '.($no_prefix ? '' : $this->prefix).$this->escape($old_name).' (', 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$this->escape($new_name).' (', $table['sql']);
+		$result = $this->query($newtable) ? true : false;
+
+		// Recreate indexes
+		if (!empty($table['indices']))
+		{
+			foreach ($table['indices'] as $cur_index)
+				$result &= $this->query($cur_index) ? true : false;
+		}
+
+		// Copy content across
+		$result &= $this->query('INSERT INTO '.($no_prefix ? '' : $this->prefix).$this->escape($new_name).' SELECT * FROM '.($no_prefix ? '' : $this->prefix).$this->escape($old_name)) ? true : false;
+
+		// Drop old table
+		$result &= $this->drop_table(($no_prefix ? '' : $this->prefix).$this->escape($table_name));
+
+		return $result;
+	}
+
+
 	function get_table_info($table_name, $no_prefix = false)
 	{
 		// Grab table info
diff --git a/include/functions.php b/include/functions.php
index e28bca1..70e60c4 100644
--- a/include/functions.php
+++ b/include/functions.php
@@ -173,6 +173,28 @@ function get_current_url($max_length = 0)
 
 
 //
+// Fetch the base_url, optionally support HTTPS and HTTP
+//
+function get_base_url($support_https = false)
+{
+	global $pun_config;
+	static $base_url;
+
+	if (!$support_https)
+		return $pun_config['o_base_url'];
+
+	if (!isset($base_url))
+	{
+		// Make sure we are using the correct protocol
+		$protocol = (!isset($_SERVER['HTTPS']) || strtolower($_SERVER['HTTPS']) == 'off') ? 'http://' : 'https://';
+		$base_url = str_replace(array('http://', 'https://'), $protocol, $pun_config['o_base_url']);
+	}
+
+	return $base_url;
+}
+
+
+//
 // Fill $pun_user with default values (for guests)
 //
 function set_default_user()
@@ -231,7 +253,7 @@ function pun_setcookie($user_id, $password_hash, $expire)
 {
 	global $cookie_name, $cookie_seed;
 
-	forum_setcookie($cookie_name, serialize(array($user_id, md5($cookie_seed.$password_hash), $expire)), $expire);
+	forum_setcookie($cookie_name, serialize(array((string) $user_id, md5($cookie_seed.$password_hash), (int) $expire)), $expire);
 }
 
 
@@ -533,7 +555,7 @@ function generate_avatar_markup($user_id)
 
 		if (file_exists(PUN_ROOT.$path) && $img_size = getimagesize(PUN_ROOT.$path))
 		{
-			$avatar_markup = '<img src="'.$pun_config['o_base_url'].'/'.$path.'?m='.filemtime(PUN_ROOT.$path).'" '.$img_size[3].' alt="" />';
+			$avatar_markup = '<img src="'.pun_htmlspecialchars(get_base_url(true).'/'.$path.'?m='.filemtime(PUN_ROOT.$path)).'" '.$img_size[3].' alt="" />';
 			break;
 		}
 	}
@@ -691,7 +713,7 @@ function delete_topic($topic_id)
 	}
 
 	// Delete any subscriptions for this topic
-	$db->query('DELETE FROM '.$db->prefix.'subscriptions WHERE topic_id='.$topic_id) or error('Unable to delete subscriptions', __FILE__, __LINE__, $db->error());
+	$db->query('DELETE FROM '.$db->prefix.'topic_subscriptions WHERE topic_id='.$topic_id) or error('Unable to delete subscriptions', __FILE__, __LINE__, $db->error());
 }
 
 
@@ -908,12 +930,10 @@ function paginate($num_pages, $cur_page, $link)
 //
 function message($message, $no_back_link = false)
 {
-	global $db, $lang_common, $pun_config, $pun_start, $tpl_main;
+	global $db, $lang_common, $pun_config, $pun_start, $tpl_main, $pun_user;
 
 	if (!defined('PUN_HEADER'))
 	{
-		global $pun_user;
-
 		$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Info']);
 		define('PUN_ACTIVE_PAGE', 'index');
 		require PUN_ROOT.'header.php';
@@ -1035,14 +1055,29 @@ if (!function_exists('file_get_contents'))
 
 
 //
-// Make sure that HTTP_REFERER matches $pun_config['o_base_url']/$script
+// Make sure that HTTP_REFERER matches base_url/script
 //
-function confirm_referrer($script)
+function confirm_referrer($script, $error_msg = false)
 {
 	global $pun_config, $lang_common;
 
-	if (!preg_match('#^'.preg_quote(str_replace('www.', '', $pun_config['o_base_url']).'/'.$script, '#').'#i', str_replace('www.', '', (isset($_SERVER['HTTP_REFERER']) ? urldecode($_SERVER['HTTP_REFERER']) : ''))))
-		message($lang_common['Bad referrer']);
+	// There is no referrer
+	if (empty($_SERVER['HTTP_REFERER']))
+		message($error_msg ? $error_msg : $lang_common['Bad referrer']);
+
+	$referrer = parse_url(strtolower($_SERVER['HTTP_REFERER']));
+	// Remove www subdomain if it exists
+	if (strpos($referrer['host'], 'www.') === 0)
+		$referrer['host'] = substr($referrer['host'], 4);
+
+	$valid = parse_url(strtolower(get_base_url().'/'.$script));
+	// Remove www subdomain if it exists
+	if (strpos($valid['host'], 'www.') === 0)
+		$valid['host'] = substr($valid['host'], 4);
+
+	// Check the host and path match. Ignore the scheme, port, etc.
+	if ($referrer['host'] != $valid['host'] || $referrer['path'] != $valid['path'])
+		message($error_msg ? $error_msg : $lang_common['Bad referrer']);
 }
 
 
@@ -1124,9 +1159,9 @@ function pun_linebreaks($str)
 //
 // A wrapper for utf8_trim for compatibility
 //
-function pun_trim($str)
+function pun_trim($str, $charlist = false)
 {
-	return utf8_trim($str);
+	return utf8_trim($str, $charlist);
 }
 
 //
@@ -1275,9 +1310,9 @@ function redirect($destination_url, $message)
 {
 	global $db, $pun_config, $lang_common, $pun_user;
 
-	// Prefix with o_base_url (unless there's already a valid URI)
+	// Prefix with base_url (unless there's already a valid URI)
 	if (strpos($destination_url, 'http://') !== 0 && strpos($destination_url, 'https://') !== 0 && strpos($destination_url, '/') !== 0)
-		$destination_url = $pun_config['o_base_url'].'/'.$destination_url;
+		$destination_url = get_base_url(true).'/'.$destination_url;
 
 	// Do a little spring cleaning
 	$destination_url = preg_replace('/([\r\n])|(%0[ad])|(;\s*data\s*:)/i', '', $destination_url);
@@ -1571,11 +1606,6 @@ function remove_bad_characters($array)
 			"\xef\xbf\xbb"	=> '',		// INTERLINEAR ANNOTATION TERMINATOR	FFFB	*
 			"\xef\xbf\xbc"	=> '',		// OBJECT REPLACEMENT CHARACTER			FFFC	*
 			"\xef\xbf\xbd"	=> '',		// REPLACEMENT CHARACTER				FFFD	*
-			"\xc2\xad"		=> '-',		// SOFT HYPHEN							00AD
-			"\xE2\x80\x9C"	=> '"',		// LEFT DOUBLE QUOTATION MARK			201C
-			"\xE2\x80\x9D"	=> '"',		// RIGHT DOUBLE QUOTATION MARK			201D
-			"\xE2\x80\x98"	=> '\'',	// LEFT SINGLE QUOTATION MARK			2018
-			"\xE2\x80\x99"	=> '\'',	// RIGHT SINGLE QUOTATION MARK			2019
 			"\xe2\x80\x80"	=> ' ',		// EN QUAD								2000	*
 			"\xe2\x80\x81"	=> ' ',		// EM QUAD								2001	*
 			"\xe2\x80\x82"	=> ' ',		// EN SPACE								2002	*
@@ -1693,6 +1723,42 @@ function forum_list_plugins($is_admin)
 	return $plugins;
 }
 
+
+//
+// Split text into chunks ($inside contains all text inside $start and $end, and $outside contains all text outside)
+//
+function split_text($text, $start, $end, &$errors, $retab = true)
+{
+	global $pun_config, $lang_common;
+
+	$tokens = explode($start, $text);
+
+	$outside[] = $tokens[0];
+
+	$num_tokens = count($tokens);
+	for ($i = 1; $i < $num_tokens; ++$i)
+	{
+		$temp = explode($end, $tokens[$i]);
+
+		if (count($temp) != 2)
+		{
+			$errors[] = $lang_common['BBCode code problem'];
+			return array(null, array($text));
+		}
+		$inside[] = $temp[0];
+		$outside[] = $temp[1];
+	}
+
+	if ($pun_config['o_indent_num_spaces'] != 8 && $retab)
+	{
+		$spaces = str_repeat(' ', $pun_config['o_indent_num_spaces']);
+		$inside = str_replace("\t", $spaces, $inside);
+	}
+
+	return array($inside, $outside);
+}
+
+
 // DEBUG FUNCTIONS BELOW
 
 //
diff --git a/include/parser.php b/include/parser.php
index a6fceab..5d6f805 100644
--- a/include/parser.php
+++ b/include/parser.php
@@ -18,20 +18,20 @@ if (!defined('PUN'))
 \]                    # closing bracket of outermost opening LIST tag
 (                     # capture contents of LIST tag in group 2
   (?:                 # non capture group for either contents or whole nested LIST
-    [^\[]*+           # unroll the loop! consume everything up to next [ (normal *)
-    (?:               # (See "Mastering Regular Expressions" chapter 6 for details)
-      (?!             # negative lookahead ensures we are NOT on [LIST*] or [/LIST]
-        \[list        # opening LIST tag
-        (?:=[1a*])?+  # with optional attribute
-        \]            # closing bracket of opening LIST tag
-        |             # or...
-        \[/list\]     # a closing LIST tag
-      )               # end negative lookahead assertion (we are not on a LIST tag)
-      \[              # match the [ which is NOT the start of LIST tag (special)
-      [^\[]*+         # consume everything up to next [ (normal *)
-    )*+               # finish up "unrolling the loop" technique (special (normal*))*
+	[^\[]*+           # unroll the loop! consume everything up to next [ (normal *)
+	(?:               # (See "Mastering Regular Expressions" chapter 6 for details)
+	  (?!             # negative lookahead ensures we are NOT on [LIST*] or [/LIST]
+		\[list        # opening LIST tag
+		(?:=[1a*])?+  # with optional attribute
+		\]            # closing bracket of opening LIST tag
+		|             # or...
+		\[/list\]     # a closing LIST tag
+	  )               # end negative lookahead assertion (we are not on a LIST tag)
+	  \[              # match the [ which is NOT the start of LIST tag (special)
+	  [^\[]*+         # consume everything up to next [ (normal *)
+	)*+               # finish up "unrolling the loop" technique (special (normal*))*
   |                   # or...
-    (?R)              # recursively match a whole nested LIST element
+	(?R)              # recursively match a whole nested LIST element
   )*                  # as many times as necessary until deepest nested LIST tag grabbed
 )                     # end capturing contents of LIST tag into group 2
 \[/list\]             # match outermost closing LIST tag
@@ -64,7 +64,7 @@ $smilies = array(
 //
 function preparse_bbcode($text, &$errors, $is_signature = false)
 {
-	global $pun_config, $lang_common, $re_list;
+	global $pun_config, $lang_common, $lang_post, $re_list;
 
 	if ($is_signature)
 	{
@@ -100,15 +100,15 @@ function preparse_bbcode($text, &$errors, $is_signature = false)
 		$text = '';
 
 		$num_tokens = count($outside);
-
 		for ($i = 0; $i < $num_tokens; ++$i)
 		{
 			$text .= $outside[$i];
 			if (isset($inside[$i]))
 				$text .= '[code]'.$inside[$i].'[/code]';
 		}
+
+		unset($inside);
 	}
-	unset($inside);
 
 	$temp_text = false;
 	if (empty($errors))
@@ -121,7 +121,14 @@ function preparse_bbcode($text, &$errors, $is_signature = false)
 	while (($new_text = strip_empty_bbcode($text, $errors)) !== false)
 	{
 		if ($new_text != $text)
+		{
 			$text = $new_text;
+			if ($new_text == '')
+			{
+				$errors[] = $lang_post['Empty after strip'];
+				break;
+			}
+		}
 		else
 			break;
 	}
@@ -143,7 +150,7 @@ function strip_empty_bbcode($text, &$errors)
 	}
 
 	// Remove empty tags
-	while (($new_text = preg_replace('/\[(b|u|s|ins|del|em|i|h|colou?r|quote|img|url|email|list)(?:\=[^\]]*)?\]\s*\[\/\1\]/', '', $text)) !== false)
+	while (($new_text = preg_replace('/\[(b|u|s|ins|del|em|i|h|colou?r|quote|img|url|email|list)(?:\=[^\]]*)?\]\s*\[\/\1\]/', '', $text)) !== NULL)
 	{
 		if ($new_text != $text)
 			$text = $new_text;
@@ -167,7 +174,7 @@ function strip_empty_bbcode($text, &$errors)
 	}
 
 	// Remove empty code tags
-	while (($new_text = preg_replace('/\[(code)\]\s*\[\/\1\]/', '', $text)) !== false)
+	while (($new_text = preg_replace('/\[(code)\]\s*\[\/\1\]/', '', $text)) !== NULL)
 	{
 		if ($new_text != $text)
 			$text = $new_text;
@@ -194,7 +201,7 @@ function preparse_tags($text, &$errors, $is_signature = false)
 	$tags_opened = $tags;
 	// and tags we need to check are closed (the same as above, added it just in case)
 	$tags_closed = $tags;
-	// Tags we can nest and the depth they can be nested to (only quotes)
+	// Tags we can nest and the depth they can be nested to
 	$tags_nested = array('quote' => $pun_config['o_quote_depth'], 'list' => 5, '*' => 5);
 	// Tags to ignore the contents of completely (just code)
 	$tags_ignore = array('code');
@@ -610,41 +617,6 @@ function preparse_list_tag($content, $type = '*', &$errors)
 
 
 //
-// Split text into chunks ($inside contains all text inside $start and $end, and $outside contains all text outside)
-//
-function split_text($text, $start, $end, &$errors, $retab = true)
-{
-	global $pun_config, $lang_common;
-
-	$tokens = explode($start, $text);
-
-	$outside[] = $tokens[0];
-
-	$num_tokens = count($tokens);
-	for ($i = 1; $i < $num_tokens; ++$i)
-	{
-		$temp = explode($end, $tokens[$i]);
-
-		if (count($temp) != 2)
-		{
-			$errors[] = $lang_common['BBCode code problem'];
-			return array(null, array($text));
-		}
-		$inside[] = $temp[0];
-		$outside[] = $temp[1];
-	}
-
-	if ($pun_config['o_indent_num_spaces'] != 8 && $retab)
-	{
-		$spaces = str_repeat(' ', $pun_config['o_indent_num_spaces']);
-		$inside = str_replace("\t", $spaces, $inside);
-	}
-
-	return array($inside, $outside);
-}
-
-
-//
 // Truncate URL if longer than 55 characters (add http:// or ftp:// if missing)
 //
 function handle_url_tag($url, $link = '', $bbcode = false)
@@ -831,7 +803,7 @@ function do_smilies($text)
 	foreach ($smilies as $smiley_text => $smiley_img)
 	{
 		if (strpos($text, $smiley_text) !== false)
-			$text = preg_replace("#(?<=[>\s])".preg_quote($smiley_text, '#')."(?=\W)#m", '<img src="'.$pun_config['o_base_url'].'/img/smilies/'.$smiley_img.'" width="15" height="15" alt="'.substr($smiley_img, 0, strrpos($smiley_img, '.')).'" />', $text);
+			$text = preg_replace("#(?<=[>\s])".preg_quote($smiley_text, '#')."(?=\W)#m", '<img src="'.pun_htmlspecialchars(get_base_url(true).'/img/smilies/'.$smiley_img).'" width="15" height="15" alt="'.substr($smiley_img, 0, strrpos($smiley_img, '.')).'" />', $text);
 	}
 
 	return substr($text, 1, -1);
diff --git a/index.php b/index.php
index 1dccb94..b9ac313 100644
--- a/index.php
+++ b/index.php
@@ -6,7 +6,7 @@
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
 
-define('PUN_ROOT', './');
+define('PUN_ROOT', dirname(__FILE__).'/');
 require PUN_ROOT.'include/common.php';
 
 
diff --git a/lang/English/admin_bans.php b/lang/English/admin_bans.php
index 72f16db..a10a3c0 100644
--- a/lang/English/admin_bans.php
+++ b/lang/English/admin_bans.php
@@ -24,7 +24,7 @@ $lang_admin_bans = array(
 
 'Ban search head'			=>	'Ban search',
 'Ban search subhead'		=>	'Enter search criteria',
-'Ban search info'			=>	'Search for bans in the database. You can enter one or more terms to search for. Wildcards in the form of asterisks (*) are accepted.',
+'Ban search info'			=>	'Search for bans in the database. You can enter one or more terms to search for. Wildcards in the form of asterisks (*) are accepted. To show all bans leave all fields empty.',
 'Date help'					=>	'(yyyy-mm-dd)',
 'Message label'				=>	'Message',
 'Expire after label'		=>	'Expire after',
diff --git a/lang/English/admin_options.php b/lang/English/admin_options.php
index e701917..c852929 100644
--- a/lang/English/admin_options.php
+++ b/lang/English/admin_options.php
@@ -7,6 +7,7 @@ $lang_admin_options = array(
 'Must enter title message'			=>	'You must enter a board title.',
 'Invalid e-mail message'			=>	'The admin email address you entered is invalid.',
 'Invalid webmaster e-mail message'	=>	'The webmaster email address you entered is invalid.',
+'SMTP passwords did not match'		=>	'You need to enter the SMTP password twice exactly the same to change it.',
 'Enter announcement here'			=>	'Enter your announcement here.',
 'Enter rules here'					=>	'Enter your rules here.',
 'Default maintenance message'		=>	'The forums are temporarily down for maintenance. Please try again in a few minutes.',
@@ -171,14 +172,17 @@ $lang_admin_options = array(
 'Admin e-mail help'					=>	'The email address of the board administrator.',
 'Webmaster e-mail label'			=>	'Webmaster email',
 'Webmaster e-mail help'				=>	'This is the address that all emails sent by the board will be addressed from.',
-'Subscriptions label'				=>	'Subscriptions',
-'Subscriptions help'				=>	'Enable users to subscribe to topics (receive email when someone replies).',
+'Forum subscriptions label'			=>	'Forum subscriptions',
+'Forum subscriptions help'			=>	'Enable users to subscribe to forums (receive email when someone creates a new topic).',
+'Topic subscriptions label'			=>	'Topic subscriptions',
+'Topic subscriptions help'			=>	'Enable users to subscribe to topics (receive email when someone replies).',
 'SMTP address label'				=>	'SMTP server address',
 'SMTP address help'					=>	'The address of an external SMTP server to send emails with. You can specify a custom port number if the SMTP server doesn\'t run on the default port 25 (example: mail.myhost.com:3580). Leave blank to use the local mail program.',
 'SMTP username label'				=>	'SMTP username',
 'SMTP username help'				=>	'Username for SMTP server. Only enter a username if it is required by the SMTP server (most servers <strong>do not</strong> require authentication).',
 'SMTP password label'				=>	'SMTP password',
-'SMTP password help'				=>	'Password for SMTP server. Only enter a password if it is required by the SMTP server (most servers <strong>do not</strong> require authentication).',
+'SMTP change password help'			=>	'Check this if you want to change or delete the currently stored password.',
+'SMTP password help'				=>	'Password for SMTP server. Only enter a password if it is required by the SMTP server (most servers <strong>do not</strong> require authentication). Please enter your password twice to confirm.',
 'SMTP SSL label'					=>	'Encrypt SMTP using SSL',
 'SMTP SSL help'						=>	'Encrypts the connection to the SMTP server using SSL. Should only be used if your SMTP server requires it and your version of PHP supports SSL.',
 
diff --git a/lang/English/admin_reports.php b/lang/English/admin_reports.php
index 152bab3..ccb9d13 100644
--- a/lang/English/admin_reports.php
+++ b/lang/English/admin_reports.php
@@ -17,4 +17,4 @@ $lang_admin_reports = array(
 'Zapped subhead'			=>	'Zapped %s by %s',
 'No zapped reports'			=>	'There are no zapped reports.',
 
-);
\ No newline at end of file
+);
diff --git a/lang/English/common.php b/lang/English/common.php
index 85c276f..7334b24 100644
--- a/lang/English/common.php
+++ b/lang/English/common.php
@@ -101,18 +101,18 @@ $lang_common = array(
 'Logged in as'						=>	'Logged in as',
 'Admin'								=>	'Administration',
 'Last visit'						=>	'Last visit: %s',
-'Show new posts'					=>	'Show new posts since last visit',
+'New posts'							=>	'New posts',
+'Active topics'						=>	'Active topics',
+'Unanswered topics'					=>	'Unanswered topics',
+'Show new posts'					=>	'Find topics with new posts since your last visit.',
+'Show active topics'				=>	'Find topics which contain recent posts.',
+'Show unanswered topics'			=>	'Find topics which have not been replied to.',
 'Mark all as read'					=>	'Mark all topics as read',
 'Mark forum read'					=>	'Mark this forum as read',
 'Title separator'					=>	' / ',
 
 // Stuff for the page footer
 'Board footer'						=>	'Board footer',
-'Search links'						=>	'Search links',
-'Show recent posts'					=>	'Show recent posts',
-'Show unanswered posts'				=>	'Show unanswered posts',
-'Show your posts'					=>	'Show your posts',
-'Show subscriptions'				=>	'Show your subscribed topics',
 'Jump to'							=>	'Jump to',
 'Go'								=>	' Go ', // Submit button in forum jump
 'Moderate topic'					=>	'Moderate topic',
diff --git a/lang/English/delete.php b/lang/English/delete.php
index 61599d2..b97478e 100644
--- a/lang/English/delete.php
+++ b/lang/English/delete.php
@@ -13,4 +13,4 @@ $lang_delete = array(
 'Post del redirect'		=>	'Post deleted. Redirecting …',
 'Topic del redirect'	=>	'Topic deleted. Redirecting …'
 
-);
\ No newline at end of file
+);
diff --git a/lang/English/forum.php b/lang/English/forum.php
index 42111da..a5973b8 100644
--- a/lang/English/forum.php
+++ b/lang/English/forum.php
@@ -9,6 +9,9 @@ $lang_forum = array(
 'Sticky'		=>	'Sticky:',
 'Closed'		=>	'Closed:',
 'Empty forum'	=>	'Forum is empty.',
-'Mod controls'	=>	'Moderator controls'
+'Mod controls'	=>	'Moderator controls',
+'Is subscribed'	=>	'You are currently subscribed to this forum',
+'Unsubscribe'	=>	'Unsubscribe',
+'Subscribe'		=>	'Subscribe to this forum'
 
 );
diff --git a/lang/English/help.php b/lang/English/help.php
index 47b5e0a..9289566 100644
--- a/lang/English/help.php
+++ b/lang/English/help.php
@@ -27,6 +27,7 @@ $lang_help = array(
 'Links info'			=>	'You can create links to other documents or to email addresses using the following tags:',
 'My email address'		=>	'My email address',
 'Images info'			=>	'If you want to display an image you can use the img tag. The text appearing after the "=" sign in the opening tag is used for the alt attribute and should be included whenever possible.',
+'FluxBB bbcode test'	=>	'FluxBB bbcode test',
 
 'Quotes'				=>	'Quotes',
 'Quotes info'			=>	'If you want to quote someone, you should use the quote tag.',
diff --git a/lang/English/mail_templates/new_topic.tpl b/lang/English/mail_templates/new_topic.tpl
new file mode 100644
index 0000000..7e2308c
--- /dev/null
+++ b/lang/English/mail_templates/new_topic.tpl
@@ -0,0 +1,11 @@
+Subject: New topic in forum: <forum_name>
+
+<poster> has posted a new topic <topic_subject> in the forum <forum_name>, to which you are subscribed.
+
+The topic is located at <topic_url>
+
+You can unsubscribe by going to <unsubscribe_url>
+
+--
+<board_mailer>
+(Do not reply to this message)
diff --git a/lang/English/mail_templates/new_reply_full.tpl b/lang/English/mail_templates/new_topic_full.tpl
similarity index 51%
copy from lang/English/mail_templates/new_reply_full.tpl
copy to lang/English/mail_templates/new_topic_full.tpl
index 7231145..3f5b408 100644
--- a/lang/English/mail_templates/new_reply_full.tpl
+++ b/lang/English/mail_templates/new_topic_full.tpl
@@ -1,6 +1,6 @@
-Subject: Reply to topic: <topic_subject>
+Subject: New topic in forum: <forum_name>
 
-<replier> has replied to the topic <topic_subject> to which you are subscribed. There may be more new replies, but this is the only notification you will receive until you visit the board again.
+<poster> has posted a new topic <topic_subject> in the forum <forum_name>, to which you are subscribed.
 
 The message reads as follows:
 -----------------------------------------------------------------------
@@ -9,7 +9,7 @@ The message reads as follows:
 
 -----------------------------------------------------------------------
 
-The post is located at <post_url>
+The topic is located at <topic_url>
 
 You can unsubscribe by going to <unsubscribe_url>
 
diff --git a/lang/English/misc.php b/lang/English/misc.php
index c6b9503..71ff526 100644
--- a/lang/English/misc.php
+++ b/lang/English/misc.php
@@ -29,9 +29,11 @@ $lang_misc = array(
 'Reason desc'					=>	'Please enter a short reason why you are reporting this post',
 
 // Subscriptions
-'Already subscribed'			=>	'You are already subscribed to this topic.',
+'Already subscribed topic'		=>	'You are already subscribed to this topic.',
+'Already subscribed forum'		=>	'You are already subscribed to this forum.',
 'Subscribe redirect'			=>	'Your subscription has been added. Redirecting …',
-'Not subscribed'				=>	'You are not subscribed to this topic.',
+'Not subscribed topic'			=>	'You are not subscribed to this topic.',
+'Not subscribed forum'			=>	'You are not subscribed to this forum.',
 'Unsubscribe redirect'			=>	'Your subscription has been removed. Redirecting …',
 
 // General forum and topic moderation
diff --git a/lang/English/post.php b/lang/English/post.php
index c02eb6b..0f16aeb 100644
--- a/lang/English/post.php
+++ b/lang/English/post.php
@@ -10,6 +10,7 @@ $lang_post = array(
 'Too long message'	=>	'Posts cannot be longer that %s bytes.',
 'All caps subject'	=>	'Subjects cannot contain only capital letters.',
 'All caps message'	=>	'Posts cannot contain only capital letters.',
+'Empty after strip'	=>	'It seems your post consisted of empty BBCodes only. It is possible that this happened because e.g. the innermost quote was discarded because of the maximum quote depth level.',
 
 // Posting
 'Post errors'		=>	'Post errors',
diff --git a/lang/English/profile.php b/lang/English/profile.php
index 69c993c..2a4e276 100644
--- a/lang/English/profile.php
+++ b/lang/English/profile.php
@@ -75,6 +75,8 @@ $lang_profile = array(
 'Registered info'				=>	'Registered: %s',
 'Last post info'				=>	'Last post: %s',
 'Show posts'					=>	'Show all posts',
+'Show topics'					=>	'Show all topics',
+'Show subscriptions'			=>	'Show all subscriptions',
 'Realname'						=>	'Real name',
 'Location'						=>	'Location',
 'Website'						=>	'Website',
diff --git a/lang/English/search.php b/lang/English/search.php
index d6a5081..a1f28c4 100644
--- a/lang/English/search.php
+++ b/lang/English/search.php
@@ -39,7 +39,8 @@ $lang_search = array(
 'No terms'					=>	'You have to enter at least one keyword and/or an author to search for.',
 'No hits'					=>	'Your search returned no hits.',
 'No user posts'				=>	'There are no posts by this user in this forum.',
-'No subscriptions'			=>	'You are currently not subscribed to any topics.',
+'No user topics'			=>	'There are no topics by this user in this forum.',
+'No subscriptions'			=>	'This user is currently not subscribed to any topics.',
 'No new posts'				=>	'There are no topics with new posts since your last visit.',
 'No recent posts'			=>	'No new posts have been made within the last 24 hours.',
 'No unanswered'				=>	'There are no unanswered posts in this forum.',
diff --git a/lang/English/update.php b/lang/English/update.php
new file mode 100644
index 0000000..d4e64b6
--- /dev/null
+++ b/lang/English/update.php
@@ -0,0 +1,77 @@
+<?php
+
+// Language definitions used in db_update.php
+
+$lang_update = array(
+
+'Update'						=>	'Update FluxBB',
+'Update message'				=>	'Your FluxBB database is out-of-date and must be upgraded in order to continue. If you are the board administrator, please follow the instructions below to complete the upgrade.',
+'Note'							=>	'Note:',
+'Members message'				=>	'This process is for board administators only. If you are a member there is nothing to worry about - the forums will be back shortly!',
+'Administrator only'			=>	'This step is for the board administrator only!',
+'Database password info'		=>	'To perform the database update please enter the database password with which FluxBB was installed. If you cannot remember, this is stored in your \'config.php\' file.',
+'Database password note'		=>	'If you are running SQLite (and hence have no database password) please use the database file name instead. This must exactly match the database file name given in your configuration file.',
+'Database password'				=>	'Database password',
+'Next'							=>	'Next',
+
+'You are running error'			=>	'You are running %1$s version %2$s. FluxBB %3$s requires at least %1$s %4$s to run properly. You must upgrade your %1$s installation before you can continue.',
+'Version mismatch error'		=>	'Version mismatch. The database \'%s\' doesn\'t seem to be running a FluxBB database schema supported by this update script.',
+'Invalid file error'			=>	'Invalid database file name. When using SQLite the database file name must be entered exactly as it appears in your \'%s\'',
+'Invalid password error'		=>	'Invalid database password. To upgrade FluxBB you must enter your database password exactly as it appears in your \'%s\'',
+'No password error'				=>	'No database password provided',
+'Script runs error'				=>	'It appears the update script is already being ran by someone else. If this is not the case, please manually delete the file \'%s\' and try again',
+'No update error'				=>	'Your forum is already as up-to-date as this script can make it',
+
+'Intro 1'						=>	'This script will update your forum database. The update procedure might take anything from a second to hours depending on the speed of the server and the size of the forum database. Don\'t forget to make a backup of the database before continuing.',
+'Intro 2'						=>	'Did you read the update instructions in the documentation? If not, start there.',
+'No charset conversion'			=>	'<strong>IMPORTANT!</strong> FluxBB has detected that this PHP environment does not have support for the encoding mechanisms required to do UTF-8 conversion from character sets other than ISO-8859-1. What this means is that if the current character set is not ISO-8859-1, FluxBB won\'t be able to convert your forum database to UTF-8 and you will have to do it manually. Instructions for doing manual charset conversion can be found in the update instructions.',
+'Enable conversion'				=>	'<strong>Enable conversion:</strong> When enabled this update script will, after it has made the required structural changes to the database, convert all text in the database from the current character set to UTF-8. This conversion is required if you\'re upgrading from version 1.2.',
+'Current character set'			=>	'<strong>Current character set:</strong> If the primary language in your forum is English, you can leave this at the default value. However, if your forum is non-English, you should enter the character set of the primary language pack used in the forum. <em>Getting this wrong can corrupt your database so don\'t just guess!</em> Note: This is required even if the old database is UTF-8.',
+'Charset conversion'			=>	'Charset conversion',
+'Enable conversion label'		=>	'<strong>Enable conversion</strong> (perform database charset conversion).',
+'Current character set label'	=>	'Current character set',
+'Current character set info'	=>	'Accept default for English forums otherwise the character set of the primary language pack.',
+'Start update'					=>	'Start update',
+'Error converting users'		=>	'Error converting users',
+'Error info 1'					=>	'There was an error converting some users. This can occur when converting from FluxBB v1.2 if multiple users have registered with very similar usernames, for example "bob" and "böb".',
+'Error info 2'					=>	'Below is a list of users who failed to convert. Please choose a new username for each user. Users who are renamed will automatically be sent an email alerting them of the change.',
+'New username'					=>	'New username',
+'Required'						=>	'(Required)',
+'Correct errors'				=>	'The following errors need to be corrected:',
+'Rename users'					=>	'Rename users',
+'Successfully updated'			=>	'Your forum database was successfully updated. You may now %s.',
+'go to index'					=>	'go to the forum index',
+
+'Unable to lock error'			=>	'Unable to write update lock. Please make sure PHP has write access to the directory \'%s\' and no-one else is currently running the update script.',
+
+'Converting'					=>	'Converting %s …',
+'Converting item'				=>	'Converting %1$s %2$s …',
+'Preparsing item'				=>	'Preparsing %1$s %2$s …',
+'Rebuilding index item'			=>	'Rebuilding index for %1$s %2$s',
+
+'ban'							=>	'ban',
+'categories'					=>	'categories',
+'censor words'					=>	'censor words',
+'configuration'					=>	'configuration',
+'forums'						=>	'forums',
+'groups'						=>	'groups',
+'post'							=>	'post',
+'ranks'							=>	'ranks',
+'report'						=>	'report',
+'topic'							=>	'topic',
+'user'							=>	'user',
+'signature'						=>	'signature',
+
+'Username too short error'		=>	'Usernames must be at least 2 characters long. Please choose another (longer) username.',
+'Username too long error'		=>	'Usernames must not be more than 25 characters long. Please choose another (shorter) username.',
+'Username Guest reserved error'	=>	'The username guest is reserved. Please choose another username.',
+'Username IP format error'		=>	'Usernames may not be in the form of an IP address. Please choose another username.',
+'Username bad characters error'	=>	'Usernames may not contain all the characters \', " and [ or ] at once. Please choose another username.',
+'Username BBCode error'			=>	'Usernames may not contain any of the text formatting tags (BBCode) that the forum uses. Please choose another username.',
+'Username duplicate error'		=>	'Someone is already registered with the username %s. The username you entered is too similar. The username must differ from that by at least one alphanumerical character (a-z or 0-9). Please choose a different username.',
+
+'JavaScript disabled'			=>	'JavaScript seems to be disabled. %s.',
+'Click here to continue'		=>	'Click here to continue',
+'Required field'				=>	'is a required field in this form.'
+
+);
diff --git a/login.php b/login.php
index ab2826c..b0c5a50 100644
--- a/login.php
+++ b/login.php
@@ -9,7 +9,7 @@
 if (isset($_GET['action']))
 	define('PUN_QUIET_VISIT', 1);
 
-define('PUN_ROOT', './');
+define('PUN_ROOT', dirname(__FILE__).'/');
 require PUN_ROOT.'include/common.php';
 
 
@@ -152,7 +152,7 @@ else if ($action == 'forget' || $action == 'forget_2')
 				$mail_message = trim(substr($mail_tpl, $first_crlf));
 
 				// Do the generic replacements first (they apply to all emails sent out here)
-				$mail_message = str_replace('<base_url>', $pun_config['o_base_url'].'/', $mail_message);
+				$mail_message = str_replace('<base_url>', get_base_url().'/', $mail_message);
 				$mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'].' '.$lang_common['Mailer'], $mail_message);
 
 				// Loop through users we found
@@ -169,7 +169,7 @@ else if ($action == 'forget' || $action == 'forget_2')
 
 					// Do the user specific replacements to the template
 					$cur_mail_message = str_replace('<username>', $cur_hit['username'], $mail_message);
-					$cur_mail_message = str_replace('<activation_url>', $pun_config['o_base_url'].'/profile.php?id='.$cur_hit['id'].'&action=change_pass&key='.$new_password_key, $cur_mail_message);
+					$cur_mail_message = str_replace('<activation_url>', get_base_url().'/profile.php?id='.$cur_hit['id'].'&action=change_pass&key='.$new_password_key, $cur_mail_message);
 					$cur_mail_message = str_replace('<new_password>', $new_password, $cur_mail_message);
 
 					pun_mail($email, $mail_subject, $cur_mail_message);
@@ -241,7 +241,24 @@ if (!$pun_user['is_guest'])
 	header('Location: index.php');
 
 // Try to determine if the data in HTTP_REFERER is valid (if not, we redirect to index.php after login)
-$redirect_url = (isset($_SERVER['HTTP_REFERER']) && preg_match('#^'.preg_quote($pun_config['o_base_url']).'/(.*?)\.php#i', $_SERVER['HTTP_REFERER'])) ? htmlspecialchars($_SERVER['HTTP_REFERER']) : $pun_config['o_base_url'].'/index.php';
+if (!empty($_SERVER['HTTP_REFERER']))
+{
+	$referrer = parse_url($_SERVER['HTTP_REFERER']);
+	// Remove www subdomain if it exists
+	if (strpos($referrer['host'], 'www.') === 0)
+		$referrer['host'] = substr($referrer['host'], 4);
+
+	$valid = parse_url(get_base_url());
+	// Remove www subdomain if it exists
+	if (strpos($valid['host'], 'www.') === 0)
+		$valid['host'] = substr($valid['host'], 4);
+
+	if ($referrer['host'] == $valid['host'] && preg_match('#^'.preg_quote($valid['path']).'/(.*?)\.php#i', $referrer['path']))
+		$redirect_url = $_SERVER['HTTP_REFERER'];
+}
+
+if (!isset($redirect_url))
+	$redirect_url = 'index.php';
 
 $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Login']);
 $required_fields = array('req_username' => $lang_common['Username'], 'req_password' => $lang_common['Password']);
@@ -259,7 +276,7 @@ require PUN_ROOT.'header.php';
 					<legend><?php echo $lang_login['Login legend'] ?></legend>
 					<div class="infldset">
 						<input type="hidden" name="form_sent" value="1" />
-						<input type="hidden" name="redirect_url" value="<?php echo $redirect_url ?>" />
+						<input type="hidden" name="redirect_url" value="<?php echo pun_htmlspecialchars($redirect_url) ?>" />
 						<label class="conl required"><strong><?php echo $lang_common['Username'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><input type="text" name="req_username" size="25" maxlength="25" tabindex="1" /><br /></label>
 						<label class="conl required"><strong><?php echo $lang_common['Password'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><input type="password" name="req_password" size="25" tabindex="2" /><br /></label>
 
diff --git a/misc.php b/misc.php
index 7b31df2..211bc05 100644
--- a/misc.php
+++ b/misc.php
@@ -9,7 +9,7 @@
 if (isset($_GET['action']))
 	define('PUN_QUIET_VISIT', 1);
 
-define('PUN_ROOT', './');
+define('PUN_ROOT', dirname(__FILE__).'/');
 require PUN_ROOT.'include/common.php';
 
 
@@ -138,7 +138,24 @@ else if (isset($_GET['email']))
 
 
 	// Try to determine if the data in HTTP_REFERER is valid (if not, we redirect to the users profile after the email is sent)
-	$redirect_url = (isset($_SERVER['HTTP_REFERER']) && preg_match('#^'.preg_quote($pun_config['o_base_url']).'/(.*?)\.php#i', $_SERVER['HTTP_REFERER'])) ? htmlspecialchars($_SERVER['HTTP_REFERER']) : 'index.php';
+	if (!empty($_SERVER['HTTP_REFERER']))
+	{
+		$referrer = parse_url($_SERVER['HTTP_REFERER']);
+		// Remove www subdomain if it exists
+		if (strpos($referrer['host'], 'www.') === 0)
+			$referrer['host'] = substr($referrer['host'], 4);
+
+		$valid = parse_url(get_base_url());
+		// Remove www subdomain if it exists
+		if (strpos($valid['host'], 'www.') === 0)
+			$valid['host'] = substr($valid['host'], 4);
+
+		if ($referrer['host'] == $valid['host'] && preg_match('#^'.preg_quote($valid['path']).'/(.*?)\.php#i', $referrer['path']))
+			$redirect_url = $_SERVER['HTTP_REFERER'];
+	}
+
+	if (!isset($redirect_url))
+		$redirect_url = 'profile.php?id='.$recipient_id;
 
 	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_misc['Send email to'].' '.pun_htmlspecialchars($recipient));
 	$required_fields = array('req_subject' => $lang_misc['Email subject'], 'req_message' => $lang_misc['Email message']);
@@ -156,7 +173,7 @@ else if (isset($_GET['email']))
 					<legend><?php echo $lang_misc['Write email'] ?></legend>
 					<div class="infldset txtarea">
 						<input type="hidden" name="form_sent" value="1" />
-						<input type="hidden" name="redirect_url" value="<?php echo $redirect_url ?>" />
+						<input type="hidden" name="redirect_url" value="<?php echo pun_htmlspecialchars($redirect_url) ?>" />
 						<label class="required"><strong><?php echo $lang_misc['Email subject'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br />
 						<input class="longinput" type="text" name="req_subject" size="75" maxlength="70" tabindex="1" /><br /></label>
 						<label class="required"><strong><?php echo $lang_misc['Email message'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br />
@@ -221,7 +238,7 @@ else if (isset($_GET['report']))
 			if ($pun_config['o_mailing_list'] != '')
 			{
 				$mail_subject = sprintf($lang_common['Report notification'], $forum_id, $subject);
-				$mail_message = sprintf($lang_common['Report message 1'], $pun_user['username'], $pun_config['o_base_url'].'/viewtopic.php?pid='.$post_id.'#p'.$post_id)."\n";
+				$mail_message = sprintf($lang_common['Report message 1'], $pun_user['username'], get_base_url().'/viewtopic.php?pid='.$post_id.'#p'.$post_id)."\n";
 				$mail_message .= sprintf($lang_common['Report message 2'], $reason)."\n";
 				$mail_message .= "\n".'--'."\n".$lang_common['Email signature'];
 
@@ -287,46 +304,93 @@ else if (isset($_GET['report']))
 }
 
 
-else if (isset($_GET['subscribe']))
+else if ($action == 'subscribe')
 {
-	if ($pun_user['is_guest'] || $pun_config['o_subscriptions'] != '1')
+	if ($pun_user['is_guest'])
 		message($lang_common['No permission']);
 
-	$topic_id = intval($_GET['subscribe']);
-	if ($topic_id < 1)
+	$topic_id = isset($_GET['tid']) ? intval($_GET['tid']) : 0;
+	$forum_id = isset($_GET['fid']) ? intval($_GET['fid']) : 0;
+	if ($topic_id < 1 && $forum_id < 1)
 		message($lang_common['Bad request']);
 
-	// Make sure the user can view the topic
-	$result = $db->query('SELECT 1 FROM '.$db->prefix.'topics AS t LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND t.id='.$topic_id.' AND t.moved_to IS NULL') or error('Unable to fetch topic info', __FILE__, __LINE__, $db->error());
-	if (!$db->num_rows($result))
-		message($lang_common['Bad request']);
+	if ($topic_id)
+	{
+		if ($pun_config['o_topic_subscriptions'] != '1')
+			message($lang_common['No permission']);
 
-	$result = $db->query('SELECT 1 FROM '.$db->prefix.'subscriptions WHERE user_id='.$pun_user['id'].' AND topic_id='.$topic_id) or error('Unable to fetch subscription info', __FILE__, __LINE__, $db->error());
-	if ($db->num_rows($result))
-		message($lang_misc['Already subscribed']);
+		// Make sure the user can view the topic
+		$result = $db->query('SELECT 1 FROM '.$db->prefix.'topics AS t LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND t.id='.$topic_id.' AND t.moved_to IS NULL') or error('Unable to fetch topic info', __FILE__, __LINE__, $db->error());
+		if (!$db->num_rows($result))
+			message($lang_common['Bad request']);
 
-	$db->query('INSERT INTO '.$db->prefix.'subscriptions (user_id, topic_id) VALUES('.$pun_user['id'].' ,'.$topic_id.')') or error('Unable to add subscription', __FILE__, __LINE__, $db->error());
+		$result = $db->query('SELECT 1 FROM '.$db->prefix.'topic_subscriptions WHERE user_id='.$pun_user['id'].' AND topic_id='.$topic_id) or error('Unable to fetch subscription info', __FILE__, __LINE__, $db->error());
+		if ($db->num_rows($result))
+			message($lang_misc['Already subscribed topic']);
+
+		$db->query('INSERT INTO '.$db->prefix.'topic_subscriptions (user_id, topic_id) VALUES('.$pun_user['id'].' ,'.$topic_id.')') or error('Unable to add subscription', __FILE__, __LINE__, $db->error());
+
+		redirect('viewtopic.php?id='.$topic_id, $lang_misc['Subscribe redirect']);
+	}
+
+	if ($forum_id)
+	{
+		if ($pun_config['o_forum_subscriptions'] != '1')
+			message($lang_common['No permission']);
+
+		// Make sure the user can view the forum
+		$result = $db->query('SELECT 1 FROM '.$db->prefix.'forums AS f LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND f.id='.$forum_id) or error('Unable to fetch forum info', __FILE__, __LINE__, $db->error());
+		if (!$db->num_rows($result))
+			message($lang_common['Bad request']);
 
-	redirect('viewtopic.php?id='.$topic_id, $lang_misc['Subscribe redirect']);
+		$result = $db->query('SELECT 1 FROM '.$db->prefix.'forum_subscriptions WHERE user_id='.$pun_user['id'].' AND forum_id='.$forum_id) or error('Unable to fetch subscription info', __FILE__, __LINE__, $db->error());
+		if ($db->num_rows($result))
+			message($lang_misc['Already subscribed forum']);
+
+		$db->query('INSERT INTO '.$db->prefix.'forum_subscriptions (user_id, forum_id) VALUES('.$pun_user['id'].' ,'.$forum_id.')') or error('Unable to add subscription', __FILE__, __LINE__, $db->error());
+
+		redirect('viewforum.php?id='.$forum_id, $lang_misc['Subscribe redirect']);
+	}
 }
 
 
-else if (isset($_GET['unsubscribe']))
+else if ($action == 'unsubscribe')
 {
-	if ($pun_user['is_guest'] || $pun_config['o_subscriptions'] != '1')
+	if ($pun_user['is_guest'])
 		message($lang_common['No permission']);
 
-	$topic_id = intval($_GET['unsubscribe']);
-	if ($topic_id < 1)
+	$topic_id = isset($_GET['tid']) ? intval($_GET['tid']) : 0;
+	$forum_id = isset($_GET['fid']) ? intval($_GET['fid']) : 0;
+	if ($topic_id < 1 && $forum_id < 1)
 		message($lang_common['Bad request']);
 
-	$result = $db->query('SELECT 1 FROM '.$db->prefix.'subscriptions WHERE user_id='.$pun_user['id'].' AND topic_id='.$topic_id) or error('Unable to fetch subscription info', __FILE__, __LINE__, $db->error());
-	if (!$db->num_rows($result))
-		message($lang_misc['Not subscribed']);
+	if ($topic_id)
+	{
+		if ($pun_config['o_topic_subscriptions'] != '1')
+			message($lang_common['No permission']);
+
+		$result = $db->query('SELECT 1 FROM '.$db->prefix.'topic_subscriptions WHERE user_id='.$pun_user['id'].' AND topic_id='.$topic_id) or error('Unable to fetch subscription info', __FILE__, __LINE__, $db->error());
+		if (!$db->num_rows($result))
+			message($lang_misc['Not subscribed topic']);
 
-	$db->query('DELETE FROM '.$db->prefix.'subscriptions WHERE user_id='.$pun_user['id'].' AND topic_id='.$topic_id) or error('Unable to remove subscription', __FILE__, __LINE__, $db->error());
+		$db->query('DELETE FROM '.$db->prefix.'topic_subscriptions WHERE user_id='.$pun_user['id'].' AND topic_id='.$topic_id) or error('Unable to remove subscription', __FILE__, __LINE__, $db->error());
 
-	redirect('viewtopic.php?id='.$topic_id, $lang_misc['Unsubscribe redirect']);
+		redirect('viewtopic.php?id='.$topic_id, $lang_misc['Unsubscribe redirect']);
+	}
+
+	if ($forum_id)
+	{
+		if ($pun_config['o_forum_subscriptions'] != '1')
+			message($lang_common['No permission']);
+
+		$result = $db->query('SELECT 1 FROM '.$db->prefix.'forum_subscriptions WHERE user_id='.$pun_user['id'].' AND forum_id='.$forum_id) or error('Unable to fetch subscription info', __FILE__, __LINE__, $db->error());
+		if (!$db->num_rows($result))
+			message($lang_misc['Not subscribed forum']);
+
+		$db->query('DELETE FROM '.$db->prefix.'forum_subscriptions WHERE user_id='.$pun_user['id'].' AND forum_id='.$forum_id) or error('Unable to remove subscription', __FILE__, __LINE__, $db->error());
+
+		redirect('viewforum.php?id='.$forum_id, $lang_misc['Unsubscribe redirect']);
+	}
 }
 
 
diff --git a/moderate.php b/moderate.php
index 52fda43..14b289d 100644
--- a/moderate.php
+++ b/moderate.php
@@ -6,7 +6,7 @@
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
 
-define('PUN_ROOT', './');
+define('PUN_ROOT', dirname(__FILE__).'/');
 require PUN_ROOT.'include/common.php';
 
 
@@ -69,7 +69,7 @@ if (isset($_GET['tid']))
 		message($lang_common['Bad request']);
 
 	// Fetch some info about the topic
-	$result = $db->query('SELECT t.subject, t.num_replies, t.first_post_id, f.id AS forum_id, forum_name FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id LEFT JOIN '.$db->prefix.'subscriptions AS s ON (t.id=s.topic_id AND s.user_id='.$pun_user['id'].') LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND f.id='.$fid.' AND t.id='.$tid.' AND t.moved_to IS NULL') or error('Unable to fetch topic info', __FILE__, __LINE__, $db->error());
+	$result = $db->query('SELECT t.subject, t.num_replies, t.first_post_id, f.id AS forum_id, forum_name FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND f.id='.$fid.' AND t.id='.$tid.' AND t.moved_to IS NULL') or error('Unable to fetch topic info', __FILE__, __LINE__, $db->error());
 	if (!$db->num_rows($result))
 		message($lang_common['Bad request']);
 
@@ -361,6 +361,7 @@ if (isset($_GET['tid']))
 		<div class="pagepost">
 			<p class="pagelink conl"><?php echo $paging_links ?></p>
 			<p class="conr modbuttons"><input type="submit" name="split_posts" value="<?php echo $lang_misc['Split'] ?>"<?php echo $button_status ?> /> <input type="submit" name="delete_posts" value="<?php echo $lang_misc['Delete'] ?>"<?php echo $button_status ?> /></p>
+			<div class="clearer"></div>
 		</div>
 	</div>
 </div>
@@ -531,7 +532,7 @@ else if (isset($_POST['merge_topics']) || isset($_POST['merge_topics_comply']))
 		$db->query('UPDATE '.$db->prefix.'posts SET topic_id='.$merge_to_tid.' WHERE topic_id IN('.implode(',', $topics).')') or error('Unable to merge the posts into the topic', __FILE__, __LINE__, $db->error());
 
 		// Delete any subscriptions
-		$db->query('DELETE FROM '.$db->prefix.'subscriptions WHERE topic_id IN('.implode(',', $topics).') AND topic_id != '.$merge_to_tid) or error('Unable to delete subscriptions', __FILE__, __LINE__, $db->error());
+		$db->query('DELETE FROM '.$db->prefix.'topic_subscriptions WHERE topic_id IN('.implode(',', $topics).') AND topic_id != '.$merge_to_tid) or error('Unable to delete subscriptions', __FILE__, __LINE__, $db->error());
 
 		// Without redirection the old topics are removed
 		if (!isset($_POST['with_redirect']))
@@ -612,7 +613,7 @@ else if (isset($_POST['delete_topics']) || isset($_POST['delete_topics_comply'])
 		$db->query('DELETE FROM '.$db->prefix.'topics WHERE id IN('.$topics.') OR moved_to IN('.$topics.')') or error('Unable to delete topic', __FILE__, __LINE__, $db->error());
 
 		// Delete any subscriptions
-		$db->query('DELETE FROM '.$db->prefix.'subscriptions WHERE topic_id IN('.$topics.')') or error('Unable to delete subscriptions', __FILE__, __LINE__, $db->error());
+		$db->query('DELETE FROM '.$db->prefix.'topic_subscriptions WHERE topic_id IN('.$topics.')') or error('Unable to delete subscriptions', __FILE__, __LINE__, $db->error());
 
 		// Create a list of the post IDs in this topic and then strip the search index
 		$result = $db->query('SELECT id FROM '.$db->prefix.'posts WHERE topic_id IN('.$topics.')') or error('Unable to fetch posts', __FILE__, __LINE__, $db->error());
@@ -914,6 +915,7 @@ else
 		<div class="pagepost">
 			<p class="pagelink conl"><?php echo $paging_links ?></p>
 			<p class="conr modbuttons"><input type="submit" name="move_topics" value="<?php echo $lang_misc['Move'] ?>"<?php echo $button_status ?> /> <input type="submit" name="delete_topics" value="<?php echo $lang_misc['Delete'] ?>"<?php echo $button_status ?> /> <input type="submit" name="merge_topics" value="<?php echo $lang_misc['Merge'] ?>"<?php echo $button_status ?> /> <input type="submit" name="open" value="<?php echo $lang_misc['Open'] ?>"<?php echo $button_status ?> /> <input type="submit" name="close" value="<?php echo $lang_misc['Close'] ?>"<?php echo $button_status ?> /></p>
+			<div class="clearer"></div>
 		</div>
 	</div>
 </div>
diff --git a/post.php b/post.php
index ecf7e61..ef31a51 100644
--- a/post.php
+++ b/post.php
@@ -6,7 +6,7 @@
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
 
-define('PUN_ROOT', './');
+define('PUN_ROOT', dirname(__FILE__).'/');
 require PUN_ROOT.'include/common.php';
 
 
@@ -21,7 +21,7 @@ if ($tid < 1 && $fid < 1 || $tid > 0 && $fid > 0)
 
 // Fetch some info about the topic and/or the forum
 if ($tid)
-	$result = $db->query('SELECT f.id, f.forum_name, f.moderators, f.redirect_url, fp.post_replies, fp.post_topics, t.subject, t.closed, s.user_id AS is_subscribed FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') LEFT JOIN '.$db->prefix.'subscriptions AS s ON (t.id=s.topic_id AND s.user_id='.$pun_user['id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND t.id='.$tid) or error('Unable to fetch forum info', __FILE__, __LINE__, $db->error());
+	$result = $db->query('SELECT f.id, f.forum_name, f.moderators, f.redirect_url, fp.post_replies, fp.post_topics, t.subject, t.closed, s.user_id AS is_subscribed FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') LEFT JOIN '.$db->prefix.'topic_subscriptions AS s ON (t.id=s.topic_id AND s.user_id='.$pun_user['id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND t.id='.$tid) or error('Unable to fetch forum info', __FILE__, __LINE__, $db->error());
 else
 	$result = $db->query('SELECT f.id, f.forum_name, f.moderators, f.redirect_url, fp.post_replies, fp.post_topics FROM '.$db->prefix.'forums AS f LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND f.id='.$fid) or error('Unable to fetch forum info', __FILE__, __LINE__, $db->error());
 
@@ -120,7 +120,7 @@ if (isset($_POST['form_sent']))
 	}
 
 	// Clean up message from POST
-	$message = pun_linebreaks(pun_trim($_POST['req_message']));
+	$orig_message = $message = pun_linebreaks(pun_trim($_POST['req_message']));
 
 	// Here we use strlen() not pun_strlen() as we want to limit the post to PUN_MAX_POSTSIZE bytes, not characters
 	if (strlen($message) > PUN_MAX_POSTSIZE)
@@ -135,11 +135,12 @@ if (isset($_POST['form_sent']))
 		$message = preparse_bbcode($message, $errors);
 	}
 
-	if ($message == '')
+	if (empty($errors) && $message == '')
 		$errors[] = $lang_post['No message'];
 
 	$hide_smilies = isset($_POST['hide_smilies']) ? '1' : '0';
 	$subscribe = isset($_POST['subscribe']) ? '1' : '0';
+	$stick_topic = isset($_POST['stick_topic']) && $is_admmod ? '1' : '0';
 
 	$now = time();
 
@@ -160,12 +161,12 @@ if (isset($_POST['form_sent']))
 				$new_pid = $db->insert_id();
 
 				// To subscribe or not to subscribe, that ...
-				if ($pun_config['o_subscriptions'] == '1')
+				if ($pun_config['o_topic_subscriptions'] == '1')
 				{
 					if ($subscribe && !$is_subscribed)
-						$db->query('INSERT INTO '.$db->prefix.'subscriptions (user_id, topic_id) VALUES('.$pun_user['id'].' ,'.$tid.')') or error('Unable to add subscription', __FILE__, __LINE__, $db->error());
+						$db->query('INSERT INTO '.$db->prefix.'topic_subscriptions (user_id, topic_id) VALUES('.$pun_user['id'].' ,'.$tid.')') or error('Unable to add subscription', __FILE__, __LINE__, $db->error());
 					else if (!$subscribe && $is_subscribed)
-						$db->query('DELETE FROM '.$db->prefix.'subscriptions WHERE user_id='.$pun_user['id'].' AND topic_id='.$tid) or error('Unable to remove subscription', __FILE__, __LINE__, $db->error());
+						$db->query('DELETE FROM '.$db->prefix.'topic_subscriptions WHERE user_id='.$pun_user['id'].' AND topic_id='.$tid) or error('Unable to remove subscription', __FILE__, __LINE__, $db->error());
 				}
 			}
 			else
@@ -188,14 +189,14 @@ if (isset($_POST['form_sent']))
 			update_forum($cur_posting['id']);
 
 			// Should we send out notifications?
-			if ($pun_config['o_subscriptions'] == '1')
+			if ($pun_config['o_topic_subscriptions'] == '1')
 			{
 				// Get the post time for the previous post in this topic
 				$result = $db->query('SELECT posted FROM '.$db->prefix.'posts WHERE topic_id='.$tid.' ORDER BY id DESC LIMIT 1, 1') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
 				$previous_post_time = $db->result($result);
 
 				// Get any subscribed users that should be notified (banned users are excluded)
-				$result = $db->query('SELECT u.id, u.email, u.notify_with_post, u.language FROM '.$db->prefix.'users AS u INNER JOIN '.$db->prefix.'subscriptions AS s ON u.id=s.user_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id='.$cur_posting['id'].' AND fp.group_id=u.group_id) LEFT JOIN '.$db->prefix.'online AS o ON u.id=o.user_id LEFT JOIN '.$db->prefix.'bans AS b ON u.username=b.username WHERE b.username IS NULL AND COALESCE(o.logged, u.last_visit)>'.$previous_post_time.' AND (fp.read_forum IS NULL OR fp.read_forum=1) AND s.topic_id='.$tid.' AND u.id!='.$pun_user['id']) or error('Unable to fetch subscription info', __FILE__, __LINE__, $db->error());
+				$result = $db->query('SELECT u.id, u.email, u.notify_with_post, u.language FROM '.$db->prefix.'users AS u INNER JOIN '.$db->prefix.'topic_subscriptions AS s ON u.id=s.user_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id='.$cur_posting['id'].' AND fp.group_id=u.group_id) LEFT JOIN '.$db->prefix.'online AS o ON u.id=o.user_id LEFT JOIN '.$db->prefix.'bans AS b ON u.username=b.username WHERE b.username IS NULL AND COALESCE(o.logged, u.last_visit)>'.$previous_post_time.' AND (fp.read_forum IS NULL OR fp.read_forum=1) AND s.topic_id='.$tid.' AND u.id!='.$pun_user['id']) or error('Unable to fetch subscription info', __FILE__, __LINE__, $db->error());
 				if ($db->num_rows($result))
 				{
 					require_once PUN_ROOT.'include/email.php';
@@ -228,16 +229,16 @@ if (isset($_POST['form_sent']))
 								$mail_subject = str_replace('<topic_subject>', '\''.$cur_posting['subject'].'\'', $mail_subject);
 								$mail_message = str_replace('<topic_subject>', '\''.$cur_posting['subject'].'\'', $mail_message);
 								$mail_message = str_replace('<replier>', $username, $mail_message);
-								$mail_message = str_replace('<post_url>', $pun_config['o_base_url'].'/viewtopic.php?pid='.$new_pid.'#p'.$new_pid, $mail_message);
-								$mail_message = str_replace('<unsubscribe_url>', $pun_config['o_base_url'].'/misc.php?unsubscribe='.$tid, $mail_message);
+								$mail_message = str_replace('<post_url>', get_base_url().'/viewtopic.php?pid='.$new_pid.'#p'.$new_pid, $mail_message);
+								$mail_message = str_replace('<unsubscribe_url>', get_base_url().'/misc.php?action=unsubscribe&tid='.$tid, $mail_message);
 								$mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'].' '.$lang_common['Mailer'], $mail_message);
 
 								$mail_subject_full = str_replace('<topic_subject>', '\''.$cur_posting['subject'].'\'', $mail_subject_full);
 								$mail_message_full = str_replace('<topic_subject>', '\''.$cur_posting['subject'].'\'', $mail_message_full);
 								$mail_message_full = str_replace('<replier>', $username, $mail_message_full);
 								$mail_message_full = str_replace('<message>', $message, $mail_message_full);
-								$mail_message_full = str_replace('<post_url>', $pun_config['o_base_url'].'/viewtopic.php?pid='.$new_pid.'#p'.$new_pid, $mail_message_full);
-								$mail_message_full = str_replace('<unsubscribe_url>', $pun_config['o_base_url'].'/misc.php?unsubscribe='.$tid, $mail_message_full);
+								$mail_message_full = str_replace('<post_url>', get_base_url().'/viewtopic.php?pid='.$new_pid.'#p'.$new_pid, $mail_message_full);
+								$mail_message_full = str_replace('<unsubscribe_url>', get_base_url().'/misc.php?action=unsubscribe&tid='.$tid, $mail_message_full);
 								$mail_message_full = str_replace('<board_mailer>', $pun_config['o_board_title'].' '.$lang_common['Mailer'], $mail_message_full);
 
 								$notification_emails[$cur_subscriber['language']][0] = $mail_subject;
@@ -265,14 +266,14 @@ if (isset($_POST['form_sent']))
 		else if ($fid)
 		{
 			// Create the topic
-			$db->query('INSERT INTO '.$db->prefix.'topics (poster, subject, posted, last_post, last_poster, forum_id) VALUES(\''.$db->escape($username).'\', \''.$db->escape($subject).'\', '.$now.', '.$now.', \''.$db->escape($username).'\', '.$fid.')') or error('Unable to create topic', __FILE__, __LINE__, $db->error());
+			$db->query('INSERT INTO '.$db->prefix.'topics (poster, subject, posted, last_post, last_poster, sticky, forum_id) VALUES(\''.$db->escape($username).'\', \''.$db->escape($subject).'\', '.$now.', '.$now.', \''.$db->escape($username).'\', '.$stick_topic.', '.$fid.')') or error('Unable to create topic', __FILE__, __LINE__, $db->error());
 			$new_tid = $db->insert_id();
 
 			if (!$pun_user['is_guest'])
 			{
 				// To subscribe or not to subscribe, that ...
-				if ($pun_config['o_subscriptions'] == '1' && $subscribe)
-					$db->query('INSERT INTO '.$db->prefix.'subscriptions (user_id, topic_id) VALUES('.$pun_user['id'].' ,'.$new_tid.')') or error('Unable to add subscription', __FILE__, __LINE__, $db->error());
+				if ($pun_config['o_topic_subscriptions'] == '1' && $subscribe)
+					$db->query('INSERT INTO '.$db->prefix.'topic_subscriptions (user_id, topic_id) VALUES('.$pun_user['id'].' ,'.$new_tid.')') or error('Unable to add subscription', __FILE__, __LINE__, $db->error());
 
 				// Create the post ("topic post")
 				$db->query('INSERT INTO '.$db->prefix.'posts (poster, poster_id, poster_ip, message, hide_smilies, posted, topic_id) VALUES(\''.$db->escape($username).'\', '.$pun_user['id'].', \''.get_remote_address().'\', \''.$db->escape($message).'\', '.$hide_smilies.', '.$now.', '.$new_tid.')') or error('Unable to create post', __FILE__, __LINE__, $db->error());
@@ -291,6 +292,80 @@ if (isset($_POST['form_sent']))
 			update_search_index('post', $new_pid, $message, $subject);
 
 			update_forum($fid);
+
+			// Should we send out notifications?
+			if ($pun_config['o_forum_subscriptions'] == '1')
+			{
+				// Get any subscribed users that should be notified (banned users are excluded)
+				$result = $db->query('SELECT u.id, u.email, u.notify_with_post, u.language FROM '.$db->prefix.'users AS u INNER JOIN '.$db->prefix.'forum_subscriptions AS s ON u.id=s.user_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id='.$cur_posting['id'].' AND fp.group_id=u.group_id) LEFT JOIN '.$db->prefix.'bans AS b ON u.username=b.username WHERE b.username IS NULL AND (fp.read_forum IS NULL OR fp.read_forum=1) AND s.forum_id='.$cur_posting['id'].' AND u.id!='.$pun_user['id']) or error('Unable to fetch subscription info', __FILE__, __LINE__, $db->error());
+				if ($db->num_rows($result))
+				{
+					require_once PUN_ROOT.'include/email.php';
+
+					$notification_emails = array();
+
+					// Loop through subscribed users and send emails
+					while ($cur_subscriber = $db->fetch_assoc($result))
+					{
+						// Is the subscription email for $cur_subscriber['language'] cached or not?
+						if (!isset($notification_emails[$cur_subscriber['language']]))
+						{
+							if (file_exists(PUN_ROOT.'lang/'.$cur_subscriber['language'].'/mail_templates/new_topic.tpl'))
+							{
+								// Load the "new reply" template
+								$mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$cur_subscriber['language'].'/mail_templates/new_topic.tpl'));
+
+								// Load the "new reply full" template (with post included)
+								$mail_tpl_full = trim(file_get_contents(PUN_ROOT.'lang/'.$cur_subscriber['language'].'/mail_templates/new_topic_full.tpl'));
+
+								// The first row contains the subject (it also starts with "Subject:")
+								$first_crlf = strpos($mail_tpl, "\n");
+								$mail_subject = trim(substr($mail_tpl, 8, $first_crlf-8));
+								$mail_message = trim(substr($mail_tpl, $first_crlf));
+
+								$first_crlf = strpos($mail_tpl_full, "\n");
+								$mail_subject_full = trim(substr($mail_tpl_full, 8, $first_crlf-8));
+								$mail_message_full = trim(substr($mail_tpl_full, $first_crlf));
+
+								$mail_subject = str_replace('<topic_subject>', '\''.$subject.'\'', $mail_subject);
+								$mail_subject = str_replace('<forum_name>', '\''.$cur_posting['forum_name'].'\'', $mail_subject);
+								$mail_message = str_replace('<topic_subject>', '\''.$subject.'\'', $mail_message);
+								$mail_message = str_replace('<forum_name>', '\''.$cur_posting['forum_name'].'\'', $mail_message);
+								$mail_message = str_replace('<poster>', $username, $mail_message);
+								$mail_message = str_replace('<topic_url>', get_base_url().'/viewtopic.php?id='.$new_tid, $mail_message);
+								$mail_message = str_replace('<unsubscribe_url>', get_base_url().'/misc.php?action=unsubscribe&fid='.$cur_posting['id'], $mail_message);
+								$mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'].' '.$lang_common['Mailer'], $mail_message);
+
+								$mail_subject_full = str_replace('<topic_subject>', '\''.$subject.'\'', $mail_subject_full);
+								$mail_subject_full = str_replace('<forum_name>', '\''.$cur_posting['forum_name'].'\'', $mail_subject_full);
+								$mail_message_full = str_replace('<topic_subject>', '\''.$subject.'\'', $mail_message_full);
+								$mail_message_full = str_replace('<forum_name>', '\''.$cur_posting['forum_name'].'\'', $mail_message_full);
+								$mail_message_full = str_replace('<poster>', $username, $mail_message_full);
+								$mail_message_full = str_replace('<message>', $message, $mail_message_full);
+								$mail_message_full = str_replace('<topic_url>', get_base_url().'/viewtopic.php?id='.$new_tid, $mail_message_full);
+								$mail_message_full = str_replace('<unsubscribe_url>', get_base_url().'/misc.php?action=unsubscribe&fid='.$cur_posting['id'], $mail_message_full);
+								$mail_message_full = str_replace('<board_mailer>', $pun_config['o_board_title'].' '.$lang_common['Mailer'], $mail_message_full);
+
+								$notification_emails[$cur_subscriber['language']][0] = $mail_subject;
+								$notification_emails[$cur_subscriber['language']][1] = $mail_message;
+								$notification_emails[$cur_subscriber['language']][2] = $mail_subject_full;
+								$notification_emails[$cur_subscriber['language']][3] = $mail_message_full;
+
+								$mail_subject = $mail_message = $mail_subject_full = $mail_message_full = null;
+							}
+						}
+
+						// We have to double check here because the templates could be missing
+						if (isset($notification_emails[$cur_subscriber['language']]))
+						{
+							if ($cur_subscriber['notify_with_post'] == '0')
+								pun_mail($cur_subscriber['email'], $notification_emails[$cur_subscriber['language']][0], $notification_emails[$cur_subscriber['language']][1]);
+							else
+								pun_mail($cur_subscriber['email'], $notification_emails[$cur_subscriber['language']][2], $notification_emails[$cur_subscriber['language']][3]);
+						}
+					}
+				}
+			}
 		}
 
 		// If we previously found out that the email was banned
@@ -298,7 +373,7 @@ if (isset($_POST['form_sent']))
 		{
 			$mail_subject = $lang_common['Banned email notification'];
 			$mail_message = sprintf($lang_common['Banned email post message'], $username, $email)."\n";
-			$mail_message .= sprintf($lang_common['Post URL'], $pun_config['o_base_url'].'/viewtopic.php?pid='.$new_pid.'#p'.$new_pid)."\n";
+			$mail_message .= sprintf($lang_common['Post URL'], get_base_url().'/viewtopic.php?pid='.$new_pid.'#p'.$new_pid)."\n";
 			$mail_message .= "\n".'--'."\n".$lang_common['Email signature'];
 
 			pun_mail($pun_config['o_mailing_list'], $mail_subject, $mail_message);
@@ -342,8 +417,36 @@ if ($tid)
 
 		list($q_poster, $q_message) = $db->fetch_row($result);
 
-		$q_message = preg_replace('%\[img(?:=.*?)?\]%', '[url]', $q_message);
-		$q_message = str_replace('[/img]', '[/url]', $q_message);
+		// If the message contains a code tag we have to split it up (text within [code][/code] shouldn't be touched)
+		if (strpos($q_message, '[code]') !== false && strpos($q_message, '[/code]') !== false)
+		{
+			$errors = array();
+			list($inside, $outside) = split_text($q_message, '[code]', '[/code]', $errors);
+			if (!empty($errors)) // Technically this shouldn't happen, since $q_message is an existing post it should only exist if it previously passed validation
+				message($errors[0]);
+
+			$q_message = implode("\1", $outside);
+		}
+
+		// Remove [img] tags from quoted message
+		$q_message = preg_replace('%\[img(?:=(?:[^\[]*?))?\]((ht|f)tps?://)([^\s<"]*?)\[/img\]%U', '\1\3', $q_message);
+
+		// If we split up the message before we have to concatenate it together again (code tags)
+		if (isset($inside))
+		{
+			$outside = explode("\1", $q_message);
+			$q_message = '';
+
+			$num_tokens = count($outside);
+			for ($i = 0; $i < $num_tokens; ++$i)
+			{
+				$q_message .= $outside[$i];
+				if (isset($inside[$i]))
+					$q_message .= '[code]'.$inside[$i].'[/code]';
+			}
+
+			unset($inside);
+		}
 
 		if ($pun_config['o_censoring'] == '1')
 			$q_message = censor_words($q_message);
@@ -498,7 +601,7 @@ if ($pun_user['is_guest'])
 if ($fid): ?>
 						<label class="required"><strong><?php echo $lang_common['Subject'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><input class="longinput" type="text" name="req_subject" value="<?php if (isset($_POST['req_subject'])) echo pun_htmlspecialchars($subject); ?>" size="80" maxlength="70" tabindex="<?php echo $cur_index++ ?>" /><br /></label>
 <?php endif; ?>						<label class="required"><strong><?php echo $lang_common['Message'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br />
-						<textarea name="req_message" rows="20" cols="95" tabindex="<?php echo $cur_index++ ?>"><?php echo isset($_POST['req_message']) ? pun_htmlspecialchars($message) : (isset($quote) ? $quote : ''); ?></textarea><br /></label>
+						<textarea name="req_message" rows="20" cols="95" tabindex="<?php echo $cur_index++ ?>"><?php echo isset($_POST['req_message']) ? pun_htmlspecialchars($orig_message) : (isset($quote) ? $quote : ''); ?></textarea><br /></label>
 						<ul class="bblinks">
 							<li><span><a href="help.php#bbcode" onclick="window.open(this.href); return false;"><?php echo $lang_common['BBCode'] ?></a> <?php echo ($pun_config['p_message_bbcode'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></span></li>
 							<li><span><a href="help.php#img" onclick="window.open(this.href); return false;"><?php echo $lang_common['img tag'] ?></a> <?php echo ($pun_config['p_message_img_tag'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></span></li>
@@ -509,12 +612,15 @@ if ($fid): ?>
 <?php
 
 $checkboxes = array();
+if ($is_admmod)
+	$checkboxes[] = '<label><input type="checkbox" name="stick_topic" value="1" tabindex="'.($cur_index++).'"'.(isset($_POST['stick_topic']) ? ' checked="checked"' : '').' />'.$lang_common['Stick topic'].'<br /></label>';
+
 if (!$pun_user['is_guest'])
 {
 	if ($pun_config['o_smilies'] == '1')
 		$checkboxes[] = '<label><input type="checkbox" name="hide_smilies" value="1" tabindex="'.($cur_index++).'"'.(isset($_POST['hide_smilies']) ? ' checked="checked"' : '').' />'.$lang_post['Hide smilies'].'<br /></label>';
 
-	if ($pun_config['o_subscriptions'] == '1')
+	if ($pun_config['o_topic_subscriptions'] == '1')
 	{
 		$subscr_checked = false;
 
diff --git a/profile.php b/profile.php
index 2aa5dae..3d86726 100644
--- a/profile.php
+++ b/profile.php
@@ -6,7 +6,7 @@
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
 
-define('PUN_ROOT', './');
+define('PUN_ROOT', dirname(__FILE__).'/');
 require PUN_ROOT.'include/common.php';
 
 // Include UTF-8 function
@@ -212,7 +212,7 @@ else if ($action == 'change_email')
 			{
 				$mail_subject = $lang_common['Banned email notification'];
 				$mail_message = sprintf($lang_common['Banned email change message'], $pun_user['username'], $new_email)."\n";
-				$mail_message .= sprintf($lang_common['User profile'], $pun_config['o_base_url'].'/profile.php?id='.$id)."\n";
+				$mail_message .= sprintf($lang_common['User profile'], get_base_url().'/profile.php?id='.$id)."\n";
 				$mail_message .= "\n".'--'."\n".$lang_common['Email signature'];
 
 				pun_mail($pun_config['o_mailing_list'], $mail_subject, $mail_message);
@@ -232,7 +232,7 @@ else if ($action == 'change_email')
 
 				$mail_subject = $lang_common['Duplicate email notification'];
 				$mail_message = sprintf($lang_common['Duplicate email change message'], $pun_user['username'], implode(', ', $dupe_list))."\n";
-				$mail_message .= sprintf($lang_common['User profile'], $pun_config['o_base_url'].'/profile.php?id='.$id)."\n";
+				$mail_message .= sprintf($lang_common['User profile'], get_base_url().'/profile.php?id='.$id)."\n";
 				$mail_message .= "\n".'--'."\n".$lang_common['Email signature'];
 
 				pun_mail($pun_config['o_mailing_list'], $mail_subject, $mail_message);
@@ -253,8 +253,8 @@ else if ($action == 'change_email')
 		$mail_message = trim(substr($mail_tpl, $first_crlf));
 
 		$mail_message = str_replace('<username>', $pun_user['username'], $mail_message);
-		$mail_message = str_replace('<base_url>', $pun_config['o_base_url'], $mail_message);
-		$mail_message = str_replace('<activation_url>', $pun_config['o_base_url'].'/profile.php?action=change_email&id='.$id.'&key='.$new_email_key, $mail_message);
+		$mail_message = str_replace('<base_url>', get_base_url(), $mail_message);
+		$mail_message = str_replace('<activation_url>', get_base_url().'/profile.php?action=change_email&id='.$id.'&key='.$new_email_key, $mail_message);
 		$mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'].' '.$lang_common['Mailer'], $mail_message);
 
 		pun_mail($new_email, $mail_subject, $mail_message);
@@ -351,13 +351,12 @@ else if ($action == 'upload_avatar' || $action == 'upload_avatar2')
 				message($lang_profile['Too large'].' '.forum_number_format($pun_config['o_avatars_size']).' '.$lang_profile['bytes'].'.');
 
 			// Move the file to the avatar directory. We do this before checking the width/height to circumvent open_basedir restrictions
-			if (!@move_uploaded_file($uploaded_file['tmp_name'], $pun_config['o_avatars_dir'].'/'.$id.'.tmp'))
+			if (!@move_uploaded_file($uploaded_file['tmp_name'], PUN_ROOT.$pun_config['o_avatars_dir'].'/'.$id.'.tmp'))
 				message($lang_profile['Move failed'].' <a href="mailto:'.$pun_config['o_admin_email'].'">'.$pun_config['o_admin_email'].'</a>.');
 
-			list($width, $height, $type,) = @getimagesize($pun_config['o_avatars_dir'].'/'.$id.'.tmp');
+			list($width, $height, $type,) = @getimagesize(PUN_ROOT.$pun_config['o_avatars_dir'].'/'.$id.'.tmp');
 
 			// Determine type
-			$extension = null;
 			if ($type == IMAGETYPE_GIF)
 				$extension = '.gif';
 			else if ($type == IMAGETYPE_JPEG)
@@ -367,21 +366,21 @@ else if ($action == 'upload_avatar' || $action == 'upload_avatar2')
 			else
 			{
 				// Invalid type
-				@unlink($pun_config['o_avatars_dir'].'/'.$id.'.tmp');
+				@unlink(PUN_ROOT.$pun_config['o_avatars_dir'].'/'.$id.'.tmp');
 				message($lang_profile['Bad type']);
 			}
 
 			// Now check the width/height
 			if (empty($width) || empty($height) || $width > $pun_config['o_avatars_width'] || $height > $pun_config['o_avatars_height'])
 			{
-				@unlink($pun_config['o_avatars_dir'].'/'.$id.'.tmp');
+				@unlink(PUN_ROOT.$pun_config['o_avatars_dir'].'/'.$id.'.tmp');
 				message($lang_profile['Too wide or high'].' '.$pun_config['o_avatars_width'].'x'.$pun_config['o_avatars_height'].' '.$lang_profile['pixels'].'.');
 			}
 
 			// Delete any old avatars and put the new one in place
 			delete_avatar($id);
-			@rename($pun_config['o_avatars_dir'].'/'.$id.'.tmp', $pun_config['o_avatars_dir'].'/'.$id.$extension);
-			@chmod($pun_config['o_avatars_dir'].'/'.$id.$extension, 0644);
+			@rename(PUN_ROOT.$pun_config['o_avatars_dir'].'/'.$id.'.tmp', PUN_ROOT.$pun_config['o_avatars_dir'].'/'.$id.$extension);
+			@chmod(PUN_ROOT.$pun_config['o_avatars_dir'].'/'.$id.$extension, 0644);
 		}
 		else
 			message($lang_profile['Unknown failure']);
@@ -407,7 +406,7 @@ else if ($action == 'upload_avatar' || $action == 'upload_avatar2')
 						<input type="hidden" name="form_sent" value="1" />
 						<input type="hidden" name="MAX_FILE_SIZE" value="<?php echo $pun_config['o_avatars_size'] ?>" />
 						<label class="required"><strong><?php echo $lang_profile['File'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><input name="req_file" type="file" size="40" /><br /></label>
-						<p><?php echo $lang_profile['Avatar desc'].' '.$pun_config['o_avatars_width'].' x '.$pun_config['o_avatars_height'].' '.$lang_profile['pixels'].' '.$lang_common['and'].' '.forum_number_format($pun_config['o_avatars_size']).' '.$lang_profile['bytes'].' ('.forum_number_format(ceil($pun_config['o_avatars_size'] / 1024)) ?> KB).</p>
+						<p><?php echo $lang_profile['Avatar desc'].' '.$pun_config['o_avatars_width'].' x '.$pun_config['o_avatars_height'].' '.$lang_profile['pixels'].' '.$lang_common['and'].' '.forum_number_format($pun_config['o_avatars_size']).' '.$lang_profile['bytes'].' ('.file_size($pun_config['o_avatars_size']).').' ?></p>
 					</div>
 				</fieldset>
 			</div>
@@ -561,7 +560,8 @@ else if (isset($_POST['delete_user']) || isset($_POST['delete_user_comply']))
 		}
 
 		// Delete any subscriptions
-		$db->query('DELETE FROM '.$db->prefix.'subscriptions WHERE user_id='.$id) or error('Unable to delete subscriptions', __FILE__, __LINE__, $db->error());
+		$db->query('DELETE FROM '.$db->prefix.'topic_subscriptions WHERE user_id='.$id) or error('Unable to delete topic subscriptions', __FILE__, __LINE__, $db->error());
+		$db->query('DELETE FROM '.$db->prefix.'forum_subscriptions WHERE user_id='.$id) or error('Unable to delete forum subscriptions', __FILE__, __LINE__, $db->error());
 
 		// Remove him/her from the online list (if they happen to be logged in)
 		$db->query('DELETE FROM '.$db->prefix.'online WHERE user_id='.$id) or error('Unable to remove user from online list', __FILE__, __LINE__, $db->error());
@@ -637,16 +637,18 @@ else if (isset($_POST['delete_user']) || isset($_POST['delete_user_comply']))
 else if (isset($_POST['form_sent']))
 {
 	// Fetch the user group of the user we are editing
-	$result = $db->query('SELECT u.group_id, g.g_moderator FROM '.$db->prefix.'users AS u INNER JOIN '.$db->prefix.'groups AS g ON (g.g_id=u.group_id) WHERE u.id='.$id) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+	$result = $db->query('SELECT u.username, u.group_id, g.g_moderator FROM '.$db->prefix.'users AS u INNER JOIN '.$db->prefix.'groups AS g ON (g.g_id=u.group_id) WHERE u.id='.$id) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
 	if (!$db->num_rows($result))
 		message($lang_common['Bad request']);
 
-	list($group_id, $is_moderator) = $db->fetch_row($result);
+	list($old_username, $group_id, $is_moderator) = $db->fetch_row($result);
 
-	if ($pun_user['id'] != $id &&
-		(!$pun_user['is_admmod'] ||
-		($pun_user['g_moderator'] == '1' && $pun_user['g_mod_edit_users'] == '0') ||
-		($pun_user['g_moderator'] == '1' && $is_moderator)))
+	if ($pun_user['id'] != $id &&																	// If we arent the user (i.e. editing your own profile)
+		(!$pun_user['is_admmod'] ||																	// and we are not an admin or mod
+		($pun_user['g_id'] != PUN_ADMIN &&															// or we aren't an admin and ...
+		($pun_user['g_mod_edit_users'] == '0' ||													// mods aren't allowed to edit users
+		$group_id == PUN_ADMIN ||																	// or the user is an admin
+		$is_moderator))))																			// or the user is another mod
 		message($lang_common['No permission']);
 
 	if ($pun_user['is_admmod'])
@@ -669,9 +671,10 @@ else if (isset($_POST['form_sent']))
 			// Make sure we got a valid language string
 			if (isset($_POST['form']['language']))
 			{
-				$form['language'] = preg_replace('#[\.\\\/]#', '', pun_trim($_POST['form']['language']));
-				if (!file_exists(PUN_ROOT.'lang/'.$form['language'].'/common.php'))
-						message($lang_common['Bad request']);
+				$languages = forum_list_langs();
+				$form['language'] = pun_trim($_POST['form']['language']);
+				if (!in_array($form['language'], $languages))
+					message($lang_common['Bad request']);
 			}
 
 			if ($pun_user['is_admmod'])
@@ -682,18 +685,19 @@ else if (isset($_POST['form_sent']))
 				if ($pun_user['g_id'] == PUN_ADMIN || ($pun_user['g_moderator'] == '1' && $pun_user['g_mod_rename_users'] == '1'))
 				{
 					$form['username'] = pun_trim($_POST['req_username']);
-					$old_username = pun_trim($_POST['old_username']);
 
-					// Check username
-					require PUN_ROOT.'lang/'.$pun_user['language'].'/register.php';
+					if ($form['username'] != $old_username)
+					{
+						// Check username
+						require PUN_ROOT.'lang/'.$pun_user['language'].'/register.php';
 
-					$errors = array();
-					check_username($form['username'], $id);
-					if (!empty($errors))
-						message($errors[0]);
+						$errors = array();
+						check_username($form['username'], $id);
+						if (!empty($errors))
+							message($errors[0]);
 
-					if ($form['username'] != $old_username)
 						$username_updated = true;
+					}
 				}
 
 				// We only allow administrators to update the post count
@@ -830,9 +834,10 @@ else if (isset($_POST['form_sent']))
 			// Make sure we got a valid style string
 			if (isset($_POST['form']['style']))
 			{
-				$form['style'] = preg_replace('#[\.\\\/]#', '', pun_trim($_POST['form']['style']));
-				if (!file_exists(PUN_ROOT.'style/'.$form['style'].'.css'))
-						message($lang_common['Bad request']);
+				$styles = forum_list_styles();
+				$form['style'] = pun_trim($_POST['form']['style']);
+				if (!in_array($form['style'], $styles))
+					message($lang_common['Bad request']);
 			}
 
 			break;
@@ -929,10 +934,12 @@ if ($user['signature'] != '')
 
 
 // View or edit?
-if ($pun_user['id'] != $id &&
-	(!$pun_user['is_admmod'] ||
-	($pun_user['g_moderator'] == '1' && $pun_user['g_mod_edit_users'] == '0') ||
-	($pun_user['g_moderator'] == '1' && $user['g_moderator'] == '1')))
+if ($pun_user['id'] != $id &&																	// If we arent the user (i.e. editing your own profile)
+	(!$pun_user['is_admmod'] ||																	// and we are not an admin or mod
+	($pun_user['g_id'] != PUN_ADMIN &&															// or we aren't an admin and ...
+	($pun_user['g_mod_edit_users'] == '0' ||													// mods aren't allowed to edit users
+	$user['g_id'] == PUN_ADMIN ||																// or the user is an admin
+	$user['g_moderator'] == '1'))))																// or the user is another mod
 {
 	$user_personal = array();
 
@@ -1033,7 +1040,7 @@ if ($pun_user['id'] != $id &&
 	if ($pun_config['o_show_post_count'] == '1' || $pun_user['is_admmod'])
 		$posts_field = forum_number_format($user['num_posts']);
 	if ($pun_user['g_search'] == '1' && $user['num_posts'] > 0)
-		$posts_field .= (($posts_field != '') ? ' - ' : '').'<a href="search.php?action=show_user&user_id='.$id.'">'.$lang_profile['Show posts'].'</a>';
+		$posts_field .= (($posts_field != '') ? ' - ' : '').'<a href="search.php?action=show_user_topics&user_id='.$id.'">'.$lang_profile['Show topics'].'</a> - <a href="search.php?action=show_user_posts&user_id='.$id.'">'.$lang_profile['Show posts'].'</a>';
 	if ($posts_field != '')
 	{
 		$user_activity[] = '<dt>'.$lang_common['Posts'].'</dt>';
@@ -1118,7 +1125,7 @@ else
 		if ($pun_user['is_admmod'])
 		{
 			if ($pun_user['g_id'] == PUN_ADMIN || $pun_user['g_mod_rename_users'] == '1')
-				$username_field = '<input type="hidden" name="old_username" value="'.pun_htmlspecialchars($user['username']).'" /><label class="required"><strong>'.$lang_common['Username'].' <span>'.$lang_common['Required'].'</span></strong><br /><input type="text" name="req_username" value="'.pun_htmlspecialchars($user['username']).'" size="25" maxlength="25" /><br /></label>'."\n";
+				$username_field = '<label class="required"><strong>'.$lang_common['Username'].' <span>'.$lang_common['Required'].'</span></strong><br /><input type="text" name="req_username" value="'.pun_htmlspecialchars($user['username']).'" size="25" maxlength="25" /><br /></label>'."\n";
 			else
 				$username_field = '<p>'.sprintf($lang_profile['Username info'], pun_htmlspecialchars($user['username'])).'</p>'."\n";
 
@@ -1135,12 +1142,23 @@ else
 		}
 
 		$posts_field = '';
+		$posts_actions = array();
+
 		if ($pun_user['g_id'] == PUN_ADMIN)
-			$posts_field = '<label>'.$lang_common['Posts'].'<br /><input type="text" name="num_posts" value="'.$user['num_posts'].'" size="8" maxlength="8" /><br /></label><p class="actions"><span><a href="search.php?action=show_user&user_id='.$id.'">'.$lang_profile['Show posts'].'</a></span></p>'."\n";
+			$posts_field .= '<label>'.$lang_common['Posts'].'<br /><input type="text" name="num_posts" value="'.$user['num_posts'].'" size="8" maxlength="8" /><br /></label>';
 		else if ($pun_config['o_show_post_count'] == '1' || $pun_user['is_admmod'])
-			$posts_field = '<p>'.sprintf($lang_profile['Posts info'], forum_number_format($user['num_posts']).($pun_user['g_search'] == '1' ? ' - <a href="search.php?action=show_user&user_id='.$id.'">'.$lang_profile['Show posts'].'</a>' : '')).'</p>'."\n";
-		else if ($pun_user['g_search'] == '1')
-			$posts_field = '<p class="actions"><span><a href="search.php?action=show_user&user_id='.$id.'">'.$lang_profile['Show posts'].'</a></span></p>'."\n";
+			$posts_actions[] = sprintf($lang_profile['Posts info'], forum_number_format($user['num_posts']));
+
+		if ($pun_user['g_search'] == '1' || $pun_user['g_id'] == PUN_ADMIN)
+		{
+			$posts_actions[] = '<a href="search.php?action=show_user_topics&user_id='.$id.'">'.$lang_profile['Show topics'].'</a>';
+			$posts_actions[] = '<a href="search.php?action=show_user_posts&user_id='.$id.'">'.$lang_profile['Show posts'].'</a>';
+
+			if ($pun_config['o_topic_subscriptions'] == '1')
+				$posts_actions[] = '<a href="search.php?action=show_subscriptions&user_id='.$id.'">'.$lang_profile['Show subscriptions'].'</a>';
+		}
+
+		$posts_field .= (!empty($posts_actions) ? '<p class="actions">'.implode(' - ', $posts_actions).'</p>' : '')."\n";
 
 
 		$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Profile'], $lang_profile['Section essentials']);
@@ -1568,13 +1586,14 @@ else
 						</div>
 					</fieldset>
 				</div>
-<?php if ($pun_config['o_subscriptions'] == '1'): ?>				<div class="inform">
+<?php if ($pun_config['o_forum_subscriptions'] == '1' || $pun_config['o_topic_subscriptions'] == '1'): ?>				<div class="inform">
 					<fieldset>
 						<legend><?php echo $lang_profile['Subscription legend'] ?></legend>
 						<div class="infldset">
 							<div class="rbox">
 								<label><input type="checkbox" name="form[notify_with_post]" value="1"<?php if ($user['notify_with_post'] == '1') echo ' checked="checked"' ?> /><?php echo $lang_profile['Notify full'] ?><br /></label>
-								<label><input type="checkbox" name="form[auto_notify]" value="1"<?php if ($user['auto_notify'] == '1') echo ' checked="checked"' ?> /><?php echo $lang_profile['Auto notify full'] ?><br /></label>
+<?php if ($pun_config['o_topic_subscriptions'] == '1'): ?>								<label><input type="checkbox" name="form[auto_notify]" value="1"<?php if ($user['auto_notify'] == '1') echo ' checked="checked"' ?> /><?php echo $lang_profile['Auto notify full'] ?><br /></label>
+<?php endif; ?>
 							</div>
 						</div>
 					</fieldset>
diff --git a/register.php b/register.php
index 71f14e3..f6d638e 100644
--- a/register.php
+++ b/register.php
@@ -6,7 +6,7 @@
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
 
-define('PUN_ROOT', './');
+define('PUN_ROOT', dirname(__FILE__).'/');
 require PUN_ROOT.'include/common.php';
 require PUN_ROOT.'include/sha256question.php';
 
@@ -171,7 +171,7 @@ if (isset($_POST['form_sent']))
 			{
 				$mail_subject = $lang_common['Banned email notification'];
 				$mail_message = sprintf($lang_common['Banned email register message'], $username, $email1)."\n";
-				$mail_message .= sprintf($lang_common['User profile'], $pun_config['o_base_url'].'/profile.php?id='.$new_uid)."\n";
+				$mail_message .= sprintf($lang_common['User profile'], get_base_url().'/profile.php?id='.$new_uid)."\n";
 				$mail_message .= "\n".'--'."\n".$lang_common['Email signature'];
 
 				pun_mail($pun_config['o_mailing_list'], $mail_subject, $mail_message);
@@ -182,7 +182,7 @@ if (isset($_POST['form_sent']))
 			{
 				$mail_subject = $lang_common['Duplicate email notification'];
 				$mail_message = sprintf($lang_common['Duplicate email register message'], $username, implode(', ', $dupe_list))."\n";
-				$mail_message .= sprintf($lang_common['User profile'], $pun_config['o_base_url'].'/profile.php?id='.$new_uid)."\n";
+				$mail_message .= sprintf($lang_common['User profile'], get_base_url().'/profile.php?id='.$new_uid)."\n";
 				$mail_message .= "\n".'--'."\n".$lang_common['Email signature'];
 
 				pun_mail($pun_config['o_mailing_list'], $mail_subject, $mail_message);
@@ -192,8 +192,8 @@ if (isset($_POST['form_sent']))
 			if ($pun_config['o_regs_report'] == '1')
 			{
 				$mail_subject = $lang_common['New user notification'];
-				$mail_message = sprintf($lang_common['New user message'], $username, $pun_config['o_base_url'].'/')."\n";
-				$mail_message .= sprintf($lang_common['User profile'], $pun_config['o_base_url'].'/profile.php?id='.$new_uid)."\n";
+				$mail_message = sprintf($lang_common['New user message'], $username, get_base_url().'/')."\n";
+				$mail_message .= sprintf($lang_common['User profile'], get_base_url().'/profile.php?id='.$new_uid)."\n";
 				$mail_message .= "\n".'--'."\n".$lang_common['Email signature'];
 
 				pun_mail($pun_config['o_mailing_list'], $mail_subject, $mail_message);
@@ -212,10 +212,10 @@ if (isset($_POST['form_sent']))
 			$mail_message = trim(substr($mail_tpl, $first_crlf));
 
 			$mail_subject = str_replace('<board_title>', $pun_config['o_board_title'], $mail_subject);
-			$mail_message = str_replace('<base_url>', $pun_config['o_base_url'].'/', $mail_message);
+			$mail_message = str_replace('<base_url>', get_base_url().'/', $mail_message);
 			$mail_message = str_replace('<username>', $username, $mail_message);
 			$mail_message = str_replace('<password>', $password1, $mail_message);
-			$mail_message = str_replace('<login_url>', $pun_config['o_base_url'].'/login.php', $mail_message);
+			$mail_message = str_replace('<login_url>', get_base_url().'/login.php', $mail_message);
 			$mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'].' '.$lang_common['Mailer'], $mail_message);
 
 			pun_mail($email1, $mail_subject, $mail_message);
diff --git a/search.php b/search.php
index 28c4661..9d8b901 100644
--- a/search.php
+++ b/search.php
@@ -9,7 +9,7 @@
 // The contents of this file are very much inspired by the file search.php
 // from the phpBB Group forum software phpBB2 (http://www.phpbb.com)
 
-define('PUN_ROOT', './');
+define('PUN_ROOT', dirname(__FILE__).'/');
 require PUN_ROOT.'include/common.php';
 
 // Load the search.php language file
@@ -31,6 +31,12 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 	$forum = (isset($_GET['forum'])) ? intval($_GET['forum']) : -1;
 	$sort_dir = (isset($_GET['sort_dir']) && $_GET['sort_dir'] == 'DESC') ? 'DESC' : 'ASC';
 
+	// Allow the old action names for backwards compatibility reasons
+	if ($action == 'show_user')
+		$action = 'show_user_posts';
+	else if ($action == 'show_24h')
+		$action = 'show_recent';
+
 	// If a search_id was supplied
 	if (isset($_GET['search_id']))
 	{
@@ -61,17 +67,20 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 		$search_in = (!isset($_GET['search_in']) || $_GET['search_in'] == 'all') ? 0 : (($_GET['search_in'] == 'message') ? 1 : -1);
 	}
 	// If it's a user search (by ID)
-	else if ($action == 'show_user')
+	else if ($action == 'show_user_posts' || $action == 'show_user_topics' || $action == 'show_subscriptions')
 	{
-		$user_id = (isset($_GET['user_id'])) ? intval($_GET['user_id']) : 0;
+		$user_id = (isset($_GET['user_id'])) ? intval($_GET['user_id']) : $pun_user['id'];
 		if ($user_id < 2)
 			message($lang_common['Bad request']);
+
+		// Subscribed topics can only be viewed by admins, moderators and the users themselves
+		if ($action == 'show_subscriptions' && !$pun_user['is_admmod'] && $user_id != $pun_user['id'])
+			message($lang_common['No permission']);
 	}
-	else
-	{
-		if ($action != 'show_new' && $action != 'show_24h' && $action != 'show_unanswered' && $action != 'show_subscriptions')
-			message($lang_common['Bad request']);
-	}
+	else if ($action == 'show_recent')
+		$interval = isset($_GET['value']) ? intval($_GET['value']) : 86400;
+	else if ($action != 'show_new' && $action != 'show_unanswered')
+		message($lang_common['Bad request']);
 
 
 	// If a valid search_id was supplied we attempt to fetch the search results from the db
@@ -285,9 +294,14 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 			if (!$num_hits)
 				message($lang_search['No hits']);
 		}
-		else if ($action == 'show_new' || $action == 'show_24h' || $action == 'show_user' || $action == 'show_subscriptions' || $action == 'show_unanswered')
+		else if ($action == 'show_new' || $action == 'show_recent' || $action == 'show_user_posts' || $action == 'show_user_topics' || $action == 'show_subscriptions' || $action == 'show_unanswered')
 		{
-			// If it's a search for new posts
+			$show_as = 'topics';
+			// We want to sort things after last post
+			$sort_by = 0;
+			$sort_dir = 'DESC';
+
+			// If it's a search for new posts since last visit
 			if ($action == 'show_new')
 			{
 				if ($pun_user['is_guest'])
@@ -299,31 +313,42 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 				if (!$num_hits)
 					message($lang_search['No new posts']);
 			}
-			// If it's a search for todays posts
-			else if ($action == 'show_24h')
+			// If it's a search for recent posts (in a certain time interval)
+			else if ($action == 'show_recent')
 			{
-				$result = $db->query('SELECT t.id FROM '.$db->prefix.'topics AS t LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND t.last_post>'.(time() - 86400).' AND t.moved_to IS NULL ORDER BY t.last_post DESC') or error('Unable to fetch topic list', __FILE__, __LINE__, $db->error());
+				$result = $db->query('SELECT t.id FROM '.$db->prefix.'topics AS t LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND t.last_post>'.(time() - $interval).' AND t.moved_to IS NULL'.(isset($_GET['fid']) ? ' AND t.forum_id='.intval($_GET['fid']) : '').' ORDER BY t.last_post DESC') or error('Unable to fetch topic list', __FILE__, __LINE__, $db->error());
 				$num_hits = $db->num_rows($result);
 
 				if (!$num_hits)
 					message($lang_search['No recent posts']);
 			}
 			// If it's a search for posts by a specific user ID
-			else if ($action == 'show_user')
+			else if ($action == 'show_user_posts')
 			{
-				$result = $db->query('SELECT t.id FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'posts AS p ON t.id=p.topic_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND p.poster_id='.$user_id.' GROUP BY t.id ORDER BY t.last_post DESC') or error('Unable to fetch topic list', __FILE__, __LINE__, $db->error());
+				$show_as = 'posts';
+
+				$result = $db->query('SELECT p.id FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'topics AS t ON p.topic_id=t.id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND p.poster_id='.$user_id.' ORDER BY p.posted DESC') or error('Unable to fetch user posts', __FILE__, __LINE__, $db->error());
 				$num_hits = $db->num_rows($result);
 
 				if (!$num_hits)
 					message($lang_search['No user posts']);
 			}
+			// If it's a search for topics by a specific user ID
+			else if ($action == 'show_user_topics')
+			{
+				$result = $db->query('SELECT t.id FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'posts AS p ON t.first_post_id=p.id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND p.poster_id='.$user_id.' ORDER BY t.last_post DESC') or error('Unable to fetch user topics', __FILE__, __LINE__, $db->error());
+				$num_hits = $db->num_rows($result);
+
+				if (!$num_hits)
+					message($lang_search['No user topics']);
+			}
 			// If it's a search for subscribed topics
 			else if ($action == 'show_subscriptions')
 			{
 				if ($pun_user['is_guest'])
 					message($lang_common['Bad request']);
 
-				$result = $db->query('SELECT t.id FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'subscriptions AS s ON (t.id=s.topic_id AND s.user_id='.$pun_user['id'].') LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) ORDER BY t.last_post DESC') or error('Unable to fetch topic list', __FILE__, __LINE__, $db->error());
+				$result = $db->query('SELECT t.id FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'topic_subscriptions AS s ON (t.id=s.topic_id AND s.user_id='.$user_id.') LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) ORDER BY t.last_post DESC') or error('Unable to fetch topic list', __FILE__, __LINE__, $db->error());
 				$num_hits = $db->num_rows($result);
 
 				if (!$num_hits)
@@ -339,17 +364,11 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 					message($lang_search['No unanswered']);
 			}
 
-			// We want to sort things after last post
-			$sort_by = 4;
-			$sort_dir = 'DESC';
-
 			$search_ids = array();
 			while ($row = $db->fetch_row($result))
 				$search_ids[] = $row[0];
 
 			$db->free_result($result);
-
-			$show_as = 'topics';
 		}
 		else
 			message($lang_common['Bad request']);
@@ -381,7 +400,7 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 
 		$db->query('INSERT INTO '.$db->prefix.'search_cache (id, ident, search_data) VALUES('.$search_id.', \''.$db->escape($ident).'\', \''.$db->escape($temp).'\')') or error('Unable to insert search results', __FILE__, __LINE__, $db->error());
 
-		if ($action != 'show_new' && $action != 'show_24h')
+		if ($action != 'show_new' && $action != 'show_recent')
 		{
 			$db->end_transaction();
 			$db->close();
@@ -410,10 +429,6 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 				$sort_by_sql = 't.forum_id';
 				break;
 
-			case 4:
-				$sort_by_sql = 't.last_post';
-				break;
-
 			default:
 				$sort_by_sql = ($show_as == 'topics') ? 't.last_post' : 'p.posted';
 				break;
@@ -641,7 +656,6 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 </div>
 <?php
 
-		$footer_style = 'search';
 		require PUN_ROOT.'footer.php';
 	}
 	else
diff --git a/style/Air.css b/style/Air.css
index 00e193b..c723b6c 100644
--- a/style/Air.css
+++ b/style/Air.css
@@ -31,7 +31,7 @@ html, body, .pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3,
 	font: 81.25%/1.462em Arial, Helvetica, sans-serif;
 }
 
-.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea {
+.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun legend {
 	font-family: Arial, Helvetica, sans-serif;
 	font-size: 1em;
 }
@@ -301,6 +301,14 @@ html, body, .pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3,
 	padding: 0;
 }
 
+#brdwelcome .conl {
+  float: left;
+}
+
+#brdwelcome .conr {
+  float: right;
+}
+
 #brdwelcome li span {
 	background: url(Air/img/bull.png) center left no-repeat;
 	padding-left: 18px;
@@ -800,6 +808,7 @@ MAIN POSTS
 .pun .postmsg {
 	width:100%;
 	overflow: hidden;
+	word-wrap: break-word;
 }
 
 .pun .blockpost .postfootright {
@@ -982,6 +991,7 @@ MAIN POSTS
 .pun .postmsg .postimg img {
 	max-width: 98%;
 	vertical-align: middle;
+	margin: 7px 0.5em 7px 0;
 }
 
 .pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
@@ -1339,6 +1349,14 @@ PROFILES (+ ADMIN MENU)
 	padding: 18px;
 }
 
+#adintro li span {
+	display: inline-block;
+	padding-left: 16px;
+	margin-left: 8px;
+	line-height: normal;
+	background: url(Air/img/bull.png) center left no-repeat;
+}
+
 #adstats .inbox, #adintro .inbox {
 	border-style: solid;
 	border-width: 1px;
@@ -1603,4 +1621,4 @@ html, body, .pun {
 
 .pun .inew .icon {
 	border-color: #91b3d9 #87a8d1 #6c85bb #7292c3;
-}
\ No newline at end of file
+}
diff --git a/style/Cobalt.css b/style/Cobalt.css
index a4c49a7..dfbddbd 100644
--- a/style/Cobalt.css
+++ b/style/Cobalt.css
@@ -74,7 +74,7 @@
 	font: 68.75%/1.4545em Verdana, Helvetica, Arial, sans-serif;
 	}
 
-.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun samp {
+.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun samp, .pun legend {
 	font-size: 1em;
 	font-family: verdana, helvetica, arial, sans-serif;
 	}
@@ -714,6 +714,12 @@
 	margin-left: 1em
 	}
 
+#adintro ul {
+	list-style-type: disc;
+	margin-left: 8px;
+	padding-left: 16px;
+	}
+
 /*****************************************************************
 8. MAIN POSTS
 *****************************************************************/
@@ -793,6 +799,7 @@
 	width:98%;
 	overflow: hidden;
 	padding-bottom: 6px;
+	word-wrap: break-word;
 	}
 
 .pun .postfootright ul, .pun .postfootright div, .pun .postfootright p,
@@ -877,6 +884,7 @@
 .pun .postmsg .postimg img {
 	max-width: 98%;
 	vertical-align: middle;
+	margin: 7px 0.5em 7px 0;
 }
 
 .pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
diff --git a/style/Earth.css b/style/Earth.css
index 23f60df..a5a918a 100644
--- a/style/Earth.css
+++ b/style/Earth.css
@@ -31,7 +31,7 @@ html, body, .pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3,
 	font: 81.25%/1.462em Arial, Helvetica, sans-serif;
 }
 
-.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea {
+.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun legend {
 	font-family: Arial, Helvetica, sans-serif;
 	font-size: 1em;
 }
@@ -301,6 +301,14 @@ html, body, .pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3,
 	padding: 0;
 }
 
+#brdwelcome .conl {
+  float: left;
+}
+
+#brdwelcome .conr {
+  float: right;
+}
+
 #brdwelcome li span {
 	background: url(Earth/img/bull.png) center left no-repeat;
 	padding-left: 18px;
@@ -800,6 +808,7 @@ MAIN POSTS
 .pun .postmsg {
 	width:100%;
 	overflow: hidden;
+	word-wrap: break-word;
 }
 
 .pun .blockpost .postfootright {
@@ -982,6 +991,7 @@ MAIN POSTS
 .pun .postmsg .postimg img {
 	max-width: 98%;
 	vertical-align: middle;
+	margin: 7px 0.5em 7px 0;
 }
 
 .pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
@@ -1339,6 +1349,14 @@ PROFILES (+ ADMIN MENU)
 	padding: 18px;
 }
 
+#adintro li span {
+	display: inline-block;
+	padding-left: 16px;
+	margin-left: 8px;
+	line-height: normal;
+	background: url(Earth/img/bull.png) center left no-repeat;
+}
+
 #adstats .inbox, #adintro .inbox {
 	border-style: solid;
 	border-width: 1px;
diff --git a/style/Fire.css b/style/Fire.css
index 803980d..d9e689c 100644
--- a/style/Fire.css
+++ b/style/Fire.css
@@ -31,7 +31,7 @@ html, body, .pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3,
 	font: 81.25%/1.462em Arial, Helvetica, sans-serif;
 }
 
-.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea {
+.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun legend {
 	font-family: Arial, Helvetica, sans-serif;
 	font-size: 1em;
 }
@@ -301,6 +301,14 @@ html, body, .pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3,
 	padding: 0;
 }
 
+#brdwelcome .conl {
+  float: left;
+}
+
+#brdwelcome .conr {
+  float: right;
+}
+
 #brdwelcome li span {
 	background: url(Fire/img/bull.png) center left no-repeat;
 	padding-left: 18px;
@@ -800,6 +808,7 @@ MAIN POSTS
 .pun .postmsg {
 	width:100%;
 	overflow: hidden;
+	word-wrap: break-word;
 }
 
 .pun .blockpost .postfootright {
@@ -982,6 +991,7 @@ MAIN POSTS
 .pun .postmsg .postimg img {
 	max-width: 98%;
 	vertical-align: middle;
+	margin: 7px 0.5em 7px 0;
 }
 
 .pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
@@ -1339,6 +1349,14 @@ PROFILES (+ ADMIN MENU)
 	padding: 18px;
 }
 
+#adintro li span {
+	display: inline-block;
+	padding-left: 16px;
+	margin-left: 8px;
+	line-height: normal;
+	background: url(Fire/img/bull.png) center left no-repeat;
+}
+
 #adstats .inbox, #adintro .inbox {
 	border-style: solid;
 	border-width: 1px;
diff --git a/style/Lithium.css b/style/Lithium.css
index 96f75a0..3f472e2 100644
--- a/style/Lithium.css
+++ b/style/Lithium.css
@@ -74,7 +74,7 @@
 	font: 68.75%/1.4545em Verdana, Helvetica, Arial, sans-serif;
 	}
 
-.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun samp {
+.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun samp, .pun legend {
 	font-size: 1em;
 	font-family: verdana, helvetica, arial, sans-serif;
 	}
@@ -714,6 +714,12 @@
 	margin-left: 1em
 	}
 
+#adintro ul {
+	list-style-type: disc;
+	margin-left: 8px;
+	padding-left: 16px;
+	}
+
 /*****************************************************************
 8. MAIN POSTS
 *****************************************************************/
@@ -793,6 +799,7 @@
 	width:98%;
 	overflow: hidden;
 	padding-bottom: 6px;
+	word-wrap: break-word;
 	}
 
 .pun .postfootright ul, .pun .postfootright div, .pun .postfootright p,
@@ -877,6 +884,7 @@
 .pun .postmsg .postimg img {
 	max-width: 98%;
 	vertical-align: middle;
+	margin: 7px 0.5em 7px 0;
 }
 
 .pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
diff --git a/style/Mercury.css b/style/Mercury.css
index 708bde6..c58b601 100644
--- a/style/Mercury.css
+++ b/style/Mercury.css
@@ -74,7 +74,7 @@
 	font: 68.75%/1.4545em Verdana, Helvetica, Arial, sans-serif;
 	}
 
-.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun samp {
+.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun samp, .pun legend {
 	font-size: 1em;
 	font-family: verdana, helvetica, arial, sans-serif;
 	}
@@ -714,6 +714,12 @@
 	margin-left: 1em
 	}
 
+#adintro ul {
+	list-style-type: disc;
+	margin-left: 8px;
+	padding-left: 16px;
+	}
+
 /*****************************************************************
 8. MAIN POSTS
 *****************************************************************/
@@ -793,6 +799,7 @@
 	width:98%;
 	overflow: hidden;
 	padding-bottom: 6px;
+	word-wrap: break-word;
 	}
 
 .pun .postfootright ul, .pun .postfootright div, .pun .postfootright p,
@@ -877,6 +884,7 @@
 .pun .postmsg .postimg img {
 	max-width: 98%;
 	vertical-align: middle;
+	margin: 7px 0.5em 7px 0;
 }
 
 .pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
diff --git a/style/Oxygen.css b/style/Oxygen.css
index 4d0fe79..c4807f5 100644
--- a/style/Oxygen.css
+++ b/style/Oxygen.css
@@ -74,7 +74,7 @@
 	font: 68.75%/1.4545em Verdana, Helvetica, Arial, sans-serif;
 	}
 
-.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun small, .pun samp {
+.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun small, .pun samp, .pun legend {
 	font-size: 1em;
 	font-family: verdana, helvetica, arial, sans-serif;
 	}
@@ -715,6 +715,12 @@
 	margin-left: 1em
 	}
 
+#adintro ul {
+	list-style-type: disc;
+	margin-left: 8px;
+	padding-left: 16px;
+	}
+
 /*****************************************************************
 8. MAIN POSTS
 *****************************************************************/
@@ -794,6 +800,7 @@
 	width:98%;
 	overflow: hidden;
 	padding-bottom: 6px;
+	word-wrap: break-word;
 	}
 
 .pun .postfootright ul, .pun .postfootright div, .pun .postfootright p,
@@ -878,6 +885,7 @@
 .pun .postmsg .postimg img {
 	max-width: 98%;
 	vertical-align: middle;
+	margin: 7px 0.5em 7px 0;
 }
 
 .pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
diff --git a/style/Radium.css b/style/Radium.css
index 505c92e..49d2586 100644
--- a/style/Radium.css
+++ b/style/Radium.css
@@ -74,7 +74,7 @@
 	font: 68.75%/1.4545em Verdana, Helvetica, Arial, sans-serif;
 	}
 
-.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun samp {
+.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun samp, .pun legend {
 	font-size: 1em;
 	font-family: verdana, helvetica, arial, sans-serif;
 	}
@@ -714,6 +714,12 @@
 	margin-left: 1em
 	}
 
+#adintro ul {
+	list-style-type: disc;
+	margin-left: 8px;
+	padding-left: 16px;
+	}
+
 /*****************************************************************
 8. MAIN POSTS
 *****************************************************************/
@@ -793,6 +799,7 @@
 	width:98%;
 	overflow: hidden;
 	padding-bottom: 6px;
+	word-wrap: break-word;
 	}
 
 .pun .postfootright ul, .pun .postfootright div, .pun .postfootright p,
@@ -877,6 +884,7 @@
 .pun .postmsg .postimg img {
 	max-width: 98%;
 	vertical-align: middle;
+	margin: 7px 0.5em 7px 0;
 }
 
 .pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
diff --git a/style/Sulfur.css b/style/Sulfur.css
index 49b3e2d..3b322f1 100644
--- a/style/Sulfur.css
+++ b/style/Sulfur.css
@@ -74,7 +74,7 @@
 	font: 68.75%/1.4545em Verdana, Helvetica, Arial, sans-serif;
 	}
 
-.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun samp {
+.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun samp, .pun legend {
 	font-size: 1em;
 	font-family: verdana, helvetica, arial, sans-serif;
 	}
@@ -714,6 +714,12 @@
 	margin-left: 1em
 	}
 
+#adintro ul {
+	list-style-type: disc;
+	margin-left: 8px;
+	padding-left: 16px;
+	}
+
 /*****************************************************************
 8. MAIN POSTS
 *****************************************************************/
@@ -793,6 +799,7 @@
 	width:98%;
 	overflow: hidden;
 	padding-bottom: 6px;
+	word-wrap: break-word;
 	}
 
 .pun .postfootright ul, .pun .postfootright div, .pun .postfootright p,
@@ -877,6 +884,7 @@
 .pun .postmsg .postimg img {
 	max-width: 98%;
 	vertical-align: middle;
+	margin: 7px 0.5em 7px 0;
 }
 
 .pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
diff --git a/style/Technetium.css b/style/Technetium.css
index 6afd693..c0ecaad 100644
--- a/style/Technetium.css
+++ b/style/Technetium.css
@@ -88,7 +88,7 @@ body {
 	font: 10pt Georgia, Times, "Times New Roman", serif
 	}
 
-.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun samp {
+.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun samp, .pun legend {
 	font-size: 1em;
 	font-family: Georgia, Times, "Times New Roman", serif;
 	}
@@ -906,6 +906,7 @@ body {
 	width:98%;
 	overflow: hidden;
 	padding-bottom: 6px;
+	word-wrap: break-word;
 	}
 
 .pun .postfootright ul, .pun .postfootright div, .pun .postfootright p,
@@ -1160,6 +1161,12 @@ body {
 	border-color: #C0CCD8;
 }
 
+#adintro ul {
+	list-style-type: disc;
+	margin-left: 8px;
+	padding-left: 16px;
+	}
+
 
 /* Posts
 ----------------------------------------------------------------*/
diff --git a/userlist.php b/userlist.php
index 5c96381..9e58e83 100644
--- a/userlist.php
+++ b/userlist.php
@@ -6,7 +6,7 @@
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
 
-define('PUN_ROOT', './');
+define('PUN_ROOT', dirname(__FILE__).'/');
 require PUN_ROOT.'include/common.php';
 
 
diff --git a/viewforum.php b/viewforum.php
index 95b25d1..b3c5033 100644
--- a/viewforum.php
+++ b/viewforum.php
@@ -6,7 +6,7 @@
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
 
-define('PUN_ROOT', './');
+define('PUN_ROOT', dirname(__FILE__).'/');
 require PUN_ROOT.'include/common.php';
 
 
@@ -22,7 +22,11 @@ if ($id < 1)
 require PUN_ROOT.'lang/'.$pun_user['language'].'/forum.php';
 
 // Fetch some info about the forum
-$result = $db->query('SELECT f.forum_name, f.redirect_url, f.moderators, f.num_topics, f.sort_by, fp.post_topics FROM '.$db->prefix.'forums AS f LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND f.id='.$id) or error('Unable to fetch forum info', __FILE__, __LINE__, $db->error());
+if (!$pun_user['is_guest'])
+	$result = $db->query('SELECT f.forum_name, f.redirect_url, f.moderators, f.num_topics, f.sort_by, fp.post_topics, s.user_id AS is_subscribed FROM '.$db->prefix.'forums AS f LEFT JOIN '.$db->prefix.'forum_subscriptions AS s ON (f.id=s.forum_id AND s.user_id='.$pun_user['id'].') LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND f.id='.$id) or error('Unable to fetch forum info', __FILE__, __LINE__, $db->error());
+else
+	$result = $db->query('SELECT f.forum_name, f.redirect_url, f.moderators, f.num_topics, f.sort_by, fp.post_topics, 0 AS is_subscribed FROM '.$db->prefix.'forums AS f LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND f.id='.$id) or error('Unable to fetch forum info', __FILE__, __LINE__, $db->error());
+
 if (!$db->num_rows($result))
 	message($lang_common['Bad request']);
 
@@ -65,6 +69,21 @@ if ($pun_config['o_feed_type'] == '1')
 else if ($pun_config['o_feed_type'] == '2')
 	$page_head = array('feed' => '<link rel="alternate" type="application/atom+xml" href="extern.php?action=feed&fid='.$id.'&type=atom" title="'.$lang_common['Atom forum feed'].'" />');
 
+$forum_actions = array();
+
+if (!$pun_user['is_guest'])
+{
+	if ($pun_config['o_forum_subscriptions'] == '1')
+	{
+		if ($cur_forum['is_subscribed'])
+			$forum_actions[] = '<span>'.$lang_forum['Is subscribed'].' - </span><a href="misc.php?action=unsubscribe&fid='.$id.'">'.$lang_forum['Unsubscribe'].'</a>';
+		else
+			$forum_actions[] = '<a href="misc.php?action=subscribe&fid='.$id.'">'.$lang_forum['Subscribe'].'</a>';
+	}
+
+	$forum_actions[] = '<a href="misc.php?action=markforumread&fid='.$id.'">'.$lang_common['Mark forum read'].'</a>';
+}
+
 $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), pun_htmlspecialchars($cur_forum['forum_name']));
 define('PUN_ALLOW_INDEX', 1);
 define('PUN_ACTIVE_PAGE', 'index');
@@ -75,7 +94,7 @@ require PUN_ROOT.'header.php';
 	<div class="inbox crumbsplus">
 		<ul class="crumbs">
 			<li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li>
-			<li><span>» </span><strong><?php echo pun_htmlspecialchars($cur_forum['forum_name']) ?></strong></li>
+			<li><span>» </span><a href="viewforum.php?id=<?php echo $id ?>"><strong><?php echo pun_htmlspecialchars($cur_forum['forum_name']) ?></strong></a></li>
 		</ul>
 		<div class="pagepost">
 			<p class="pagelink conl"><?php echo $paging_links ?></p>
@@ -111,6 +130,9 @@ if ($db->num_rows($result))
 	for ($i = 0;$cur_topic_id = $db->result($result, $i);$i++)
 		$topic_ids[] = $cur_topic_id;
 
+	if (empty($topic_ids))
+		error('The topic table and forum table seem to be out of sync!', __FILE__, __LINE__);
+
 	// Fetch list of topics to display on this page
 	if ($pun_user['is_guest'] || $pun_config['o_show_dot'] == '0')
 	{
@@ -251,8 +273,9 @@ else
 		</div>
 		<ul class="crumbs">
 			<li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li>
-			<li><span>» </span><strong><?php echo pun_htmlspecialchars($cur_forum['forum_name']) ?></strong></li>
+			<li><span>» </span><a href="viewforum.php?id=<?php echo $id ?>"><strong><?php echo pun_htmlspecialchars($cur_forum['forum_name']) ?></strong></a></li>
 		</ul>
+<?php echo (!empty($forum_actions) ? "\t\t".'<p class="subscribelink clearb">'.implode(' - ', $forum_actions).'</p>'."\n" : '') ?>
 		<div class="clearer"></div>
 	</div>
 </div>
diff --git a/viewtopic.php b/viewtopic.php
index c874dcb..ee45171 100644
--- a/viewtopic.php
+++ b/viewtopic.php
@@ -6,7 +6,7 @@
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
 
-define('PUN_ROOT', './');
+define('PUN_ROOT', dirname(__FILE__).'/');
 require PUN_ROOT.'include/common.php';
 
 
@@ -27,25 +27,17 @@ require PUN_ROOT.'lang/'.$pun_user['language'].'/topic.php';
 // If a post ID is specified we determine topic ID and page number so we can redirect to the correct message
 if ($pid)
 {
-	$result = $db->query('SELECT topic_id FROM '.$db->prefix.'posts WHERE id='.$pid) or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+	$result = $db->query('SELECT topic_id, posted FROM '.$db->prefix.'posts WHERE id='.$pid) or error(__FILE__, __LINE__);
 	if (!$db->num_rows($result))
 		message($lang_common['Bad request']);
 
-	$id = $db->result($result);
+	list($id, $posted) = $db->fetch_row($result);
 
-	// Determine on what page the post is located (depending on $pun_user['disp_posts'])
-	$result = $db->query('SELECT id FROM '.$db->prefix.'posts WHERE topic_id='.$id.' ORDER BY posted') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
-	$num_posts = $db->num_rows($result);
+	// Determine on what page the post is located (depending on $forum_user['disp_posts'])
+	$result = $db->query('SELECT COUNT(id) FROM '.$db->prefix.'posts WHERE topic_id='.$id.' AND posted<'.$posted) or error(__FILE__, __LINE__);
+	$num_posts = $db->result($result) + 1;
 
-	for ($i = 0; $i < $num_posts; ++$i)
-	{
-		$cur_id = $db->result($result, $i);
-		if ($cur_id == $pid)
-			break;
-	}
-	++$i; // we started at 0
-
-	$_GET['p'] = ceil($i / $pun_user['disp_posts']);
+	$_GET['p'] = ceil($num_posts / $pun_user['disp_posts']);
 }
 
 // If action=new, we redirect to the first new post (if any)
@@ -57,7 +49,7 @@ else if ($action == 'new')
 		$tracked_topics = get_tracked_topics();
 		$last_viewed = isset($tracked_topics['topics'][$id]) ? $tracked_topics['topics'][$id] : $pun_user['last_visit'];
 
-		$result = $db->query('SELECT MIN(id) FROM '.$db->prefix.'posts WHERE topic_id='.$id.' AND posted>'.$last_viewed) or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+		$result = $db->query('SELECT MIN(id) FROM '.$db->prefix.'posts WHERE topic_id='.$id.' AND posted>'.$last_viewed) or error('Unable to fetch first new post info', __FILE__, __LINE__, $db->error());
 		$first_new_post_id = $db->result($result);
 
 		if ($first_new_post_id)
@@ -75,7 +67,7 @@ else if ($action == 'new')
 // If action=last, we redirect to the last post
 else if ($action == 'last')
 {
-	$result = $db->query('SELECT MAX(id) FROM '.$db->prefix.'posts WHERE topic_id='.$id) or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+	$result = $db->query('SELECT MAX(id) FROM '.$db->prefix.'posts WHERE topic_id='.$id) or error('Unable to fetch last post info', __FILE__, __LINE__, $db->error());
 	$last_post_id = $db->result($result);
 
 	if ($last_post_id)
@@ -88,9 +80,9 @@ else if ($action == 'last')
 
 // Fetch some info about the topic
 if (!$pun_user['is_guest'])
-	$result = $db->query('SELECT t.poster, t.subject, t.closed, t.num_replies, t.sticky, t.first_post_id, f.id AS forum_id, f.forum_name, f.moderators, fp.post_replies, s.user_id AS is_subscribed FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id LEFT JOIN '.$db->prefix.'subscriptions AS s ON (t.id=s.topic_id AND s.user_id='.$pun_user['id'].') LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND t.id='.$id.' AND t.moved_to IS NULL') or error('Unable to fetch topic info', __FILE__, __LINE__, $db->error());
+	$result = $db->query('SELECT t.poster, t.subject, t.closed, t.num_replies, t.sticky, t.first_post_id, f.id AS forum_id, f.forum_name, f.moderators, fp.post_replies, s.user_id AS is_subscribed FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id LEFT JOIN '.$db->prefix.'topic_subscriptions AS s ON (t.id=s.topic_id AND s.user_id='.$pun_user['id'].') LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND t.id='.$id.' AND t.moved_to IS NULL') or error('Unable to fetch topic info', __FILE__, __LINE__, $db->error());
 else
-	$result = $db->query('SELECT t.subject, t.closed, t.num_replies, t.sticky, t.first_post_id, f.id AS forum_id, f.forum_name, f.moderators, fp.post_replies, 0 FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND t.id='.$id.' AND t.moved_to IS NULL') or error('Unable to fetch topic info', __FILE__, __LINE__, $db->error());
+	$result = $db->query('SELECT t.subject, t.closed, t.num_replies, t.sticky, t.first_post_id, f.id AS forum_id, f.forum_name, f.moderators, fp.post_replies, 0 AS is_subscribed FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND t.id='.$id.' AND t.moved_to IS NULL') or error('Unable to fetch topic info', __FILE__, __LINE__, $db->error());
 
 if (!$db->num_rows($result))
 	message($lang_common['Bad request']);
@@ -222,13 +214,13 @@ if ($pun_config['o_quickpost'] == '1' &&
 	$quickpost = true;
 }
 
-if (!$pun_user['is_guest'] && $pun_config['o_subscriptions'] == '1')
+if (!$pun_user['is_guest'] && $pun_config['o_topic_subscriptions'] == '1')
 {
 	if ($cur_topic['is_subscribed'])
 		// I apologize for the variable naming here. It's a mix of subscription and action I guess :-)
-		$subscraction = "\t\t".'<p class="subscribelink clearb"><span>'.$lang_topic['Is subscribed'].' - </span><a href="misc.php?unsubscribe='.$id.'">'.$lang_topic['Unsubscribe'].'</a></p>'."\n";
+		$subscraction = "\t\t".'<p class="subscribelink clearb"><span>'.$lang_topic['Is subscribed'].' - </span><a href="misc.php?action=unsubscribe&tid='.$id.'">'.$lang_topic['Unsubscribe'].'</a></p>'."\n";
 	else
-		$subscraction = "\t\t".'<p class="subscribelink clearb"><a href="misc.php?subscribe='.$id.'">'.$lang_topic['Subscribe'].'</a></p>'."\n";
+		$subscraction = "\t\t".'<p class="subscribelink clearb"><a href="misc.php?action=subscribe&tid='.$id.'">'.$lang_topic['Subscribe'].'</a></p>'."\n";
 }
 else
 	$subscraction = '';
@@ -249,7 +241,7 @@ require PUN_ROOT.'header.php';
 		<ul class="crumbs">
 			<li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li>
 			<li><span>» </span><a href="viewforum.php?id=<?php echo $cur_topic['forum_id'] ?>"><?php echo pun_htmlspecialchars($cur_topic['forum_name']) ?></a></li>
-			<li><span>» </span><strong><?php echo pun_htmlspecialchars($cur_topic['subject']) ?></strong></li>
+			<li><span>» </span><a href="viewtopic.php?id=<?php echo $id ?>"><strong><?php echo pun_htmlspecialchars($cur_topic['subject']) ?></strong></a></li>
 		</ul>
 		<div class="pagepost">
 			<p class="pagelink conl"><?php echo $paging_links ?></p>
@@ -273,6 +265,9 @@ $post_ids = array();
 for ($i = 0;$cur_post_id = $db->result($result, $i);$i++)
 	$post_ids[] = $cur_post_id;
 
+if (empty($post_ids))
+	error('The post table and topic table seem to be out of sync!', __FILE__, __LINE__);
+
 // Retrieve the posts (and their respective poster/online status)
 $result = $db->query('SELECT u.email, u.title, u.url, u.location, u.signature, u.email_setting, u.num_posts, u.registered, u.admin_note, p.id, p.poster AS username, p.poster_id, p.poster_ip, p.poster_email, p.message, p.hide_smilies, p.posted, p.edited, p.edited_by, g.g_id, g.g_user_title, o.user_id AS is_online FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'users AS u ON u.id=p.poster_id INNER JOIN '.$db->prefix.'groups AS g ON g.g_id=u.group_id LEFT JOIN '.$db->prefix.'online AS o ON (o.user_id=u.id AND o.user_id!=1 AND o.idle=0) WHERE p.id IN ('.implode(',', $post_ids).') ORDER BY p.id', true) or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
 while ($cur_post = $db->fetch_assoc($result))
@@ -452,7 +447,7 @@ while ($cur_post = $db->fetch_assoc($result))
 		<ul class="crumbs">
 			<li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li>
 			<li><span>» </span><a href="viewforum.php?id=<?php echo $cur_topic['forum_id'] ?>"><?php echo pun_htmlspecialchars($cur_topic['forum_name']) ?></a></li>
-			<li><span>» </span><strong><?php echo pun_htmlspecialchars($cur_topic['subject']) ?></strong></li>
+			<li><span>» </span><a href="viewtopic.php?id=<?php echo $id ?>"><strong><?php echo pun_htmlspecialchars($cur_topic['subject']) ?></strong></a></li>
 		</ul>
 <?php echo $subscraction ?>
 		<div class="clearer"></div>
@@ -476,7 +471,7 @@ if ($quickpost)
 					<div class="infldset txtarea">
 						<input type="hidden" name="form_sent" value="1" />
 						<input type="hidden" name="form_user" value="<?php echo pun_htmlspecialchars($pun_user['username']) ?>" />
-<?php if ($pun_config['o_subscriptions'] == '1' && ($pun_user['auto_notify'] == '1' || $cur_topic['is_subscribed'])): ?>						<input type="hidden" name="subscribe" value="1" />
+<?php if ($pun_config['o_topic_subscriptions'] == '1' && ($pun_user['auto_notify'] == '1' || $cur_topic['is_subscribed'])): ?>						<input type="hidden" name="subscribe" value="1" />
 <?php endif; ?>						<label><textarea name="req_message" rows="7" cols="75" tabindex="1"></textarea></label>
 						<ul class="bblinks">
 							<li><span><a href="help.php#bbcode" onclick="window.open(this.href); return false;"><?php echo $lang_common['BBCode'] ?></a> <?php echo ($pun_config['p_message_bbcode'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></span></li>



More information about the Xfce4-commits mailing list