diff --git a/.gitignore b/.gitignore
index fef353b..194371a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,13 @@
+/*.sublime-project
+/.project
+/.settings/
+/nbproject/
+*.sublime-project
 /cache/cache_*.php
+/cache/*.cache
 /cache/db_update.lock
 /modules/
-/include/user/*
+/include/user/*.php
 /img/avatars/*.png
 /img/avatars/*.jpg
 /img/avatars/*.gif
diff --git a/admin_bans.php b/admin_bans.php
index acd2a6e..8b70428 100644
--- a/admin_bans.php
+++ b/admin_bans.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -15,7 +15,7 @@ require PUN_ROOT.'include/common_admin.php';
 
 
 if ($pun_user['g_id'] != PUN_ADMIN && ($pun_user['g_moderator'] != '1' || $pun_user['g_mod_ban_users'] == '0'))
-	message($lang_common['No permission']);
+	message($lang_common['No permission'], false, '403 Forbidden');
 
 // Load the admin_bans.php language file
 require PUN_ROOT.'lang/'.$admin_language.'/admin_bans.php';
@@ -183,16 +183,16 @@ else if (isset($_POST['add_edit_ban']))
 	confirm_referrer('admin_bans.php');
 
 	$ban_user = pun_trim($_POST['ban_user']);
-	$ban_ip = trim($_POST['ban_ip']);
-	$ban_email = strtolower(trim($_POST['ban_email']));
+	$ban_ip = pun_trim($_POST['ban_ip']);
+	$ban_email = strtolower(pun_trim($_POST['ban_email']));
 	$ban_message = pun_trim($_POST['ban_message']);
-	$ban_expire = trim($_POST['ban_expire']);
+	$ban_expire = pun_trim($_POST['ban_expire']);
 
 	if ($ban_user == '' && $ban_ip == '' && $ban_email == '')
 		message($lang_admin_bans['Must enter message']);
 	else if (strtolower($ban_user) == 'guest')
 		message($lang_admin_bans['Cannot ban guest message']);
-	
+
 	// Make sure we're not banning an admin or moderator
 	if (!empty($ban_user))
 	{
@@ -200,13 +200,13 @@ else if (isset($_POST['add_edit_ban']))
 		if ($db->num_rows($result))
 		{
 			$group_id = $db->result($result);
-			
+
 			if ($group_id == PUN_ADMIN)
 				message(sprintf($lang_admin_bans['User is admin message'], pun_htmlspecialchars($ban_user)));
-	
+
 			$result = $db->query('SELECT g_moderator FROM '.$db->prefix.'groups WHERE g_id='.$group_id) or error('Unable to fetch group info', __FILE__, __LINE__, $db->error());
 			$is_moderator_group = $db->result($result);
-	
+
 			if ($is_moderator_group)
 				message(sprintf($lang_admin_bans['User is mod message'], pun_htmlspecialchars($ban_user)));
 		}
@@ -215,7 +215,7 @@ else if (isset($_POST['add_edit_ban']))
 	// Validate IP/IP range (it's overkill, I know)
 	if ($ban_ip != '')
 	{
-		$ban_ip = preg_replace('/\s{2,}/S', ' ', $ban_ip);
+		$ban_ip = preg_replace('%\s{2,}%S', ' ', $ban_ip);
 		$addresses = explode(' ', $ban_ip);
 		$addresses = array_map('pun_trim', $addresses);
 
@@ -244,7 +244,7 @@ else if (isset($_POST['add_edit_ban']))
 				{
 					$octets[$c] = (strlen($octets[$c]) > 1) ? ltrim($octets[$c], "0") : $octets[$c];
 
-					if ($c > 3 || preg_match('/[^0-9]/', $octets[$c]) || intval($octets[$c]) > 255)
+					if ($c > 3 || preg_match('%[^0-9]%', $octets[$c]) || intval($octets[$c]) > 255)
 						message($lang_admin_bans['Invalid IP message']);
 				}
 
@@ -259,7 +259,7 @@ else if (isset($_POST['add_edit_ban']))
 	require PUN_ROOT.'include/email.php';
 	if ($ban_email != '' && !is_valid_email($ban_email))
 	{
-		if (!preg_match('/^[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$/', $ban_email))
+		if (!preg_match('%^[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$%', $ban_email))
 			message($lang_admin_bans['Invalid e-mail message']);
 	}
 
@@ -330,8 +330,8 @@ else if (isset($_GET['find_ban']))
 	$form = array_map('pun_trim', $form);
 	$conditions = $query_str = array();
 
-	$expire_after = isset($_GET['expire_after']) ? trim($_GET['expire_after']) : '';
-	$expire_before = isset($_GET['expire_before']) ? trim($_GET['expire_before']) : '';
+	$expire_after = isset($_GET['expire_after']) ? pun_trim($_GET['expire_after']) : '';
+	$expire_before = isset($_GET['expire_before']) ? pun_trim($_GET['expire_before']) : '';
 	$order_by = isset($_GET['order_by']) && in_array($_GET['order_by'], array('username', 'ip', 'email', 'expire')) ? 'b.'.$_GET['order_by'] : 'b.username';
 	$direction = isset($_GET['direction']) && $_GET['direction'] == 'DESC' ? 'DESC' : 'ASC';
 
diff --git a/admin_categories.php b/admin_categories.php
index 2877cd3..ab4d430 100644
--- a/admin_categories.php
+++ b/admin_categories.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -15,7 +15,7 @@ require PUN_ROOT.'include/common_admin.php';
 
 
 if ($pun_user['g_id'] != PUN_ADMIN)
-	message($lang_common['No permission']);
+	message($lang_common['No permission'], false, '403 Forbidden');
 
 // Load the admin_categories.php language file
 require PUN_ROOT.'lang/'.$admin_language.'/admin_categories.php';
@@ -133,12 +133,12 @@ else if (isset($_POST['update'])) // Change position and name of the categories
 	foreach ($categories as $cat_id => $cur_cat)
 	{
 		$cur_cat['name'] = pun_trim($cur_cat['name']);
-		$cur_cat['order'] = trim($cur_cat['order']);
+		$cur_cat['order'] = pun_trim($cur_cat['order']);
 
 		if ($cur_cat['name'] == '')
 			message($lang_admin_categories['Must enter name message']);
 
-		if ($cur_cat['order'] == '' || preg_match('/[^0-9]/', $cur_cat['order']))
+		if ($cur_cat['order'] == '' || preg_match('%[^0-9]%', $cur_cat['order']))
 			message($lang_admin_categories['Must enter integer message']);
 
 		$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());
diff --git a/admin_censoring.php b/admin_censoring.php
index 06d2a85..420ca62 100644
--- a/admin_censoring.php
+++ b/admin_censoring.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -15,7 +15,7 @@ require PUN_ROOT.'include/common_admin.php';
 
 
 if ($pun_user['g_id'] != PUN_ADMIN)
-	message($lang_common['No permission']);
+	message($lang_common['No permission'], false, '403 Forbidden');
 
 // Load the admin_censoring.php language file
 require PUN_ROOT.'lang/'.$admin_language.'/admin_censoring.php';
diff --git a/admin_forums.php b/admin_forums.php
index 9fe2adc..1f1c13f 100644
--- a/admin_forums.php
+++ b/admin_forums.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -15,7 +15,7 @@ require PUN_ROOT.'include/common_admin.php';
 
 
 if ($pun_user['g_id'] != PUN_ADMIN)
-	message($lang_common['No permission']);
+	message($lang_common['No permission'], false, '403 Forbidden');
 
 // Load the admin_forums.php language file
 require PUN_ROOT.'lang/'.$admin_language.'/admin_forums.php';
@@ -128,7 +128,7 @@ else if (isset($_POST['update_positions']))
 	foreach ($_POST['position'] as $forum_id => $disp_position)
 	{
 		$disp_position = trim($disp_position);
-		if ($disp_position == '' || preg_match('/[^0-9]/', $disp_position))
+		if ($disp_position == '' || preg_match('%[^0-9]%', $disp_position))
 			message($lang_admin_forums['Must be integer message']);
 
 		$db->query('UPDATE '.$db->prefix.'forums SET disp_position='.$disp_position.' WHERE id='.intval($forum_id)) or error('Unable to update forum', __FILE__, __LINE__, $db->error());
@@ -159,7 +159,7 @@ else if (isset($_GET['edit_forum']))
 		$forum_desc = pun_linebreaks(pun_trim($_POST['forum_desc']));
 		$cat_id = intval($_POST['cat_id']);
 		$sort_by = intval($_POST['sort_by']);
-		$redirect_url = isset($_POST['redirect_url']) ? trim($_POST['redirect_url']) : null;
+		$redirect_url = isset($_POST['redirect_url']) ? pun_trim($_POST['redirect_url']) : null;
 
 		if ($forum_name == '')
 			message($lang_admin_forums['Must enter name message']);
@@ -308,6 +308,8 @@ else if (isset($_GET['edit_forum']))
 
 	$result = $db->query('SELECT g.g_id, g.g_title, g.g_read_board, g.g_post_replies, g.g_post_topics, fp.read_forum, fp.post_replies, fp.post_topics FROM '.$db->prefix.'groups AS g LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (g.g_id=fp.group_id AND fp.forum_id='.$forum_id.') WHERE g.g_id!='.PUN_ADMIN.' ORDER BY g.g_id') or error('Unable to fetch group forum permission list', __FILE__, __LINE__, $db->error());
 
+	$cur_index = 7;
+
 	while ($cur_perm = $db->fetch_assoc($result))
 	{
 		$read_forum = ($cur_perm['read_forum'] != '0') ? true : false;
@@ -324,15 +326,15 @@ else if (isset($_GET['edit_forum']))
 									<th class="atcl"><?php echo pun_htmlspecialchars($cur_perm['g_title']) ?></th>
 									<td<?php if (!$read_forum_def) echo ' class="nodefault"'; ?>>
 										<input type="hidden" name="read_forum_old[<?php echo $cur_perm['g_id'] ?>]" value="<?php echo ($read_forum) ? '1' : '0'; ?>" />
-										<input type="checkbox" name="read_forum_new[<?php echo $cur_perm['g_id'] ?>]" value="1"<?php echo ($read_forum) ? ' checked="checked"' : ''; ?><?php echo ($cur_perm['g_read_board'] == '0') ? ' disabled="disabled"' : ''; ?> />
+										<input type="checkbox" name="read_forum_new[<?php echo $cur_perm['g_id'] ?>]" value="1"<?php echo ($read_forum) ? ' checked="checked"' : ''; ?><?php echo ($cur_perm['g_read_board'] == '0') ? ' disabled="disabled"' : ''; ?> tabindex="<?php echo $cur_index++ ?>" />
 									</td>
 									<td<?php if (!$post_replies_def && $cur_forum['redirect_url'] == '') echo ' class="nodefault"'; ?>>
 										<input type="hidden" name="post_replies_old[<?php echo $cur_perm['g_id'] ?>]" value="<?php echo ($post_replies) ? '1' : '0'; ?>" />
-										<input type="checkbox" name="post_replies_new[<?php echo $cur_perm['g_id'] ?>]" value="1"<?php echo ($post_replies) ? ' checked="checked"' : ''; ?><?php echo ($cur_forum['redirect_url'] != '') ? ' disabled="disabled"' : ''; ?> />
+										<input type="checkbox" name="post_replies_new[<?php echo $cur_perm['g_id'] ?>]" value="1"<?php echo ($post_replies) ? ' checked="checked"' : ''; ?><?php echo ($cur_forum['redirect_url'] != '') ? ' disabled="disabled"' : ''; ?> tabindex="<?php echo $cur_index++ ?>" />
 									</td>
 									<td<?php if (!$post_topics_def && $cur_forum['redirect_url'] == '') echo ' class="nodefault"'; ?>>
 										<input type="hidden" name="post_topics_old[<?php echo $cur_perm['g_id'] ?>]" value="<?php echo ($post_topics) ? '1' : '0'; ?>" />
-										<input type="checkbox" name="post_topics_new[<?php echo $cur_perm['g_id'] ?>]" value="1"<?php echo ($post_topics) ? ' checked="checked"' : ''; ?><?php echo ($cur_forum['redirect_url'] != '') ? ' disabled="disabled"' : ''; ?> />
+										<input type="checkbox" name="post_topics_new[<?php echo $cur_perm['g_id'] ?>]" value="1"<?php echo ($post_topics) ? ' checked="checked"' : ''; ?><?php echo ($cur_forum['redirect_url'] != '') ? ' disabled="disabled"' : ''; ?> tabindex="<?php echo $cur_index++ ?>" />
 									</td>
 								</tr>
 <?php
@@ -342,11 +344,11 @@ else if (isset($_GET['edit_forum']))
 ?>
 							</tbody>
 							</table>
-							<div class="fsetsubmit"><input type="submit" name="revert_perms" value="<?php echo $lang_admin_forums['Revert to default'] ?>" /></div>
+							<div class="fsetsubmit"><input type="submit" name="revert_perms" value="<?php echo $lang_admin_forums['Revert to default'] ?>" tabindex="<?php echo $cur_index++ ?>" /></div>
 						</div>
 					</fieldset>
 				</div>
-				<p class="submitend"><input type="submit" name="save" value="<?php echo $lang_admin_common['Save changes'] ?>" /></p>
+				<p class="submitend"><input type="submit" name="save" value="<?php echo $lang_admin_common['Save changes'] ?>" tabindex="<?php echo $cur_index++ ?>" /></p>
 			</form>
 		</div>
 	</div>
@@ -415,7 +417,7 @@ if ($db->num_rows($result) > 0)
 				<p class="submittop"><input type="submit" name="update_positions" value="<?php echo $lang_admin_forums['Update positions'] ?>" tabindex="3" /></p>
 <?php
 
-$tabindex_count = 4;
+$cur_index = 4;
 
 $cur_category = 0;
 while ($cur_forum = $db->fetch_assoc($result))
@@ -446,13 +448,12 @@ while ($cur_forum = $db->fetch_assoc($result))
 
 ?>
 								<tr>
-									<td class="tcl"><a href="admin_forums.php?edit_forum=<?php echo $cur_forum['fid'] ?>"><?php echo $lang_admin_forums['Edit link'] ?></a> | <a href="admin_forums.php?del_forum=<?php echo $cur_forum['fid'] ?>"><?php echo $lang_admin_forums['Delete link'] ?></a></td>
-									<td class="tc2"><input type="text" name="position[<?php echo $cur_forum['fid'] ?>]" size="3" maxlength="3" value="<?php echo $cur_forum['disp_position'] ?>" tabindex="<?php echo $tabindex_count ?>" /></td>
+									<td class="tcl"><a href="admin_forums.php?edit_forum=<?php echo $cur_forum['fid'] ?>" tabindex="<?php echo $cur_index++ ?>"><?php echo $lang_admin_forums['Edit link'] ?></a> | <a href="admin_forums.php?del_forum=<?php echo $cur_forum['fid'] ?>" tabindex="<?php echo $cur_index++ ?>"><?php echo $lang_admin_forums['Delete link'] ?></a></td>
+									<td class="tc2"><input type="text" name="position[<?php echo $cur_forum['fid'] ?>]" size="3" maxlength="3" value="<?php echo $cur_forum['disp_position'] ?>" tabindex="<?php echo $cur_index++ ?>" /></td>
 									<td class="tcr"><strong><?php echo pun_htmlspecialchars($cur_forum['forum_name']) ?></strong></td>
 								</tr>
 <?php
 
-	$tabindex_count += 2;
 }
 
 ?>
@@ -461,7 +462,7 @@ while ($cur_forum = $db->fetch_assoc($result))
 						</div>
 					</fieldset>
 				</div>
-				<p class="submitend"><input type="submit" name="update_positions" value="<?php echo $lang_admin_forums['Update positions'] ?>" tabindex="<?php echo $tabindex_count ?>" /></p>
+				<p class="submitend"><input type="submit" name="update_positions" value="<?php echo $lang_admin_forums['Update positions'] ?>" tabindex="<?php echo $cur_index++ ?>" /></p>
 			</form>
 		</div>
 <?php
diff --git a/admin_groups.php b/admin_groups.php
index 1e1e9bd..888befc 100644
--- a/admin_groups.php
+++ b/admin_groups.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -15,7 +15,7 @@ require PUN_ROOT.'include/common_admin.php';
 
 
 if ($pun_user['g_id'] != PUN_ADMIN)
-	message($lang_common['No permission']);
+	message($lang_common['No permission'], false, '403 Forbidden');
 
 // Load the admin_censoring.php language file
 require PUN_ROOT.'lang/'.$admin_language.'/admin_groups.php';
@@ -101,128 +101,135 @@ if (isset($_POST['add_group']) || isset($_GET['edit_group']))
 								<tr>
 									<th scope="row"><?php echo $lang_admin_groups['Rename users label'] ?></th>
 									<td>
-										<input type="radio" name="mod_rename_users" value="1"<?php if ($group['g_mod_rename_users'] == '1') echo ' checked="checked"' ?> tabindex="5" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="mod_rename_users" value="0"<?php if ($group['g_mod_rename_users'] == '0') echo ' checked="checked"' ?> tabindex="6" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="mod_rename_users" value="1"<?php if ($group['g_mod_rename_users'] == '1') echo ' checked="checked"' ?> tabindex="7" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="mod_rename_users" value="0"<?php if ($group['g_mod_rename_users'] == '0') echo ' checked="checked"' ?> tabindex="8" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
 										<span><?php echo $lang_admin_groups['Rename users help'] ?></span>
 									</td>
 								</tr>
 								<tr>
 									<th scope="row"><?php echo $lang_admin_groups['Change passwords label'] ?></th>
 									<td>
-										<input type="radio" name="mod_change_passwords" value="1"<?php if ($group['g_mod_change_passwords'] == '1') echo ' checked="checked"' ?> tabindex="5" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="mod_change_passwords" value="0"<?php if ($group['g_mod_change_passwords'] == '0') echo ' checked="checked"' ?> tabindex="6" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="mod_change_passwords" value="1"<?php if ($group['g_mod_change_passwords'] == '1') echo ' checked="checked"' ?> tabindex="9" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="mod_change_passwords" value="0"<?php if ($group['g_mod_change_passwords'] == '0') echo ' checked="checked"' ?> tabindex="10" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
 										<span><?php echo $lang_admin_groups['Change passwords help'] ?></span>
 									</td>
 								</tr>
 								<tr>
 									<th scope="row"><?php echo $lang_admin_groups['Ban users label'] ?></th>
 									<td>
-										<input type="radio" name="mod_ban_users" value="1"<?php if ($group['g_mod_ban_users'] == '1') echo ' checked="checked"' ?> tabindex="5" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="mod_ban_users" value="0"<?php if ($group['g_mod_ban_users'] == '0') echo ' checked="checked"' ?> tabindex="6" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="mod_ban_users" value="1"<?php if ($group['g_mod_ban_users'] == '1') echo ' checked="checked"' ?> tabindex="11" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="mod_ban_users" value="0"<?php if ($group['g_mod_ban_users'] == '0') echo ' checked="checked"' ?> tabindex="12" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
 										<span><?php echo $lang_admin_groups['Ban users help'] ?></span>
 									</td>
 								</tr>
 <?php endif; endif; ?>								<tr>
 									<th scope="row"><?php echo $lang_admin_groups['Read board label'] ?></th>
 									<td>
-										<input type="radio" name="read_board" value="1"<?php if ($group['g_read_board'] == '1') echo ' checked="checked"' ?> tabindex="3" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="read_board" value="0"<?php if ($group['g_read_board'] == '0') echo ' checked="checked"' ?> tabindex="4" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="read_board" value="1"<?php if ($group['g_read_board'] == '1') echo ' checked="checked"' ?> tabindex="13" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="read_board" value="0"<?php if ($group['g_read_board'] == '0') echo ' checked="checked"' ?> tabindex="14" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
 										<span><?php echo $lang_admin_groups['Read board help'] ?></span>
 									</td>
 								</tr>
 								<tr>
 									<th scope="row"><?php echo $lang_admin_groups['View user info label'] ?></th>
 									<td>
-										<input type="radio" name="view_users" value="1"<?php if ($group['g_view_users'] == '1') echo ' checked="checked"' ?> tabindex="3" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="view_users" value="0"<?php if ($group['g_view_users'] == '0') echo ' checked="checked"' ?> tabindex="4" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="view_users" value="1"<?php if ($group['g_view_users'] == '1') echo ' checked="checked"' ?> tabindex="15" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="view_users" value="0"<?php if ($group['g_view_users'] == '0') echo ' checked="checked"' ?> tabindex="16" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
 										<span><?php echo $lang_admin_groups['View user info help'] ?></span>
 									</td>
 								</tr>
 								<tr>
 									<th scope="row"><?php echo $lang_admin_groups['Post replies label'] ?></th>
 									<td>
-										<input type="radio" name="post_replies" value="1"<?php if ($group['g_post_replies'] == '1') echo ' checked="checked"' ?> tabindex="5" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="post_replies" value="0"<?php if ($group['g_post_replies'] == '0') echo ' checked="checked"' ?> tabindex="6" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="post_replies" value="1"<?php if ($group['g_post_replies'] == '1') echo ' checked="checked"' ?> tabindex="17" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="post_replies" value="0"<?php if ($group['g_post_replies'] == '0') echo ' checked="checked"' ?> tabindex="18" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
 										<span><?php echo $lang_admin_groups['Post replies help'] ?></span>
 									</td>
 								</tr>
 								<tr>
 									<th scope="row"><?php echo $lang_admin_groups['Post topics label'] ?></th>
 									<td>
-										<input type="radio" name="post_topics" value="1"<?php if ($group['g_post_topics'] == '1') echo ' checked="checked"' ?> tabindex="7" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="post_topics" value="0"<?php if ($group['g_post_topics'] == '0') echo ' checked="checked"' ?> tabindex="8" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="post_topics" value="1"<?php if ($group['g_post_topics'] == '1') echo ' checked="checked"' ?> tabindex="19" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="post_topics" value="0"<?php if ($group['g_post_topics'] == '0') echo ' checked="checked"' ?> tabindex="20" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
 										<span><?php echo $lang_admin_groups['Post topics help'] ?></span>
 									</td>
 								</tr>
 <?php if ($group['g_id'] != PUN_GUEST): ?>								<tr>
 									<th scope="row"><?php echo $lang_admin_groups['Edit posts label'] ?></th>
 									<td>
-										<input type="radio" name="edit_posts" value="1"<?php if ($group['g_edit_posts'] == '1') echo ' checked="checked"' ?> tabindex="11" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="edit_posts" value="0"<?php if ($group['g_edit_posts'] == '0') echo ' checked="checked"' ?> tabindex="12" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="edit_posts" value="1"<?php if ($group['g_edit_posts'] == '1') echo ' checked="checked"' ?> tabindex="21" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="edit_posts" value="0"<?php if ($group['g_edit_posts'] == '0') echo ' checked="checked"' ?> tabindex="22" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
 										<span><?php echo $lang_admin_groups['Edit posts help'] ?></span>
 									</td>
 								</tr>
 								<tr>
 									<th scope="row"><?php echo $lang_admin_groups['Delete posts label'] ?></th>
 									<td>
-										<input type="radio" name="delete_posts" value="1"<?php if ($group['g_delete_posts'] == '1') echo ' checked="checked"' ?> tabindex="13" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="delete_posts" value="0"<?php if ($group['g_delete_posts'] == '0') echo ' checked="checked"' ?> tabindex="14" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="delete_posts" value="1"<?php if ($group['g_delete_posts'] == '1') echo ' checked="checked"' ?> tabindex="23" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="delete_posts" value="0"<?php if ($group['g_delete_posts'] == '0') echo ' checked="checked"' ?> tabindex="24" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
 										<span><?php echo $lang_admin_groups['Delete posts help'] ?></span>
 									</td>
 								</tr>
 								<tr>
 									<th scope="row"><?php echo $lang_admin_groups['Delete topics label'] ?></th>
 									<td>
-										<input type="radio" name="delete_topics" value="1"<?php if ($group['g_delete_topics'] == '1') echo ' checked="checked"' ?> tabindex="15" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="delete_topics" value="0"<?php if ($group['g_delete_topics'] == '0') echo ' checked="checked"' ?> tabindex="16" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="delete_topics" value="1"<?php if ($group['g_delete_topics'] == '1') echo ' checked="checked"' ?> tabindex="25" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="delete_topics" value="0"<?php if ($group['g_delete_topics'] == '0') echo ' checked="checked"' ?> tabindex="26" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
 										<span><?php echo $lang_admin_groups['Delete topics help'] ?></span>
 									</td>
 								</tr>
 								<tr>
 									<th scope="row"><?php echo $lang_admin_groups['Set own title label'] ?></th>
 									<td>
-										<input type="radio" name="set_title" value="1"<?php if ($group['g_set_title'] == '1') echo ' checked="checked"' ?> tabindex="17" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="set_title" value="0"<?php if ($group['g_set_title'] == '0') echo ' checked="checked"' ?> tabindex="18" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="set_title" value="1"<?php if ($group['g_set_title'] == '1') echo ' checked="checked"' ?> tabindex="27" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="set_title" value="0"<?php if ($group['g_set_title'] == '0') echo ' checked="checked"' ?> tabindex="28" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
 										<span><?php echo $lang_admin_groups['Set own title help'] ?></span>
 									</td>
 								</tr>
 <?php endif; ?>								<tr>
 									<th scope="row"><?php echo $lang_admin_groups['User search label'] ?></th>
 									<td>
-										<input type="radio" name="search" value="1"<?php if ($group['g_search'] == '1') echo ' checked="checked"' ?> tabindex="19" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="search" value="0"<?php if ($group['g_search'] == '0') echo ' checked="checked"' ?> tabindex="20" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="search" value="1"<?php if ($group['g_search'] == '1') echo ' checked="checked"' ?> tabindex="29" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="search" value="0"<?php if ($group['g_search'] == '0') echo ' checked="checked"' ?> tabindex="30" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
 										<span><?php echo $lang_admin_groups['User search help'] ?></span>
 									</td>
 								</tr>
 								<tr>
 									<th scope="row"><?php echo $lang_admin_groups['User list search label'] ?></th>
 									<td>
-										<input type="radio" name="search_users" value="1"<?php if ($group['g_search_users'] == '1') echo ' checked="checked"' ?> tabindex="21" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="search_users" value="0"<?php if ($group['g_search_users'] == '0') echo ' checked="checked"' ?> tabindex="22" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="search_users" value="1"<?php if ($group['g_search_users'] == '1') echo ' checked="checked"' ?> tabindex="31" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="search_users" value="0"<?php if ($group['g_search_users'] == '0') echo ' checked="checked"' ?> tabindex="32" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
 										<span><?php echo $lang_admin_groups['User list search help'] ?></span>
 									</td>
 								</tr>
 <?php if ($group['g_id'] != PUN_GUEST): ?>								<tr>
 									<th scope="row"><?php echo $lang_admin_groups['Send e-mails label'] ?></th>
 									<td>
-										<input type="radio" name="send_email" value="1"<?php if ($group['g_send_email'] == '1') echo ' checked="checked"' ?> tabindex="21" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="send_email" value="0"<?php if ($group['g_send_email'] == '0') echo ' checked="checked"' ?> tabindex="22" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="send_email" value="1"<?php if ($group['g_send_email'] == '1') echo ' checked="checked"' ?> tabindex="33" />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="send_email" value="0"<?php if ($group['g_send_email'] == '0') echo ' checked="checked"' ?> tabindex="34" />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
 										<span><?php echo $lang_admin_groups['Send e-mails help'] ?></span>
 									</td>
 								</tr>
 <?php endif; ?>								<tr>
 									<th scope="row"><?php echo $lang_admin_groups['Post flood label'] ?></th>
 									<td>
-										<input type="text" name="post_flood" size="5" maxlength="4" value="<?php echo $group['g_post_flood'] ?>" tabindex="24" />
+										<input type="text" name="post_flood" size="5" maxlength="4" value="<?php echo $group['g_post_flood'] ?>" tabindex="35" />
 										<span><?php echo $lang_admin_groups['Post flood help'] ?></span>
 									</td>
 								</tr>
 								<tr>
 									<th scope="row"><?php echo $lang_admin_groups['Search flood label'] ?></th>
 									<td>
-										<input type="text" name="search_flood" size="5" maxlength="4" value="<?php echo $group['g_search_flood'] ?>" tabindex="25" />
+										<input type="text" name="search_flood" size="5" maxlength="4" value="<?php echo $group['g_search_flood'] ?>" tabindex="36" />
 										<span><?php echo $lang_admin_groups['Search flood help'] ?></span>
 									</td>
 								</tr>
 <?php if ($group['g_id'] != PUN_GUEST): ?>								<tr>
 									<th scope="row"><?php echo $lang_admin_groups['E-mail flood label'] ?></th>
 									<td>
-										<input type="text" name="email_flood" size="5" maxlength="4" value="<?php echo $group['g_email_flood'] ?>" tabindex="26" />
+										<input type="text" name="email_flood" size="5" maxlength="4" value="<?php echo $group['g_email_flood'] ?>" tabindex="37" />
 										<span><?php echo $lang_admin_groups['E-mail flood help'] ?></span>
 									</td>
 								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_groups['Report flood label'] ?></th>
+									<td>
+										<input type="text" name="report_flood" size="5" maxlength="4" value="<?php echo $group['g_report_flood'] ?>" tabindex="38" />
+										<span><?php echo $lang_admin_groups['Report flood help'] ?></span>
+									</td>
+								</tr>
 <?php endif; endif; ?>							</table>
 <?php if ($group['g_moderator'] == '1' ): ?>							<p class="warntext"><?php echo $lang_admin_groups['Moderator info'] ?></p>
 <?php endif; ?>						</div>
 					</fieldset>
 				</div>
-				<p class="submitend"><input type="submit" name="add_edit_group" value="<?php echo $lang_admin_common['Save'] ?>" tabindex="26" /></p>
+				<p class="submitend"><input type="submit" name="add_edit_group" value="<?php echo $lang_admin_common['Save'] ?>" tabindex="39" /></p>
 			</form>
 		</div>
 	</div>
@@ -260,9 +267,10 @@ else if (isset($_POST['add_edit_group']))
 	$search = isset($_POST['search']) ? intval($_POST['search']) : '1';
 	$search_users = isset($_POST['search_users']) ? intval($_POST['search_users']) : '1';
 	$send_email = (isset($_POST['send_email']) && $_POST['send_email'] == '1') || $is_admin_group ? '1' : '0';
-	$post_flood = isset($_POST['post_flood']) ? intval($_POST['post_flood']) : '0';
-	$search_flood = isset($_POST['search_flood']) ? intval($_POST['search_flood']) : '0';
-	$email_flood = isset($_POST['email_flood']) ? intval($_POST['email_flood']) : '0';
+	$post_flood = (isset($_POST['post_flood']) && $_POST['post_flood'] >= 0) ? intval($_POST['post_flood']) : '0';
+	$search_flood = (isset($_POST['search_flood']) && $_POST['search_flood'] >= 0) ? intval($_POST['search_flood']) : '0';
+	$email_flood = (isset($_POST['email_flood']) && $_POST['email_flood'] >= 0) ? intval($_POST['email_flood']) : '0';
+	$report_flood = (isset($_POST['report_flood']) && $_POST['report_flood'] >= 0) ? intval($_POST['report_flood']) : '0';
 
 	if ($title == '')
 		message($lang_admin_groups['Must enter title message']);
@@ -275,7 +283,7 @@ else if (isset($_POST['add_edit_group']))
 		if ($db->num_rows($result))
 			message(sprintf($lang_admin_groups['Title already exists message'], pun_htmlspecialchars($title)));
 
-		$db->query('INSERT INTO '.$db->prefix.'groups (g_title, g_user_title, g_moderator, g_mod_edit_users, g_mod_rename_users, g_mod_change_passwords, g_mod_ban_users, g_read_board, g_view_users, g_post_replies, g_post_topics, g_edit_posts, g_delete_posts, g_delete_topics, g_set_title, g_search, g_search_users, g_send_email, g_post_flood, g_search_flood, g_email_flood) VALUES(\''.$db->escape($title).'\', '.$user_title.', '.$moderator.', '.$mod_edit_users.', '.$mod_rename_users.', '.$mod_change_passwords.', '.$mod_ban_users.', '.$read_board.', '.$view_users.', '.$post_replies.', '.$post_topics.', '.$edit_posts.', '.$delete_posts.', '.$delete_topics.', '.$set_title.', '.$search.', '.$search_users.', '.$send_email.', '.$post_flood.', '.$search_flood.', '.$email_flood.')') or error('Unable to add group', __FILE__, __LINE__, $db->error());
+		$db->query('INSERT INTO '.$db->prefix.'groups (g_title, g_user_title, g_moderator, g_mod_edit_users, g_mod_rename_users, g_mod_change_passwords, g_mod_ban_users, g_read_board, g_view_users, g_post_replies, g_post_topics, g_edit_posts, g_delete_posts, g_delete_topics, g_set_title, g_search, g_search_users, g_send_email, g_post_flood, g_search_flood, g_email_flood, g_report_flood) VALUES(\''.$db->escape($title).'\', '.$user_title.', '.$moderator.', '.$mod_edit_users.', '.$mod_rename_users.', '.$mod_change_passwords.', '.$mod_ban_users.', '.$read_board.', '.$view_users.', '.$post_replies.', '.$post_topics.', '.$edit_posts.', '.$delete_posts.', '.$delete_topics.', '.$set_title.', '.$search.', '.$search_users.', '.$send_email.', '.$post_flood.', '.$search_flood.', '.$email_flood.', '.$report_flood.')') or error('Unable to add group', __FILE__, __LINE__, $db->error());
 		$new_group_id = $db->insert_id();
 
 		// Now lets copy the forum specific permissions from the group which this group is based on
@@ -289,7 +297,7 @@ else if (isset($_POST['add_edit_group']))
 		if ($db->num_rows($result))
 			message(sprintf($lang_admin_groups['Title already exists message'], pun_htmlspecialchars($title)));
 
-		$db->query('UPDATE '.$db->prefix.'groups SET g_title=\''.$db->escape($title).'\', g_user_title='.$user_title.', g_moderator='.$moderator.', g_mod_edit_users='.$mod_edit_users.', g_mod_rename_users='.$mod_rename_users.', g_mod_change_passwords='.$mod_change_passwords.', g_mod_ban_users='.$mod_ban_users.', g_read_board='.$read_board.', g_view_users='.$view_users.', g_post_replies='.$post_replies.', g_post_topics='.$post_topics.', g_edit_posts='.$edit_posts.', g_delete_posts='.$delete_posts.', g_delete_topics='.$delete_topics.', g_set_title='.$set_title.', g_search='.$search.', g_search_users='.$search_users.', g_send_email='.$send_email.', g_post_flood='.$post_flood.', g_search_flood='.$search_flood.', g_email_flood='.$email_flood.' WHERE g_id='.intval($_POST['group_id'])) or error('Unable to update group', __FILE__, __LINE__, $db->error());
+		$db->query('UPDATE '.$db->prefix.'groups SET g_title=\''.$db->escape($title).'\', g_user_title='.$user_title.', g_moderator='.$moderator.', g_mod_edit_users='.$mod_edit_users.', g_mod_rename_users='.$mod_rename_users.', g_mod_change_passwords='.$mod_change_passwords.', g_mod_ban_users='.$mod_ban_users.', g_read_board='.$read_board.', g_view_users='.$view_users.', g_post_replies='.$post_replies.', g_post_topics='.$post_topics.', g_edit_posts='.$edit_posts.', g_delete_posts='.$delete_posts.', g_delete_topics='.$delete_topics.', g_set_title='.$set_title.', g_search='.$search.', g_search_users='.$search_users.', g_send_email='.$send_email.', g_post_flood='.$post_flood.', g_search_flood='.$search_flood.', g_email_flood='.$email_flood.', g_report_flood='.$report_flood.' WHERE g_id='.intval($_POST['group_id'])) or error('Unable to update group', __FILE__, __LINE__, $db->error());
 	}
 
 	// Regenerate the quick jump cache
@@ -393,7 +401,7 @@ else if (isset($_GET['del_group']))
 						</div>
 					</fieldset>
 				</div>
-				<p class="buttons"><input type="submit" name="del_group_comply" value="<?php echo $lang_admin_common['Delete'] ?>" /><a href="javascript:history.go(-1)"><?php echo $lang_admin_common['Go back'] ?></a></p>
+				<p class="buttons"><input type="submit" name="del_group_comply" value="<?php echo $lang_admin_common['Delete'] ?>" tabindex="1" /><a href="javascript:history.go(-1)" tabindex="2"><?php echo $lang_admin_common['Go back'] ?></a></p>
 			</form>
 		</div>
 	</div>
@@ -540,10 +548,12 @@ while ($cur_group = $db->fetch_assoc($result))
 							<table cellspacing="0">
 <?php
 
+$cur_index = 5;
+
 $result = $db->query('SELECT g_id, g_title FROM '.$db->prefix.'groups ORDER BY g_id') or error('Unable to fetch user group list', __FILE__, __LINE__, $db->error());
 
 while ($cur_group = $db->fetch_assoc($result))
-	echo "\t\t\t\t\t\t\t\t".'<tr><th scope="row"><a href="admin_groups.php?edit_group='.$cur_group['g_id'].'">'.$lang_admin_groups['Edit link'].'</a>'.(($cur_group['g_id'] > PUN_MEMBER) ? ' | <a href="admin_groups.php?del_group='.$cur_group['g_id'].'">'.$lang_admin_groups['Delete link'].'</a>' : '').'</th><td>'.pun_htmlspecialchars($cur_group['g_title']).'</td></tr>'."\n";
+	echo "\t\t\t\t\t\t\t\t".'<tr><th scope="row"><a href="admin_groups.php?edit_group='.$cur_group['g_id'].'" tabindex="'.$cur_index++.'">'.$lang_admin_groups['Edit link'].'</a>'.(($cur_group['g_id'] > PUN_MEMBER) ? ' | <a href="admin_groups.php?del_group='.$cur_group['g_id'].'" tabindex="'.$cur_index++.'">'.$lang_admin_groups['Delete link'].'</a>' : '').'</th><td>'.pun_htmlspecialchars($cur_group['g_title']).'</td></tr>'."\n";
 
 ?>
 							</table>
diff --git a/admin_index.php b/admin_index.php
index 0064f6a..5d82031 100644
--- a/admin_index.php
+++ b/admin_index.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -15,7 +15,7 @@ require PUN_ROOT.'include/common_admin.php';
 
 
 if (!$pun_user['is_admmod'])
-	message($lang_common['No permission']);
+	message($lang_common['No permission'], false, '403 Forbidden');
 
 // Load the admin_index.php language file
 require PUN_ROOT.'lang/'.$admin_language.'/admin_index.php';
@@ -70,7 +70,7 @@ if (@file_exists('/proc/loadavg') && is_readable('/proc/loadavg'))
 	$load_averages = @explode(' ', $load_averages);
 	$server_load = isset($load_averages[2]) ? $load_averages[0].' '.$load_averages[1].' '.$load_averages[2] : $lang_admin_index['Not available'];
 }
-else if (!in_array(PHP_OS, array('WINNT', 'WIN32')) && preg_match('/averages?: ([0-9\.]+),?\s+([0-9\.]+),?\s+([0-9\.]+)/i', @exec('uptime'), $load_averages))
+else if (!in_array(PHP_OS, array('WINNT', 'WIN32')) && preg_match('%averages?: ([0-9\.]+),?\s+([0-9\.]+),?\s+([0-9\.]+)%i', @exec('uptime'), $load_averages))
 	$server_load = $load_averages[1].' '.$load_averages[2].' '.$load_averages[3];
 else
 	$server_load = $lang_admin_index['Not available'];
@@ -147,24 +147,25 @@ 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>') ?>
+						<?php printf($lang_admin_index['FluxBB version data']."\n", $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>
-						<?php printf($lang_admin_index['Server load data'], $server_load, $num_online) ?>
+						<?php printf($lang_admin_index['Server load data']."\n", $server_load, $num_online) ?>
 					</dd>
 <?php if ($pun_user['g_id'] == PUN_ADMIN): ?>					<dt><?php echo $lang_admin_index['Environment label'] ?></dt>
 					<dd>
 						<?php printf($lang_admin_index['Environment data OS'], PHP_OS) ?><br />
 						<?php printf($lang_admin_index['Environment data version'], phpversion(), '<a href="admin_index.php?action=phpinfo">'.$lang_admin_index['Show info'].'</a>') ?><br />
-						<?php printf($lang_admin_index['Environment data acc'], $php_accelerator) ?>
+						<?php printf($lang_admin_index['Environment data acc']."\n", $php_accelerator) ?>
 					</dd>
 					<dt><?php echo $lang_admin_index['Database label'] ?></dt>
 					<dd>
 						<?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; ?>					</dd><?php endif; ?>
+<?php if (isset($total_records) && isset($total_size)): ?>						<br /><?php printf($lang_admin_index['Database data rows']."\n", forum_number_format($total_records)) ?>
+						<br /><?php printf($lang_admin_index['Database data size']."\n", $total_size) ?>
+<?php endif; ?>					</dd>
+<?php endif; ?>
 				</dl>
 			</div>
 		</div>
diff --git a/admin_loader.php b/admin_loader.php
index ceaf51c..0eedc47 100644
--- a/admin_loader.php
+++ b/admin_loader.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -15,17 +15,17 @@ require PUN_ROOT.'include/common_admin.php';
 
 
 if (!$pun_user['is_admmod'])
-	message($lang_common['No permission']);
+	message($lang_common['No permission'], false, '403 Forbidden');
 
 // The plugin to load should be supplied via GET
 $plugin = isset($_GET['plugin']) ? $_GET['plugin'] : '';
-if (!preg_match('/^AM?P_(\w*?)\.php$/i', $plugin))
+if (!preg_match('%^AM?P_(\w*?)\.php$%i', $plugin))
 	message($lang_common['Bad request']);
 
 // AP_ == Admins only, AMP_ == admins and moderators
 $prefix = substr($plugin, 0, strpos($plugin, '_'));
 if ($pun_user['g_moderator'] == '1' && $prefix == 'AP')
-	message($lang_common['No permission']);
+	message($lang_common['No permission'], false, '403 Forbidden');
 
 // Make sure the file actually exists
 if (!file_exists(PUN_ROOT.'plugins/'.$plugin))
diff --git a/admin_maintenance.php b/admin_maintenance.php
index fe58efe..aff2958 100644
--- a/admin_maintenance.php
+++ b/admin_maintenance.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -17,12 +17,12 @@ require PUN_ROOT.'include/common_admin.php';
 
 
 if ($pun_user['g_id'] != PUN_ADMIN)
-	message($lang_common['No permission']);
+	message($lang_common['No permission'], false, '403 Forbidden');
 
 // Load the admin_maintenance.php language file
 require PUN_ROOT.'lang/'.$admin_language.'/admin_maintenance.php';
 
-$action = isset($_REQUEST['action']) ? trim($_REQUEST['action']) : '';
+$action = isset($_REQUEST['action']) ? pun_trim($_REQUEST['action']) : '';
 
 if ($action == 'rebuild')
 {
@@ -125,7 +125,7 @@ h1 {
 
 if ($action == 'prune')
 {
-	$prune_from = trim($_POST['prune_from']);
+	$prune_from = pun_trim($_POST['prune_from']);
 	$prune_sticky = intval($_POST['prune_sticky']);
 
 	if (isset($_POST['prune_comply']))
@@ -172,8 +172,8 @@ if ($action == 'prune')
 		redirect('admin_maintenance.php', $lang_admin_maintenance['Posts pruned redirect']);
 	}
 
-	$prune_days = trim($_POST['req_prune_days']);
-	if ($prune_days == '' || preg_match('/[^0-9]/', $prune_days))
+	$prune_days = pun_trim($_POST['req_prune_days']);
+	if ($prune_days == '' || preg_match('%[^0-9]%', $prune_days))
 		message($lang_admin_maintenance['Days must be integer message']);
 
 	$prune_date = time() - ($prune_days * 86400);
diff --git a/admin_options.php b/admin_options.php
index fb4396c..48e2244 100644
--- a/admin_options.php
+++ b/admin_options.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -15,7 +15,7 @@ require PUN_ROOT.'include/common_admin.php';
 
 
 if ($pun_user['g_id'] != PUN_ADMIN)
-	message($lang_common['No permission']);
+	message($lang_common['No permission'], false, '403 Forbidden');
 
 // Load the admin_options.php language file
 require PUN_ROOT.'lang/'.$admin_language.'/admin_options.php';
@@ -34,20 +34,20 @@ if (isset($_POST['form_sent']))
 		'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']),
-		'timeout_online'		=> intval($_POST['form']['timeout_online']),
-		'redirect_delay'		=> intval($_POST['form']['redirect_delay']),
+		'timeout_visit'			=> (intval($_POST['form']['timeout_visit']) > 0) ? intval($_POST['form']['timeout_visit']) : 1,
+		'timeout_online'		=> (intval($_POST['form']['timeout_online']) > 0) ? intval($_POST['form']['timeout_online']) : 1,
+		'redirect_delay'		=> (intval($_POST['form']['redirect_delay']) >= 0) ? intval($_POST['form']['redirect_delay']) : 0,
 		'show_version'			=> $_POST['form']['show_version'] != '1' ? '0' : '1',
 		'show_user_info'		=> $_POST['form']['show_user_info'] != '1' ? '0' : '1',
 		'show_post_count'		=> $_POST['form']['show_post_count'] != '1' ? '0' : '1',
 		'smilies'				=> $_POST['form']['smilies'] != '1' ? '0' : '1',
 		'smilies_sig'			=> $_POST['form']['smilies_sig'] != '1' ? '0' : '1',
 		'make_links'			=> $_POST['form']['make_links'] != '1' ? '0' : '1',
-		'topic_review'			=> intval($_POST['form']['topic_review']),
+		'topic_review'			=> (intval($_POST['form']['topic_review']) >= 0) ? intval($_POST['form']['topic_review']) : 0,
 		'disp_topics_default'	=> intval($_POST['form']['disp_topics_default']),
 		'disp_posts_default'	=> intval($_POST['form']['disp_posts_default']),
-		'indent_num_spaces'		=> intval($_POST['form']['indent_num_spaces']),
-		'quote_depth'			=> intval($_POST['form']['quote_depth']),
+		'indent_num_spaces'		=> (intval($_POST['form']['indent_num_spaces']) >= 0) ? intval($_POST['form']['indent_num_spaces']) : 0,
+		'quote_depth'			=> (intval($_POST['form']['quote_depth']) > 0) ? intval($_POST['form']['quote_depth']) : 1,
 		'quickpost'				=> $_POST['form']['quickpost'] != '1' ? '0' : '1',
 		'users_online'			=> $_POST['form']['users_online'] != '1' ? '0' : '1',
 		'censoring'				=> $_POST['form']['censoring'] != '1' ? '0' : '1',
@@ -65,9 +65,9 @@ if (isset($_POST['form_sent']))
 		'mailing_list'			=> pun_trim($_POST['form']['mailing_list']),
 		'avatars'				=> $_POST['form']['avatars'] != '1' ? '0' : '1',
 		'avatars_dir'			=> pun_trim($_POST['form']['avatars_dir']),
-		'avatars_width'			=> intval($_POST['form']['avatars_width']),
-		'avatars_height'		=> intval($_POST['form']['avatars_height']),
-		'avatars_size'			=> intval($_POST['form']['avatars_size']),
+		'avatars_width'			=> (intval($_POST['form']['avatars_width']) > 0) ? intval($_POST['form']['avatars_width']) : 1,
+		'avatars_height'		=> (intval($_POST['form']['avatars_height']) > 0) ? intval($_POST['form']['avatars_height']) : 1,
+		'avatars_size'			=> (intval($_POST['form']['avatars_size']) > 0) ? intval($_POST['form']['avatars_size']) : 1,
 		'admin_email'			=> strtolower(pun_trim($_POST['form']['admin_email'])),
 		'webmaster_email'		=> strtolower(pun_trim($_POST['form']['webmaster_email'])),
 		'forum_subscriptions'	=> $_POST['form']['forum_subscriptions'] != '1' ? '0' : '1',
@@ -118,7 +118,7 @@ if (isset($_POST['form_sent']))
 		message($lang_admin_options['Invalid webmaster e-mail message']);
 
 	if ($form['mailing_list'] != '')
-		$form['mailing_list'] = strtolower(preg_replace('/\s/S', '', $form['mailing_list']));
+		$form['mailing_list'] = strtolower(preg_replace('%\s%S', '', $form['mailing_list']));
 
 	// Make sure avatars_dir doesn't end with a slash
 	if (substr($form['avatars_dir'], -1) == '/')
@@ -303,7 +303,7 @@ generate_admin_menu('options');
 								<tr>
 									<th scope="row"><?php echo $lang_admin_options['DST label'] ?></th>
 									<td>
-										<input type="radio" name="form[default_dst]" value="1"<?php if ($pun_config['o_default_dst'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[default_dst]" value="0"<?php if ($pun_config['o_default_dst'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="form[default_dst]" id="form_default_dst_1" value="1"<?php if ($pun_config['o_default_dst'] == '1') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_default_dst_1"><strong><?php echo $lang_admin_common['Yes'] ?></strong></label>&#160;&#160;&#160;<input type="radio" name="form[default_dst]" id="form_default_dst_0" value="0"<?php if ($pun_config['o_default_dst'] == '0') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_default_dst_0"><strong><?php echo $lang_admin_common['No'] ?></strong></label>
 										<span><?php echo $lang_admin_options['DST help'] ?></span>
 									</td>
 								</tr>
@@ -411,42 +411,42 @@ generate_admin_menu('options');
 								<tr>
 									<th scope="row"><?php echo $lang_admin_options['Version number label'] ?></th>
 									<td>
-										<input type="radio" name="form[show_version]" value="1"<?php if ($pun_config['o_show_version'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[show_version]" value="0"<?php if ($pun_config['o_show_version'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="form[show_version]" id="form_show_version_1" value="1"<?php if ($pun_config['o_show_version'] == '1') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_show_version_1"><strong><?php echo $lang_admin_common['Yes'] ?></strong></label>&#160;&#160;&#160;<input type="radio" name="form[show_version]" id="form_show_version_0" value="0"<?php if ($pun_config['o_show_version'] == '0') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_show_version_0"><strong><?php echo $lang_admin_common['No'] ?></strong></label>
 										<span><?php echo $lang_admin_options['Version number help'] ?></span>
 									</td>
 								</tr>
 								<tr>
 									<th scope="row"><?php echo $lang_admin_options['Info in posts label'] ?></th>
 									<td>
-										<input type="radio" name="form[show_user_info]" value="1"<?php if ($pun_config['o_show_user_info'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[show_user_info]" value="0"<?php if ($pun_config['o_show_user_info'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="form[show_user_info]" id="form_show_user_info_1" value="1"<?php if ($pun_config['o_show_user_info'] == '1') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_show_user_info_1"><strong><?php echo $lang_admin_common['Yes'] ?></strong></label>&#160;&#160;&#160;<input type="radio" name="form[show_user_info]" id="form_show_user_info_0" value="0"<?php if ($pun_config['o_show_user_info'] == '0') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_show_user_info_0"><strong><?php echo $lang_admin_common['No'] ?></strong></label>
 										<span><?php echo $lang_admin_options['Info in posts help'] ?></span>
 									</td>
 								</tr>
 								<tr>
 									<th scope="row"><?php echo $lang_admin_options['Post count label'] ?></th>
 									<td>
-										<input type="radio" name="form[show_post_count]" value="1"<?php if ($pun_config['o_show_post_count'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[show_post_count]" value="0"<?php if ($pun_config['o_show_post_count'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="form[show_post_count]" id="form_show_post_count_1" value="1"<?php if ($pun_config['o_show_post_count'] == '1') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_show_post_count_1"><strong><?php echo $lang_admin_common['Yes'] ?></strong></label>&#160;&#160;&#160;<input type="radio" name="form[show_post_count]" id="form_show_post_count_0" value="0"<?php if ($pun_config['o_show_post_count'] == '0') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_show_post_count_0"><strong><?php echo $lang_admin_common['No'] ?></strong></label>
 										<span><?php echo $lang_admin_options['Post count help'] ?></span>
 									</td>
 								</tr>
 								<tr>
 									<th scope="row"><?php echo $lang_admin_options['Smilies label'] ?></th>
 									<td>
-										<input type="radio" name="form[smilies]" value="1"<?php if ($pun_config['o_smilies'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[smilies]" value="0"<?php if ($pun_config['o_smilies'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="form[smilies]" id="form_smilies_1" value="1"<?php if ($pun_config['o_smilies'] == '1') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_smilies_1"><strong><?php echo $lang_admin_common['Yes'] ?></strong></label>&#160;&#160;&#160;<input type="radio" name="form[smilies]" id="form_smilies_0" value="0"<?php if ($pun_config['o_smilies'] == '0') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_smilies_0"><strong><?php echo $lang_admin_common['No'] ?></strong></label>
 										<span><?php echo $lang_admin_options['Smilies help'] ?></span>
 									</td>
 								</tr>
 								<tr>
 									<th scope="row"><?php echo $lang_admin_options['Smilies sigs label'] ?></th>
 									<td>
-										<input type="radio" name="form[smilies_sig]" value="1"<?php if ($pun_config['o_smilies_sig'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[smilies_sig]" value="0"<?php if ($pun_config['o_smilies_sig'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="form[smilies_sig]" id="form_smilies_sig_1" value="1"<?php if ($pun_config['o_smilies_sig'] == '1') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_smilies_sig_1"><strong><?php echo $lang_admin_common['Yes'] ?></strong></label>&#160;&#160;&#160;<input type="radio" name="form[smilies_sig]" id="form_smilies_sig_0" value="0"<?php if ($pun_config['o_smilies_sig'] == '0') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_smilies_sig_0"><strong><?php echo $lang_admin_common['No'] ?></strong></label>
 										<span><?php echo $lang_admin_options['Smilies sigs help'] ?></span>
 									</td>
 								</tr>
 								<tr>
 									<th scope="row"><?php echo $lang_admin_options['Clickable links label'] ?></th>
 									<td>
-										<input type="radio" name="form[make_links]" value="1"<?php if ($pun_config['o_make_links'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[make_links]" value="0"<?php if ($pun_config['o_make_links'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="form[make_links]" id="form_make_links_1" value="1"<?php if ($pun_config['o_make_links'] == '1') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_make_links_1"><strong><?php echo $lang_admin_common['Yes'] ?></strong></label>&#160;&#160;&#160;<input type="radio" name="form[make_links]" id="form_make_links_0" value="0"<?php if ($pun_config['o_make_links'] == '0') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_make_links_0"><strong><?php echo $lang_admin_common['No'] ?></strong></label>
 										<span><?php echo $lang_admin_options['Clickable links help'] ?></span>
 									</td>
 								</tr>
@@ -497,70 +497,70 @@ generate_admin_menu('options');
 								<tr>
 									<th scope="row"><?php echo $lang_admin_options['Quick post label'] ?></th>
 									<td>
-										<input type="radio" name="form[quickpost]" value="1"<?php if ($pun_config['o_quickpost'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[quickpost]" value="0"<?php if ($pun_config['o_quickpost'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="form[quickpost]" id="form_quickpost_1" value="1"<?php if ($pun_config['o_quickpost'] == '1') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_quickpost_1"><strong><?php echo $lang_admin_common['Yes'] ?></strong></label>&#160;&#160;&#160;<input type="radio" name="form[quickpost]" id="form_quickpost_0" value="0"<?php if ($pun_config['o_quickpost'] == '0') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_quickpost_0"><strong><?php echo $lang_admin_common['No'] ?></strong></label>
 										<span><?php echo $lang_admin_options['Quick post help'] ?></span>
 									</td>
 								</tr>
 								<tr>
 									<th scope="row"><?php echo $lang_admin_options['Users online label'] ?></th>
 									<td>
-										<input type="radio" name="form[users_online]" value="1"<?php if ($pun_config['o_users_online'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[users_online]" value="0"<?php if ($pun_config['o_users_online'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="form[users_online]" id="form_users_online_1" value="1"<?php if ($pun_config['o_users_online'] == '1') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_users_online_1"><strong><?php echo $lang_admin_common['Yes'] ?></strong></label>&#160;&#160;&#160;<input type="radio" name="form[users_online]" id="form_users_online_0" value="0"<?php if ($pun_config['o_users_online'] == '0') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_users_online_0"><strong><?php echo $lang_admin_common['No'] ?></strong></label>
 										<span><?php echo $lang_admin_options['Users online help'] ?></span>
 									</td>
 								</tr>
 								<tr>
-									<th scope="row"><a name="censoring"><?php echo $lang_admin_options['Censor words label'] ?></a></th>
+									<th scope="row"><a name="censoring"></a><?php echo $lang_admin_options['Censor words label'] ?></th>
 									<td>
-										<input type="radio" name="form[censoring]" value="1"<?php if ($pun_config['o_censoring'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[censoring]" value="0"<?php if ($pun_config['o_censoring'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="form[censoring]" id="form_censoring_1" value="1"<?php if ($pun_config['o_censoring'] == '1') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_censoring_1"><strong><?php echo $lang_admin_common['Yes'] ?></strong></label>&#160;&#160;&#160;<input type="radio" name="form[censoring]" id="form_censoring_0" value="0"<?php if ($pun_config['o_censoring'] == '0') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_censoring_0"><strong><?php echo $lang_admin_common['No'] ?></strong></label>
 										<span><?php printf($lang_admin_options['Censor words help'], '<a href="admin_censoring.php">'.$lang_admin_common['Censoring'].'</a>') ?></span>
 									</td>
 								</tr>
 								<tr>
-									<th scope="row"><a name="signatures"><?php echo $lang_admin_options['Signatures label'] ?></a></th>
+									<th scope="row"><a name="signatures"></a><?php echo $lang_admin_options['Signatures label'] ?></th>
 									<td>
-										<input type="radio" name="form[signatures]" value="1"<?php if ($pun_config['o_signatures'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[signatures]" value="0"<?php if ($pun_config['o_signatures'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="form[signatures]" id="form_signatures_1" value="1"<?php if ($pun_config['o_signatures'] == '1') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_signatures_1"><strong><?php echo $lang_admin_common['Yes'] ?></strong></label>&#160;&#160;&#160;<input type="radio" name="form[signatures]" id="form_signatures_0" value="0"<?php if ($pun_config['o_signatures'] == '0') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_signatures_0"><strong><?php echo $lang_admin_common['No'] ?></strong></label>
 										<span><?php echo $lang_admin_options['Signatures help'] ?></span>
 									</td>
 								</tr>
 								<tr>
-									<th scope="row"><a name="ranks"><?php echo $lang_admin_options['User ranks label'] ?></a></th>
+									<th scope="row"><a name="ranks"></a><?php echo $lang_admin_options['User ranks label'] ?></th>
 									<td>
-										<input type="radio" name="form[ranks]" value="1"<?php if ($pun_config['o_ranks'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[ranks]" value="0"<?php if ($pun_config['o_ranks'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="form[ranks]" id="form_ranks_1" value="1"<?php if ($pun_config['o_ranks'] == '1') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_ranks_1"><strong><?php echo $lang_admin_common['Yes'] ?></strong></label>&#160;&#160;&#160;<input type="radio" name="form[ranks]" id="form_ranks_0" value="0"<?php if ($pun_config['o_ranks'] == '0') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_ranks_0"><strong><?php echo $lang_admin_common['No'] ?></strong></label>
 										<span><?php printf($lang_admin_options['User ranks help'], '<a href="admin_ranks.php">'.$lang_admin_common['Ranks'].'</a>') ?></span>
 									</td>
 								</tr>
 								<tr>
 									<th scope="row"><?php echo $lang_admin_options['User has posted label'] ?></th>
 									<td>
-										<input type="radio" name="form[show_dot]" value="1"<?php if ($pun_config['o_show_dot'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[show_dot]" value="0"<?php if ($pun_config['o_show_dot'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="form[show_dot]" id="form_show_dot_1" value="1"<?php if ($pun_config['o_show_dot'] == '1') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_show_dot_1"><strong><?php echo $lang_admin_common['Yes'] ?></strong></label>&#160;&#160;&#160;<input type="radio" name="form[show_dot]" id="form_show_dot_0" value="0"<?php if ($pun_config['o_show_dot'] == '0') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_show_dot_0"><strong><?php echo $lang_admin_common['No'] ?></strong></label>
 										<span><?php echo $lang_admin_options['User has posted help'] ?></span>
 									</td>
 								</tr>
 								<tr>
 									<th scope="row"><?php echo $lang_admin_options['Topic views label'] ?></th>
 									<td>
-										<input type="radio" name="form[topic_views]" value="1"<?php if ($pun_config['o_topic_views'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[topic_views]" value="0"<?php if ($pun_config['o_topic_views'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="form[topic_views]" id="form_topic_views_1" value="1"<?php if ($pun_config['o_topic_views'] == '1') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_topic_views_1"><strong><?php echo $lang_admin_common['Yes'] ?></strong></label>&#160;&#160;&#160;<input type="radio" name="form[topic_views]" id="form_topic_views_0" value="0"<?php if ($pun_config['o_topic_views'] == '0') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_topic_views_0"><strong><?php echo $lang_admin_common['No'] ?></strong></label>
 										<span><?php echo $lang_admin_options['Topic views help'] ?></span>
 									</td>
 								</tr>
 								<tr>
 									<th scope="row"><?php echo $lang_admin_options['Quick jump label'] ?></th>
 									<td>
-										<input type="radio" name="form[quickjump]" value="1"<?php if ($pun_config['o_quickjump'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[quickjump]" value="0"<?php if ($pun_config['o_quickjump'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="form[quickjump]" id="form_quickjump_1" value="1"<?php if ($pun_config['o_quickjump'] == '1') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_quickjump_1"><strong><?php echo $lang_admin_common['Yes'] ?></strong></label>&#160;&#160;&#160;<input type="radio" name="form[quickjump]" id="form_quickjump_0" value="0"<?php if ($pun_config['o_quickjump'] == '0') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_quickjump_0"><strong><?php echo $lang_admin_common['No'] ?></strong></label>
 										<span><?php echo $lang_admin_options['Quick jump help'] ?></span>
 									</td>
 								</tr>
 								<tr>
 									<th scope="row"><?php echo $lang_admin_options['GZip label'] ?></th>
 									<td>
-										<input type="radio" name="form[gzip]" value="1"<?php if ($pun_config['o_gzip'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[gzip]" value="0"<?php if ($pun_config['o_gzip'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="form[gzip]" id="form_gzip_1" value="1"<?php if ($pun_config['o_gzip'] == '1') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_gzip_1"><strong><?php echo $lang_admin_common['Yes'] ?></strong></label>&#160;&#160;&#160;<input type="radio" name="form[gzip]" id="form_gzip_0" value="0"<?php if ($pun_config['o_gzip'] == '0') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_gzip_0"><strong><?php echo $lang_admin_common['No'] ?></strong></label>
 										<span><?php echo $lang_admin_options['GZip help'] ?></span>
 									</td>
 								</tr>
 								<tr>
 									<th scope="row"><?php echo $lang_admin_options['Search all label'] ?></th>
 									<td>
-										<input type="radio" name="form[search_all_forums]" value="1"<?php if ($pun_config['o_search_all_forums'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[search_all_forums]" value="0"<?php if ($pun_config['o_search_all_forums'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="form[search_all_forums]" id="form_search_all_forums_1" value="1"<?php if ($pun_config['o_search_all_forums'] == '1') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_search_all_forums_1"><strong><?php echo $lang_admin_common['Yes'] ?></strong></label>&#160;&#160;&#160;<input type="radio" name="form[search_all_forums]" id="form_search_all_forums_0" value="0"<?php if ($pun_config['o_search_all_forums'] == '0') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_search_all_forums_0"><strong><?php echo $lang_admin_common['No'] ?></strong></label>
 										<span><?php echo $lang_admin_options['Search all help'] ?></span>
 									</td>
 								</tr>
@@ -583,7 +583,7 @@ generate_admin_menu('options');
 								<tr>
 									<th scope="row"><?php echo $lang_admin_options['Default feed label'] ?></th>
 									<td>
-										<input type="radio" name="form[feed_type]" value="0"<?php if ($pun_config['o_feed_type'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_options['None'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[feed_type]" value="1"<?php if ($pun_config['o_feed_type'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_options['RSS'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[feed_type]" value="2"<?php if ($pun_config['o_feed_type'] == '2') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_options['Atom'] ?></strong>
+										<input type="radio" name="form[feed_type]" id="form_feed_type_0" value="0"<?php if ($pun_config['o_feed_type'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_options['None'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[feed_type]" id="form_feed_type_1" value="1"<?php if ($pun_config['o_feed_type'] == '1') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_feed_type_0"><strong><?php echo $lang_admin_options['RSS'] ?></strong></label>&#160;&#160;&#160;<input type="radio" name="form[feed_type]" id="form_feed_type_2" value="2"<?php if ($pun_config['o_feed_type'] == '2') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_feed_type_1"><strong><?php echo $lang_admin_options['Atom'] ?></strong></label>
 										<span><?php echo $lang_admin_options['Default feed help'] ?></span>
 									</td>
 								</tr>
@@ -616,7 +616,7 @@ generate_admin_menu('options');
 								<tr>
 									<th scope="row"><?php echo $lang_admin_options['Reporting method label'] ?></th>
 									<td>
-										<input type="radio" name="form[report_method]" value="0"<?php if ($pun_config['o_report_method'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_options['Internal'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[report_method]" value="1"<?php if ($pun_config['o_report_method'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_options['By e-mail'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[report_method]" value="2"<?php if ($pun_config['o_report_method'] == '2') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_options['Both'] ?></strong>
+										<input type="radio" name="form[report_method]" id="form_report_method_0" value="0"<?php if ($pun_config['o_report_method'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_options['Internal'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[report_method]" id="form_report_method_1" value="1"<?php if ($pun_config['o_report_method'] == '1') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_report_method_0"><strong><?php echo $lang_admin_options['By e-mail'] ?></strong></label>&#160;&#160;&#160;<input type="radio" name="form[report_method]" id="form_report_method_2" value="2"<?php if ($pun_config['o_report_method'] == '2') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_report_method_1"><strong><?php echo $lang_admin_options['Both'] ?></strong></label>
 										<span><?php echo $lang_admin_options['Reporting method help'] ?></span>
 									</td>
 								</tr>
@@ -639,7 +639,7 @@ generate_admin_menu('options');
 								<tr>
 									<th scope="row"><?php echo $lang_admin_options['Use avatars label'] ?></th>
 									<td>
-										<input type="radio" name="form[avatars]" value="1"<?php if ($pun_config['o_avatars'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[avatars]" value="0"<?php if ($pun_config['o_avatars'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="form[avatars]" id="form_avatars_1" value="1"<?php if ($pun_config['o_avatars'] == '1') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_avatars_1"><strong><?php echo $lang_admin_common['Yes'] ?></strong></label>&#160;&#160;&#160;<input type="radio" name="form[avatars]" id="form_avatars_0" value="0"<?php if ($pun_config['o_avatars'] == '0') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_avatars_0"><strong><?php echo $lang_admin_common['No'] ?></strong></label>
 										<span><?php echo $lang_admin_options['Use avatars help'] ?></span>
 									</td>
 								</tr>
@@ -697,14 +697,14 @@ generate_admin_menu('options');
 								<tr>
 									<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"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[forum_subscriptions]" value="0"<?php if ($pun_config['o_forum_subscriptions'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="form[forum_subscriptions]" id="form_forum_subscriptions_1" value="1"<?php if ($pun_config['o_forum_subscriptions'] == '1') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_forum_subscriptions_1"><strong><?php echo $lang_admin_common['Yes'] ?></strong></label>&#160;&#160;&#160;<input type="radio" name="form[forum_subscriptions]" id="form_forum_subscriptions_0" value="0"<?php if ($pun_config['o_forum_subscriptions'] == '0') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_forum_subscriptions_0"><strong><?php echo $lang_admin_common['No'] ?></strong></label>
 										<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[topic_subscriptions]" value="1"<?php if ($pun_config['o_topic_subscriptions'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[topic_subscriptions]" value="0"<?php if ($pun_config['o_topic_subscriptions'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="form[topic_subscriptions]" id="form_topic_subscriptions_1" value="1"<?php if ($pun_config['o_topic_subscriptions'] == '1') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_topic_subscriptions_1"><strong><?php echo $lang_admin_common['Yes'] ?></strong></label>&#160;&#160;&#160;<input type="radio" name="form[topic_subscriptions]" id="form_topic_subscriptions_0" value="0"<?php if ($pun_config['o_topic_subscriptions'] == '0') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_topic_subscriptions_0"><strong><?php echo $lang_admin_common['No'] ?></strong></label>
 										<span><?php echo $lang_admin_options['Topic subscriptions help'] ?></span>
 									</td>
 								</tr>
@@ -725,7 +725,7 @@ generate_admin_menu('options');
 								<tr>
 									<th scope="row"><?php echo $lang_admin_options['SMTP password label'] ?></th>
 									<td>
-										<span><input type="checkbox" name="form[smtp_change_pass]" value="1" />&#160;&#160;<?php echo $lang_admin_options['SMTP change password help'] ?></span>
+										<span><input type="checkbox" name="form[smtp_change_pass]" id="form_smtp_change_pass" value="1" />&#160;&#160;<label class="conl" for="form_smtp_change_pass"><?php echo $lang_admin_options['SMTP change password help'] ?></label></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 ?>" />
@@ -735,7 +735,7 @@ generate_admin_menu('options');
 								<tr>
 									<th scope="row"><?php echo $lang_admin_options['SMTP SSL label'] ?></th>
 									<td>
-										<input type="radio" name="form[smtp_ssl]" value="1"<?php if ($pun_config['o_smtp_ssl'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[smtp_ssl]" value="0"<?php if ($pun_config['o_smtp_ssl'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="form[smtp_ssl]" id="form_smtp_ssl_1" value="1"<?php if ($pun_config['o_smtp_ssl'] == '1') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_smtp_ssl_1"><strong><?php echo $lang_admin_common['Yes'] ?></strong></label>&#160;&#160;&#160;<input type="radio" name="form[smtp_ssl]" id="form_smtp_ssl_0" value="0"<?php if ($pun_config['o_smtp_ssl'] == '0') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_smtp_ssl_0"><strong><?php echo $lang_admin_common['No'] ?></strong></label>
 										<span><?php echo $lang_admin_options['SMTP SSL help'] ?></span>
 									</td>
 								</tr>
@@ -751,28 +751,28 @@ generate_admin_menu('options');
 								<tr>
 									<th scope="row"><?php echo $lang_admin_options['Allow new label'] ?></th>
 									<td>
-										<input type="radio" name="form[regs_allow]" value="1"<?php if ($pun_config['o_regs_allow'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[regs_allow]" value="0"<?php if ($pun_config['o_regs_allow'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="form[regs_allow]" id="form_regs_allow_1" value="1"<?php if ($pun_config['o_regs_allow'] == '1') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_regs_allow_1"><strong><?php echo $lang_admin_common['Yes'] ?></strong></label>&#160;&#160;&#160;<input type="radio" name="form[regs_allow]" id="form_regs_allow_0" value="0"<?php if ($pun_config['o_regs_allow'] == '0') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_regs_allow_0"><strong><?php echo $lang_admin_common['No'] ?></strong></label>
 										<span><?php echo $lang_admin_options['Allow new help'] ?></span>
 									</td>
 								</tr>
 								<tr>
 									<th scope="row"><?php echo $lang_admin_options['Verify label'] ?></th>
 									<td>
-										<input type="radio" name="form[regs_verify]" value="1"<?php if ($pun_config['o_regs_verify'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[regs_verify]" value="0"<?php if ($pun_config['o_regs_verify'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="form[regs_verify]" id="form_regs_verify_1" value="1"<?php if ($pun_config['o_regs_verify'] == '1') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_regs_verify_1"><strong><?php echo $lang_admin_common['Yes'] ?></strong></label>&#160;&#160;&#160;<input type="radio" name="form[regs_verify]" id="form_regs_verify_0" value="0"<?php if ($pun_config['o_regs_verify'] == '0') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_regs_verify_0"><strong><?php echo $lang_admin_common['No'] ?></strong></label>
 										<span><?php echo $lang_admin_options['Verify help'] ?></span>
 									</td>
 								</tr>
 								<tr>
 									<th scope="row"><?php echo $lang_admin_options['Report new label'] ?></th>
 									<td>
-										<input type="radio" name="form[regs_report]" value="1"<?php if ($pun_config['o_regs_report'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[regs_report]" value="0"<?php if ($pun_config['o_regs_report'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="form[regs_report]" id="form_regs_report_1" value="1"<?php if ($pun_config['o_regs_report'] == '1') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_regs_report_1"><strong><?php echo $lang_admin_common['Yes'] ?></strong></label>&#160;&#160;&#160;<input type="radio" name="form[regs_report]" id="form_regs_report_0" value="0"<?php if ($pun_config['o_regs_report'] == '0') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_regs_report_0"><strong><?php echo $lang_admin_common['No'] ?></strong></label>
 										<span><?php echo $lang_admin_options['Report new help'] ?></span>
 									</td>
 								</tr>
 								<tr>
 									<th scope="row"><?php echo $lang_admin_options['Use rules label'] ?></th>
 									<td>
-										<input type="radio" name="form[rules]" value="1"<?php if ($pun_config['o_rules'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[rules]" value="0"<?php if ($pun_config['o_rules'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="form[rules]" id="form_rules_1" value="1"<?php if ($pun_config['o_rules'] == '1') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_rules_1"><strong><?php echo $lang_admin_common['Yes'] ?></strong></label>&#160;&#160;&#160;<input type="radio" name="form[rules]" id="form_rules_0" value="0"<?php if ($pun_config['o_rules'] == '0') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_rules_0"><strong><?php echo $lang_admin_common['No'] ?></strong></label>
 										<span><?php echo $lang_admin_options['Use rules help'] ?></span>
 									</td>
 								</tr>
@@ -787,9 +787,9 @@ generate_admin_menu('options');
 									<th scope="row"><?php echo $lang_admin_options['E-mail default label'] ?></th>
 									<td>
 										<span><?php echo $lang_admin_options['E-mail default help'] ?></span>
-										<input type="radio" name="form[default_email_setting]" value="0"<?php if ($pun_config['o_default_email_setting'] == '0') echo ' checked="checked"' ?> />&#160;<?php echo $lang_admin_options['Display e-mail label'] ?><br />
-										<input type="radio" name="form[default_email_setting]" value="1"<?php if ($pun_config['o_default_email_setting'] == '1') echo ' checked="checked"' ?> />&#160;<?php echo $lang_admin_options['Hide allow form label'] ?><br />
-										<input type="radio" name="form[default_email_setting]" value="2"<?php if ($pun_config['o_default_email_setting'] == '2') echo ' checked="checked"' ?> />&#160;<?php echo $lang_admin_options['Hide both label'] ?><br />
+										<input type="radio" name="form[default_email_setting]" id="form_default_email_setting_0" value="0"<?php if ($pun_config['o_default_email_setting'] == '0') echo ' checked="checked"' ?> />&#160;<?php echo $lang_admin_options['Display e-mail label'] ?><br />
+										<input type="radio" name="form[default_email_setting]" id="form_default_email_setting_1" value="1"<?php if ($pun_config['o_default_email_setting'] == '1') echo ' checked="checked"' ?> />&#160;<?php echo $lang_admin_options['Hide allow form label'] ?><br />
+										<input type="radio" name="form[default_email_setting]" id="form_default_email_setting_2" value="2"<?php if ($pun_config['o_default_email_setting'] == '2') echo ' checked="checked"' ?> />&#160;<?php echo $lang_admin_options['Hide both label'] ?><br />
 									</td>
 								</tr>
 							</table>
@@ -804,7 +804,7 @@ generate_admin_menu('options');
 								<tr>
 									<th scope="row"><?php echo $lang_admin_options['Display announcement label'] ?></th>
 									<td>
-										<input type="radio" name="form[announcement]" value="1"<?php if ($pun_config['o_announcement'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[announcement]" value="0"<?php if ($pun_config['o_announcement'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="form[announcement]" id="form_announcement_1" value="1"<?php if ($pun_config['o_announcement'] == '1') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_announcement_1"><strong><?php echo $lang_admin_common['Yes'] ?></strong></label>&#160;&#160;&#160;<input type="radio" name="form[announcement]" id="form_announcement_0" value="0"<?php if ($pun_config['o_announcement'] == '0') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_announcement_0"><strong><?php echo $lang_admin_common['No'] ?></strong></label>
 										<span><?php echo $lang_admin_options['Display announcement help'] ?></span>
 									</td>
 								</tr>
@@ -825,9 +825,9 @@ generate_admin_menu('options');
 						<div class="infldset">
 							<table class="aligntop" cellspacing="0">
 								<tr>
-									<th scope="row"><a name="maintenance"><?php echo $lang_admin_options['Maintenance mode label'] ?></a></th>
+									<th scope="row"><a name="maintenance"></a><?php echo $lang_admin_options['Maintenance mode label'] ?></th>
 									<td>
-										<input type="radio" name="form[maintenance]" value="1"<?php if ($pun_config['o_maintenance'] == '1') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['Yes'] ?></strong>&#160;&#160;&#160;<input type="radio" name="form[maintenance]" value="0"<?php if ($pun_config['o_maintenance'] == '0') echo ' checked="checked"' ?> />&#160;<strong><?php echo $lang_admin_common['No'] ?></strong>
+										<input type="radio" name="form[maintenance]" id="form_maintenance_1" value="1"<?php if ($pun_config['o_maintenance'] == '1') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_maintenance_1"><strong><?php echo $lang_admin_common['Yes'] ?></strong></label>&#160;&#160;&#160;<input type="radio" name="form[maintenance]" id="form_maintenance_0" value="0"<?php if ($pun_config['o_maintenance'] == '0') echo ' checked="checked"' ?> />&#160;<label class="conl" for="form_maintenance_0"><strong><?php echo $lang_admin_common['No'] ?></strong></label>
 										<span><?php echo $lang_admin_options['Maintenance mode help'] ?></span>
 									</td>
 								</tr>
diff --git a/admin_permissions.php b/admin_permissions.php
index c92e01d..a239979 100644
--- a/admin_permissions.php
+++ b/admin_permissions.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -15,7 +15,7 @@ require PUN_ROOT.'include/common_admin.php';
 
 
 if ($pun_user['g_id'] != PUN_ADMIN)
-	message($lang_common['No permission']);
+	message($lang_common['No permission'], false, '403 Forbidden');
 
 // Load the admin_permissions.php language file
 require PUN_ROOT.'lang/'.$admin_language.'/admin_permissions.php';
@@ -28,6 +28,10 @@ if (isset($_POST['form_sent']))
 
 	foreach ($form as $key => $input)
 	{
+		// Make sure the input is never a negative value
+		if($input < 0)
+			$input = 0;
+
 		// Only update values that have changed
 		if (array_key_exists('p_'.$key, $pun_config) && $pun_config['p_'.$key] != $input)
 			$db->query('UPDATE '.$db->prefix.'config SET conf_value='.$input.' WHERE conf_name=\'p_'.$db->escape($key).'\'') or error('Unable to update board config', __FILE__, __LINE__, $db->error());
diff --git a/admin_ranks.php b/admin_ranks.php
index 421e812..e054915 100644
--- a/admin_ranks.php
+++ b/admin_ranks.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -15,7 +15,7 @@ require PUN_ROOT.'include/common_admin.php';
 
 
 if ($pun_user['g_id'] != PUN_ADMIN)
-	message($lang_common['No permission']);
+	message($lang_common['No permission'], false, '403 Forbidden');
 
 // Load the admin_ranks.php language file
 require PUN_ROOT.'lang/'.$admin_language.'/admin_ranks.php';
@@ -26,12 +26,12 @@ if (isset($_POST['add_rank']))
 	confirm_referrer('admin_ranks.php');
 
 	$rank = pun_trim($_POST['new_rank']);
-	$min_posts = trim($_POST['new_min_posts']);
+	$min_posts = pun_trim($_POST['new_min_posts']);
 
 	if ($rank == '')
 		message($lang_admin_ranks['Must enter title message']);
 
-	if ($min_posts == '' || preg_match('/[^0-9]/', $min_posts))
+	if ($min_posts == '' || preg_match('%[^0-9]%', $min_posts))
 		message($lang_admin_ranks['Must be integer message']);
 
 	// Make sure there isn't already a rank with the same min_posts value
@@ -59,12 +59,12 @@ else if (isset($_POST['update']))
 	$id = intval(key($_POST['update']));
 
 	$rank = pun_trim($_POST['rank'][$id]);
-	$min_posts = trim($_POST['min_posts'][$id]);
+	$min_posts = pun_trim($_POST['min_posts'][$id]);
 
 	if ($rank == '')
 		message($lang_admin_ranks['Must enter title message']);
 
-	if ($min_posts == '' || preg_match('/[^0-9]/', $min_posts))
+	if ($min_posts == '' || preg_match('%[^0-9]%', $min_posts))
 		message($lang_admin_ranks['Must be integer message']);
 
 	// Make sure there isn't already a rank with the same min_posts value
diff --git a/admin_reports.php b/admin_reports.php
index 4f7d091..f2ce80d 100644
--- a/admin_reports.php
+++ b/admin_reports.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -15,7 +15,7 @@ require PUN_ROOT.'include/common_admin.php';
 
 
 if (!$pun_user['is_admmod'])
-	message($lang_common['No permission']);
+	message($lang_common['No permission'], false, '403 Forbidden');
 
 // Load the admin_reports.php language file
 require PUN_ROOT.'lang/'.$admin_language.'/admin_reports.php';
diff --git a/admin_users.php b/admin_users.php
index d9c74d9..a016871 100644
--- a/admin_users.php
+++ b/admin_users.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -15,7 +15,7 @@ require PUN_ROOT.'include/common_admin.php';
 
 
 if (!$pun_user['is_admmod'])
-	message($lang_common['No permission']);
+	message($lang_common['No permission'], false, '403 Forbidden');
 
 // Load the admin_users.php language file
 require PUN_ROOT.'lang/'.$admin_language.'/admin_users.php';
@@ -123,9 +123,9 @@ if (isset($_GET['ip_stats']))
 
 if (isset($_GET['show_users']))
 {
-	$ip = trim($_GET['show_users']);
+	$ip = pun_trim($_GET['show_users']);
 
-	if (!@preg_match('/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', $ip) && !@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}:))$/', $ip))
+	if (!@preg_match('%^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$%', $ip) && !@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}:))$%', $ip))
 		message($lang_admin_users['Bad IP message']);
 
 	// Fetch user count
@@ -257,29 +257,29 @@ if (isset($_GET['show_users']))
 else if (isset($_POST['move_users']) || isset($_POST['move_users_comply']))
 {
 	if ($pun_user['g_id'] > PUN_ADMIN)
-		message($lang_common['No permission']);
+		message($lang_common['No permission'], false, '403 Forbidden');
 
 	confirm_referrer('admin_users.php');
-	
+
 	if (isset($_POST['users']))
 	{
 		$user_ids = is_array($_POST['users']) ? array_keys($_POST['users']) : explode(',', $_POST['users']);
 		$user_ids = array_map('intval', $user_ids);
-		
+
 		// Delete invalid IDs
 		$user_ids = array_diff($user_ids, array(0, 1));
 	}
 	else
 		$user_ids = array();
-	
+
 	if (empty($user_ids))
 		message($lang_admin_users['No users selected']);
-	
+
 	// Are we trying to batch move any admins?
 	$result = $db->query('SELECT COUNT(*) FROM '.$db->prefix.'users WHERE id IN ('.implode(',', $user_ids).') AND group_id='.PUN_ADMIN) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
 	if ($db->result($result) > 0)
 		message($lang_admin_users['No move admins message']);
-	
+
 	// Fetch all user groups
 	$all_groups = array();
 	$result = $db->query('SELECT g_id, g_title FROM '.$db->prefix.'groups WHERE g_id NOT IN ('.PUN_GUEST.','.PUN_ADMIN.') ORDER BY g_title ASC') or error('Unable to fetch groups', __FILE__, __LINE__, $db->error());
@@ -289,11 +289,11 @@ else if (isset($_POST['move_users']) || isset($_POST['move_users_comply']))
 	if (isset($_POST['move_users_comply']))
 	{
 		$new_group = isset($_POST['new_group']) && isset($all_groups[$_POST['new_group']]) ? $_POST['new_group'] : message($lang_admin_users['Invalid group message']);
-		
+
 		// Is the new group a moderator group?
 		$result = $db->query('SELECT g_moderator FROM '.$db->prefix.'groups WHERE g_id='.$new_group) or error('Unable to fetch group info', __FILE__, __LINE__, $db->error());
 		$new_group_mod = $db->result($result);
-		
+
 		// Fetch user groups
 		$user_groups = array();
 		$result = $db->query('SELECT id, group_id FROM '.$db->prefix.'users WHERE id IN ('.implode(',', $user_ids).')') or error('Unable to fetch user groups', __FILE__, __LINE__, $db->error());
@@ -301,10 +301,10 @@ else if (isset($_POST['move_users']) || isset($_POST['move_users_comply']))
 		{
 			if (!isset($user_groups[$cur_user['group_id']]))
 				$user_groups[$cur_user['group_id']] = array();
-			
+
 			$user_groups[$cur_user['group_id']][] = $cur_user['id'];
 		}
-		
+
 		// Are any users moderators?
 		$group_ids = array_keys($user_groups);
 		$result = $db->query('SELECT g_id, g_moderator FROM '.$db->prefix.'groups WHERE g_id IN ('.implode(',', $group_ids).')') or error('Unable to fetch group moderators', __FILE__, __LINE__, $db->error());
@@ -313,7 +313,7 @@ else if (isset($_POST['move_users']) || isset($_POST['move_users_comply']))
 			if ($cur_group['g_moderator'] == '0')
 				unset($user_groups[$cur_group['g_id']]);
 		}
-		
+
 		if (!empty($user_groups) && $new_group != PUN_ADMIN && $new_group_mod != '1')
 		{
 			// Fetch forum list and clean up their moderator list
@@ -321,25 +321,25 @@ else if (isset($_POST['move_users']) || isset($_POST['move_users_comply']))
 			while ($cur_forum = $db->fetch_assoc($result))
 			{
 				$cur_moderators = ($cur_forum['moderators'] != '') ? unserialize($cur_forum['moderators']) : array();
-	
+
 				foreach ($user_groups as $group_users)
 					$cur_moderators = array_diff($cur_moderators, $group_users);
-				
+
 				$cur_moderators = (!empty($cur_moderators)) ? '\''.$db->escape(serialize($cur_moderators)).'\'' : 'NULL';
 				$db->query('UPDATE '.$db->prefix.'forums SET moderators='.$cur_moderators.' WHERE id='.$cur_forum['id']) or error('Unable to update forum', __FILE__, __LINE__, $db->error());
 			}
 		}
-		
+
 		// Change user group
 		$db->query('UPDATE '.$db->prefix.'users SET group_id='.$new_group.' WHERE id IN ('.implode(',', $user_ids).')') or error('Unable to change user group', __FILE__, __LINE__, $db->error());
-		
+
 		redirect('admin_users.php', $lang_admin_users['Users move redirect']);
 	}
 
 	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Users'], $lang_admin_users['Move users']);
 	define('PUN_ACTIVE_PAGE', 'admin');
 	require PUN_ROOT.'header.php';
-	
+
 	generate_admin_menu('users');
 
 ?>
@@ -383,24 +383,24 @@ else if (isset($_POST['move_users']) || isset($_POST['move_users_comply']))
 else if (isset($_POST['delete_users']) || isset($_POST['delete_users_comply']))
 {
 	if ($pun_user['g_id'] > PUN_ADMIN)
-		message($lang_common['No permission']);
+		message($lang_common['No permission'], false, '403 Forbidden');
 
 	confirm_referrer('admin_users.php');
-	
+
 	if (isset($_POST['users']))
 	{
 		$user_ids = is_array($_POST['users']) ? array_keys($_POST['users']) : explode(',', $_POST['users']);
 		$user_ids = array_map('intval', $user_ids);
-		
+
 		// Delete invalid IDs
 		$user_ids = array_diff($user_ids, array(0, 1));
 	}
 	else
 		$user_ids = array();
-	
+
 	if (empty($user_ids))
 		message($lang_admin_users['No users selected']);
-	
+
 	// Are we trying to delete any admins?
 	$result = $db->query('SELECT COUNT(*) FROM '.$db->prefix.'users WHERE id IN ('.implode(',', $user_ids).') AND group_id='.PUN_ADMIN) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
 	if ($db->result($result) > 0)
@@ -415,10 +415,10 @@ else if (isset($_POST['delete_users']) || isset($_POST['delete_users_comply']))
 		{
 			if (!isset($user_groups[$cur_user['group_id']]))
 				$user_groups[$cur_user['group_id']] = array();
-			
+
 			$user_groups[$cur_user['group_id']][] = $cur_user['id'];
 		}
-		
+
 		// Are any users moderators?
 		$group_ids = array_keys($user_groups);
 		$result = $db->query('SELECT g_id, g_moderator FROM '.$db->prefix.'groups WHERE g_id IN ('.implode(',', $group_ids).')') or error('Unable to fetch group moderators', __FILE__, __LINE__, $db->error());
@@ -427,7 +427,7 @@ else if (isset($_POST['delete_users']) || isset($_POST['delete_users_comply']))
 			if ($cur_group['g_moderator'] == '0')
 				unset($user_groups[$cur_group['g_id']]);
 		}
-		
+
 		// Fetch forum list and clean up their moderator list
 		$result = $db->query('SELECT id, moderators FROM '.$db->prefix.'forums') or error('Unable to fetch forum list', __FILE__, __LINE__, $db->error());
 		while ($cur_forum = $db->fetch_assoc($result))
@@ -436,18 +436,18 @@ else if (isset($_POST['delete_users']) || isset($_POST['delete_users_comply']))
 
 			foreach ($user_groups as $group_users)
 				$cur_moderators = array_diff($cur_moderators, $group_users);
-			
+
 			$cur_moderators = (!empty($cur_moderators)) ? '\''.$db->escape(serialize($cur_moderators)).'\'' : 'NULL';
 			$db->query('UPDATE '.$db->prefix.'forums SET moderators='.$cur_moderators.' WHERE id='.$cur_forum['id']) or error('Unable to update forum', __FILE__, __LINE__, $db->error());
 		}
-		
+
 		// Delete any subscriptions
 		$db->query('DELETE FROM '.$db->prefix.'topic_subscriptions WHERE user_id IN ('.implode(',', $user_ids).')') or error('Unable to delete topic subscriptions', __FILE__, __LINE__, $db->error());
 		$db->query('DELETE FROM '.$db->prefix.'forum_subscriptions WHERE user_id IN ('.implode(',', $user_ids).')') or error('Unable to delete forum subscriptions', __FILE__, __LINE__, $db->error());
-		
+
 		// Remove them from the online list (if they happen to be logged in)
 		$db->query('DELETE FROM '.$db->prefix.'online WHERE user_id IN ('.implode(',', $user_ids).')') or error('Unable to remove users from online list', __FILE__, __LINE__, $db->error());
-		
+
 		// Should we delete all posts made by these users?
 		if (isset($_POST['delete_posts']))
 		{
@@ -482,14 +482,20 @@ else if (isset($_POST['delete_users']) || isset($_POST['delete_users_comply']))
 		// Delete user avatars
 		foreach ($user_ids as $user_id)
 			delete_avatar($user_id);
-		
+
+		// Regenerate the users info cache
+		if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+			require PUN_ROOT.'include/cache.php';
+
+		generate_users_info_cache();
+
 		redirect('admin_users.php', $lang_admin_users['Users delete redirect']);
 	}
 
 	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Users'], $lang_admin_users['Delete users']);
 	define('PUN_ACTIVE_PAGE', 'admin');
 	require PUN_ROOT.'header.php';
-	
+
 	generate_admin_menu('users');
 
 ?>
@@ -526,64 +532,64 @@ else if (isset($_POST['delete_users']) || isset($_POST['delete_users_comply']))
 else if (isset($_POST['ban_users']) || isset($_POST['ban_users_comply']))
 {
 	if ($pun_user['g_id'] != PUN_ADMIN && ($pun_user['g_moderator'] != '1' || $pun_user['g_mod_ban_users'] == '0'))
-		message($lang_common['No permission']);
+		message($lang_common['No permission'], false, '403 Forbidden');
 
 	confirm_referrer('admin_users.php');
-	
+
 	if (isset($_POST['users']))
 	{
 		$user_ids = is_array($_POST['users']) ? array_keys($_POST['users']) : explode(',', $_POST['users']);
 		$user_ids = array_map('intval', $user_ids);
-		
+
 		// Delete invalid IDs
 		$user_ids = array_diff($user_ids, array(0, 1));
 	}
 	else
 		$user_ids = array();
-	
+
 	if (empty($user_ids))
 		message($lang_admin_users['No users selected']);
-	
+
 	// Are we trying to ban any admins?
 	$result = $db->query('SELECT COUNT(*) FROM '.$db->prefix.'users WHERE id IN ('.implode(',', $user_ids).') AND group_id='.PUN_ADMIN) or error('Unable to fetch group info', __FILE__, __LINE__, $db->error());
 	if ($db->result($result) > 0)
 		message($lang_admin_users['No ban admins message']);
-	
+
 	// Also, we cannot ban moderators
 	$result = $db->query('SELECT COUNT(*) FROM '.$db->prefix.'users AS u INNER JOIN '.$db->prefix.'groups AS g ON u.group_id=g.g_id WHERE g.g_moderator=1 AND u.id IN ('.implode(',', $user_ids).')') or error('Unable to fetch moderator group info', __FILE__, __LINE__, $db->error());
 	if ($db->result($result) > 0)
 		message($lang_admin_users['No ban mods message']);
-	
+
 	if (isset($_POST['ban_users_comply']))
 	{
 		$ban_message = pun_trim($_POST['ban_message']);
 		$ban_expire = pun_trim($_POST['ban_expire']);
 		$ban_the_ip = isset($_POST['ban_the_ip']) ? intval($_POST['ban_the_ip']) : 0;
-		
+
 		if ($ban_expire != '' && $ban_expire != 'Never')
 		{
 			$ban_expire = strtotime($ban_expire.' GMT');
-	
+
 			if ($ban_expire == -1 || !$ban_expire)
 				message($lang_admin_users['Invalid date message'].' '.$lang_admin_users['Invalid date reasons']);
-	
+
 			$diff = ($pun_user['timezone'] + $pun_user['dst']) * 3600;
 			$ban_expire -= $diff;
-	
+
 			if ($ban_expire <= time())
 				message($lang_admin_users['Invalid date message'].' '.$lang_admin_users['Invalid date reasons']);
 		}
 		else
 			$ban_expire = 'NULL';
-	
+
 		$ban_message = ($ban_message != '') ? '\''.$db->escape($ban_message).'\'' : 'NULL';
-		
+
 		// Fetch user information
 		$user_info = array();
 		$result = $db->query('SELECT id, username, email, registration_ip FROM '.$db->prefix.'users WHERE id IN ('.implode(',', $user_ids).')') or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
 		while ($cur_user = $db->fetch_assoc($result))
 			$user_info[$cur_user['id']] = array('username' => $cur_user['username'], 'email' => $cur_user['email'], 'ip' => $cur_user['registration_ip']);
-		
+
 		// Overwrite the registration IP with one from the last post (if it exists)
 		if ($ban_the_ip != 0)
 		{
@@ -591,23 +597,23 @@ else if (isset($_POST['ban_users']) || isset($_POST['ban_users_comply']))
 			while ($cur_address = $db->fetch_assoc($result))
 				$user_info[$cur_address['poster_id']]['ip'] = $cur_address['poster_ip'];
 		}
-		
+
 		// And insert the bans!
 		foreach ($user_ids as $user_id)
 		{
 			$ban_username = '\''.$db->escape($user_info[$user_id]['username']).'\'';
 			$ban_email = '\''.$db->escape($user_info[$user_id]['email']).'\'';
 			$ban_ip = ($ban_the_ip != 0) ? '\''.$db->escape($user_info[$user_id]['ip']).'\'' : 'NULL';
-			
+
 			$db->query('INSERT INTO '.$db->prefix.'bans (username, ip, email, message, expire, ban_creator) VALUES('.$ban_username.', '.$ban_ip.', '.$ban_email.', '.$ban_message.', '.$ban_expire.', '.$pun_user['id'].')') or error('Unable to add ban', __FILE__, __LINE__, $db->error());
 		}
-		
+
 		// Regenerate the bans cache
 		if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
 			require PUN_ROOT.'include/cache.php';
-	
+
 		generate_bans_cache();
-	
+
 		redirect('admin_users.php', $lang_admin_users['Users banned redirect']);
 	}
 
@@ -674,13 +680,15 @@ else if (isset($_GET['find_user']))
 	$form = array_map('pun_trim', $form);
 	$conditions = $query_str = array();
 
-	$posts_greater = isset($_GET['posts_greater']) ? trim($_GET['posts_greater']) : '';
-	$posts_less = isset($_GET['posts_less']) ? trim($_GET['posts_less']) : '';
-	$last_post_after = isset($_GET['last_post_after']) ? trim($_GET['last_post_after']) : '';
-	$last_post_before = isset($_GET['last_post_before']) ? trim($_GET['last_post_before']) : '';
-	$registered_after = isset($_GET['registered_after']) ? trim($_GET['registered_after']) : '';
-	$registered_before = isset($_GET['registered_before']) ? trim($_GET['registered_before']) : '';
-	$order_by = isset($_GET['order_by']) && in_array($_GET['order_by'], array('username', 'email', 'num_posts', 'last_post', 'registered')) ? $_GET['order_by'] : 'username';
+	$posts_greater = isset($_GET['posts_greater']) ? pun_trim($_GET['posts_greater']) : '';
+	$posts_less = isset($_GET['posts_less']) ? pun_trim($_GET['posts_less']) : '';
+	$last_post_after = isset($_GET['last_post_after']) ? pun_trim($_GET['last_post_after']) : '';
+	$last_post_before = isset($_GET['last_post_before']) ? pun_trim($_GET['last_post_before']) : '';
+	$last_visit_after = isset($_GET['last_visit_after']) ? pun_trim($_GET['last_visit_after']) : '';
+	$last_visit_before = isset($_GET['last_visit_before']) ? pun_trim($_GET['last_visit_before']) : '';
+	$registered_after = isset($_GET['registered_after']) ? pun_trim($_GET['registered_after']) : '';
+	$registered_before = isset($_GET['registered_before']) ? pun_trim($_GET['registered_before']) : '';
+	$order_by = isset($_GET['order_by']) && in_array($_GET['order_by'], array('username', 'email', 'num_posts', 'last_post', 'last_visit', 'registered')) ? $_GET['order_by'] : 'username';
 	$direction = isset($_GET['direction']) && $_GET['direction'] == 'DESC' ? 'DESC' : 'ASC';
 	$user_group = isset($_GET['user_group']) ? intval($_GET['user_group']) : -1;
 
@@ -688,7 +696,7 @@ else if (isset($_GET['find_user']))
 	$query_str[] = 'direction='.$direction;
 	$query_str[] = 'user_group='.$user_group;
 
-	if (preg_match('/[^0-9]/', $posts_greater.$posts_less))
+	if (preg_match('%[^0-9]%', $posts_greater.$posts_less))
 		message($lang_admin_users['Non numeric message']);
 
 	// Try to convert date/time to timestamps
@@ -712,6 +720,26 @@ else if (isset($_GET['find_user']))
 
 		$conditions[] = 'u.last_post<'.$last_post_before;
 	}
+	if ($last_visit_after != '')
+	{
+		$query_str[] = 'last_visit_after='.$last_visit_after;
+
+		$last_visit_after = strtotime($last_visit_after);
+		if ($last_visit_after === false || $last_visit_after == -1)
+			message($lang_admin_users['Invalid date time message']);
+
+		$conditions[] = 'u.last_visit>'.$last_visit_after;
+	}
+	if ($last_visit_before != '')
+	{
+		$query_str[] = 'last_visit_before='.$last_visit_before;
+
+		$last_visit_before = strtotime($last_visit_before);
+		if ($last_visit_before === false || $last_visit_before == -1)
+			message($lang_admin_users['Invalid date time message']);
+
+		$conditions[] = 'u.last_visit<'.$last_visit_before;
+	}
 	if ($registered_after != '')
 	{
 		$query_str[] = 'registered_after='.$registered_after;
@@ -769,7 +797,7 @@ else if (isset($_GET['find_user']))
 
 	// Generate paging links
 	$paging_links = '<span class="pages-label">'.$lang_common['Pages'].' </span>'.paginate($num_pages, $p, 'admin_users.php?find_user=&amp;'.implode('&amp;', $query_str));
-	
+
 	// Some helper variables for permissions
 	$can_delete = $can_move = $pun_user['g_id'] == PUN_ADMIN;
 	$can_ban = $pun_user['g_id'] == PUN_ADMIN || ($pun_user['g_moderator'] == '1' && $pun_user['g_mod_ban_users'] == '1');
@@ -839,7 +867,7 @@ else if (isset($_GET['find_user']))
 					<td class="tc5"><?php echo ($user_data['admin_note'] != '') ? pun_htmlspecialchars($user_data['admin_note']) : '&#160;' ?></td>
 					<td class="tcr"><?php echo $actions ?></td>
 <?php if ($can_action): ?>					<td class="tcmod"><input type="checkbox" name="users[<?php echo $user_data['id'] ?>]" value="1" /></td>
-<?php endif; ?> 
+<?php endif; ?>
 				</tr>
 <?php
 
@@ -969,6 +997,16 @@ else
 									<span><?php echo $lang_admin_users['Date help'] ?></span></td>
 								</tr>
 								<tr>
+									<th scope="row"><?php echo $lang_admin_users['Last visit after label'] ?></th>
+									<td><input type="text" name="last_visit_after" size="24" maxlength="19" tabindex="17" />
+									<span><?php echo $lang_admin_users['Date help'] ?></span></td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_users['Last visit before label'] ?></th>
+									<td><input type="text" name="last_visit_before" size="24" maxlength="19" tabindex="18" />
+									<span><?php echo $lang_admin_users['Date help'] ?></span></td>
+								</tr>
+								<tr>
 									<th scope="row"><?php echo $lang_admin_users['Registered after label'] ?></th>
 									<td><input type="text" name="registered_after" size="24" maxlength="19" tabindex="19" />
 									<span><?php echo $lang_admin_users['Date help'] ?></span></td>
@@ -986,6 +1024,7 @@ else
 											<option value="email"><?php echo $lang_admin_users['Order by e-mail'] ?></option>
 											<option value="num_posts"><?php echo $lang_admin_users['Order by posts'] ?></option>
 											<option value="last_post"><?php echo $lang_admin_users['Order by last post'] ?></option>
+											<option value="last_visit"><?php echo $lang_admin_users['Order by last visit'] ?></option>
 											<option value="registered"><?php echo $lang_admin_users['Order by registered'] ?></option>
 										</select>&#160;&#160;&#160;<select name="direction" tabindex="22">
 											<option value="ASC" selected="selected"><?php echo $lang_admin_users['Ascending'] ?></option>
diff --git a/common.js b/common.js
index b7ff12f..47a7155 100644
--- a/common.js
+++ b/common.js
@@ -1,4 +1,10 @@
 
+/**
+ * Copyright (C) 2008-2012 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
 function select_checkboxes(curFormId, link, new_string)
 {
 	var curForm = document.getElementById(curFormId);
@@ -29,4 +35,4 @@ function unselect_checkboxes(curFormId, link, new_string)
 	link.innerHTML = new_string;
 
 	return false;
-}
\ No newline at end of file
+}
diff --git a/db_update.php b/db_update.php
index b15b777..51f4ff9 100644
--- a/db_update.php
+++ b/db_update.php
@@ -1,15 +1,15 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
 
 // The FluxBB version this script updates to
-define('UPDATE_TO', '1.4.5');
+define('UPDATE_TO', '1.4.9');
 
-define('UPDATE_TO_DB_REVISION', 11);
+define('UPDATE_TO_DB_REVISION', 15);
 define('UPDATE_TO_SI_REVISION', 2);
 define('UPDATE_TO_PARSER_REVISION', 2);
 
@@ -129,6 +129,7 @@ $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.'/common.php';
 require PUN_ROOT.'lang/'.$default_lang.'/update.php';
 
 // Check current version
@@ -253,7 +254,7 @@ function dcr2utf8($src)
 //
 function convert_to_utf8(&$str, $old_charset)
 {
-	if ($str === null || $str == '')
+	if (is_null($str) || $str == '')
 		return false;
 
 	$save = $str;
@@ -277,8 +278,8 @@ function convert_to_utf8(&$str, $old_charset)
 		$str = html_entity_decode($str, ENT_QUOTES, 'UTF-8');
 
 	// Replace numeric entities
-	$str = preg_replace_callback('/&#([0-9]+);/', 'utf8_callback_1', $str);
-	$str = preg_replace_callback('/&#x([a-f0-9]+);/i', 'utf8_callback_2', $str);
+	$str = preg_replace_callback('%&#([0-9]+);%', 'utf8_callback_1', $str);
+	$str = preg_replace_callback('%&#x([a-f0-9]+);%i', 'utf8_callback_2', $str);
 
 	// Remove "bad" characters
 	$str = remove_bad_characters($str);
@@ -330,7 +331,7 @@ function alter_table_utf8($table)
 	$result = $db->query('SHOW FULL COLUMNS FROM '.$table) or error('Unable to fetch column information', __FILE__, __LINE__, $db->error());
 	while ($cur_column = $db->fetch_assoc($result))
 	{
-		if ($cur_column['Collation'] === null)
+		if (is_null($cur_column['Collation']))
 			continue;
 
 		list($type) = explode('(', $cur_column['Type']);
@@ -339,7 +340,7 @@ function alter_table_utf8($table)
 			$allow_null = ($cur_column['Null'] == 'YES');
 			$collate = (substr($cur_column['Collation'], -3) == 'bin') ? 'utf8_bin' : 'utf8_general_ci';
 
-			$db->alter_field($table, $cur_column['Field'], preg_replace('/'.$type.'/i', $types[$type], $cur_column['Type']), $allow_null, $cur_column['Default'], null, true) or error('Unable to alter field to binary', __FILE__, __LINE__, $db->error());
+			$db->alter_field($table, $cur_column['Field'], preg_replace('%'.$type.'%i', $types[$type], $cur_column['Type']), $allow_null, $cur_column['Default'], null, true) or error('Unable to alter field to binary', __FILE__, __LINE__, $db->error());
 			$db->alter_field($table, $cur_column['Field'], $cur_column['Type'].' CHARACTER SET utf8 COLLATE '.$collate, $allow_null, $cur_column['Default'], null, true) or error('Unable to alter field to utf8', __FILE__, __LINE__, $db->error());
 		}
 	}
@@ -358,7 +359,7 @@ function convert_table_utf8($table, $callback, $old_charset, $key = null, $start
 	if ($mysql)
 	{
 		// Only set up the tables if we are doing this in 1 go, or its the first go
-		if ($start_at === null || $start_at == 0)
+		if (is_null($start_at) || $start_at == 0)
 		{
 			// Drop any temp table that exists, in-case it's left over from a failed update
 			$db->drop_table($table.'_utf8', true) or error('Unable to drop left over temp table', __FILE__, __LINE__, $db->error());
@@ -374,7 +375,7 @@ function convert_table_utf8($table, $callback, $old_charset, $key = null, $start
 		$db->set_names($old_connection_charset);
 
 		// Move & Convert everything
-		$result = $db->query('SELECT * FROM '.$table.($start_at === null ? '' : ' WHERE '.$key.'>'.$start_at).' ORDER BY '.$key.' ASC'.($start_at === null ? '' : ' LIMIT '.PER_PAGE), false) or error('Unable to select from old table', __FILE__, __LINE__, $db->error());
+		$result = $db->query('SELECT * FROM '.$table.(is_null($start_at) ? '' : ' WHERE '.$key.'>'.$start_at).' ORDER BY '.$key.' ASC'.(is_null($start_at) ? '' : ' LIMIT '.PER_PAGE), false) or error('Unable to select from old table', __FILE__, __LINE__, $db->error());
 
 		// Change back to utf8 mode so we can insert it into the new table
 		$db->set_names('utf8');
@@ -385,15 +386,15 @@ function convert_table_utf8($table, $callback, $old_charset, $key = null, $start
 
 			$temp = array();
 			foreach ($cur_item as $idx => $value)
-				$temp[$idx] = $value === null ? 'NULL' : '\''.$db->escape($value).'\'';
+				$temp[$idx] = is_null($value) ? 'NULL' : '\''.$db->escape($value).'\'';
 
-			$db->query('INSERT INTO '.$table.'_utf8('.implode(',', array_keys($temp)).') VALUES ('.implode(',', array_values($temp)).')') or ($error_callback === null ? error('Unable to insert data to new table', __FILE__, __LINE__, $db->error()) : call_user_func($error_callback, $cur_item));
+			$db->query('INSERT INTO '.$table.'_utf8('.implode(',', array_keys($temp)).') VALUES ('.implode(',', array_values($temp)).')') or (is_null($error_callback) ? error('Unable to insert data to new table', __FILE__, __LINE__, $db->error()) : call_user_func($error_callback, $cur_item));
 
 			$end_at = $cur_item[$key];
 		}
 
 		// If we aren't doing this all in 1 go and $end_at has a value (i.e. we have processed at least 1 row), figure out if we have more to do or not
-		if ($start_at !== null && $end_at > 0)
+		if (!is_null($start_at) && $end_at > 0)
 		{
 			$result = $db->query('SELECT 1 FROM '.$table.' WHERE '.$key.'>'.$end_at.' ORDER BY '.$key.' ASC LIMIT 1') or error('Unable to check for next row', __FILE__, __LINE__, $db->error());
 			$finished = $db->num_rows($result) == 0;
@@ -416,14 +417,14 @@ function convert_table_utf8($table, $callback, $old_charset, $key = null, $start
 	else
 	{
 		// Convert everything
-		$result = $db->query('SELECT * FROM '.$table.($start_at === null ? '' : ' WHERE '.$key.'>'.$start_at).' ORDER BY '.$key.' ASC'.($start_at === null ? '' : ' LIMIT '.PER_PAGE)) or error('Unable to select from table', __FILE__, __LINE__, $db->error());
+		$result = $db->query('SELECT * FROM '.$table.(is_null($start_at) ? '' : ' WHERE '.$key.'>'.$start_at).' ORDER BY '.$key.' ASC'.(is_null($start_at ) ? '' : ' LIMIT '.PER_PAGE)) or error('Unable to select from table', __FILE__, __LINE__, $db->error());
 		while ($cur_item = $db->fetch_assoc($result))
 		{
 			$cur_item = call_user_func($callback, $cur_item, $old_charset);
 
 			$temp = array();
 			foreach ($cur_item as $idx => $value)
-				$temp[] = $idx.'='.($value === null ? 'NULL' : '\''.$db->escape($value).'\'');
+				$temp[] = $idx.'='.(is_null($value) ? 'NULL' : '\''.$db->escape($value).'\'');
 
 			if (!empty($temp))
 				$db->query('UPDATE '.$table.' SET '.implode(', ', $temp).' WHERE '.$key.'=\''.$db->escape($cur_item[$key]).'\'') or error('Unable to update data', __FILE__, __LINE__, $db->error());
@@ -431,7 +432,7 @@ function convert_table_utf8($table, $callback, $old_charset, $key = null, $start
 			$end_at = $cur_item[$key];
 		}
 
-		if ($start_at !== null && $end_at > 0)
+		if (!is_null($start_at) && $end_at > 0)
 		{
 			$result = $db->query('SELECT 1 FROM '.$table.' WHERE '.$key.'>'.$end_at.' ORDER BY '.$key.' ASC LIMIT 1') or error('Unable to check for next row', __FILE__, __LINE__, $db->error());
 			if ($db->num_rows($result) == 0)
@@ -459,17 +460,59 @@ $query_str = '';
 // Show form
 if (empty($stage))
 {
+	if (file_exists(FORUM_CACHE_DIR.'db_update.lock'))
+	{
+		// Deal with newlines, tabs and multiple spaces
+		$pattern = array("\t", '  ', '  ');
+		$replace = array('&#160; &#160; ', '&#160; ', ' &#160;');
+		$message = str_replace($pattern, $replace, $pun_config['o_maintenance_message']);
+
+?>
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php echo $lang_common['lang_identifier'] ?>" lang="<?php echo $lang_common['lang_identifier'] ?>" dir="<?php echo $lang_common['lang_direction'] ?>">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title><?php echo $lang_update['Maintenance'] ?></title>
+<link rel="stylesheet" type="text/css" href="style/<?php echo $default_style ?>.css" />
+</head>
+<body>
+
+<div id="punmaint" class="pun">
+<div class="top-box"><div><!-- Top Corners --></div></div>
+<div class="punwrap">
+
+<div id="brdmain">
+<div class="block">
+	<h2><?php echo $lang_update['Maintenance'] ?></h2>
+	<div class="box">
+		<div class="inbox">
+			<p><?php echo $message ?></p>
+		</div>
+	</div>
+</div>
+</div>
+
+</div>
+<div class="end-box"><div><!-- Bottom Corners --></div></div>
+</div>
+
+</body>
+</html>
+<?php
+
+	}
+	else
+	{
 
 ?>
 <!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">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php echo $lang_common['lang_identifier'] ?>" lang="<?php echo $lang_common['lang_identifier'] ?>" dir="<?php echo $lang_common['lang_direction'] ?>">
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 <title><?php echo $lang_update['Update'] ?></title>
 <link rel="stylesheet" type="text/css" href="style/<?php echo $default_style ?>.css" />
 </head>
-<body onload="document.getElementById('install').req_db_type.focus();document.getElementById('install').start.disabled=false;">
+<body onload="document.getElementById('install').req_db_pass.focus();document.getElementById('install').start.disabled=false;">
 
 <div id="pundb_update" class="pun">
 <div class="top-box"><div><!-- Top Corners --></div></div>
@@ -488,7 +531,7 @@ if (empty($stage))
 <div class="blockform">
 	<h2><span><?php echo $lang_update['Update'] ?></span></h2>
 	<div class="box">
-		<form method="post" action="db_update.php">
+		<form id="install" method="post" action="db_update.php">
 			<input type="hidden" name="stage" value="start" />
 			<div class="inform">
 				<fieldset>
@@ -497,6 +540,11 @@ if (empty($stage))
 						<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>
+						<p><?php echo $lang_update['Maintenance message info'] ?></p>
+						<div class="txtarea">
+							<label class="required"><strong><?php echo $lang_update['Maintenance message'] ?> <span><?php echo $lang_update['Required'] ?></span></strong><br />
+							<textarea name="req_maintenance_message" rows="4" cols="65"><?php echo pun_htmlspecialchars($pun_config['o_maintenance_message']) ?></textarea><br /></label>
+						</div>
 					</div>
 				</fieldset>
 			</div>
@@ -559,6 +607,7 @@ if (empty($stage))
 </html>
 <?php
 
+	}
 	$db->end_transaction();
 	$db->close();
 	exit;
@@ -572,7 +621,7 @@ $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']));
+	$req_db_pass = strtolower(pun_trim($_POST['req_db_pass']));
 
 	switch ($db_type)
 	{
@@ -602,11 +651,30 @@ if (isset($_POST['req_db_pass']))
 
 		fwrite($fh, $uid);
 		fclose($fh);
+
+		// Update maintenance message
+		if ($_POST['req_maintenance_message'] != '')
+			$maintenance_message = pun_trim(pun_linebreaks($_POST['req_maintenance_message']));
+		else
+		{
+			// Load the admin_options.php language file
+			require PUN_ROOT.'lang/'.$default_lang.'/admin_options.php';
+
+			$maintenance_message = $lang_admin_options['Default maintenance message'];
+		}
+
+		$db->query('UPDATE '.$db->prefix.'config SET conf_value=\''.$db->escape($maintenance_message).'\' WHERE conf_name=\'o_maintenance_message\'') or error('Unable to update board config', __FILE__, __LINE__, $db->error());
+
+		// Regenerate the config cache
+		if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+			require PUN_ROOT.'include/cache.php';
+
+		generate_config_cache();
 	}
 }
 else if (isset($_GET['uid']))
 {
-	$uid = trim($_GET['uid']);
+	$uid = pun_trim($_GET['uid']);
 	if (!$lock || $lock != $uid) // The lock doesn't exist or doesn't match the given UID
 		$lock_error = true;
 }
@@ -719,7 +787,7 @@ switch ($stage)
 			{
 				// Make an educated guess regarding base_url
 				$base_url  = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https://' : 'http://';	// protocol
-				$base_url .= preg_replace('/:(80|443)$/', '', $_SERVER['HTTP_HOST']);							// host[:port]
+				$base_url .= preg_replace('%:(80|443)$%', '', $_SERVER['HTTP_HOST']);							// host[:port]
 				$base_url .= str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME']));							// path
 			}
 
@@ -753,7 +821,7 @@ switch ($stage)
 				$mod_gid = $db->result($result);
 			else
 			{
-				$db->query('INSERT INTO '.$db->prefix.'groups (g_title, g_user_title, g_moderator, g_mod_edit_users, g_mod_rename_users, g_mod_change_passwords, g_mod_ban_users, g_read_board, g_view_users, g_post_replies, g_post_topics, g_edit_posts, g_delete_posts, g_delete_topics, g_set_title, g_search, g_search_users, g_send_email, g_post_flood, g_search_flood, g_email_flood) VALUES('."'Moderators', 'Moderator', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0)") or error('Unable to add group', __FILE__, __LINE__, $db->error());
+				$db->query('INSERT INTO '.$db->prefix.'groups (g_title, g_user_title, g_moderator, g_mod_edit_users, g_mod_rename_users, g_mod_change_passwords, g_mod_ban_users, g_read_board, g_view_users, g_post_replies, g_post_topics, g_edit_posts, g_delete_posts, g_delete_topics, g_set_title, g_search, g_search_users, g_send_email, g_post_flood, g_search_flood, g_email_flood, g_report_flood) VALUES('."'Moderators', 'Moderator', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0)") or error('Unable to add group', __FILE__, __LINE__, $db->error());
 				$mod_gid = $db->insert_id();
 			}
 
@@ -916,9 +984,14 @@ switch ($stage)
 		$db->add_field('groups', 'g_send_email', 'TINYINT(1)', false, 1, 'g_search_users') or error('Unable to add g_send_email field', __FILE__, __LINE__, $db->error());
 		$db->add_field('groups', 'g_email_flood', 'SMALLINT(6)', false, 60, 'g_search_flood') or error('Unable to add g_email_flood field', __FILE__, __LINE__, $db->error());
 
-		// Set non-default g_send_email and g_flood_email values properly
+		// Add the last_report_sent column to the users table and the g_report_flood
+		// column to the groups table
+		$db->add_field('users', 'last_report_sent', 'INT(10) UNSIGNED', true, null, 'last_email_sent') or error('Unable to add last_report_sent field', __FILE__, __LINE__, $db->error());
+		$db->add_field('groups', 'g_report_flood', 'SMALLINT(6)', false, 60, 'g_email_flood') or error('Unable to add g_report_flood field', __FILE__, __LINE__, $db->error());
+
+		// Set non-default g_send_email, g_flood_email and g_flood_report values properly
 		$db->query('UPDATE '.$db->prefix.'groups SET g_send_email = 0 WHERE g_id = 3') or error('Unable to update group email permissions', __FILE__, __LINE__, $db->error());
-		$db->query('UPDATE '.$db->prefix.'groups SET g_email_flood = 0 WHERE g_id IN (1,2,3)') or error('Unable to update group email permissions', __FILE__, __LINE__, $db->error());
+		$db->query('UPDATE '.$db->prefix.'groups SET g_email_flood = 0, g_report_flood = 0 WHERE g_id IN (1,2,3)') or error('Unable to update group email permissions', __FILE__, __LINE__, $db->error());
 
 		// Add the auto notify/subscription option to the users table
 		$db->add_field('users', 'auto_notify', 'TINYINT(1)', false, 0, 'notify_with_post') or error('Unable to add auto_notify field', __FILE__, __LINE__, $db->error());
@@ -1086,6 +1159,10 @@ switch ($stage)
 		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());
 
+		// For MySQL(i) without InnoDB, change the engine of the online table (for performance reasons)
+		if ($db_type == 'mysql' || $db_type == 'mysqli')
+			$db->query('ALTER TABLE '.$db->prefix.'online ENGINE = MyISAM') or error('Unable to change engine type of online table to MyISAM', __FILE__, __LINE__, $db->error());
+
 		// Should we do charset conversion or not?
 		if (strpos($cur_version, '1.2') === 0 && isset($_POST['convert_charset']))
 			$query_str = '?stage=conv_bans&req_old_charset='.$old_charset;
@@ -1458,14 +1535,14 @@ switch ($stage)
 					$errors[$id][] = $lang_update['Username too long error'];
 				else if (!strcasecmp($username, 'Guest'))
 					$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))
+				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][] = $lang_update['Username IP format error'];
 				else if ((strpos($username, '[') !== false || strpos($username, ']') !== false) && strpos($username, '\'') !== false && strpos($username, '"') !== false)
 					$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))
+				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][] = $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(ucp_preg_replace('/[^\p{L}\p{N}]/u', '', $username)).'\')) AND id>1') or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+				$result = $db->query('SELECT username FROM '.$db->prefix.'users WHERE (UPPER(username)=UPPER(\''.$db->escape($username).'\') OR UPPER(username)=UPPER(\''.$db->escape(ucp_preg_replace('%[^\p{L}\p{N}]%u', '', $username)).'\')) AND id>1') or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
 
 				if ($db->num_rows($result))
 				{
@@ -1480,7 +1557,7 @@ switch ($stage)
 
 					$temp = array();
 					foreach ($cur_user as $idx => $value)
-						$temp[$idx] = $value === null ? 'NULL' : '\''.$db->escape($value).'\'';
+						$temp[$idx] = is_null($value) ? 'NULL' : '\''.$db->escape($value).'\'';
 
 					// Insert the renamed user
 					$db->query('INSERT INTO '.$db->prefix.'users('.implode(',', array_keys($temp)).') VALUES ('.implode(',', array_values($temp)).')') or error('Unable to insert data to new table', __FILE__, __LINE__, $db->error());
@@ -1535,7 +1612,7 @@ switch ($stage)
 					$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);
+					$mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'], $mail_message);
 
 					pun_mail($cur_user['email'], $mail_subject, $mail_message);
 
@@ -1551,7 +1628,7 @@ switch ($stage)
 ?>
 <!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">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php echo $lang_common['lang_identifier'] ?>" lang="<?php echo $lang_common['lang_identifier'] ?>" dir="<?php echo $lang_common['lang_direction'] ?>">
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 <title><?php echo $lang_update['Update'] ?></title>
@@ -1788,7 +1865,7 @@ foreach ($errors[$id] as $cur_error)
 ?>
 <!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">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php echo $lang_common['lang_identifier'] ?>" lang="<?php echo $lang_common['lang_identifier'] ?>" dir="<?php echo $lang_common['lang_direction'] ?>">
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 <title><?php echo $lang_update['Update'] ?></title>
@@ -1828,4 +1905,4 @@ $db->end_transaction();
 $db->close();
 
 if ($query_str != '')
-	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>');
+	exit('<script type="text/javascript">window.location="db_update.php'.$query_str.'&uid='.$uid.'"</script><noscript><meta http-equiv="refresh" content="0;url=db_update.php'.$query_str.'&uid='.$uid.'" /></noscript>');
diff --git a/delete.php b/delete.php
index 7844c4b..f022f53 100644
--- a/delete.php
+++ b/delete.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -11,17 +11,17 @@ require PUN_ROOT.'include/common.php';
 
 
 if ($pun_user['g_read_board'] == '0')
-	message($lang_common['No view']);
+	message($lang_common['No view'], false, '403 Forbidden');
 
 
 $id = isset($_GET['id']) ? intval($_GET['id']) : 0;
 if ($id < 1)
-	message($lang_common['Bad request']);
+	message($lang_common['Bad request'], false, '404 Not Found');
 
 // 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.first_post_id, t.closed, p.posted, 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']);
+	message($lang_common['Bad request'], false, '404 Not Found');
 
 $cur_post = $db->fetch_assoc($result);
 
@@ -40,7 +40,7 @@ if (($pun_user['g_delete_posts'] == '0' ||
 	$cur_post['poster_id'] != $pun_user['id'] ||
 	$cur_post['closed'] == '1') &&
 	!$is_admmod)
-	message($lang_common['No permission']);
+	message($lang_common['No permission'], false, '403 Forbidden');
 
 // Load the delete.php language file
 require PUN_ROOT.'lang/'.$pun_user['language'].'/delete.php';
diff --git a/edit.php b/edit.php
index 1f35edd..f9b036b 100644
--- a/edit.php
+++ b/edit.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -11,17 +11,17 @@ require PUN_ROOT.'include/common.php';
 
 
 if ($pun_user['g_read_board'] == '0')
-	message($lang_common['No view']);
+	message($lang_common['No view'], false, '403 Forbidden');
 
 
 $id = isset($_GET['id']) ? intval($_GET['id']) : 0;
 if ($id < 1)
-	message($lang_common['Bad request']);
+	message($lang_common['Bad request'], false, '404 Not Found');
 
 // 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.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']);
+	message($lang_common['Bad request'], false, '404 Not Found');
 
 $cur_post = $db->fetch_assoc($result);
 
@@ -42,7 +42,7 @@ if (($pun_user['g_edit_posts'] == '0' ||
 	$cur_post['poster_id'] != $pun_user['id'] ||
 	$cur_post['closed'] == '1') &&
 	!$is_admmod)
-	message($lang_common['No permission']);
+	message($lang_common['No permission'], false, '403 Forbidden');
 
 // Load the post.php/edit.php language file
 require PUN_ROOT.'lang/'.$pun_user['language'].'/post.php';
@@ -108,6 +108,9 @@ if (isset($_POST['form_sent']))
 	$stick_topic = isset($_POST['stick_topic']) ? '1' : '0';
 	if (!$is_admmod)
 		$stick_topic = $cur_post['sticky'];
+	
+	// Replace four-byte characters (MySQL cannot handle them)
+	$message = strip_bad_multibyte_chars($message);
 
 	// Did everything go according to plan?
 	if (empty($errors) && !isset($_POST['preview']))
diff --git a/extern.php b/extern.php
index 364229e..eb4bc94 100644
--- a/extern.php
+++ b/extern.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
diff --git a/footer.php b/footer.php
index 6c92e00..b6b8a7a 100644
--- a/footer.php
+++ b/footer.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
diff --git a/header.php b/header.php
index 0f300db..b4905aa 100644
--- a/header.php
+++ b/header.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -41,7 +41,7 @@ else
 $tpl_main = file_get_contents($tpl_file);
 
 // START SUBST - <pun_include "*">
-preg_match_all('#<pun_include "([^/\\\\]*?)\.(php[45]?|inc|html?|txt)">#', $tpl_main, $pun_includes, PREG_SET_ORDER);
+preg_match_all('%<pun_include "([^/\\\\]*?)\.(php[45]?|inc|html?|txt)">%i', $tpl_main, $pun_includes, PREG_SET_ORDER);
 
 foreach ($pun_includes as $cur_include)
 {
@@ -104,7 +104,7 @@ if (isset($required_fields))
 /* <![CDATA[ */
 function process_form(the_form)
 {
-	var element_names = {
+	var required_fields = {
 <?php
 	// Output a JavaScript object with localised field names
 	$tpl_temp = count($required_fields);
@@ -120,14 +120,11 @@ function process_form(the_form)
 		for (var i = 0; i < the_form.length; ++i)
 		{
 			var elem = the_form.elements[i];
-			if (elem.name && (/^req_/.test(elem.name)))
+			if (elem.name && required_fields[elem.name] && !elem.value && elem.type && (/^(?:text(?:area)?|password|file)$/i.test(elem.type)))
 			{
-				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('"' + required_fields[elem.name] + '" <?php echo $lang_common['required field'] ?>');
+				elem.focus();
+				return false;
 			}
 		}
 	}
@@ -142,12 +139,8 @@ function process_form(the_form)
 // JavaScript tricks for IE6 and older
 echo '<!--[if lte IE 6]><script type="text/javascript" src="style/imports/minmax.js"></script><![endif]-->'."\n";
 
-if (!isset($page_head))
-	$page_head = array();
-
-$page_head['top'] = '<link rel="top" href="index.php" title="'.$lang_common['Forum index'].'" />';
-
-echo implode("\n", $page_head)."\n";
+if (isset($page_head))
+	echo implode("\n", $page_head)."\n";
 
 $tpl_temp = trim(ob_get_contents());
 $tpl_main = str_replace('<pun_head>', $tpl_temp, $tpl_main);
@@ -212,7 +205,7 @@ else
 // Are there any additional navlinks we should insert into the array before imploding it?
 if ($pun_user['g_read_board'] == '1' && $pun_config['o_additional_navlinks'] != '')
 {
-	if (preg_match_all('#([0-9]+)\s*=\s*(.*?)\n#s', $pun_config['o_additional_navlinks']."\n", $extra_links))
+	if (preg_match_all('%([0-9]+)\s*=\s*(.*?)\n%s', $pun_config['o_additional_navlinks']."\n", $extra_links))
 	{
 		// Insert any additional links into the $links array (at the correct index)
 		$num_links = count($extra_links[1]);
@@ -230,7 +223,7 @@ $tpl_main = str_replace('<pun_navlinks>', $tpl_temp, $tpl_main);
 $page_statusinfo = $page_topicsearches = array();
 
 if ($pun_user['is_guest'])
-	$page_statusinfo = '<p>'.$lang_common['Not logged in'].'</p>';
+	$page_statusinfo = '<p class="conl">'.$lang_common['Not logged in'].'</p>';
 else
 {
 	$page_statusinfo[] = '<li><span>'.$lang_common['Logged in as'].' <strong>'.pun_htmlspecialchars($pun_user['username']).'</strong></span></li>';
@@ -266,7 +259,7 @@ if ($pun_user['g_read_board'] == '1' && $pun_user['g_search'] == '1')
 
 
 // Generate all that jazz
-$tpl_temp = '<div id="brdwelcome" class="inbox">'."\n\t\t\t";
+$tpl_temp = '<div id="brdwelcome" class="inbox">';
 
 // The status information
 if (is_array($page_statusinfo))
@@ -283,10 +276,10 @@ if (!empty($page_topicsearches))
 {
 	$tpl_temp .= "\n\t\t\t".'<ul class="conr">';
 	$tpl_temp .= "\n\t\t\t\t".'<li><span>'.$lang_common['Topic searches'].' '.implode(' | ', $page_topicsearches).'</span></li>';
-	$tpl_temp .= "\n\t\t\t".'</ul>'."\n\t\t\t".'<div class="clearer"></div>';
+	$tpl_temp .= "\n\t\t\t".'</ul>';
 }
 
-$tpl_temp .= "\n\t\t".'</div>';
+$tpl_temp .= "\n\t\t\t".'<div class="clearer"></div>'."\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 c1e94ae..caa7c23 100644
--- a/help.php
+++ b/help.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -14,7 +14,7 @@ require PUN_ROOT.'include/common.php';
 
 
 if ($pun_user['g_read_board'] == '0')
-	message($lang_common['No view']);
+	message($lang_common['No view'], false, '403 Forbidden');
 
 
 // Load the help.php language file
@@ -55,12 +55,21 @@ require PUN_ROOT.'header.php';
 		<p><?php echo $lang_help['Links info'] ?></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>[url=/help.php]<?php echo $lang_help['This help page'] ?>[/url]</code> <?php echo $lang_help['produces'] ?> <samp><a href="<?php echo pun_htmlspecialchars(get_base_url(true).'/help.php') ?>"><?php echo $lang_help['This help page'] ?></a></samp></p>
 		<p><code>[email]myname@mydomain.com[/email]</code> <?php echo $lang_help['produces'] ?> <samp><a href="mailto:myname@mydomain.com">myname@mydomain.com</a></samp></p>
 		<p><code>[email=myname@mydomain.com]<?php echo $lang_help['My email address'] ?>[/email]</code> <?php echo $lang_help['produces'] ?> <samp><a href="mailto:myname@mydomain.com"><?php echo $lang_help['My email address'] ?></a></samp></p>
+		<p><code>[topic=1]<?php echo $lang_help['Test topic'] ?>[/topic]</code> <?php echo $lang_help['produces'] ?> <samp><a href="<?php echo pun_htmlspecialchars(get_base_url(true).'/viewtopic.php?id=1') ?>"><?php echo $lang_help['Test topic'] ?></a></samp></p>
+		<p><code>[topic]1[/topic]</code> <?php echo $lang_help['produces'] ?> <samp><a href="<?php echo pun_htmlspecialchars(get_base_url(true).'/viewtopic.php?id=1') ?>"><?php echo pun_htmlspecialchars(get_base_url(true).'/viewtopic.php?id=1') ?></a></samp></p>
+		<p><code>[post=1]<?php echo $lang_help['Test post'] ?>[/post]</code> <?php echo $lang_help['produces'] ?> <samp><a href="<?php echo pun_htmlspecialchars(get_base_url(true).'/viewtopic.php?pid=1#p1') ?>"><?php echo $lang_help['Test post'] ?></a></samp></p>
+		<p><code>[post]1[/post]</code> <?php echo $lang_help['produces'] ?> <samp><a href="<?php echo pun_htmlspecialchars(get_base_url(true).'/viewtopic.php?pid=1#p1') ?>"><?php echo pun_htmlspecialchars(get_base_url(true).'/viewtopic.php?pid=1#p1') ?></a></samp></p>
+		<p><code>[forum=1]<?php echo $lang_help['Test forum'] ?>[/forum]</code> <?php echo $lang_help['produces'] ?> <samp><a href="<?php echo pun_htmlspecialchars(get_base_url(true).'/viewforum.php?id=1') ?>"><?php echo $lang_help['Test forum'] ?></a></samp></p>
+		<p><code>[forum]1[/forum]</code> <?php echo $lang_help['produces'] ?> <samp><a href="<?php echo pun_htmlspecialchars(get_base_url(true).'/viewforum.php?id=1') ?>"><?php echo pun_htmlspecialchars(get_base_url(true).'/viewforum.php?id=1') ?></a></samp></p>
+		<p><code>[user=2]<?php echo $lang_help['Test user'] ?>[/user]</code> <?php echo $lang_help['produces'] ?> <samp><a href="<?php echo pun_htmlspecialchars(get_base_url(true).'/profile.php?id=2') ?>"><?php echo $lang_help['Test user'] ?></a></samp></p>
+		<p><code>[user]2[/user]</code> <?php echo $lang_help['produces'] ?> <samp><a href="<?php echo pun_htmlspecialchars(get_base_url(true).'/profile.php?id=2') ?>"><?php echo pun_htmlspecialchars(get_base_url(true).'/profile.php?id=2') ?></a></samp></p>
 	</div>
 	<div class="inbox">
 		<p><a name="img"></a><?php echo $lang_help['Images info'] ?></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>
+		<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 style="height: 21px" 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>
diff --git a/img/smilies/big_smile.png b/img/smilies/big_smile.png
index b29719b..dad2fe8 100644
Binary files a/img/smilies/big_smile.png and b/img/smilies/big_smile.png differ
diff --git a/img/smilies/cool.png b/img/smilies/cool.png
index a875361..6b74528 100644
Binary files a/img/smilies/cool.png and b/img/smilies/cool.png differ
diff --git a/img/smilies/hmm.png b/img/smilies/hmm.png
index 69e416a..2dbc8aa 100644
Binary files a/img/smilies/hmm.png and b/img/smilies/hmm.png differ
diff --git a/img/smilies/lol.png b/img/smilies/lol.png
index dd9058b..45cb27e 100644
Binary files a/img/smilies/lol.png and b/img/smilies/lol.png differ
diff --git a/img/smilies/mad.png b/img/smilies/mad.png
index 011079d..3a26df0 100644
Binary files a/img/smilies/mad.png and b/img/smilies/mad.png differ
diff --git a/img/smilies/neutral.png b/img/smilies/neutral.png
index 9bc5ca0..2471da5 100644
Binary files a/img/smilies/neutral.png and b/img/smilies/neutral.png differ
diff --git a/img/smilies/roll.png b/img/smilies/roll.png
index e2e8e2a..f0ef64b 100644
Binary files a/img/smilies/roll.png and b/img/smilies/roll.png differ
diff --git a/img/smilies/sad.png b/img/smilies/sad.png
index c14e144..f03ae85 100644
Binary files a/img/smilies/sad.png and b/img/smilies/sad.png differ
diff --git a/img/smilies/smile.png b/img/smilies/smile.png
index 44cc239..27299dc 100644
Binary files a/img/smilies/smile.png and b/img/smilies/smile.png differ
diff --git a/img/smilies/tongue.png b/img/smilies/tongue.png
index 5302c89..5de9741 100644
Binary files a/img/smilies/tongue.png and b/img/smilies/tongue.png differ
diff --git a/img/smilies/wink.png b/img/smilies/wink.png
index 595c1fe..6807feb 100644
Binary files a/img/smilies/wink.png and b/img/smilies/wink.png differ
diff --git a/img/smilies/yikes.png b/img/smilies/yikes.png
index bb4aefd..64a2c70 100644
Binary files a/img/smilies/yikes.png and b/img/smilies/yikes.png differ
diff --git a/img/test.png b/img/test.png
index 7b46d9e..34eb90a 100644
Binary files a/img/test.png and b/img/test.png differ
diff --git a/include/cache.php b/include/cache.php
index 99c2487..e4b9112 100644
--- a/include/cache.php
+++ b/include/cache.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -20,6 +20,8 @@ function generate_config_cache()
 
 	// Get the forum config from the DB
 	$result = $db->query('SELECT * FROM '.$db->prefix.'config', true) or error('Unable to fetch forum config', __FILE__, __LINE__, $db->error());
+
+	$output = array();
 	while ($cur_config_item = $db->fetch_row($result))
 		$output[$cur_config_item[0]] = $cur_config_item[1];
 
@@ -183,7 +185,7 @@ function generate_censoring_cache()
 	for ($i = 0; $i < $num_words; $i++)
 	{
 		list($search_for[$i], $replace_with[$i]) = $db->fetch_row($result);
-		$search_for[$i] = '/(?<=[^\p{L}\p{N}])('.str_replace('\*', '[\p{L}\p{N}]*?', preg_quote($search_for[$i], '/')).')(?=[^\p{L}\p{N}])/iu';
+		$search_for[$i] = '%(?<=[^\p{L}\p{N}])('.str_replace('\*', '[\p{L}\p{N}]*?', preg_quote($search_for[$i], '%')).')(?=[^\p{L}\p{N}])%iu';
 	}
 
 	// Output censored words as PHP code
diff --git a/include/common.php b/include/common.php
index 676927a..b3bf44b 100644
--- a/include/common.php
+++ b/include/common.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -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.5');
+define('FORUM_VERSION', '1.4.9');
 
-define('FORUM_DB_REVISION', 11);
+define('FORUM_DB_REVISION', 15);
 define('FORUM_SI_REVISION', 2);
 define('FORUM_PARSER_REVISION', 2);
 
@@ -70,8 +70,8 @@ setlocale(LC_CTYPE, 'C');
 if (get_magic_quotes_runtime())
 	set_magic_quotes_runtime(0);
 
-// Strip slashes from GET/POST/COOKIE (if magic_quotes_gpc is enabled)
-if (get_magic_quotes_gpc())
+// Strip slashes from GET/POST/COOKIE/REQUEST/FILES (if magic_quotes_gpc is enabled)
+if (!defined('FORUM_DISABLE_STRIPSLASHES') && get_magic_quotes_gpc())
 {
 	function stripslashes_array($array)
 	{
@@ -82,6 +82,13 @@ if (get_magic_quotes_gpc())
 	$_POST = stripslashes_array($_POST);
 	$_COOKIE = stripslashes_array($_COOKIE);
 	$_REQUEST = stripslashes_array($_REQUEST);
+	if (is_array($_FILES))
+	{
+		// Don't strip valid slashes from tmp_name path on Windows
+		foreach ($_FILES AS $key => $value)
+			$_FILES[$key]['tmp_name'] = str_replace('\\', '\\\\', $value['tmp_name']);
+		$_FILES = stripslashes_array($_FILES);
+	}
 }
 
 // If a cookie name is not specified in config.php, we use the default (pun_cookie)
@@ -120,13 +127,13 @@ if (!defined('PUN_CONFIG_LOADED'))
 
 // Verify that we are running the proper database schema revision
 if (!isset($pun_config['o_database_revision']) || $pun_config['o_database_revision'] < FORUM_DB_REVISION ||
-		!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, '<'))
-	{
-		header('Location: db_update.php');
-		exit;
-	}
+	!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, '<'))
+{
+	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 5ce1319..594b401 100644
--- a/include/common_admin.php
+++ b/include/common_admin.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -14,7 +14,7 @@ if (!defined('PUN'))
 if (file_exists(PUN_ROOT.'lang/'.$pun_user['language'].'/admin_common.php'))
 	$admin_language = $pun_user['language'];
 else if (file_exists(PUN_ROOT.'lang/'.$pun_config['o_default_lang'].'/admin_common.php'))
-	$admin_language = $pun_config['language'];
+	$admin_language = $pun_config['o_default_lang'];
 else
 	$admin_language = 'English';
 
@@ -83,8 +83,8 @@ function generate_admin_menu($page = '')
 				<ul>
 <?php
 
-		foreach ($plugins as $cur_plugin)
-			echo "\t\t\t\t\t".'<li'.(($page == $cur_plugin[1]) ? ' class="isactive"' : '').'><a href="admin_loader.php?plugin='.$cur_plugin[1].'">'.str_replace('_', ' ', $cur_plugin[0]).'</a></li>'."\n";
+		foreach ($plugins as $plugin_name => $plugin)
+			echo "\t\t\t\t\t".'<li'.(($page == $plugin_name) ? ' class="isactive"' : '').'><a href="admin_loader.php?plugin='.$plugin_name.'">'.str_replace('_', ' ', $plugin).'</a></li>'."\n";
 
 ?>
 				</ul>
diff --git a/include/dblayer/common_db.php b/include/dblayer/common_db.php
index a832376..5b9e67e 100644
--- a/include/dblayer/common_db.php
+++ b/include/dblayer/common_db.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -15,27 +15,27 @@ if (!defined('PUN'))
 switch ($db_type)
 {
 	case 'mysql':
-		require PUN_ROOT.'include/dblayer/mysql.php';
+		require_once PUN_ROOT.'include/dblayer/mysql.php';
 		break;
 
 	case 'mysql_innodb':
-		require PUN_ROOT.'include/dblayer/mysql_innodb.php';
+		require_once PUN_ROOT.'include/dblayer/mysql_innodb.php';
 		break;
 
 	case 'mysqli':
-		require PUN_ROOT.'include/dblayer/mysqli.php';
+		require_once PUN_ROOT.'include/dblayer/mysqli.php';
 		break;
 
 	case 'mysqli_innodb':
-		require PUN_ROOT.'include/dblayer/mysqli_innodb.php';
+		require_once PUN_ROOT.'include/dblayer/mysqli_innodb.php';
 		break;
 
 	case 'pgsql':
-		require PUN_ROOT.'include/dblayer/pgsql.php';
+		require_once PUN_ROOT.'include/dblayer/pgsql.php';
 		break;
 
 	case 'sqlite':
-		require PUN_ROOT.'include/dblayer/sqlite.php';
+		require_once PUN_ROOT.'include/dblayer/sqlite.php';
 		break;
 
 	default:
diff --git a/include/dblayer/mysql.php b/include/dblayer/mysql.php
index f35609f..256125e 100644
--- a/include/dblayer/mysql.php
+++ b/include/dblayer/mysql.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -24,7 +24,7 @@ class DBLayer
 	var $error_msg = 'Unknown';
 
 	var $datatype_transformations = array(
-		'/^SERIAL$/'	=>	'INT(10) UNSIGNED AUTO_INCREMENT'
+		'%^SERIAL$%'	=>	'INT(10) UNSIGNED AUTO_INCREMENT'
 	);
 
 
@@ -204,7 +204,7 @@ class DBLayer
 
 		return array(
 			'name'		=> 'MySQL Standard',
-			'version'	=> preg_replace('/^([^-]+).*$/', '\\1', $this->result($result))
+			'version'	=> preg_replace('%^([^-]+).*$%', '\\1', $this->result($result))
 		);
 	}
 
@@ -318,10 +318,10 @@ class DBLayer
 
 		$field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
 
-		if ($default_value !== null && !is_int($default_value) && !is_float($default_value))
+		if (!is_null($default_value) && !is_int($default_value) && !is_float($default_value))
 			$default_value = '\''.$this->escape($default_value).'\'';
 
-		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.$field_name.' '.$field_type.($allow_null ? ' ' : ' NOT NULL').($default_value !== null ? ' DEFAULT '.$default_value : ' ').($after_field != null ? ' AFTER '.$after_field : '')) ? true : false;
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.$field_name.' '.$field_type.($allow_null ? ' ' : ' NOT NULL').(!is_null($default_value) ? ' DEFAULT '.$default_value : ' ').(!is_null($after_field) ? ' AFTER '.$after_field : '')) ? true : false;
 	}
 
 
@@ -332,10 +332,10 @@ class DBLayer
 
 		$field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
 
-		if ($default_value !== null && !is_int($default_value) && !is_float($default_value))
+		if (!is_null($default_value) && !is_int($default_value) && !is_float($default_value))
 			$default_value = '\''.$this->escape($default_value).'\'';
 
-		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' MODIFY '.$field_name.' '.$field_type.($allow_null ? ' ' : ' NOT NULL').($default_value !== null ? ' DEFAULT '.$default_value : ' ').($after_field != null ? ' AFTER '.$after_field : '')) ? true : false;
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' MODIFY '.$field_name.' '.$field_type.($allow_null ? ' ' : ' NOT NULL').(!is_null($default_value) ? ' DEFAULT '.$default_value : ' ').(!is_null($after_field) ? ' AFTER '.$after_field : '')) ? true : false;
 	}
 
 
diff --git a/include/dblayer/mysql_innodb.php b/include/dblayer/mysql_innodb.php
index 8071a3b..01ca724 100644
--- a/include/dblayer/mysql_innodb.php
+++ b/include/dblayer/mysql_innodb.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -25,7 +25,7 @@ class DBLayer
 	var $error_msg = 'Unknown';
 
 	var $datatype_transformations = array(
-		'/^SERIAL$/'	=>	'INT(10) UNSIGNED AUTO_INCREMENT'
+		'%^SERIAL$%'	=>	'INT(10) UNSIGNED AUTO_INCREMENT'
 	);
 
 
@@ -218,7 +218,7 @@ class DBLayer
 
 		return array(
 			'name'		=> 'MySQL Standard (InnoDB)',
-			'version'	=> preg_replace('/^([^-]+).*$/', '\\1', $this->result($result))
+			'version'	=> preg_replace('%^([^-]+).*$%', '\\1', $this->result($result))
 		);
 	}
 
@@ -332,10 +332,10 @@ class DBLayer
 
 		$field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
 
-		if ($default_value !== null && !is_int($default_value) && !is_float($default_value))
+		if (!is_null($default_value) && !is_int($default_value) && !is_float($default_value))
 			$default_value = '\''.$this->escape($default_value).'\'';
 
-		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.$field_name.' '.$field_type.($allow_null ? ' ' : ' NOT NULL').($default_value !== null ? ' DEFAULT '.$default_value : ' ').($after_field != null ? ' AFTER '.$after_field : '')) ? true : false;
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.$field_name.' '.$field_type.($allow_null ? ' ' : ' NOT NULL').(!is_null($default_value) ? ' DEFAULT '.$default_value : ' ').(!is_null($after_field) ? ' AFTER '.$after_field : '')) ? true : false;
 	}
 
 
@@ -346,10 +346,10 @@ class DBLayer
 
 		$field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
 
-		if ($default_value !== null && !is_int($default_value) && !is_float($default_value))
+		if (!is_null($default_value) && !is_int($default_value) && !is_float($default_value))
 			$default_value = '\''.$this->escape($default_value).'\'';
 
-		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' MODIFY '.$field_name.' '.$field_type.($allow_null ? ' ' : ' NOT NULL').($default_value !== null ? ' DEFAULT '.$default_value : ' ').($after_field != null ? ' AFTER '.$after_field : '')) ? true : false;
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' MODIFY '.$field_name.' '.$field_type.($allow_null ? ' ' : ' NOT NULL').(!is_null($default_value) ? ' DEFAULT '.$default_value : ' ').(!is_null($after_field) ? ' AFTER '.$after_field : '')) ? true : false;
 	}
 
 
diff --git a/include/dblayer/mysqli.php b/include/dblayer/mysqli.php
index cab2776..8b2675b 100644
--- a/include/dblayer/mysqli.php
+++ b/include/dblayer/mysqli.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -24,7 +24,7 @@ class DBLayer
 	var $error_msg = 'Unknown';
 
 	var $datatype_transformations = array(
-		'/^SERIAL$/'	=>	'INT(10) UNSIGNED AUTO_INCREMENT'
+		'%^SERIAL$%'	=>	'INT(10) UNSIGNED AUTO_INCREMENT'
 	);
 
 
@@ -211,7 +211,7 @@ class DBLayer
 
 		return array(
 			'name'		=> 'MySQL Improved',
-			'version'	=> preg_replace('/^([^-]+).*$/', '\\1', $this->result($result))
+			'version'	=> preg_replace('%^([^-]+).*$%', '\\1', $this->result($result))
 		);
 	}
 
@@ -325,10 +325,10 @@ class DBLayer
 
 		$field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
 
-		if ($default_value !== null && !is_int($default_value) && !is_float($default_value))
+		if (!is_null($default_value) && !is_int($default_value) && !is_float($default_value))
 			$default_value = '\''.$this->escape($default_value).'\'';
 
-		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.$field_name.' '.$field_type.($allow_null ? ' ' : ' NOT NULL').($default_value !== null ? ' DEFAULT '.$default_value : ' ').($after_field != null ? ' AFTER '.$after_field : '')) ? true : false;
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.$field_name.' '.$field_type.($allow_null ? ' ' : ' NOT NULL').(!is_null($default_value) ? ' DEFAULT '.$default_value : ' ').(!is_null($after_field) ? ' AFTER '.$after_field : '')) ? true : false;
 	}
 
 
@@ -339,10 +339,10 @@ class DBLayer
 
 		$field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
 
-		if ($default_value !== null && !is_int($default_value) && !is_float($default_value))
+		if (!is_null($default_value) && !is_int($default_value) && !is_float($default_value))
 			$default_value = '\''.$this->escape($default_value).'\'';
 
-		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' MODIFY '.$field_name.' '.$field_type.($allow_null ? ' ' : ' NOT NULL').($default_value !== null ? ' DEFAULT '.$default_value : ' ').($after_field != null ? ' AFTER '.$after_field : '')) ? true : false;
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' MODIFY '.$field_name.' '.$field_type.($allow_null ? ' ' : ' NOT NULL').(!is_null($default_value) ? ' DEFAULT '.$default_value : ' ').(!is_null($after_field) ? ' AFTER '.$after_field : '')) ? true : false;
 	}
 
 
diff --git a/include/dblayer/mysqli_innodb.php b/include/dblayer/mysqli_innodb.php
index cefbd73..3a07431 100644
--- a/include/dblayer/mysqli_innodb.php
+++ b/include/dblayer/mysqli_innodb.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -25,7 +25,7 @@ class DBLayer
 	var $error_msg = 'Unknown';
 
 	var $datatype_transformations = array(
-		'/^SERIAL$/'	=>	'INT(10) UNSIGNED AUTO_INCREMENT'
+		'%^SERIAL$%'	=>	'INT(10) UNSIGNED AUTO_INCREMENT'
 	);
 
 
@@ -224,7 +224,7 @@ class DBLayer
 
 		return array(
 			'name'		=> 'MySQL Improved (InnoDB)',
-			'version'	=> preg_replace('/^([^-]+).*$/', '\\1', $this->result($result))
+			'version'	=> preg_replace('%^([^-]+).*$%', '\\1', $this->result($result))
 		);
 	}
 
@@ -338,10 +338,10 @@ class DBLayer
 
 		$field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
 
-		if ($default_value !== null && !is_int($default_value) && !is_float($default_value))
+		if (!is_null($default_value) && !is_int($default_value) && !is_float($default_value))
 			$default_value = '\''.$this->escape($default_value).'\'';
 
-		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.$field_name.' '.$field_type.($allow_null ? ' ' : ' NOT NULL').($default_value !== null ? ' DEFAULT '.$default_value : ' ').($after_field != null ? ' AFTER '.$after_field : '')) ? true : false;
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.$field_name.' '.$field_type.($allow_null ? ' ' : ' NOT NULL').(!is_null($default_value) ? ' DEFAULT '.$default_value : ' ').(!is_null($after_field) ? ' AFTER '.$after_field : '')) ? true : false;
 	}
 
 
@@ -352,10 +352,10 @@ class DBLayer
 
 		$field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
 
-		if ($default_value !== null && !is_int($default_value) && !is_float($default_value))
+		if (!is_null($default_value) && !is_int($default_value) && !is_float($default_value))
 			$default_value = '\''.$this->escape($default_value).'\'';
 
-		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' MODIFY '.$field_name.' '.$field_type.($allow_null ? ' ' : ' NOT NULL').($default_value !== null ? ' DEFAULT '.$default_value : ' ').($after_field != null ? ' AFTER '.$after_field : '')) ? true : false;
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' MODIFY '.$field_name.' '.$field_type.($allow_null ? ' ' : ' NOT NULL').(!is_null($default_value) ? ' DEFAULT '.$default_value : ' ').(!is_null($after_field) ? ' AFTER '.$after_field : '')) ? true : false;
 	}
 
 
diff --git a/include/dblayer/pgsql.php b/include/dblayer/pgsql.php
index 9d4547e..3a73118 100644
--- a/include/dblayer/pgsql.php
+++ b/include/dblayer/pgsql.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -26,12 +26,12 @@ class DBLayer
 	var $error_msg = 'Unknown';
 
 	var $datatype_transformations = array(
-		'/^(TINY|SMALL)INT( )?(\\([0-9]+\\))?( )?(UNSIGNED)?$/i'			=>	'SMALLINT',
-		'/^(MEDIUM)?INT( )?(\\([0-9]+\\))?( )?(UNSIGNED)?$/i'				=>	'INTEGER',
-		'/^BIGINT( )?(\\([0-9]+\\))?( )?(UNSIGNED)?$/i'						=>	'BIGINT',
-		'/^(TINY|MEDIUM|LONG)?TEXT$/i'										=>	'TEXT',
-		'/^DOUBLE( )?(\\([0-9,]+\\))?( )?(UNSIGNED)?$/i'					=>	'DOUBLE PRECISION',
-		'/^FLOAT( )?(\\([0-9]+\\))?( )?(UNSIGNED)?$/i'						=>	'REAL'
+		'%^(TINY|SMALL)INT( )?(\\([0-9]+\\))?( )?(UNSIGNED)?$%i'			=>	'SMALLINT',
+		'%^(MEDIUM)?INT( )?(\\([0-9]+\\))?( )?(UNSIGNED)?$%i'				=>	'INTEGER',
+		'%^BIGINT( )?(\\([0-9]+\\))?( )?(UNSIGNED)?$%i'						=>	'BIGINT',
+		'%^(TINY|MEDIUM|LONG)?TEXT$%i'										=>	'TEXT',
+		'%^DOUBLE( )?(\\([0-9,]+\\))?( )?(UNSIGNED)?$%i'					=>	'DOUBLE PRECISION',
+		'%^FLOAT( )?(\\([0-9]+\\))?( )?(UNSIGNED)?$%i'						=>	'REAL'
 	);
 
 
@@ -100,7 +100,7 @@ class DBLayer
 	function query($sql, $unbuffered = false) // $unbuffered is ignored since there is no pgsql_unbuffered_query()
 	{
 		if (strrpos($sql, 'LIMIT') !== false)
-			$sql = preg_replace('#LIMIT ([0-9]+),([ 0-9]+)#', 'LIMIT \\2 OFFSET \\1', $sql);
+			$sql = preg_replace('%LIMIT ([0-9]+),([ 0-9]+)%', 'LIMIT \\2 OFFSET \\1', $sql);
 
 		if (defined('PUN_SHOW_QUERIES'))
 			$q_start = get_microtime();
@@ -173,7 +173,7 @@ class DBLayer
 
 		if ($query_id && $this->last_query_text[$query_id] != '')
 		{
-			if (preg_match('/^INSERT INTO ([a-z0-9\_\-]+)/is', $this->last_query_text[$query_id], $table_name))
+			if (preg_match('%^INSERT INTO ([a-z0-9\_\-]+)%is', $this->last_query_text[$query_id], $table_name))
 			{
 				// Hack (don't ask)
 				if (substr($table_name[1], -6) == 'groups')
@@ -266,7 +266,7 @@ class DBLayer
 
 		return array(
 			'name'		=> 'PostgreSQL',
-			'version'	=> preg_replace('/^[^0-9]+([^\s,-]+).*$/', '\\1', $this->result($result))
+			'version'	=> preg_replace('%^[^0-9]+([^\s,-]+).*$%', '\\1', $this->result($result))
 		);
 	}
 
@@ -371,7 +371,7 @@ class DBLayer
 
 		$result = $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.$field_name.' '.$field_type) ? true : false;
 
-		if ($default_value !== null)
+		if (!is_null($default_value))
 		{
 			if (!is_int($default_value) && !is_float($default_value))
 				$default_value = '\''.$this->escape($default_value).'\'';
diff --git a/include/dblayer/sqlite.php b/include/dblayer/sqlite.php
index cf6d4c2..e83ec70 100644
--- a/include/dblayer/sqlite.php
+++ b/include/dblayer/sqlite.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -25,9 +25,9 @@ class DBLayer
 	var $error_msg = 'Unknown';
 
 	var $datatype_transformations = array(
-		'/^SERIAL$/'															=>	'INTEGER',
-		'/^(TINY|SMALL|MEDIUM|BIG)?INT( )?(\\([0-9]+\\))?( )?(UNSIGNED)?$/i'	=>	'INTEGER',
-		'/^(TINY|MEDIUM|LONG)?TEXT$/i'											=>	'TEXT'
+		'%^SERIAL$%'															=>	'INTEGER',
+		'%^(TINY|SMALL|MEDIUM|BIG)?INT( )?(\\([0-9]+\\))?( )?(UNSIGNED)?$%i'	=>	'INTEGER',
+		'%^(TINY|MEDIUM|LONG)?TEXT$%i'											=>	'TEXT'
 	);
 
 
@@ -49,7 +49,7 @@ class DBLayer
 		if (!is_readable($db_name))
 			error('Unable to open database \''.$db_name.'\' for reading. Permission denied', __FILE__, __LINE__);
 
-		if (!is_writable($db_name))
+		if (!forum_is_writable($db_name))
 			error('Unable to open database \''.$db_name.'\' for writing. Permission denied', __FILE__, __LINE__);
 
 		if ($p_connect)
@@ -279,7 +279,7 @@ class DBLayer
 		if (!$this->num_rows($result))
 			return false;
 
-		return preg_match('/[\r\n]'.preg_quote($field_name).' /', $this->result($result));
+		return preg_match('%[\r\n]'.preg_quote($field_name, '%').' %', $this->result($result));
 	}
 
 
@@ -345,7 +345,7 @@ class DBLayer
 		if (!$this->table_exists($table_name, $no_prefix))
 			return true;
 
-		return $this->query('DROP TABLE '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+		return $this->query('DROP TABLE '.($no_prefix ? '' : $this->prefix).$this->escape($table_name)) ? true : false;
 	}
 
 
@@ -372,7 +372,7 @@ class DBLayer
 		$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));
+		$result &= $this->drop_table($table_name, $no_prefix);
 
 		return $result;
 	}
@@ -405,7 +405,7 @@ class DBLayer
 		$table['columns'] = array();
 		foreach ($table_lines as $table_line)
 		{
-			$table_line = pun_trim($table_line);
+			$table_line = trim($table_line, " \t\n\r,"); // trim spaces, tabs, newlines, and commas
 			if (substr($table_line, 0, 12) == 'CREATE TABLE')
 				continue;
 			else if (substr($table_line, 0, 11) == 'PRIMARY KEY')
@@ -413,7 +413,7 @@ class DBLayer
 			else if (substr($table_line, 0, 6) == 'UNIQUE')
 				$table['unique'] = $table_line;
 			else if (substr($table_line, 0, strpos($table_line, ' ')) != '')
-				$table['columns'][substr($table_line, 0, strpos($table_line, ' '))] = pun_trim(substr($table_line, strpos($table_line, ' ')));
+				$table['columns'][substr($table_line, 0, strpos($table_line, ' '))] = trim(substr($table_line, strpos($table_line, ' ')));
 		}
 
 		return $table;
@@ -438,29 +438,29 @@ class DBLayer
 		$query = $field_type;
 		if (!$allow_null)
 			$query .= ' NOT NULL';
-		if ($default_value === null || $default_value === '')
+		if (is_null($default_value) || $default_value === '')
 			$default_value = '\'\'';
 
 		$query .= ' DEFAULT '.$default_value;
 
 		$old_columns = array_keys($table['columns']);
-		array_insert($table['columns'], $after_field, $query.',', $field_name);
+		array_insert($table['columns'], $after_field, $query, $field_name);
 
 		$new_table = 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).' (';
 
 		foreach ($table['columns'] as $cur_column => $column_details)
-			$new_table .= "\n".$cur_column.' '.$column_details;
+			$new_table .= "\n".$cur_column.' '.$column_details.',';
 
 		if (isset($table['unique']))
 			$new_table .= "\n".$table['unique'].',';
 
 		if (isset($table['primary_key']))
-			$new_table .= "\n".$table['primary_key'];
+			$new_table .= "\n".$table['primary_key'].',';
 
 		$new_table = trim($new_table, ',')."\n".');';
 
 		// Drop old table
-		$result &= $this->drop_table(($no_prefix ? '' : $this->prefix).$this->escape($table_name));
+		$result &= $this->drop_table($table_name, $no_prefix);
 
 		// Create new table
 		$result &= $this->query($new_table) ? true : false;
@@ -476,7 +476,7 @@ class DBLayer
 		$result &= $this->query('INSERT INTO '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).' ('.implode(', ', $old_columns).') SELECT * FROM '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'_t'.$now) ? true : false;
 
 		// Drop temp table
-		$result &= $this->drop_table(($no_prefix ? '' : $this->prefix).$this->escape($table_name).'_t'.$now);
+		$result &= $this->drop_table($table_name.'_t'.$now, $no_prefix);
 
 		return $result;
 	}
@@ -509,18 +509,18 @@ class DBLayer
 		$new_table = 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).' (';
 
 		foreach ($table['columns'] as $cur_column => $column_details)
-			$new_table .= "\n".$cur_column.' '.$column_details;
+			$new_table .= "\n".$cur_column.' '.$column_details.',';
 
 		if (isset($table['unique']))
 			$new_table .= "\n".$table['unique'].',';
 
 		if (isset($table['primary_key']))
-			$new_table .= "\n".$table['primary_key'];
+			$new_table .= "\n".$table['primary_key'].',';
 
 		$new_table = trim($new_table, ',')."\n".');';
 
 		// Drop old table
-		$result &= $this->drop_table(($no_prefix ? '' : $this->prefix).$this->escape($table_name));
+		$result &= $this->drop_table($table_name, $no_prefix);
 
 		// Create new table
 		$result &= $this->query($new_table) ? true : false;
@@ -537,7 +537,7 @@ class DBLayer
 		$result &= $this->query('INSERT INTO '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).' SELECT '.implode(', ', $new_columns).' FROM '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'_t'.$now) ? true : false;
 
 		// Drop temp table
-		$result &= $this->drop_table(($no_prefix ? '' : $this->prefix).$this->escape($table_name).'_t'.$now);
+		$result &= $this->drop_table($table_name.'_t'.$now, $no_prefix);
 
 		return $result;
 	}
diff --git a/include/email.php b/include/email.php
index 376172d..85543d1 100644
--- a/include/email.php
+++ b/include/email.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -20,7 +20,7 @@ function is_valid_email($email)
 	if (strlen($email) > 80)
 		return false;
 
-	return preg_match('/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|("[^"]+"))@((\[\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\])|(([a-zA-Z\d\-]+\.)+[a-zA-Z]{2,}))$/', $email);
+	return preg_match('%^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|("[^"]+"))@((\[\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\])|(([a-zA-Z\d\-]+\.)+[a-zA-Z]{2,}))$%', $email);
 }
 
 
@@ -56,6 +56,157 @@ function encode_mail_text($str)
 
 
 //
+// Make a post email safe
+//
+function bbcode2email($text, $wrap_length = 72)
+{
+	static $base_url;
+
+	if (!isset($base_url))
+		$base_url = get_base_url();
+
+	$text = pun_trim($text, "\t\n ");
+
+	$shortcut_urls = array(
+		'topic' => '/viewtopic.php?id=$1',
+		'post' => '/viewtopic.php?pid=$1#p$1',
+		'forum' => '/viewforum.php?id=$1',
+		'user' => '/profile.php?id=$1',
+	);
+
+	// Split code blocks and text so BBcode in codeblocks won't be touched
+	list($code, $text) = extract_blocks($text, '[code]', '[/code]');
+
+	// Strip all bbcodes, except the quote, url, img, email, code and list items bbcodes
+	$text = preg_replace(array(
+		'%\[/?(?!(?:quote|url|topic|post|user|forum|img|email|code|list|\*))[a-z]+(?:=[^\]]+)?\]%i',
+		'%\n\[/?list(?:=[^\]]+)?\]%i' // A separate regex for the list tags to get rid of some whitespace
+	), '', $text);
+
+	// Match the deepest nested bbcode
+	// An adapted example from Mastering Regular Expressions
+	$match_quote_regex = '%
+		\[(quote|\*|url|img|email|topic|post|user|forum)(?:=([^\]]+))?\]
+		(
+			(?>[^\[]*)
+			(?>
+				(?!\[/?\1(?:=[^\]]+)?\])
+				\[
+				[^\[]*
+			)*
+		)
+		\[/\1\]
+	%ix';
+
+	$url_index = 1;
+	$url_stack = array();
+	while (preg_match($match_quote_regex, $text, $matches))
+	{
+		// Quotes
+		if ($matches[1] == 'quote')
+		{
+			// Put '>' or '> ' at the start of a line
+			$replacement = preg_replace(
+				array('%^(?=\>)%m', '%^(?!\>)%m'),
+				array('>', '> '),
+				$matches[2]." said:\n".$matches[3]);
+		}
+
+		// List items
+		elseif ($matches[1] == '*')
+		{
+			$replacement = ' * '.$matches[3];
+		}
+
+		// URLs and emails
+		elseif (in_array($matches[1], array('url', 'email')))
+		{
+			if (!empty($matches[2]))
+			{
+				$replacement = '['.$matches[3].']['.$url_index.']';
+				$url_stack[$url_index] = $matches[2];
+				$url_index++;
+			}
+			else
+				$replacement = '['.$matches[3].']';
+		}
+
+		// Images
+		elseif ($matches[1] == 'img')
+		{
+			if (!empty($matches[2]))
+				$replacement = '['.$matches[2].']['.$url_index.']';
+			else
+				$replacement = '['.basename($matches[3]).']['.$url_index.']';
+
+			$url_stack[$url_index] = $matches[3];
+			$url_index++;
+		}
+
+		// Topic, post, forum and user URLs
+		elseif (in_array($matches[1], array('topic', 'post', 'forum', 'user')))
+		{
+			$url = isset($shortcut_urls[$matches[1]]) ? $base_url.$shortcut_urls[$matches[1]] : '';
+
+			if (!empty($matches[2]))
+			{
+				$replacement = '['.$matches[3].']['.$url_index.']';
+				$url_stack[$url_index] = str_replace('$1', $matches[2], $url);
+				$url_index++;
+			}
+			else
+				$replacement = '['.str_replace('$1', $matches[3], $url).']';
+		}
+
+		// Update the main text if there is a replacment
+		if (!is_null($replacement))
+		{
+			$text = str_replace($matches[0], $replacement, $text);
+			$replacement = null;
+		}
+	}
+
+	// Put code blocks and text together
+	if (isset($code))
+	{
+		$parts = explode("\1", $text);
+		$text = '';
+		foreach ($parts as $i => $part)
+		{
+			$text .= $part;
+			if (isset($code[$i]))
+				$text .= trim($code[$i], "\n\r");
+		}
+	}
+
+	// Put URLs at the bottom
+	if ($url_stack)
+	{
+		$text .= "\n\n";
+		foreach ($url_stack as $i => $url)
+			$text .= "\n".' ['.$i.']: '.$url;
+	}
+
+	// Wrap lines if $wrap_length is higher than -1
+	if ($wrap_length > -1)
+	{
+		// Split all lines and wrap them individually
+		$parts = explode("\n", $text);
+		foreach ($parts as $k => $part)
+		{
+			preg_match('%^(>+ )?(.*)%', $part, $matches);
+			$parts[$k] = wordwrap($matches[1].$matches[2], $wrap_length -
+				strlen($matches[1]), "\n".$matches[1]);
+		}
+
+		return implode("\n", $parts);
+	}
+	else
+		return $text;
+}
+
+
+//
 // Wrapper for PHP's mail()
 //
 function pun_mail($to, $subject, $message, $reply_to_email = '', $reply_to_name = '')
@@ -63,19 +214,19 @@ function pun_mail($to, $subject, $message, $reply_to_email = '', $reply_to_name
 	global $pun_config, $lang_common;
 
 	// Default sender/return address
-	$from_name = str_replace('"', '', $pun_config['o_board_title'].' '.$lang_common['Mailer']);
+	$from_name = sprintf($lang_common['Mailer'], $pun_config['o_board_title']);
 	$from_email = $pun_config['o_webmaster_email'];
 
 	// Do a little spring cleaning
-	$to = pun_trim(preg_replace('#[\n\r]+#s', '', $to));
-	$subject = pun_trim(preg_replace('#[\n\r]+#s', '', $subject));
-	$from_email = pun_trim(preg_replace('#[\n\r:]+#s', '', $from_email));
-	$from_name = pun_trim(preg_replace('#[\n\r:]+#s', '', str_replace('"', '', $from_name)));
-	$reply_to_email = pun_trim(preg_replace('#[\n\r:]+#s', '', $reply_to_email));
-	$reply_to_name = pun_trim(preg_replace('#[\n\r:]+#s', '', str_replace('"', '', $reply_to_name)));
+	$to = pun_trim(preg_replace('%[\n\r]+%s', '', $to));
+	$subject = pun_trim(preg_replace('%[\n\r]+%s', '', $subject));
+	$from_email = pun_trim(preg_replace('%[\n\r:]+%s', '', $from_email));
+	$from_name = pun_trim(preg_replace('%[\n\r:]+%s', '', str_replace('"', '', $from_name)));
+	$reply_to_email = pun_trim(preg_replace('%[\n\r:]+%s', '', $reply_to_email));
+	$reply_to_name = pun_trim(preg_replace('%[\n\r:]+%s', '', str_replace('"', '', $reply_to_name)));
 
 	// Set up some headers to take advantage of UTF-8
-	$from = encode_mail_text($from_name).' <'.$from_email.'>';
+	$from = '"'.encode_mail_text($from_name).'" <'.$from_email.'>';
 	$subject = encode_mail_text($subject);
 
 	$headers = 'From: '.$from."\r\n".'Date: '.gmdate('r')."\r\n".'MIME-Version: 1.0'."\r\n".'Content-transfer-encoding: 8bit'."\r\n".'Content-type: text/plain; charset=utf-8'."\r\n".'X-Mailer: FluxBB Mailer';
@@ -83,24 +234,25 @@ function pun_mail($to, $subject, $message, $reply_to_email = '', $reply_to_name
 	// If we specified a reply-to email, we deal with it here
 	if (!empty($reply_to_email))
 	{
-		$reply_to = encode_mail_text($reply_to_name).' <'.$reply_to_email.'>';
+		$reply_to = '"'.encode_mail_text($reply_to_name).'" <'.$reply_to_email.'>';
 
 		$headers .= "\r\n".'Reply-To: '.$reply_to;
 	}
 
-	// Make sure all linebreaks are CRLF in message (and strip out any NULL bytes)
-	$message = str_replace(array("\n", "\0"), array("\r\n", ''), pun_linebreaks($message));
+	// Make sure all linebreaks are LF in message (and strip out any NULL bytes)
+	$message = str_replace("\0", '', pun_linebreaks($message));
 
 	if ($pun_config['o_smtp_host'] != '')
+	{
+		// Headers should be \r\n
+		// Message should be ??
+		$message = str_replace("\n", "\r\n", $message);
 		smtp_mail($to, $subject, $message, $headers);
+	}
 	else
 	{
-		// Change the linebreaks used in the headers according to OS
-		if (strtoupper(substr(PHP_OS, 0, 3)) == 'MAC')
-			$headers = str_replace("\r\n", "\r", $headers);
-		else if (strtoupper(substr(PHP_OS, 0, 3)) != 'WIN')
-			$headers = str_replace("\r\n", "\n", $headers);
-
+		// Headers should be \r\n
+		// Message should be \n
 		mail($to, $subject, $message, $headers);
 	}
 }
diff --git a/include/functions.php b/include/functions.php
index 8ba8ca5..3a52a85 100644
--- a/include/functions.php
+++ b/include/functions.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -26,7 +26,7 @@ function check_cookie(&$pun_user)
 	$now = time();
 
 	// If the cookie is set and it matches the correct pattern, then read the values from it
-	if (isset($_COOKIE[$cookie_name]) && preg_match('/^(\d+)\|([0-9a-fA-F]+)\|(\d+)\|([0-9a-fA-F]+)$/', $_COOKIE[$cookie_name], $matches))
+	if (isset($_COOKIE[$cookie_name]) && preg_match('%^(\d+)\|([0-9a-fA-F]+)\|(\d+)\|([0-9a-fA-F]+)$%', $_COOKIE[$cookie_name], $matches))
 	{
 		$cookie = array(
 			'user_id'			=> intval($matches[1]),
@@ -246,7 +246,7 @@ function set_default_user()
 	// Fetch guest user
 	$result = $db->query('SELECT u.*, g.*, o.logged, o.last_post, o.last_search FROM '.$db->prefix.'users AS u INNER JOIN '.$db->prefix.'groups AS g ON u.group_id=g.g_id LEFT JOIN '.$db->prefix.'online AS o ON o.ident=\''.$remote_addr.'\' WHERE u.id=1') or error('Unable to fetch guest information', __FILE__, __LINE__, $db->error());
 	if (!$db->num_rows($result))
-		exit('Unable to fetch guest information. The table \''.$db->prefix.'users\' must contain an entry with id = 1 that represents anonymous users.');
+		exit('Unable to fetch guest information. Your database must contain both a guest user and a guest user group.');
 
 	$pun_user = $db->fetch_assoc($result);
 
@@ -428,7 +428,7 @@ function check_username($username, $exclude_id = null)
 	global $db, $pun_config, $errors, $lang_prof_reg, $lang_register, $lang_common, $pun_bans;
 
 	// Convert multiple whitespace characters into one (to prevent people from registering with indistinguishable usernames)
-	$username = preg_replace('#\s+#s', ' ', $username);
+	$username = preg_replace('%\s+%s', ' ', $username);
 
 	// Validate username
 	if (pun_strlen($username) < 2)
@@ -437,11 +437,11 @@ function check_username($username, $exclude_id = null)
 		$errors[] = $lang_prof_reg['Username too long'];
 	else if (!strcasecmp($username, 'Guest') || !strcasecmp($username, $lang_common['Guest']))
 		$errors[] = $lang_prof_reg['Username guest'];
-	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))
+	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[] = $lang_prof_reg['Username IP'];
 	else if ((strpos($username, '[') !== false || strpos($username, ']') !== false) && strpos($username, '\'') !== false && strpos($username, '"') !== false)
 		$errors[] = $lang_prof_reg['Username reserved chars'];
-	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))
+	else if (preg_match('%(?:\[/?(?:b|u|s|ins|del|em|i|h|colou?r|quote|code|img|url|email|list|\*|topic|post|forum|user)\]|\[(?:img|url|quote|list)=)%i', $username))
 		$errors[] = $lang_prof_reg['Username BBCode'];
 
 	// Check username for any censored words
@@ -451,7 +451,7 @@ function check_username($username, $exclude_id = null)
 	// Check that the username (or a too similar username) is not already registered
 	$query = ($exclude_id) ? ' AND id!='.$exclude_id : '';
 
-	$result = $db->query('SELECT username FROM '.$db->prefix.'users WHERE (UPPER(username)=UPPER(\''.$db->escape($username).'\') OR UPPER(username)=UPPER(\''.$db->escape(ucp_preg_replace('/[^\p{L}\p{N}]/u', '', $username)).'\')) AND id>1'.$query) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+	$result = $db->query('SELECT username FROM '.$db->prefix.'users WHERE (UPPER(username)=UPPER(\''.$db->escape($username).'\') OR UPPER(username)=UPPER(\''.$db->escape(ucp_preg_replace('%[^\p{L}\p{N}]%u', '', $username)).'\')) AND id>1'.$query) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
 
 	if ($db->num_rows($result))
 	{
@@ -566,7 +566,7 @@ function generate_page_title($page_title, $p = null)
 
 	$page_title = array_reverse($page_title);
 
-	if ($p != null)
+	if (!is_null($p))
 		$page_title[0] .= ' ('.sprintf($lang_common['Page'], forum_number_format($p)).')';
 
 	$crumbs = implode($lang_common['Title separator'], $page_title);
@@ -619,7 +619,7 @@ function get_tracked_topics()
 	if (!$cookie_data)
 		return array('topics' => array(), 'forums' => array());
 
-	if (strlen($cookie_data) > 4048)
+	if (strlen($cookie_data) > FORUM_MAX_COOKIE_SIZE)
 		return array('topics' => array(), 'forums' => array());
 
 	// Unserialize data from cookie
@@ -882,7 +882,7 @@ function paginate($num_pages, $cur_page, $link)
 	{
 		// Add a previous page link
 		if ($num_pages > 1 && $cur_page > 1)
-			$pages[] = '<a'.(empty($pages) ? ' class="item1"' : '').' href="'.$link.'&amp;p='.($cur_page - 1).'">'.$lang_common['Previous'].'</a>';
+			$pages[] = '<a rel="prev" '.(empty($pages) ? ' class="item1"' : '').' href="'.$link.'&amp;p='.($cur_page - 1).'">'.$lang_common['Previous'].'</a>';
 
 		if ($cur_page > 3)
 		{
@@ -913,7 +913,7 @@ function paginate($num_pages, $cur_page, $link)
 
 		// Add a next page link
 		if ($num_pages > 1 && !$link_to_all && $cur_page < $num_pages)
-			$pages[] = '<a'.(empty($pages) ? ' class="item1"' : '').' href="'.$link.'&amp;p='.($cur_page +1).'">'.$lang_common['Next'].'</a>';
+			$pages[] = '<a rel="next" '.(empty($pages) ? ' class="item1"' : '').' href="'.$link.'&amp;p='.($cur_page +1).'">'.$lang_common['Next'].'</a>';
 	}
 
 	return implode(' ', $pages);
@@ -923,10 +923,15 @@ function paginate($num_pages, $cur_page, $link)
 //
 // Display a message
 //
-function message($message, $no_back_link = false)
+function message($message, $no_back_link = false, $http_status = null)
 {
 	global $db, $lang_common, $pun_config, $pun_start, $tpl_main, $pun_user;
 
+	// Did we receive a custom header?
+	if(!is_null($http_status)) {
+		header('HTTP/1.1 ' . $http_status);
+	}
+
 	if (!defined('PUN_HEADER'))
 	{
 		$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Info']);
@@ -965,10 +970,10 @@ function format_time($timestamp, $date_only = false, $date_format = null, $time_
 	$timestamp += $diff;
 	$now = time();
 
-	if($date_format == null)
+	if(is_null($date_format))
 		$date_format = $forum_date_formats[$pun_user['date_format']];
 
-	if($time_format == null)
+	if(is_null($time_format))
 		$time_format = $forum_time_formats[$pun_user['time_format']];
 
 	$date = gmdate($date_format, $timestamp);
@@ -1091,8 +1096,11 @@ function get_remote_address()
 			// X-Forwarded-For: client1, proxy1, proxy2
 			// where the value is a comma+space separated list of IP addresses, the left-most being the farthest downstream client,
 			// and each successive proxy that passed the request adding the IP address where it received the request from.
-			$remote_addr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
-			$remote_addr = trim($remote_addr[0]);
+			$forwarded_for = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
+			$forwarded_for = trim($forwarded_for[0]);
+
+			if (@preg_match('%^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$%', $forwarded_for) || @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}:))$%', $forwarded_for))
+				$remote_addr = $forwarded_for;
 		}
 	}
 
@@ -1152,7 +1160,7 @@ function pun_linebreaks($str)
 //
 function pun_trim($str, $charlist = false)
 {
-	return utf8_trim($str, $charlist);
+	return is_string($str) ? utf8_trim($str, $charlist) : '';
 }
 
 //
@@ -1172,7 +1180,7 @@ function is_all_uppercase($string)
 //
 function array_insert(&$input, $offset, $element, $key = null)
 {
-	if ($key == null)
+	if (is_null($key))
 		$key = $offset;
 
 	// Determine the proper offset if we're using a string
@@ -1224,7 +1232,7 @@ function maintenance_message()
 	$tpl_maint = file_get_contents($tpl_file);
 
 	// START SUBST - <pun_include "*">
-	preg_match_all('#<pun_include "([^/\\\\]*?)\.(php[45]?|inc|html?|txt)">#', $tpl_maint, $pun_includes, PREG_SET_ORDER);
+	preg_match_all('%<pun_include "([^/\\\\]*?)\.(php[45]?|inc|html?|txt)">%i', $tpl_maint, $pun_includes, PREG_SET_ORDER);
 
 	foreach ($pun_includes as $cur_include)
 	{
@@ -1314,7 +1322,7 @@ function redirect($destination_url, $message)
 		$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);
+	$destination_url = preg_replace('%([\r\n])|(\%0[ad])|(;\s*data\s*:)%i', '', $destination_url);
 
 	// If the delay is 0 seconds, we might as well skip the redirect all together
 	if ($pun_config['o_redirect_delay'] == '0')
@@ -1343,7 +1351,7 @@ function redirect($destination_url, $message)
 	$tpl_redir = file_get_contents($tpl_file);
 
 	// START SUBST - <pun_include "*">
-	preg_match_all('#<pun_include "([^/\\\\]*?)\.(php[45]?|inc|html?|txt)">#', $tpl_redir, $pun_includes, PREG_SET_ORDER);
+	preg_match_all('%<pun_include "([^/\\\\]*?)\.(php[45]?|inc|html?|txt)">%i', $tpl_redir, $pun_includes, PREG_SET_ORDER);
 
 	foreach ($pun_includes as $cur_include)
 	{
@@ -1498,7 +1506,7 @@ H2 {MARGIN: 0; COLOR: #FFFFFF; BACKGROUND-COLOR: #B84623; FONT-SIZE: 1.1em; PADD
 	<div>
 <?php
 
-	if (defined('PUN_DEBUG') && $file !== null && $line !== null)
+	if (defined('PUN_DEBUG') && !is_null($file) && !is_null($line))
 	{
 		echo "\t\t".'<strong>File:</strong> '.$file.'<br />'."\n\t\t".'<strong>Line:</strong> '.$line.'<br /><br />'."\n\t\t".'<strong>FluxBB reported</strong>: '.$message."\n";
 
@@ -1627,7 +1635,7 @@ function remove_bad_characters($array)
 	$array = utf8_bad_strip($array);
 
 	// Remove control characters
-	$array = preg_replace('/[\x{00}-\x{08}\x{0b}-\x{0c}\x{0e}-\x{1f}]/', '', $array);
+	$array = preg_replace('%[\x00-\x08\x0b-\x0c\x0e-\x1f]%', '', $array);
 
 	// Replace some "bad" characters
 	$array = str_replace(array_keys($bad_utf8_chars), array_values($bad_utf8_chars), $array);
@@ -1641,12 +1649,14 @@ function remove_bad_characters($array)
 //
 function file_size($size)
 {
+	global $lang_common;
+
 	$units = array('B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB');
 
 	for ($i = 0; $size > 1024; $i++)
 		$size /= 1024;
 
-	return round($size, 2).' '.$units[$i];
+	return sprintf($lang_common['Size unit '.$units[$i]], round($size, 2));
 }
 
 
@@ -1736,10 +1746,12 @@ function forum_list_plugins($is_admin)
 		$suffix = substr($entry, strlen($entry) - 4);
 
 		if ($suffix == '.php' && ((!$is_admin && $prefix == 'AMP') || ($is_admin && ($prefix == 'AP' || $prefix == 'AMP'))))
-			$plugins[] = array(substr($entry, strpos($entry, '_') + 1, -4), $entry);
+			$plugins[$entry] = substr($entry, strpos($entry, '_') + 1, -4);
 	}
 	$d->close();
 
+	natcasesort($plugins);
+
 	return $plugins;
 }
 
@@ -1747,37 +1759,87 @@ function forum_list_plugins($is_admin)
 //
 // 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)
+function split_text($text, $start, $end, $retab = true)
 {
 	global $pun_config, $lang_common;
 
-	$tokens = explode($start, $text);
+	$result = array(0 => array(), 1 => array()); // 0 = inside, 1 = outside
 
-	$outside[] = $tokens[0];
+	// split the text into parts
+	$parts = preg_split('%'.preg_quote($start, '%').'(.*)'.preg_quote($end, '%').'%Us', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
+	$num_parts = count($parts);
 
-	$num_tokens = count($tokens);
-	for ($i = 1; $i < $num_tokens; ++$i)
+	// preg_split results in outside parts having even indices, inside parts having odd
+	for ($i = 0;$i < $num_parts;$i++)
+		$result[1 - ($i % 2)][] = $parts[$i];
+
+	if ($pun_config['o_indent_num_spaces'] != 8 && $retab)
 	{
-		$temp = explode($end, $tokens[$i]);
+		$spaces = str_repeat(' ', $pun_config['o_indent_num_spaces']);
+		$result[1] = str_replace("\t", $spaces, $result[1]);
+	}
+
+	return $result;
+}
 
-		if (count($temp) != 2)
+
+//
+// Extract blocks from a text with a starting and ending string
+// This function always matches the most outer block so nesting is possible
+//
+function extract_blocks($text, $start, $end, $retab = true)
+{
+	global $pun_config;
+
+	$code = array();
+	$start_len = strlen($start);
+	$end_len = strlen($end);
+	$regex = '%(?:'.preg_quote($start, '%').'|'.preg_quote($end, '%').')%';
+	$matches = array();
+
+	if (preg_match_all($regex, $text, $matches))
+	{
+		$counter = $offset = 0;
+		$start_pos = $end_pos = false;
+
+		foreach ($matches[0] as $match)
 		{
-			$errors[] = $lang_common['BBCode code problem'];
-			return array(null, array($text));
+			if ($match == $start)
+			{
+				if ($counter == 0)
+					$start_pos = strpos($text, $start);
+				$counter++;
+			}
+			elseif ($match == $end)
+			{
+				$counter--;
+				if ($counter == 0)
+					$end_pos = strpos($text, $end, $offset + 1);
+				$offset = strpos($text, $end, $offset + 1);
+			}
+
+			if ($start_pos !== false && $end_pos !== false)
+			{
+				$code[] = substr($text, $start_pos + $start_len,
+					$end_pos - $start_pos - $start_len);
+				$text = substr_replace($text, "\1", $start_pos,
+					$end_pos - $start_pos + $end_len);
+				$start_pos = $end_pos = false;
+				$offset = 0;
+			}
 		}
-		$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);
+		$text = str_replace("\t", $spaces, $text);
 	}
 
-	return array($inside, $outside);
+	return array($code, $text);
 }
 
+
 //
 // function url_valid($url) {
 //
@@ -1897,7 +1959,7 @@ function url_valid($url)
 function ucp_preg_replace($pattern, $replace, $subject)
 {
 	$replaced = preg_replace($pattern, $replace, $subject);
-	
+
 	// If preg_replace() returns false, this probably means unicode support is not built-in, so we need to modify the pattern a little
 	if ($replaced === false)
 	{
@@ -1905,16 +1967,75 @@ function ucp_preg_replace($pattern, $replace, $subject)
 		{
 			foreach ($pattern as $cur_key => $cur_pattern)
 				$pattern[$cur_key] = str_replace('\p{L}\p{N}', '\w', $cur_pattern);
-			
+
 			$replaced = preg_replace($pattern, $replace, $subject);
 		}
 		else
 			$replaced = preg_replace(str_replace('\p{L}\p{N}', '\w', $pattern), $replace, $subject);
 	}
-	
+
 	return $replaced;
 }
 
+//
+// Replace four-byte characters with a question mark
+//
+// As MySQL cannot properly handle four-byte characters with the default utf-8
+// charset up until version 5.5.3 (where a special charset has to be used), they
+// need to be replaced, by question marks in this case.
+//
+function strip_bad_multibyte_chars($str)
+{
+	$result = '';
+	$length = strlen($str);
+
+	for ($i = 0; $i < $length; $i++)
+	{
+		// Replace four-byte characters (11110www 10zzzzzz 10yyyyyy 10xxxxxx)
+		$ord = ord($str[$i]);
+		if ($ord >= 240 && $ord <= 244)
+		{
+			$result .= '?';
+			$i += 3;
+		}
+		else
+		{
+			$result .= $str[$i];
+		}
+	}
+
+	return $result;
+}
+
+//
+// Check whether a file/folder is writable.
+//
+// This function also works on Windows Server where ACLs seem to be ignored.
+//
+function forum_is_writable($path)
+{
+	if (is_dir($path))
+	{
+		$path = rtrim($path, '/').'/';
+		return forum_is_writable($path.uniqid(mt_rand()).'.tmp');
+	}
+
+	// Check temporary file for read/write capabilities
+	$rm = file_exists($path);
+	$f = @fopen($path, 'a');
+
+	if ($f === false)
+		return false;
+
+	fclose($f);
+
+	if (!$rm)
+		@unlink($path);
+
+	return true;
+}
+
+
 // DEBUG FUNCTIONS BELOW
 
 //
diff --git a/include/parser.php b/include/parser.php
index 02449b7..238932d 100644
--- a/include/parser.php
+++ b/include/parser.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -76,16 +76,13 @@ function preparse_bbcode($text, &$errors, $is_signature = false)
 
 	// If the message contains a code tag we have to split it up (text within [code][/code] shouldn't be touched)
 	if (strpos($text, '[code]') !== false && strpos($text, '[/code]') !== false)
-	{
-		list($inside, $outside) = split_text($text, '[code]', '[/code]', $errors);
-		$text = implode("\1", $outside);
-	}
+		list($inside, $text) = extract_blocks($text, '[code]', '[/code]');
 
 	// Tidy up lists
-	$temp = preg_replace($re_list, 'preparse_list_tag(\'$2\', \'$1\', $errors)', $text);
+	$temp = preg_replace($re_list, 'preparse_list_tag(\'$2\', \'$1\')', $text);
 
 	// If the regex failed
-	if ($temp === null)
+	if (is_null($temp))
 		$errors[] = $lang_common['BBCode list size error'];
 	else
 		$text = str_replace('*'."\0".']', '*]', $temp);
@@ -118,7 +115,7 @@ function preparse_bbcode($text, &$errors, $is_signature = false)
 		$text = $temp_text;
 
 	// Remove empty tags
-	while (($new_text = strip_empty_bbcode($text, $errors)) !== false)
+	while (($new_text = strip_empty_bbcode($text)) !== false)
 	{
 		if ($new_text != $text)
 		{
@@ -140,17 +137,14 @@ function preparse_bbcode($text, &$errors, $is_signature = false)
 //
 // Strip empty bbcode tags from some text
 //
-function strip_empty_bbcode($text, &$errors)
+function strip_empty_bbcode($text)
 {
 	// If the message contains a code tag we have to split it up (empty tags within [code][/code] are fine)
 	if (strpos($text, '[code]') !== false && strpos($text, '[/code]') !== false)
-	{
-		list($inside, $outside) = split_text($text, '[code]', '[/code]', $errors);
-		$text = implode("\1", $outside);
-	}
+		list($inside, $text) = extract_blocks($text, '[code]', '[/code]');
 
 	// 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)) !== NULL)
+	while (!is_null($new_text = preg_replace('%\[(b|u|s|ins|del|em|i|h|colou?r|quote|img|url|email|list|topic|post|forum|user)(?:\=[^\]]*)?\]\s*\[/\1\]%', '', $text)))
 	{
 		if ($new_text != $text)
 			$text = $new_text;
@@ -161,20 +155,18 @@ function strip_empty_bbcode($text, &$errors)
 	// If we split up the message before we have to concatenate it together again (code tags)
 	if (isset($inside))
 	{
-		$outside = explode("\1", $text);
+		$parts = explode("\1", $text);
 		$text = '';
-
-		$num_tokens = count($outside);
-		for ($i = 0; $i < $num_tokens; ++$i)
+		foreach ($parts as $i => $part)
 		{
-			$text .= $outside[$i];
+			$text .= $part;
 			if (isset($inside[$i]))
 				$text .= '[code]'.$inside[$i].'[/code]';
 		}
 	}
 
 	// Remove empty code tags
-	while (($new_text = preg_replace('/\[(code)\]\s*\[\/\1\]/', '', $text)) !== NULL)
+	while (!is_null($new_text = preg_replace('%\[(code)\]\s*\[/\1\]%', '', $text)))
 	{
 		if ($new_text != $text)
 			$text = $new_text;
@@ -196,7 +188,7 @@ function preparse_tags($text, &$errors, $is_signature = false)
 	// Start off by making some arrays of bbcode tags and what we need to do with each one
 
 	// List of all the tags
-	$tags = array('quote', 'code', 'b', 'i', 'u', 's', 'ins', 'del', 'em', 'color', 'colour', 'url', 'email', 'img', 'list', '*', 'h');
+	$tags = array('quote', 'code', 'b', 'i', 'u', 's', 'ins', 'del', 'em', 'color', 'colour', 'url', 'email', 'img', 'list', '*', 'h', 'topic', 'post', 'forum', 'user');
 	// List of tags that we need to check are open (You could not put b,i,u in here then illegal nesting like [b][i][/b][/i] would be allowed)
 	$tags_opened = $tags;
 	// and tags we need to check are closed (the same as above, added it just in case)
@@ -208,26 +200,30 @@ function preparse_tags($text, &$errors, $is_signature = false)
 	// Block tags, block tags can only go within another block tag, they cannot be in a normal tag
 	$tags_block = array('quote', 'code', 'list', 'h', '*');
 	// Inline tags, we do not allow new lines in these
-	$tags_inline = array('b', 'i', 'u', 's', 'ins', 'del', 'em', 'color', 'colour', 'h');
+	$tags_inline = array('b', 'i', 'u', 's', 'ins', 'del', 'em', 'color', 'colour', 'h', 'topic', 'post', 'forum', 'user');
 	// Tags we trim interior space
 	$tags_trim = array('img');
 	// Tags we remove quotes from the argument
-	$tags_quotes = array('url', 'email', 'img');
+	$tags_quotes = array('url', 'email', 'img', 'topic', 'post', 'forum', 'user');
 	// Tags we limit bbcode in
 	$tags_limit_bbcode = array(
-		'*' 	=> array('b', 'i', 'u', 's', 'ins', 'del', 'em', 'color', 'colour', 'url', 'email', 'list', 'img', 'code'),
+		'*' 	=> array('b', 'i', 'u', 's', 'ins', 'del', 'em', 'color', 'colour', 'url', 'email', 'list', 'img', 'code', 'topic', 'post', 'forum', 'user'),
 		'list' 	=> array('*'),
-		'url' 	=> array('b', 'i', 'u', 's', 'ins', 'del', 'em', 'color', 'colour', 'img'),
-		'email' => array('b', 'i', 'u', 's', 'ins', 'del', 'em', 'color', 'colour', 'img'),
+		'url' 	=> array('img'),
+		'email' => array('img'),
+		'topic' => array('img'),
+		'post'  => array('img'),
+		'forum' => array('img'),
+		'user'  => array('img'),
 		'img' 	=> array(),
-		'h'		=> array('b', 'i', 'u', 's', 'ins', 'del', 'em', 'color', 'colour', 'url', 'email'),
+		'h'		=> array('b', 'i', 'u', 's', 'ins', 'del', 'em', 'color', 'colour', 'url', 'email', 'topic', 'post', 'forum', 'user'),
 	);
 	// Tags we can automatically fix bad nesting
-	$tags_fix = array('quote', 'b', 'i', 'u', 's', 'ins', 'del', 'em', 'color', 'colour', 'url', 'email', 'h');
+	$tags_fix = array('quote', 'b', 'i', 'u', 's', 'ins', 'del', 'em', 'color', 'colour', 'url', 'email', 'h', 'topic', 'post', 'forum', 'user');
 
-	$split_text = preg_split("/(\[[\*a-zA-Z0-9-\/]*?(?:=.*?)?\])/", $text, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
+	$split_text = preg_split('%(\[[\*a-zA-Z0-9-/]*?(?:=.*?)?\])%', $text, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
 
-	$open_tags = array('post');
+	$open_tags = array('fluxbb-bbcode');
 	$open_args = array('');
 	$opened_tag = 0;
 	$new_text = '';
@@ -235,6 +231,7 @@ function preparse_tags($text, &$errors, $is_signature = false)
 	$current_nest = '';
 	$current_depth = array();
 	$limit_bbcode = $tags;
+	$count_ignored = array();
 
 	foreach ($split_text as $current)
 	{
@@ -245,7 +242,6 @@ function preparse_tags($text, &$errors, $is_signature = false)
 		if (substr($current, 0, 1) != '[' || substr($current, -1, 1) != ']')
 		{
 			// It's not a bbcode tag so we put it on the end and continue
-
 			// If we are nested too deeply don't add to the end
 			if ($current_nest)
 				continue;
@@ -255,7 +251,7 @@ function preparse_tags($text, &$errors, $is_signature = false)
 			if (in_array($open_tags[$opened_tag], $tags_inline) && strpos($current, "\n") !== false)
 			{
 				// Deal with new lines
-				$split_current = preg_split("/(\n\n+)/", $current, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
+				$split_current = preg_split('%(\n\n+)%', $current, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
 				$current = '';
 
 				if (!pun_trim($split_current[0], "\n")) // The first part is a linebreak so we need to handle any open tags first
@@ -357,13 +353,24 @@ function preparse_tags($text, &$errors, $is_signature = false)
 			$current = strtolower($current);
 
 		// This is if we are currently in a tag which escapes other bbcode such as code
+		// We keep a count of ignored bbcodes (code tags) so we can nest them, but
+		// only balanced sets of tags can be nested
 		if ($current_ignore)
 		{
+			// Increase the current ignored tags counter
+			if ('['.$current_ignore.']' == $current)
+				$count_ignored[$current_tag]++;
+
+			// Decrease the current ignored tags counter
 			if ('[/'.$current_ignore.']' == $current)
+				$count_ignored[$current_tag]--;
+
+			if ('[/'.$current_ignore.']' == $current && $count_ignored[$current_tag] == 0)
 			{
 				// We've finished the ignored section
 				$current = '[/'.$current_tag.']';
 				$current_ignore = '';
+				$count_ignored = array();
 			}
 
 			$new_text .= $current;
@@ -398,7 +405,6 @@ function preparse_tags($text, &$errors, $is_signature = false)
 		if (substr($current, 1, 1) == '/')
 		{
 			// This is if we are closing a tag
-
 			if ($opened_tag == 0 || !in_array($current_tag, $open_tags))
 			{
 				// We tried to close a tag which is not open
@@ -521,6 +527,7 @@ function preparse_tags($text, &$errors, $is_signature = false)
 			{
 				// It's an ignore tag so we don't need to worry about what's inside it
 				$current_ignore = $current_tag;
+				$count_ignored[$current_tag] = 1;
 				$new_text .= $current;
 				continue;
 			}
@@ -552,7 +559,7 @@ function preparse_tags($text, &$errors, $is_signature = false)
 			// Remove quotes from arguments for certain tags
 			if (strpos($current, '=') !== false && in_array($current_tag, $tags_quotes))
 			{
-				$current = preg_replace('#\['.$current_tag.'=("|\'|)(.*?)\\1\]\s*#i', '['.$current_tag.'=$2]', $current);
+				$current = preg_replace('%\['.$current_tag.'=("|\'|)(.*?)\\1\]\s*%i', '['.$current_tag.'=$2]', $current);
 			}
 
 			if (in_array($current_tag, array_keys($tags_limit_bbcode)))
@@ -591,7 +598,7 @@ function preparse_tags($text, &$errors, $is_signature = false)
 //
 // Preparse the contents of [list] bbcode
 //
-function preparse_list_tag($content, $type = '*', &$errors)
+function preparse_list_tag($content, $type = '*')
 {
 	global $lang_common, $re_list;
 
@@ -600,7 +607,7 @@ function preparse_list_tag($content, $type = '*', &$errors)
 
 	if (strpos($content,'[list') !== false)
 	{
-		$content = preg_replace($re_list, 'preparse_list_tag(\'$2\', \'$1\', $errors)', $content);
+		$content = preg_replace($re_list, 'preparse_list_tag(\'$2\', \'$1\')', $content);
 	}
 
 	$items = explode('[*]', str_replace('\"', '"', $content));
@@ -622,11 +629,18 @@ function preparse_list_tag($content, $type = '*', &$errors)
 function handle_url_tag($url, $link = '', $bbcode = false)
 {
 	$url = pun_trim($url);
+
+	// Deal with [url][img]http://example.com/test.png[/img][/url]
+	if (preg_match('%<img src=\\\\"(.*?)\\\\"%', $url, $matches))
+		return handle_url_tag($matches[1], $url, $bbcode);
+
 	$full_url = str_replace(array(' ', '\'', '`', '"'), array('%20', '', '', ''), $url);
 	if (strpos($url, 'www.') === 0) // If it starts with www, we add http://
 		$full_url = 'http://'.$full_url;
 	else if (strpos($url, 'ftp.') === 0) // Else if it starts with ftp, we add ftp://
 		$full_url = 'ftp://'.$full_url;
+	else if (strpos($url, '/') === 0) // Allow for relative URLs that start with a slash
+		$full_url = get_base_url(true).$full_url;
 	else if (!preg_match('#^([a-z0-9]{3,6})://#', $url)) // Else if it doesn't start with abcdef://, we add http://
 		$full_url = 'http://'.$full_url;
 
@@ -661,7 +675,7 @@ function handle_img_tag($url, $is_signature = false, $alt = null)
 {
 	global $lang_common, $pun_user;
 
-	if ($alt == null)
+	if (is_null($alt))
 		$alt = basename($url);
 
 	$img_tag = '<a href="'.$url.'">&lt;'.$lang_common['Image link'].' - '.$alt.'&gt;</a>';
@@ -713,9 +727,9 @@ function do_bbcode($text, $is_signature = false)
 
 	if (strpos($text, '[quote') !== false)
 	{
-		$text = preg_replace('#\[quote\]\s*#', '</p><div class="quotebox"><blockquote><div><p>', $text);
-		$text = preg_replace('#\[quote=(&quot;|&\#039;|"|\'|)(.*?)\\1\]#se', '"</p><div class=\"quotebox\"><cite>".str_replace(array(\'[\', \'\\"\'), array(\'&#91;\', \'"\'), \'$2\')." ".$lang_common[\'wrote\']."</cite><blockquote><div><p>"', $text);
-		$text = preg_replace('#\s*\[\/quote\]#S', '</p></div></blockquote></div><p>', $text);
+		$text = preg_replace('%\[quote\]\s*%', '</p><div class="quotebox"><blockquote><div><p>', $text);
+		$text = preg_replace('%\[quote=(&quot;|&\#039;|"|\'|)(.*?)\\1\]%se', '"</p><div class=\"quotebox\"><cite>".str_replace(array(\'[\', \'\\"\'), array(\'&#91;\', \'"\'), \'$2\')." ".$lang_common[\'wrote\']."</cite><blockquote><div><p>"', $text);
+		$text = preg_replace('%\s*\[\/quote\]%S', '</p></div></blockquote></div><p>', $text);
 	}
 
 	if (!$is_signature)
@@ -724,15 +738,15 @@ function do_bbcode($text, $is_signature = false)
 		$replace[] = 'handle_list_tag(\'$2\', \'$1\')';
 	}
 
-	$pattern[] = '#\[b\](.*?)\[/b\]#ms';
-	$pattern[] = '#\[i\](.*?)\[/i\]#ms';
-	$pattern[] = '#\[u\](.*?)\[/u\]#ms';
-	$pattern[] = '#\[s\](.*?)\[/s\]#ms';
-	$pattern[] = '#\[del\](.*?)\[/del\]#ms';
-	$pattern[] = '#\[ins\](.*?)\[/ins\]#ms';
-	$pattern[] = '#\[em\](.*?)\[/em\]#ms';
-	$pattern[] = '#\[colou?r=([a-zA-Z]{3,20}|\#[0-9a-fA-F]{6}|\#[0-9a-fA-F]{3})](.*?)\[/colou?r\]#ms';
-	$pattern[] = '#\[h\](.*?)\[/h\]#ms';
+	$pattern[] = '%\[b\](.*?)\[/b\]%ms';
+	$pattern[] = '%\[i\](.*?)\[/i\]%ms';
+	$pattern[] = '%\[u\](.*?)\[/u\]%ms';
+	$pattern[] = '%\[s\](.*?)\[/s\]%ms';
+	$pattern[] = '%\[del\](.*?)\[/del\]%ms';
+	$pattern[] = '%\[ins\](.*?)\[/ins\]%ms';
+	$pattern[] = '%\[em\](.*?)\[/em\]%ms';
+	$pattern[] = '%\[colou?r=([a-zA-Z]{3,20}|\#[0-9a-fA-F]{6}|\#[0-9a-fA-F]{3})](.*?)\[/colou?r\]%ms';
+	$pattern[] = '%\[h\](.*?)\[/h\]%ms';
 
 	$replace[] = '<strong>$1</strong>';
 	$replace[] = '<em>$1</em>';
@@ -746,8 +760,8 @@ function do_bbcode($text, $is_signature = false)
 
 	if (($is_signature && $pun_config['p_sig_img_tag'] == '1') || (!$is_signature && $pun_config['p_message_img_tag'] == '1'))
 	{
-		$pattern[] = '#\[img\]((ht|f)tps?://)([^\s<"]*?)\[/img\]#e';
-		$pattern[] = '#\[img=([^\[]*?)\]((ht|f)tps?://)([^\s<"]*?)\[/img\]#e';
+		$pattern[] = '%\[img\]((ht|f)tps?://)([^\s<"]*?)\[/img\]%e';
+		$pattern[] = '%\[img=([^\[]*?)\]((ht|f)tps?://)([^\s<"]*?)\[/img\]%e';
 		if ($is_signature)
 		{
 			$replace[] = 'handle_img_tag(\'$1$3\', true)';
@@ -760,15 +774,31 @@ function do_bbcode($text, $is_signature = false)
 		}
 	}
 
-	$pattern[] = '#\[url\]([^\[]*?)\[/url\]#e';
-	$pattern[] = '#\[url=([^\[]+?)\](.*?)\[/url\]#e';
-	$pattern[] = '#\[email\]([^\[]*?)\[/email\]#';
-	$pattern[] = '#\[email=([^\[]+?)\](.*?)\[/email\]#';
+	$pattern[] = '%\[url\]([^\[]*?)\[/url\]%e';
+	$pattern[] = '%\[url=([^\[]+?)\](.*?)\[/url\]%e';
+	$pattern[] = '%\[email\]([^\[]*?)\[/email\]%';
+	$pattern[] = '%\[email=([^\[]+?)\](.*?)\[/email\]%';
+	$pattern[] = '%\[topic\]([1-9]\d*)\[/topic\]%e';
+	$pattern[] = '%\[topic=([1-9]\d*)\](.*?)\[/topic\]%e';
+	$pattern[] = '%\[post\]([1-9]\d*)\[/post\]%e';
+	$pattern[] = '%\[post=([1-9]\d*)\](.*?)\[/post\]%e';
+	$pattern[] = '%\[forum\]([1-9]\d*)\[/forum\]%e';
+	$pattern[] = '%\[forum=([1-9]\d*)\](.*?)\[/forum\]%e';
+	$pattern[] = '%\[user\]([1-9]\d*)\[/user\]%e';
+	$pattern[] = '%\[user=([1-9]\d*)\](.*?)\[/user\]%e';
 
 	$replace[] = 'handle_url_tag(\'$1\')';
 	$replace[] = 'handle_url_tag(\'$1\', \'$2\')';
 	$replace[] = '<a href="mailto:$1">$1</a>';
 	$replace[] = '<a href="mailto:$1">$2</a>';
+	$replace[] = 'handle_url_tag(\''.get_base_url(true).'/viewtopic.php?id=$1\')';
+	$replace[] = 'handle_url_tag(\''.get_base_url(true).'/viewtopic.php?id=$1\', \'$2\')';
+	$replace[] = 'handle_url_tag(\''.get_base_url(true).'/viewtopic.php?pid=$1#p$1\')';
+	$replace[] = 'handle_url_tag(\''.get_base_url(true).'/viewtopic.php?pid=$1#p$1\', \'$2\')';
+	$replace[] = 'handle_url_tag(\''.get_base_url(true).'/viewforum.php?id=$1\')';
+	$replace[] = 'handle_url_tag(\''.get_base_url(true).'/viewforum.php?id=$1\', \'$2\')';
+	$replace[] = 'handle_url_tag(\''.get_base_url(true).'/profile.php?id=$1\')';
+	$replace[] = 'handle_url_tag(\''.get_base_url(true).'/profile.php?id=$1\', \'$2\')';
 
 	// This thing takes a while! :)
 	$text = preg_replace($pattern, $replace, $text);
@@ -784,8 +814,8 @@ function do_clickable($text)
 {
 	$text = ' '.$text;
 
-	$text = ucp_preg_replace('#(?<=[\s\]\)])(<)?(\[)?(\()?([\'"]?)(https?|ftp|news){1}://([\p{L}\p{N}\-]+\.([\p{L}\p{N}\-]+\.)*[\p{L}\p{N}]+(:[0-9]+)?(/[^\s\[]*[^\s.,?!\[;:-])?)\4(?(3)(\)))(?(2)(\]))(?(1)(>))(?![^\s]*\[/(?:url|img)\])#uie', 'stripslashes(\'$1$2$3$4\').handle_url_tag(\'$5://$6\', \'$5://$6\', true).stripslashes(\'$4$10$11$12\')', $text);
-	$text = ucp_preg_replace('#(?<=[\s\]\)])(<)?(\[)?(\()?([\'"]?)(www|ftp)\.(([\p{L}\p{N}\-]+\.)*[\p{L}\p{N}]+(:[0-9]+)?(/[^\s\[]*[^\s.,?!\[;:-])?)\4(?(3)(\)))(?(2)(\]))(?(1)(>))(?![^\s]*\[/(?:url|img)\])#uie', 'stripslashes(\'$1$2$3$4\').handle_url_tag(\'$5.$6\', \'$5.$6\', true).stripslashes(\'$4$10$11$12\')', $text);
+	$text = ucp_preg_replace('%(?<=[\s\]\)])(<)?(\[)?(\()?([\'"]?)(https?|ftp|news){1}://([\p{L}\p{N}\-]+\.([\p{L}\p{N}\-]+\.)*[\p{L}\p{N}]+(:[0-9]+)?(/(?:[^\s\[]*[^\s.,?!\[;:-])?)?)\4(?(3)(\)))(?(2)(\]))(?(1)(>))(?![^\s]*\[/(?:url|img)\])%uie', 'stripslashes(\'$1$2$3$4\').handle_url_tag(\'$5://$6\', \'$5://$6\', true).stripslashes(\'$4$10$11$12\')', $text);
+	$text = ucp_preg_replace('%(?<=[\s\]\)])(<)?(\[)?(\()?([\'"]?)(www|ftp)\.(([\p{L}\p{N}\-]+\.)*[\p{L}\p{N}]+(:[0-9]+)?(/(?:[^\s\[]*[^\s.,?!\[;:-])?)?)\4(?(3)(\)))(?(2)(\]))(?(1)(>))(?![^\s]*\[/(?:url|img)\])%uie', 'stripslashes(\'$1$2$3$4\').handle_url_tag(\'$5.$6\', \'$5.$6\', true).stripslashes(\'$4$10$11$12\')', $text);
 
 	return substr($text, 1);
 }
@@ -803,7 +833,7 @@ function do_smilies($text)
 	foreach ($smilies as $smiley_text => $smiley_img)
 	{
 		if (strpos($text, $smiley_text) !== false)
-			$text = ucp_preg_replace('#(?<=[>\s])'.preg_quote($smiley_text, '#').'(?=[^\p{L}\p{N}])#um', '<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);
+			$text = ucp_preg_replace('%(?<=[>\s])'.preg_quote($smiley_text, '%').'(?=[^\p{L}\p{N}])%um', '<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);
@@ -825,10 +855,7 @@ function parse_message($text, $hide_smilies)
 
 	// If the message contains a code tag we have to split it up (text within [code][/code] shouldn't be touched)
 	if (strpos($text, '[code]') !== false && strpos($text, '[/code]') !== false)
-	{
-		list($inside, $outside) = split_text($text, '[code]', '[/code]', $errors);
-		$text = implode("\1", $outside);
-	}
+		list($inside, $text) = extract_blocks($text, '[code]', '[/code]');
 
 	if ($pun_config['p_message_bbcode'] == '1' && strpos($text, '[') !== false && strpos($text, ']') !== false)
 		$text = do_bbcode($text);
@@ -844,16 +871,12 @@ function parse_message($text, $hide_smilies)
 	// If we split up the message before we have to concatenate it together again (code tags)
 	if (isset($inside))
 	{
-		$outside = explode("\1", $text);
+		$parts = explode("\1", $text);
 		$text = '';
-
-		$num_tokens = count($outside);
-
-		for ($i = 0; $i < $num_tokens; ++$i)
+		foreach ($parts as $i => $part)
 		{
-			$text .= $outside[$i];
+			$text .= $part;
 			if (isset($inside[$i]))
-			//	$text .= '</p><div class="codebox"><pre><code>'.pun_trim($inside[$i], "\n\r").'</code></pre></div><p>';
 			{
 				$num_lines = (substr_count($inside[$i], "\n"));
 				$text .= '</p><div class="codebox"><pre'.(($num_lines > 28) ? ' class="vscroll"' : '').'><code>'.pun_trim($inside[$i], "\n\r").'</code></pre></div><p>';
@@ -861,10 +884,31 @@ function parse_message($text, $hide_smilies)
 		}
 	}
 
+	return clean_paragraphs($text);
+}
+
+
+//
+// Clean up paragraphs and line breaks
+//
+function clean_paragraphs($text)
+{
 	// Add paragraph tag around post, but make sure there are no empty paragraphs
-	$text = preg_replace('#<br />\s*?<br />((\s*<br />)*)#i', "</p>$1<p>", $text);
-	$text = str_replace('<p><br />', '<p>', $text);
-	$text = str_replace('<p></p>', '', '<p>'.$text.'</p>');
+
+	$text = '<p>'.$text.'</p>';
+
+	// Replace any breaks next to paragraphs so our replace below catches them
+	$text = preg_replace('%(</?p>)(?:\s*?<br />){1,2}%i', '$1', $text);
+	$text = preg_replace('%(?:<br />\s*?){1,2}(</?p>)%i', '$1', $text);
+
+	// Remove any empty paragraph tags (inserted via quotes/lists/code/etc) which should be stripped
+	$text = str_replace('<p></p>', '', $text);
+
+	$text = preg_replace('%<br />\s*?<br />%i', '</p><p>', $text);
+
+	$text = str_replace('<p><br />', '<br /><p>', $text);
+	$text = str_replace('<br /></p>', '</p><br />', $text);
+	$text = str_replace('<p></p>', '<br /><br />', $text);
 
 	return $text;
 }
@@ -895,10 +939,5 @@ function parse_signature($text)
 	$replace = array('<br />', '&#160; &#160; ', '&#160; ', ' &#160;');
 	$text = str_replace($pattern, $replace, $text);
 
-	// Add paragraph tag around post, but make sure there are no empty paragraphs
-	$text = preg_replace('#<br />\s*?<br />((\s*<br />)*)#i', "</p>$1<p>", $text);
-	$text = str_replace('<p><br />', '<p>', $text);
-	$text = str_replace('<p></p>', '', '<p>'.$text.'</p>');
-
-	return $text;
+	return clean_paragraphs($text);
 }
diff --git a/include/search_idx.php b/include/search_idx.php
index 7f3a834..550f348 100644
--- a/include/search_idx.php
+++ b/include/search_idx.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -48,16 +48,16 @@ define('PUN_CJK_HANGUL_REGEX', '['.
 function split_words($text, $idx)
 {
 	// Remove BBCode
-	$text = preg_replace('/\[\/?(b|u|s|ins|del|em|i|h|colou?r|quote|code|img|url|email|list)(?:\=[^\]]*)?\]/', ' ', $text);
+	$text = preg_replace('%\[/?(b|u|s|ins|del|em|i|h|colou?r|quote|code|img|url|email|list|topic|post|forum|user)(?:\=[^\]]*)?\]%', ' ', $text);
 
 	// Remove any apostrophes or dashes which aren't part of words
-	$text = substr(ucp_preg_replace('/((?<=[^\p{L}\p{N}])[\'\-]|[\'\-](?=[^\p{L}\p{N}]))/u', '', ' '.$text.' '), 1, -1);
+	$text = substr(ucp_preg_replace('%((?<=[^\p{L}\p{N}])[\'\-]|[\'\-](?=[^\p{L}\p{N}]))%u', '', ' '.$text.' '), 1, -1);
 
 	// Remove punctuation and symbols (actually anything that isn't a letter or number), allow apostrophes and dashes (and % * if we aren't indexing)
-	$text = ucp_preg_replace('/(?![\'\-'.($idx ? '' : '%\*').'])[^\p{L}\p{N}]+/u', ' ', $text);
+	$text = ucp_preg_replace('%(?![\'\-'.($idx ? '' : '\%\*').'])[^\p{L}\p{N}]+%u', ' ', $text);
 
 	// Replace multiple whitespace or dashes
-	$text = preg_replace('/(\s){2,}/u', '\1', $text);
+	$text = preg_replace('%(\s){2,}%u', '\1', $text);
 
 	// Fill an array with all the words
 	$words = array_unique(explode(' ', $text));
@@ -108,6 +108,9 @@ function validate_search_word($word, $idx)
 	if (is_cjk($word))
 		return !$idx;
 
+	// Exclude % and * when checking whether current word is valid
+	$word = str_replace(array('%', '*'), '', $word);
+
 	// Check the word is within the min/max length
 	$num_chars = pun_strlen($word);
 	return $num_chars >= PUN_SEARCH_MIN_WORD && $num_chars <= PUN_SEARCH_MAX_WORD;
@@ -128,7 +131,7 @@ function is_keyword($word)
 //
 function is_cjk($word)
 {
-	return preg_match('/^'.PUN_CJK_HANGUL_REGEX.'+$/u', $word) ? true : false;
+	return preg_match('%^'.PUN_CJK_HANGUL_REGEX.'+$%u', $word) ? true : false;
 }
 
 
@@ -142,9 +145,10 @@ function strip_bbcode($text)
 	if (!isset($patterns))
 	{
 		$patterns = array(
-			'%\[img=([^\]]*+)\][^[]*+\[/img\]%'									=>	'$1',	// Keep the alt description
-			'%\[(url|email)=[^\]]*+\]([^[]*+(?:(?!\[/\1\])\[[^[]*+)*)\[/\1\]%' =>	'$2',	// Keep the text
-			'%\[(img|url|email)\]([^[]*+(?:(?!\[/\1\])\[[^[]*+)*)\[/\1\]%'		=>	'',		// Remove the whole thing
+			'%\[img=([^\]]*+)\]([^[]*+)\[/img\]%'									=>	'$2 $1',	// Keep the url and description
+			'%\[(url|email)=([^\]]*+)\]([^[]*+(?:(?!\[/\1\])\[[^[]*+)*)\[/\1\]%'	=>	'$2 $3',	// Keep the url and text
+			'%\[(img|url|email)\]([^[]*+(?:(?!\[/\1\])\[[^[]*+)*)\[/\1\]%'			=>	'$2',		// Keep the url
+			'%\[(topic|post|forum|user)\][1-9]\d*\[/\1\]%'							=>	' ',		// Do not index topic/post/forum/user ID
 		);
 	}
 
diff --git a/include/user/index.html b/include/user/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/include/user/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/include/utf8/strcspn.php b/include/utf8/strcspn.php
index 1e3756d..b05e327 100644
--- a/include/utf8/strcspn.php
+++ b/include/utf8/strcspn.php
@@ -24,7 +24,7 @@ function utf8_strcspn($str, $mask, $start=null, $length=null)
 
 	$mask = preg_replace('!([\\\\\\-\\]\\[/^])!','\\\${1}', $mask);
 
-	if ($start !== null || $length !== null)
+	if (!is_null($start) || !is_null($length))
 		$str = utf8_substr($str, $start, $length);
 
 	preg_match('/^[^'.$mask.']+/u', $str, $matches);
diff --git a/include/utf8/strspn.php b/include/utf8/strspn.php
index 424ceb7..49d300a 100644
--- a/include/utf8/strspn.php
+++ b/include/utf8/strspn.php
@@ -20,7 +20,7 @@ function utf8_strspn($str, $mask, $start=null, $length=null)
 {
 	$mask = preg_replace('!([\\\\\\-\\]\\[/^])!', '\\\${1}', $mask);
 
-	if ($start !== null || $length !== null)
+	if (!is_null($start)|| !is_null($length))
 		$str = utf8_substr($str, $start, $length);
 
 	preg_match('/^['.$mask.']+/u', $str, $matches);
diff --git a/include/utf8/substr_replace.php b/include/utf8/substr_replace.php
index 7fc7369..20a43b5 100644
--- a/include/utf8/substr_replace.php
+++ b/include/utf8/substr_replace.php
@@ -18,7 +18,7 @@ function utf8_substr_replace($str, $repl, $start , $length=null)
 	preg_match_all('/./us', $str, $ar);
 	preg_match_all('/./us', $repl, $rar);
 
-	if($length === null)
+	if(is_null($length))
 		$length = utf8_strlen($str);
 
 	array_splice($ar[0], $start, $length, $rar[0]);
diff --git a/include/utf8/utf8.php b/include/utf8/utf8.php
index 281f18c..661b2d7 100644
--- a/include/utf8/utf8.php
+++ b/include/utf8/utf8.php
@@ -34,7 +34,7 @@ if (!defined('UTF8'))
 
 if (extension_loaded('mbstring') && !defined('UTF8_USE_MBSTRING') && !defined('UTF8_USE_NATIVE'))
 	define('UTF8_USE_MBSTRING', true);
-else
+else if (!defined('UTF8_USE_NATIVE'))
 	define('UTF8_USE_NATIVE', true);
 
 // utf8_strpos() and utf8_strrpos() need utf8_bad_strip() to strip invalid
diff --git a/include/utf8/utils/bad.php b/include/utf8/utils/bad.php
index 78e9d17..2704294 100644
--- a/include/utf8/utils/bad.php
+++ b/include/utf8/utils/bad.php
@@ -114,33 +114,9 @@ function utf8_bad_findall($str)
 * @package utf8
 * @subpackage bad
 */
-function utf8_bad_strip($str)
+function utf8_bad_strip($original)
 {
-	$UTF8_BAD =
-		'([\x00-\x7F]'.                          # ASCII (including control chars)
-		'|[\xC2-\xDF][\x80-\xBF]'.               # Non-overlong 2-byte
-		'|\xE0[\xA0-\xBF][\x80-\xBF]'.           # Excluding overlongs
-		'|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}'.    # Straight 3-byte
-		'|\xED[\x80-\x9F][\x80-\xBF]'.           # Excluding surrogates
-		'|\xF0[\x90-\xBF][\x80-\xBF]{2}'.        # Planes 1-3
-		'|[\xF1-\xF3][\x80-\xBF]{3}'.            # Planes 4-15
-		'|\xF4[\x80-\x8F][\x80-\xBF]{2}'.        # Plane 16
-		'|(.{1}))';                              # Invalid byte
-
-	ob_start();
-
-	while (preg_match('/'.$UTF8_BAD.'/S', $str, $matches))
-	{
-		if (!isset($matches[2]))
-			echo $matches[0];
-
-		$str = substr($str, strlen($matches[0]));
-	}
-
-	$result = ob_get_contents();
-	ob_end_clean();
-
-	return $result;
+	return utf8_bad_replace($original, '');
 }
 
 /**
@@ -156,33 +132,52 @@ function utf8_bad_strip($str)
 * @package utf8
 * @subpackage bad
 */
-function utf8_bad_replace($str, $replace='?')
-{
-	$UTF8_BAD =
-		'([\x00-\x7F]'.                          # ASCII (including control chars)
-		'|[\xC2-\xDF][\x80-\xBF]'.               # Non-overlong 2-byte
-		'|\xE0[\xA0-\xBF][\x80-\xBF]'.           # Excluding overlongs
-		'|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}'.    # Straight 3-byte
-		'|\xED[\x80-\x9F][\x80-\xBF]'.           # Excluding surrogates
-		'|\xF0[\x90-\xBF][\x80-\xBF]{2}'.        # Planes 1-3
-		'|[\xF1-\xF3][\x80-\xBF]{3}'.            # Planes 4-15
-		'|\xF4[\x80-\x8F][\x80-\xBF]{2}'.        # Plane 16
-		'|(.{1}))';                              # Invalid byte
+function utf8_bad_replace($original, $replace = '?') {
+	$result = '';
+
+	$strlen = strlen($original);
+	for ($i = 0; $i < $strlen;) {
+		$char = $original[$i++];
+		$byte = ord($char);
+
+		if ($byte < 0x80) $bytes = 0; // 1-bytes (00000000 - 01111111)
+		else if ($byte < 0xC0) { // 1-bytes (10000000 - 10111111)
+			$result .= $replace;
+			continue;
+		}
+		else if ($byte < 0xE0) $bytes = 1; // 2-bytes (11000000 - 11011111)
+		else if ($byte < 0xF0) $bytes = 2; // 3-bytes (11100000 - 11101111)
+		else if ($byte < 0xF8) $bytes = 3; // 4-bytes (11110000 - 11110111)
+		else if ($byte < 0xFC) $bytes = 4; // 5-bytes (11111000 - 11111011)
+		else if ($byte < 0xFE) $bytes = 5; // 6-bytes (11111100 - 11111101)
+		else { // Otherwise it's something invalid
+			$result .= $replace;
+			continue;
+		}
 
-	ob_start();
+		// Check our input actually has enough data
+		if ($i + $bytes > $strlen) {
+			$result .= $replace;
+			continue;
+		}
 
-	while (preg_match('/'.$UTF8_BAD.'/S', $str, $matches))
-	{
-		if (!isset($matches[2]))
-			echo $matches[0];
-		else
-			echo $replace;
+		// If we've got this far then we have a multiple-byte character
+		for ($j = 0; $j < $bytes; $j++) {
+			$byte = $original[$i + $j];
 
-		$str = substr($str, strlen($matches[0]));
-	}
+			$char .= $byte;
+			$byte = ord($byte);
 
-	$result = ob_get_contents();
-	ob_end_clean();
+			// Every following byte must be 10000000 - 10111111
+			if ($byte < 0x80 || $byte > 0xBF) {
+				$result .= $replace;
+				continue 2;
+			}
+		}
+
+		$i += $bytes;
+		$result .= $char;
+	}
 
 	return $result;
 }
diff --git a/index.php b/index.php
index e80e73b..0f257ac 100644
--- a/index.php
+++ b/index.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -11,7 +11,7 @@ require PUN_ROOT.'include/common.php';
 
 
 if ($pun_user['g_read_board'] == '0')
-	message($lang_common['No view']);
+	message($lang_common['No view'], false, '403 Forbidden');
 
 
 // Load the index.php language file
diff --git a/install.php b/install.php
index f4e2e78..a5c699a 100644
--- a/install.php
+++ b/install.php
@@ -1,15 +1,15 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
 
 // The FluxBB version this script installs
-define('FORUM_VERSION', '1.4.5');
+define('FORUM_VERSION', '1.4.9');
 
-define('FORUM_DB_REVISION', 11);
+define('FORUM_DB_REVISION', 15);
 define('FORUM_SI_REVISION', 2);
 define('FORUM_PARSER_REVISION', 2);
 
@@ -22,40 +22,6 @@ define('PUN_SEARCH_MAX_WORD', 20);
 
 define('PUN_ROOT', dirname(__FILE__).'/');
 
-// If we've been passed a default language, use it
-$install_lang = isset($_REQUEST['install_lang']) ? trim($_REQUEST['install_lang']) : 'English';
-
-// If such a language pack doesn't exist, or isn't up-to-date enough to translate this page, default to English
-if (!file_exists(PUN_ROOT.'lang/'.$install_lang.'/install.php'))
-	$install_lang = 'English';
-
-require PUN_ROOT.'lang/'.$install_lang.'/install.php';
-
-if (file_exists(PUN_ROOT.'config.php'))
-{
-	// Check to see whether FluxBB is already installed
-	include PUN_ROOT.'config.php';
-
-	// If we have the 1.3-legacy constant defined, define the proper 1.4 constant so we don't get an incorrect "need to install" message
-	if (defined('FORUM'))
-		define('PUN', FORUM);
-
-	// If PUN is defined, config.php is probably valid and thus the software is installed
-	if (defined('PUN'))
-		exit($lang_install['Already installed']);
-}
-
-// Define PUN because email.php requires it
-define('PUN', 1);
-
-// If the cache directory is not specified, we use the default setting
-if (!defined('FORUM_CACHE_DIR'))
-	define('FORUM_CACHE_DIR', PUN_ROOT.'cache/');
-
-// Make sure we are running at least MIN_PHP_VERSION
-if (!function_exists('version_compare') || version_compare(PHP_VERSION, MIN_PHP_VERSION, '<'))
-	exit(sprintf($lang_install['You are running error'], 'PHP', PHP_VERSION, FORUM_VERSION, MIN_PHP_VERSION));
-
 // Load the functions script
 require PUN_ROOT.'include/functions.php';
 
@@ -95,6 +61,42 @@ if (get_magic_quotes_gpc())
 // Turn off PHP time limit
 @set_time_limit(0);
 
+
+// If we've been passed a default language, use it
+$install_lang = isset($_REQUEST['install_lang']) ? pun_trim($_REQUEST['install_lang']) : 'English';
+
+// If such a language pack doesn't exist, or isn't up-to-date enough to translate this page, default to English
+if (!file_exists(PUN_ROOT.'lang/'.$install_lang.'/install.php'))
+	$install_lang = 'English';
+
+require PUN_ROOT.'lang/'.$install_lang.'/install.php';
+
+if (file_exists(PUN_ROOT.'config.php'))
+{
+	// Check to see whether FluxBB is already installed
+	include PUN_ROOT.'config.php';
+
+	// If we have the 1.3-legacy constant defined, define the proper 1.4 constant so we don't get an incorrect "need to install" message
+	if (defined('FORUM'))
+		define('PUN', FORUM);
+
+	// If PUN is defined, config.php is probably valid and thus the software is installed
+	if (defined('PUN'))
+		exit($lang_install['Already installed']);
+}
+
+// Define PUN because email.php requires it
+define('PUN', 1);
+
+// If the cache directory is not specified, we use the default setting
+if (!defined('FORUM_CACHE_DIR'))
+	define('FORUM_CACHE_DIR', PUN_ROOT.'cache/');
+
+// Make sure we are running at least MIN_PHP_VERSION
+if (!function_exists('version_compare') || version_compare(PHP_VERSION, MIN_PHP_VERSION, '<'))
+	exit(sprintf($lang_install['You are running error'], 'PHP', PHP_VERSION, FORUM_VERSION, MIN_PHP_VERSION));
+
+
 //
 // Generate output to be used for config.php
 //
@@ -129,7 +131,7 @@ if (!isset($_POST['form_sent']))
 {
 	// Make an educated guess regarding base_url
 	$base_url  = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https://' : 'http://';	// protocol
-	$base_url .= preg_replace('/:(80|443)$/', '', $_SERVER['HTTP_HOST']);							// host[:port]
+	$base_url .= preg_replace('%:(80|443)$%', '', $_SERVER['HTTP_HOST']);							// host[:port]
 	$base_url .= str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME']));							// path
 
 	if (substr($base_url, -1) == '/')
@@ -172,11 +174,11 @@ else
 		$alerts[] = $lang_install['Username 2'];
 	else if (!strcasecmp($username, 'Guest'))
 		$alerts[] = $lang_install['Username 3'];
-	else if (preg_match('/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/', $username) || preg_match('/((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))/', $username))
+	else if (preg_match('%[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}%', $username) || preg_match('%((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))%', $username))
 		$alerts[] = $lang_install['Username 4'];
 	else if ((strpos($username, '[') !== false || strpos($username, ']') !== false) && strpos($username, '\'') !== false && strpos($username, '"') !== false)
 		$alerts[] = $lang_install['Username 5'];
-	else if (preg_match('/(?:\[\/?(?:b|u|i|h|colou?r|quote|code|img|url|email|list)\]|\[(?:code|quote|list)=)/i', $username))
+	else if (preg_match('%(?:\[/?(?:b|u|i|h|colou?r|quote|code|img|url|email|list)\]|\[(?:code|quote|list)=)%i', $username))
 		$alerts[] = $lang_install['Username 6'];
 
 	if (pun_strlen($password1) < 4)
@@ -203,11 +205,11 @@ else
 }
 
 // Check if the cache directory is writable
-if (!@is_writable(FORUM_CACHE_DIR))
+if (!forum_is_writable(FORUM_CACHE_DIR))
 	$alerts[] = sprintf($lang_install['Alert cache'], FORUM_CACHE_DIR);
 
 // Check if default avatar directory is writable
-if (!@is_writable(PUN_ROOT.'img/avatars/'))
+if (!forum_is_writable(PUN_ROOT.'img/avatars/'))
 	$alerts[] = sprintf($lang_install['Alert avatar'], PUN_ROOT.'img/avatars/');
 
 if (!isset($_POST['form_sent']) || !empty($alerts))
@@ -253,11 +255,10 @@ if (!isset($_POST['form_sent']) || !empty($alerts))
 /* <![CDATA[ */
 function process_form(the_form)
 {
-	var element_names = {
+	var required_fields = {
 		"req_db_type": "<?php echo $lang_install['Database type'] ?>",
 		"req_db_host": "<?php echo $lang_install['Database server hostname'] ?>",
 		"req_db_name": "<?php echo $lang_install['Database name'] ?>",
-		"db_prefix": "<?php echo $lang_install['Table prefix'] ?>",
 		"req_username": "<?php echo $lang_install['Administrator username'] ?>",
 		"req_password1": "<?php echo $lang_install['Administrator password 1'] ?>",
 		"req_password2": "<?php echo $lang_install['Administrator password 2'] ?>",
@@ -270,14 +271,11 @@ function process_form(the_form)
 		for (var i = 0; i < the_form.length; ++i)
 		{
 			var elem = the_form.elements[i];
-			if (elem.name && (/^req_/.test(elem.name)))
+			if (elem.name && required_fields[elem.name] && !elem.value && elem.type && (/^(?:text(?:area)?|password|file)$/i.test(elem.type)))
 			{
-				if (!elem.value && elem.type && (/^(?:text(?:area)?|password|file)$/i.test(elem.type)))
-				{
-					alert('"' + element_names[elem.name] + '" <?php echo $lang_install['Required field'] ?>');
-					elem.focus();
-					return false;
-				}
+				alert('"' + required_fields[elem.name] + '" <?php echo $lang_install['Required field'] ?>');
+				elem.focus();
+				return false;
 			}
 		}
 	}
@@ -286,7 +284,7 @@ function process_form(the_form)
 /* ]]> */
 </script>
 </head>
-<body onload="document.getElementById('install').req_db_type.focus();document.getElementById('install').start.disabled=false;">
+<body onload="document.getElementById('install').req_db_type.focus();document.getElementById('install').start.disabled=false;" onunload="">
 
 <div id="puninstall" class="pun">
 <div class="top-box"><div><!-- Top Corners --></div></div>
@@ -336,7 +334,7 @@ function process_form(the_form)
 <?php endif; ?>
 
 <div class="blockform">
-	<h2><span><?php echo $lang_install['Install'] ?></span></h2>
+	<h2><span><?php echo sprintf($lang_install['Install'], FORUM_VERSION) ?></span></h2>
 	<div class="box">
 		<form id="install" method="post" action="install.php" onsubmit="this.start.disabled=true;if(process_form(this)){return true;}else{this.start.disabled=false;return false;}">
 		<div><input type="hidden" name="form_sent" value="1" /><input type="hidden" name="install_lang" value="<?php echo pun_htmlspecialchars($install_lang) ?>" /></div>
@@ -579,7 +577,7 @@ else
 	$db = new DBLayer($db_host, $db_username, $db_password, $db_name, $db_prefix, false);
 
 	// Validate prefix
-	if (strlen($db_prefix) > 0 && (!preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $db_prefix) || strlen($db_prefix) > 40))
+	if (strlen($db_prefix) > 0 && (!preg_match('%^[a-zA-Z_][a-zA-Z0-9_]*$%', $db_prefix) || strlen($db_prefix) > 40))
 		error(sprintf($lang_install['Table prefix error'], $db->prefix));
 
 	// Do some DB type specific checks
@@ -945,6 +943,11 @@ else
 				'datatype'		=> 'SMALLINT(6)',
 				'allow_null'	=> false,
 				'default'		=> '60'
+			),
+			'g_report_flood'			=> array(
+				'datatype'		=> 'SMALLINT(6)',
+				'allow_null'	=> false,
+				'default'		=> '60'
 			)
 		),
 		'PRIMARY KEY'	=> array('g_id')
@@ -990,8 +993,7 @@ else
 		'INDEXES'		=> array(
 			'ident_idx'		=> array('ident'),
 			'logged_idx'	=> array('logged')
-		),
-		'ENGINE'		=> 'HEAP'
+		)
 	);
 
 	if ($db_type == 'mysql' || $db_type == 'mysqli' || $db_type == 'mysql_innodb' || $db_type == 'mysqli_innodb')
@@ -1485,7 +1487,7 @@ else
 			'language'			=> array(
 				'datatype'		=> 'VARCHAR(25)',
 				'allow_null'	=> false,
-				'default'		=> '\'English\''
+				'default'		=> '\''.$db->escape($default_lang).'\''
 			),
 			'style'				=> array(
 				'datatype'		=> 'VARCHAR(25)',
@@ -1509,6 +1511,10 @@ else
 				'datatype'		=> 'INT(10) UNSIGNED',
 				'allow_null'	=> true
 			),
+			'last_report_sent'	=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> true
+			),
 			'registered'		=> array(
 				'datatype'		=> 'INT(10) UNSIGNED',
 				'allow_null'	=> false,
@@ -1555,13 +1561,13 @@ else
 	$now = time();
 
 	// Insert the four preset groups
-	$db->query('INSERT INTO '.$db->prefix.'groups ('.($db_type != 'pgsql' ? 'g_id, ' : '').'g_title, g_user_title, g_moderator, g_mod_edit_users, g_mod_rename_users, g_mod_change_passwords, g_mod_ban_users, g_read_board, g_view_users, g_post_replies, g_post_topics, g_edit_posts, g_delete_posts, g_delete_topics, g_set_title, g_search, g_search_users, g_send_email, g_post_flood, g_search_flood, g_email_flood) VALUES('.($db_type != 'pgsql' ? '1, ' : '').'\''.$db->escape($lang_install['Administrators']).'\', \''.$db->escape($lang_install['Administrator']).'\', 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0)') or error('Unable to add group', __FILE__, __LINE__, $db->error());
+	$db->query('INSERT INTO '.$db->prefix.'groups ('.($db_type != 'pgsql' ? 'g_id, ' : '').'g_title, g_user_title, g_moderator, g_mod_edit_users, g_mod_rename_users, g_mod_change_passwords, g_mod_ban_users, g_read_board, g_view_users, g_post_replies, g_post_topics, g_edit_posts, g_delete_posts, g_delete_topics, g_set_title, g_search, g_search_users, g_send_email, g_post_flood, g_search_flood, g_email_flood, g_report_flood) VALUES('.($db_type != 'pgsql' ? '1, ' : '').'\''.$db->escape($lang_install['Administrators']).'\', \''.$db->escape($lang_install['Administrator']).'\', 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0)') or error('Unable to add group', __FILE__, __LINE__, $db->error());
 
-	$db->query('INSERT INTO '.$db->prefix.'groups ('.($db_type != 'pgsql' ? 'g_id, ' : '').'g_title, g_user_title, g_moderator, g_mod_edit_users, g_mod_rename_users, g_mod_change_passwords, g_mod_ban_users, g_read_board, g_view_users, g_post_replies, g_post_topics, g_edit_posts, g_delete_posts, g_delete_topics, g_set_title, g_search, g_search_users, g_send_email, g_post_flood, g_search_flood, g_email_flood) VALUES('.($db_type != 'pgsql' ? '2, ' : '').'\''.$db->escape($lang_install['Moderators']).'\', \''.$db->escape($lang_install['Moderator']).'\', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0)') or error('Unable to add group', __FILE__, __LINE__, $db->error());
+	$db->query('INSERT INTO '.$db->prefix.'groups ('.($db_type != 'pgsql' ? 'g_id, ' : '').'g_title, g_user_title, g_moderator, g_mod_edit_users, g_mod_rename_users, g_mod_change_passwords, g_mod_ban_users, g_read_board, g_view_users, g_post_replies, g_post_topics, g_edit_posts, g_delete_posts, g_delete_topics, g_set_title, g_search, g_search_users, g_send_email, g_post_flood, g_search_flood, g_email_flood, g_report_flood) VALUES('.($db_type != 'pgsql' ? '2, ' : '').'\''.$db->escape($lang_install['Moderators']).'\', \''.$db->escape($lang_install['Moderator']).'\', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0)') or error('Unable to add group', __FILE__, __LINE__, $db->error());
 
-	$db->query('INSERT INTO '.$db->prefix.'groups ('.($db_type != 'pgsql' ? 'g_id, ' : '').'g_title, g_user_title, g_moderator, g_mod_edit_users, g_mod_rename_users, g_mod_change_passwords, g_mod_ban_users, g_read_board, g_view_users, g_post_replies, g_post_topics, g_edit_posts, g_delete_posts, g_delete_topics, g_set_title, g_search, g_search_users, g_send_email, g_post_flood, g_search_flood, g_email_flood) VALUES('.($db_type != 'pgsql' ? '3, ' : '').'\''.$db->escape($lang_install['Guests']).'\', NULL, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 60, 30, 0)') or error('Unable to add group', __FILE__, __LINE__, $db->error());
+	$db->query('INSERT INTO '.$db->prefix.'groups ('.($db_type != 'pgsql' ? 'g_id, ' : '').'g_title, g_user_title, g_moderator, g_mod_edit_users, g_mod_rename_users, g_mod_change_passwords, g_mod_ban_users, g_read_board, g_view_users, g_post_replies, g_post_topics, g_edit_posts, g_delete_posts, g_delete_topics, g_set_title, g_search, g_search_users, g_send_email, g_post_flood, g_search_flood, g_email_flood, g_report_flood) VALUES('.($db_type != 'pgsql' ? '3, ' : '').'\''.$db->escape($lang_install['Guests']).'\', NULL, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 60, 30, 0, 0)') or error('Unable to add group', __FILE__, __LINE__, $db->error());
 
-	$db->query('INSERT INTO '.$db->prefix.'groups ('.($db_type != 'pgsql' ? 'g_id, ' : '').'g_title, g_user_title, g_moderator, g_mod_edit_users, g_mod_rename_users, g_mod_change_passwords, g_mod_ban_users, g_read_board, g_view_users, g_post_replies, g_post_topics, g_edit_posts, g_delete_posts, g_delete_topics, g_set_title, g_search, g_search_users, g_send_email, g_post_flood, g_search_flood, g_email_flood) VALUES('.($db_type != 'pgsql' ? '4, ' : '').'\''.$db->escape($lang_install['Members']).'\', NULL, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 60, 30, 60)') or error('Unable to add group', __FILE__, __LINE__, $db->error());
+	$db->query('INSERT INTO '.$db->prefix.'groups ('.($db_type != 'pgsql' ? 'g_id, ' : '').'g_title, g_user_title, g_moderator, g_mod_edit_users, g_mod_rename_users, g_mod_change_passwords, g_mod_ban_users, g_read_board, g_view_users, g_post_replies, g_post_topics, g_edit_posts, g_delete_posts, g_delete_topics, g_set_title, g_search, g_search_users, g_send_email, g_post_flood, g_search_flood, g_email_flood, g_report_flood) VALUES('.($db_type != 'pgsql' ? '4, ' : '').'\''.$db->escape($lang_install['Members']).'\', NULL, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 60, 30, 60, 60)') or error('Unable to add group', __FILE__, __LINE__, $db->error());
 
 	// Insert guest and first admin user
 	$db->query('INSERT INTO '.$db_prefix.'users (group_id, username, password, email) VALUES(3, \''.$db->escape($lang_install['Guest']).'\', \''.$db->escape($lang_install['Guest']).'\', \''.$db->escape($lang_install['Guest']).'\')')
@@ -1574,90 +1580,90 @@ else
 	$avatars = in_array(strtolower(@ini_get('file_uploads')), array('on', 'true', '1')) ? 1 : 0;
 
 	// Insert config data
-	$config = array(
-		'o_cur_version'				=> "'".FORUM_VERSION."'",
-		'o_database_revision'		=> "'".FORUM_DB_REVISION."'",
-		'o_searchindex_revision'	=> "'".FORUM_SI_REVISION."'",
-		'o_parser_revision'			=> "'".FORUM_PARSER_REVISION."'",
-		'o_board_title'				=> "'".$db->escape($title)."'",
-		'o_board_desc'				=> "'".$db->escape($description)."'",
-		'o_default_timezone'		=> "'0'",
-		'o_time_format'				=> "'H:i:s'",
-		'o_date_format'				=> "'Y-m-d'",
-		'o_timeout_visit'			=> "'1800'",
-		'o_timeout_online'			=> "'300'",
-		'o_redirect_delay'			=> "'1'",
-		'o_show_version'			=> "'0'",
-		'o_show_user_info'			=> "'1'",
-		'o_show_post_count'			=> "'1'",
-		'o_signatures'				=> "'1'",
-		'o_smilies'					=> "'1'",
-		'o_smilies_sig'				=> "'1'",
-		'o_make_links'				=> "'1'",
-		'o_default_lang'			=> "'".$db->escape($default_lang)."'",
-		'o_default_style'			=> "'".$db->escape($default_style)."'",
-		'o_default_user_group'		=> "'4'",
-		'o_topic_review'			=> "'15'",
-		'o_disp_topics_default'		=> "'30'",
-		'o_disp_posts_default'		=> "'25'",
-		'o_indent_num_spaces'		=> "'4'",
-		'o_quote_depth'				=> "'3'",
-		'o_quickpost'				=> "'1'",
-		'o_users_online'			=> "'1'",
-		'o_censoring'				=> "'0'",
-		'o_ranks'					=> "'1'",
-		'o_show_dot'				=> "'0'",
-		'o_topic_views'				=> "'1'",
-		'o_quickjump'				=> "'1'",
-		'o_gzip'					=> "'0'",
-		'o_additional_navlinks'		=> "''",
-		'o_report_method'			=> "'0'",
-		'o_regs_report'				=> "'0'",
-		'o_default_email_setting'	=> "'1'",
-		'o_mailing_list'			=> "'".$email."'",
-		'o_avatars'					=> "'".$avatars."'",
-		'o_avatars_dir'				=> "'img/avatars'",
-		'o_avatars_width'			=> "'60'",
-		'o_avatars_height'			=> "'60'",
-		'o_avatars_size'			=> "'10240'",
-		'o_search_all_forums'		=> "'1'",
-		'o_base_url'				=> "'".$db->escape($base_url)."'",
-		'o_admin_email'				=> "'".$email."'",
-		'o_webmaster_email'			=> "'".$email."'",
-		'o_forum_subscriptions'		=> "'1'",
-		'o_topic_subscriptions'		=> "'1'",
-		'o_smtp_host'				=> "NULL",
-		'o_smtp_user'				=> "NULL",
-		'o_smtp_pass'				=> "NULL",
-		'o_smtp_ssl'				=> "'0'",
-		'o_regs_allow'				=> "'1'",
-		'o_regs_verify'				=> "'0'",
-		'o_announcement'			=> "'0'",
-		'o_announcement_message'	=> "'".$db->escape($lang_install['Announcement'])."'",
-		'o_rules'					=> "'0'",
-		'o_rules_message'			=> "'".$db->escape($lang_install['Rules'])."'",
-		'o_maintenance'				=> "'0'",
-		'o_maintenance_message'		=> "'".$db->escape($lang_install['Maintenance message'])."'",
-		'o_default_dst'				=> "'0'",
-		'o_feed_type'				=> "'2'",
-		'o_feed_ttl'				=> "'0'",
-		'p_message_bbcode'			=> "'1'",
-		'p_message_img_tag'			=> "'1'",
-		'p_message_all_caps'		=> "'1'",
-		'p_subject_all_caps'		=> "'1'",
-		'p_sig_all_caps'			=> "'1'",
-		'p_sig_bbcode'				=> "'1'",
-		'p_sig_img_tag'				=> "'0'",
-		'p_sig_length'				=> "'400'",
-		'p_sig_lines'				=> "'4'",
-		'p_allow_banned_email'		=> "'1'",
-		'p_allow_dupe_email'		=> "'0'",
-		'p_force_guest_email'		=> "'1'"
+	$pun_config = array(
+		'o_cur_version'				=> FORUM_VERSION,
+		'o_database_revision'		=> FORUM_DB_REVISION,
+		'o_searchindex_revision'	=> FORUM_SI_REVISION,
+		'o_parser_revision'			=> FORUM_PARSER_REVISION,
+		'o_board_title'				=> $title,
+		'o_board_desc'				=> $description,
+		'o_default_timezone'		=> 0,
+		'o_time_format'				=> 'H:i:s',
+		'o_date_format'				=> 'Y-m-d',
+		'o_timeout_visit'			=> 1800,
+		'o_timeout_online'			=> 300,
+		'o_redirect_delay'			=> 1,
+		'o_show_version'			=> 0,
+		'o_show_user_info'			=> 1,
+		'o_show_post_count'			=> 1,
+		'o_signatures'				=> 1,
+		'o_smilies'					=> 1,
+		'o_smilies_sig'				=> 1,
+		'o_make_links'				=> 1,
+		'o_default_lang'			=> $default_lang,
+		'o_default_style'			=> $default_style,
+		'o_default_user_group'		=> 4,
+		'o_topic_review'			=> 15,
+		'o_disp_topics_default'		=> 30,
+		'o_disp_posts_default'		=> 25,
+		'o_indent_num_spaces'		=> 4,
+		'o_quote_depth'				=> 3,
+		'o_quickpost'				=> 1,
+		'o_users_online'			=> 1,
+		'o_censoring'				=> 0,
+		'o_ranks'					=> 1,
+		'o_show_dot'				=> 0,
+		'o_topic_views'				=> 1,
+		'o_quickjump'				=> 1,
+		'o_gzip'					=> 0,
+		'o_additional_navlinks'		=> '',
+		'o_report_method'			=> 0,
+		'o_regs_report'				=> 0,
+		'o_default_email_setting'	=> 1,
+		'o_mailing_list'			=> $email,
+		'o_avatars'					=> $avatars,
+		'o_avatars_dir'				=> 'img/avatars',
+		'o_avatars_width'			=> 60,
+		'o_avatars_height'			=> 60,
+		'o_avatars_size'			=> 10240,
+		'o_search_all_forums'		=> 1,
+		'o_base_url'				=> $base_url,
+		'o_admin_email'				=> $email,
+		'o_webmaster_email'			=> $email,
+		'o_forum_subscriptions'		=> 1,
+		'o_topic_subscriptions'		=> 1,
+		'o_smtp_host'				=> NULL,
+		'o_smtp_user'				=> NULL,
+		'o_smtp_pass'				=> NULL,
+		'o_smtp_ssl'				=> 0,
+		'o_regs_allow'				=> 1,
+		'o_regs_verify'				=> 0,
+		'o_announcement'			=> 0,
+		'o_announcement_message'	=> $lang_install['Announcement'],
+		'o_rules'					=> 0,
+		'o_rules_message'			=> $lang_install['Rules'],
+		'o_maintenance'				=> 0,
+		'o_maintenance_message'		=> $lang_install['Maintenance message'],
+		'o_default_dst'				=> 0,
+		'o_feed_type'				=> 2,
+		'o_feed_ttl'				=> 0,
+		'p_message_bbcode'			=> 1,
+		'p_message_img_tag'			=> 1,
+		'p_message_all_caps'		=> 1,
+		'p_subject_all_caps'		=> 1,
+		'p_sig_all_caps'			=> 1,
+		'p_sig_bbcode'				=> 1,
+		'p_sig_img_tag'				=> 0,
+		'p_sig_length'				=> 400,
+		'p_sig_lines'				=> 4,
+		'p_allow_banned_email'		=> 1,
+		'p_allow_dupe_email'		=> 0,
+		'p_force_guest_email'		=> 1
 	);
 
-	foreach ($config as $conf_name => $conf_value)
+	foreach ($pun_config as $conf_name => $conf_value)
 	{
-		$db->query('INSERT INTO '.$db_prefix."config (conf_name, conf_value) VALUES('$conf_name', $conf_value)")
+		$db->query('INSERT INTO '.$db_prefix.'config (conf_name, conf_value) VALUES(\''.$conf_name.'\', '.(is_null($conf_value) ? 'NULL' : '\''.$db->escape($conf_value).'\'').')')
 			or error('Unable to insert into table '.$db_prefix.'config. Please check your configuration and try again', __FILE__, __LINE__, $db->error());
 	}
 
@@ -1685,7 +1691,6 @@ else
 
 	// Index the test post so searching for it works
 	require PUN_ROOT.'include/search_idx.php';
-	$pun_config['o_default_lang'] = $default_lang;
 	update_search_index('post', 1, $message, $subject);
 
 	$db->end_transaction();
@@ -1705,7 +1710,7 @@ else
 
 	// Attempt to write config.php and serve it up for download if writing fails
 	$written = false;
-	if (is_writable(PUN_ROOT))
+	if (forum_is_writable(PUN_ROOT))
 	{
 		$fh = @fopen(PUN_ROOT.'config.php', 'wb');
 		if ($fh)
diff --git a/lang/English/admin_groups.php b/lang/English/admin_groups.php
index dd6ad56..4b1b4ad 100644
--- a/lang/English/admin_groups.php
+++ b/lang/English/admin_groups.php
@@ -26,7 +26,7 @@ $lang_admin_groups = array(
 'Group delete head'				=>	'Group delete',
 'Confirm delete subhead'		=>	'Confirm delete group',
 'Confirm delete info'			=>	'Are you sure that you want to delete the group <strong>%s</strong>?',
-'Confirm delete warn'			=>	'WARNING! After you deleted a group you can not restore it.',
+'Confirm delete warn'			=>	'WARNING! After you deleted a group you cannot restore it.',
 'Delete group head'				=>	'Delete group',
 'Move users subhead'			=>	'Move users currently in group',
 'Move users info'				=>	'The group <strong>%s</strong> currently has <strong>%s</strong> members. Please select a group to which these members will be assigned upon deletion.',
@@ -77,6 +77,8 @@ $lang_admin_groups = array(
 'Search flood help'				=>	'Number of seconds that users in this group have to wait between searches. Set to 0 to disable.',
 'E-mail flood label'			=>	'Email flood interval',
 'E-mail flood help'				=>	'Number of seconds that users in this group have to wait between emails. Set to 0 to disable.',
+'Report flood label'			=>	'Report flood interval',
+'Report flood help'				=>	'Number of seconds that users in this group have to wait between reports. Set to 0 to disable.',
 'Moderator info'				=>	'Please note that in order for a user in this group to have moderator abilities, he/she must be assigned to moderate one or more forums. This is done via the user administration page of the user\'s profile.',
 
 );
diff --git a/lang/English/admin_index.php b/lang/English/admin_index.php
index 9f9cdc7..bd2cd97 100644
--- a/lang/English/admin_index.php
+++ b/lang/English/admin_index.php
@@ -32,14 +32,14 @@ $lang_admin_index = array(
 'Show info'							=>	'Show info',
 'Environment data version'			=>	'PHP: %s - %s',
 'Environment data acc'				=>	'Accelerator: %s',
-'Turck MMCache'						=>	'Turk MMCache',
-'Turck MMCache link'				=>	'turck-mmcache.sourceforge.net',
+'Turck MMCache'						=>	'Turck MMCache',
+'Turck MMCache link'				=>	'turck-mmcache.sourceforge.net/',
 'ionCube PHP Accelerator'			=>	'ionCube PHP Accelerator',
 'ionCube PHP Accelerator link'		=>	'www.php-accelerator.co.uk/',
 'Alternative PHP Cache (APC)'		=>	'Alternative PHP Cache (APC)',
 'Alternative PHP Cache (APC) link'	=>	'www.php.net/apc/',
 'Zend Optimizer'					=>	'Zend Optimizer',
-'Zend Optimizer link'				=>	'www.zend.com/products/guard/zend-optimizer',
+'Zend Optimizer link'				=>	'www.zend.com/products/guard/zend-optimizer/',
 'eAccelerator'						=>	'eAccelerator',
 'eAccelerator link'					=>	'www.eaccelerator.net/',
 'XCache'							=>	'XCache',
diff --git a/lang/English/admin_options.php b/lang/English/admin_options.php
index 00923ce..0274bdf 100644
--- a/lang/English/admin_options.php
+++ b/lang/English/admin_options.php
@@ -51,27 +51,27 @@ $lang_admin_options = array(
 'UTC'								=>	'(UTC) Western European, Greenwich',
 'UTC+01:00'							=>	'(UTC+01:00) Central European, West African',
 'UTC+02:00'							=>	'(UTC+02:00) Eastern European, Central African',
-'UTC+03:00'							=>	'(UTC+03:00) Moscow, Eastern African',
+'UTC+03:00'							=>	'(UTC+03:00) Eastern African',
 'UTC+03:30'							=>	'(UTC+03:30) Iran',
-'UTC+04:00'							=>	'(UTC+04:00) Gulf, Samara',
+'UTC+04:00'							=>	'(UTC+04:00) Moscow, Gulf, Samara',
 'UTC+04:30'							=>	'(UTC+04:30) Afghanistan',
-'UTC+05:00'							=>	'(UTC+05:00) Pakistan, Yekaterinburg',
+'UTC+05:00'							=>	'(UTC+05:00) Pakistan',
 'UTC+05:30'							=>	'(UTC+05:30) India, Sri Lanka',
 'UTC+05:45'							=>	'(UTC+05:45) Nepal',
-'UTC+06:00'							=>	'(UTC+06:00) Bangladesh, Bhutan, Novosibirsk',
+'UTC+06:00'							=>	'(UTC+06:00) Bangladesh, Bhutan, Yekaterinburg',
 'UTC+06:30'							=>	'(UTC+06:30) Cocos Islands, Myanmar',
-'UTC+07:00'							=>	'(UTC+07:00) Indochina, Krasnoyarsk',
-'UTC+08:00'							=>	'(UTC+08:00) Greater China, Australian Western, Irkutsk',
+'UTC+07:00'							=>	'(UTC+07:00) Indochina, Novosibirsk',
+'UTC+08:00'							=>	'(UTC+08:00) Greater China, Australian Western, Krasnoyarsk',
 'UTC+08:45'							=>	'(UTC+08:45) Southeastern Western Australia',
-'UTC+09:00'							=>	'(UTC+09:00) Japan, Korea, Chita',
+'UTC+09:00'							=>	'(UTC+09:00) Japan, Korea, Chita, Irkutsk',
 'UTC+09:30'							=>	'(UTC+09:30) Australian Central',
-'UTC+10:00'							=>	'(UTC+10:00) Australian Eastern, Vladivostok',
+'UTC+10:00'							=>	'(UTC+10:00) Australian Eastern',
 'UTC+10:30'							=>	'(UTC+10:30) Lord Howe',
-'UTC+11:00'							=>	'(UTC+11:00) Solomon Island, Magadan',
+'UTC+11:00'							=>	'(UTC+11:00) Solomon Island, Vladivostok',
 'UTC+11:30'							=>	'(UTC+11:30) Norfolk Island',
-'UTC+12:00'							=>	'(UTC+12:00) New Zealand, Fiji, Kamchatka',
+'UTC+12:00'							=>	'(UTC+12:00) New Zealand, Fiji, Magadan',
 'UTC+12:45'							=>	'(UTC+12:45) Chatham Islands',
-'UTC+13:00'							=>	'(UTC+13:00) Tonga, Phoenix Islands',
+'UTC+13:00'							=>	'(UTC+13:00) Tonga, Phoenix Islands, Kamchatka',
 'UTC+14:00'							=>	'(UTC+14:00) Line Islands',
 
 // Timeout Section
diff --git a/lang/English/admin_users.php b/lang/English/admin_users.php
index 76d6639..49fad72 100644
--- a/lang/English/admin_users.php
+++ b/lang/English/admin_users.php
@@ -58,6 +58,8 @@ $lang_admin_users = array(
 'Last post after label'		=>	'Last post is after',
 'Date help'					=>	'(yyyy-mm-dd hh:mm:ss)',
 'Last post before label'	=>	'Last post is before',
+'Last visit after label'	=>	'Last visit is after',
+'Last visit before label'	=>	'Last visit is before',
 'Registered after label'	=>	'Registered after',
 'Registered before label'	=>	'Registered before',
 'Order by label'			=>	'Order by',
@@ -65,6 +67,7 @@ $lang_admin_users = array(
 'Order by e-mail'			=>	'Email',
 'Order by posts'			=>	'Number of posts',
 'Order by last post'		=>	'Last post',
+'Order by last visit'		=>	'Last visit',
 'Order by registered'		=>	'Registered',
 'Ascending'					=>	'Ascending',
 'Descending'				=>	'Descending',
diff --git a/lang/English/common.php b/lang/English/common.php
index 7a88e18..6a0c85e 100644
--- a/lang/English/common.php
+++ b/lang/English/common.php
@@ -64,12 +64,11 @@ $lang_common = array(
 'and'								=>	'and',
 'Image link'						=>	'image', // This is displayed (i.e. <image>) instead of images when "Show images" is disabled in the profile
 'wrote'								=>	'wrote:', // For [quote]'s
-'Mailer'							=>	'Mailer', // As in "MyForums Mailer" in the signature of outgoing emails
+'Mailer'							=>	'%s Mailer', // As in "MyForums Mailer" in the signature of outgoing emails
 'Important information'				=>	'Important information',
 'Write message legend'				=>	'Write your message and submit',
 'Previous'							=>	'Previous',
 'Next'								=>	'Next',
-'Forum index'						=>	'Forum index',
 'Spacer'							=>	'…', // Ellipsis for paginate
 
 // Title
@@ -137,24 +136,6 @@ $lang_common = array(
 'Query'								=>	'Query',
 'Total query time'					=>	'Total query time: %s',
 
-// Email related notifications
-'New user notification'				=>	'Alert - New registration',
-'New user message'					=>	'User \'%s\' registered in the forums at %s',
-'Banned email notification'			=>	'Alert - Banned email detected',
-'Banned email register message'		=>	'User \'%s\' registered with banned email address: %s',
-'Banned email change message'		=>	'User \'%s\' changed to banned email address: %s',
-'Banned email post message'			=>	'User \'%s\' posted with banned email address: %s',
-'Duplicate email notification'		=>	'Alert - Duplicate email detected',
-'Duplicate email register message'	=>	'User \'%s\' registered with an email address that also belongs to: %s',
-'Duplicate email change message'	=>	'User \'%s\' changed to an email address that also belongs to: %s',
-'Report notification'				=>	'Report(%d) - \'%s\'',
-'Report message 1'					=>	'User \'%s\' has reported the following message: %s',
-'Report message 2'					=>	'Reason: %s',
-
-'User profile'						=>	'User profile: %s',
-'Post URL'							=>	'Post URL: %s',
-'Email signature'					=>	'Forum Mailer'."\n".'(Do not reply to this message)',
-
 // For extern.php RSS feed
 'RSS description'					=>	'The most recent topics at %s.',
 'RSS description topic'				=>	'The most recent posts in %s.',
@@ -170,4 +151,13 @@ $lang_common = array(
 'New reports'						=>	'There are new reports',
 'Maintenance mode enabled'			=>	'Maintenance mode is enabled!',
 
+// Units for file sizes
+'Size unit B'						=>	'%s B',
+'Size unit KiB'						=>	'%s KiB',
+'Size unit MiB'						=>	'%s MiB',
+'Size unit GiB'						=>	'%s GiB',
+'Size unit TiB'						=>	'%s TiB',
+'Size unit PiB'						=>	'%s PiB',
+'Size unit EiB'						=>	'%s EiB',
+
 );
diff --git a/lang/English/help.php b/lang/English/help.php
index 9289566..7b4e533 100644
--- a/lang/English/help.php
+++ b/lang/English/help.php
@@ -25,10 +25,16 @@ $lang_help = array(
 
 'Links and images'		=>	'Links and images',
 'Links info'			=>	'You can create links to other documents or to email addresses using the following tags:',
+'This help page'		=>	'This help page',
 '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',
 
+'Test topic'			=>	'Test topic',
+'Test post'				=>	'Test post',
+'Test forum'			=>	'Test forum',
+'Test user'				=>	'Test user',
+
 'Quotes'				=>	'Quotes',
 'Quotes info'			=>	'If you want to quote someone, you should use the quote tag.',
 'Quotes info 2'			=>	'If you don\'t want to quote anyone in particular, you can use the quote tag without specifying a name.',
diff --git a/lang/English/install.php b/lang/English/install.php
index 4fa1a2d..318faf3 100644
--- a/lang/English/install.php
+++ b/lang/English/install.php
@@ -36,7 +36,7 @@ $lang_install = array(
 'Required field'				=>	'is a required field in this form.',
 'FluxBB Installation'			=>	'FluxBB Installation',
 'Welcome'						=>	'You are about to install FluxBB. In order to install FluxBB, you must complete the form set out below. If you encounter any difficulties with the installation, please refer to the documentation.',
-'Install'						=>	'Install FluxBB 1.4',
+'Install'						=>	'Install FluxBB %s',
 'Errors'						=>	'The following errors need to be corrected:',
 'Database setup'				=>	'Database setup',
 'Info 1'						=>	'Please enter the requested information in order to setup your database for FluxBB. You must know all the information asked for before proceeding with the installation.',
@@ -102,7 +102,7 @@ $lang_install = array(
 'Announcement'					=>	'Enter your announcement here.',
 'Rules'							=>	'Enter your rules here',
 'Maintenance message'			=>	'The forums are temporarily down for maintenance. Please try again in a few minutes.',
-'Test post'						=>	'Test post',
+'Test post'						=>	'Test topic',
 'Message'						=>	'If you are looking at this (which I guess you are), the install of FluxBB appears to have worked! Now log in and head over to the administration control panel to configure your forum.',
 'Test category'					=>	'Test category',
 'Test forum'					=>	'Test forum',
diff --git a/lang/English/mail_templates/activate_email.tpl b/lang/English/mail_templates/activate_email.tpl
index f17066f..49095c0 100644
--- a/lang/English/mail_templates/activate_email.tpl
+++ b/lang/English/mail_templates/activate_email.tpl
@@ -8,5 +8,5 @@ To change your email address, please visit the following page:
 <activation_url>
 
 --
-<board_mailer>
+<board_mailer> Mailer
 (Do not reply to this message)
diff --git a/lang/English/mail_templates/activate_password.tpl b/lang/English/mail_templates/activate_password.tpl
index 33b2b3e..b408927 100644
--- a/lang/English/mail_templates/activate_password.tpl
+++ b/lang/English/mail_templates/activate_password.tpl
@@ -10,5 +10,5 @@ To change your password, please visit the following page:
 <activation_url>
 
 --
-<board_mailer>
+<board_mailer> Mailer
 (Do not reply to this message)
diff --git a/lang/English/mail_templates/banned_email_change.tpl b/lang/English/mail_templates/banned_email_change.tpl
new file mode 100644
index 0000000..276662f
--- /dev/null
+++ b/lang/English/mail_templates/banned_email_change.tpl
@@ -0,0 +1,9 @@
+Subject: Alert - Banned email detected
+
+User '<username>' changed to banned email address: <email>
+
+User profile: <profile_url>
+
+--
+<board_mailer> Mailer
+(Do not reply to this message)
diff --git a/lang/English/mail_templates/banned_email_post.tpl b/lang/English/mail_templates/banned_email_post.tpl
new file mode 100644
index 0000000..f7e0243
--- /dev/null
+++ b/lang/English/mail_templates/banned_email_post.tpl
@@ -0,0 +1,9 @@
+Subject: Alert - Banned email detected
+
+User '<username>' posted with banned email address: <email>
+
+Post URL: <post_url>
+
+--
+<board_mailer> Mailer
+(Do not reply to this message)
diff --git a/lang/English/mail_templates/banned_email_register.tpl b/lang/English/mail_templates/banned_email_register.tpl
new file mode 100644
index 0000000..f0085ec
--- /dev/null
+++ b/lang/English/mail_templates/banned_email_register.tpl
@@ -0,0 +1,9 @@
+Subject: Alert - Banned email detected
+
+User '<username>' registered with banned email address: <email>
+
+User profile: <profile_url>
+
+--
+<board_mailer> Mailer
+(Do not reply to this message)
diff --git a/lang/English/mail_templates/dupe_email_change.tpl b/lang/English/mail_templates/dupe_email_change.tpl
new file mode 100644
index 0000000..583fb24
--- /dev/null
+++ b/lang/English/mail_templates/dupe_email_change.tpl
@@ -0,0 +1,9 @@
+Subject: Alert - Duplicate email detected
+
+User '<username>' changed to an email address that also belongs to: <dupe_list>
+
+User profile: <profile_url>
+
+--
+<board_mailer> Mailer
+(Do not reply to this message)
diff --git a/lang/English/mail_templates/dupe_email_register.tpl b/lang/English/mail_templates/dupe_email_register.tpl
new file mode 100644
index 0000000..b1cb363
--- /dev/null
+++ b/lang/English/mail_templates/dupe_email_register.tpl
@@ -0,0 +1,9 @@
+Subject: Alert - Duplicate email detected
+
+User '<username>' registered with an email address that also belongs to: <dupe_list>
+
+User profile: <profile_url>
+
+--
+<board_mailer> Mailer
+(Do not reply to this message)
diff --git a/lang/English/mail_templates/form_email.tpl b/lang/English/mail_templates/form_email.tpl
index b862422..e3e0d5f 100644
--- a/lang/English/mail_templates/form_email.tpl
+++ b/lang/English/mail_templates/form_email.tpl
@@ -10,4 +10,4 @@ The message reads as follows:
 -----------------------------------------------------------------------
 
 --
-<board_mailer>
+<board_mailer> Mailer
diff --git a/lang/English/mail_templates/new_reply.tpl b/lang/English/mail_templates/new_reply.tpl
index b423bf5..286914e 100644
--- a/lang/English/mail_templates/new_reply.tpl
+++ b/lang/English/mail_templates/new_reply.tpl
@@ -7,5 +7,5 @@ The post is located at <post_url>
 You can unsubscribe by going to <unsubscribe_url>
 
 --
-<board_mailer>
+<board_mailer> Mailer
 (Do not reply to this message)
diff --git a/lang/English/mail_templates/new_reply_full.tpl b/lang/English/mail_templates/new_reply_full.tpl
index 99371c2..4fbc777 100644
--- a/lang/English/mail_templates/new_reply_full.tpl
+++ b/lang/English/mail_templates/new_reply_full.tpl
@@ -2,6 +2,8 @@ Subject: Reply to topic: '<topic_subject>'
 
 <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.
 
+The post is located at <post_url>
+
 The message reads as follows:
 -----------------------------------------------------------------------
 
@@ -9,10 +11,8 @@ The message reads as follows:
 
 -----------------------------------------------------------------------
 
-The post is located at <post_url>
-
 You can unsubscribe by going to <unsubscribe_url>
 
 --
-<board_mailer>
+<board_mailer> Mailer
 (Do not reply to this message)
diff --git a/lang/English/mail_templates/new_report.tpl b/lang/English/mail_templates/new_report.tpl
new file mode 100644
index 0000000..dedb113
--- /dev/null
+++ b/lang/English/mail_templates/new_report.tpl
@@ -0,0 +1,9 @@
+Subject: Report(<forum_id>) - '<topic_subject>'
+
+User '<username>' has reported the following message: <post_url>
+
+Reason: <reason>
+
+--
+<board_mailer> Mailer
+(Do not reply to this message)
diff --git a/lang/English/mail_templates/new_topic.tpl b/lang/English/mail_templates/new_topic.tpl
index ae5e927..2bc048b 100644
--- a/lang/English/mail_templates/new_topic.tpl
+++ b/lang/English/mail_templates/new_topic.tpl
@@ -7,5 +7,5 @@ The topic is located at <topic_url>
 You can unsubscribe by going to <unsubscribe_url>
 
 --
-<board_mailer>
+<board_mailer> Mailer
 (Do not reply to this message)
diff --git a/lang/English/mail_templates/new_topic_full.tpl b/lang/English/mail_templates/new_topic_full.tpl
index 25afd49..f70c726 100644
--- a/lang/English/mail_templates/new_topic_full.tpl
+++ b/lang/English/mail_templates/new_topic_full.tpl
@@ -2,6 +2,8 @@ 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>
+
 The message reads as follows:
 -----------------------------------------------------------------------
 
@@ -9,10 +11,8 @@ The message reads as follows:
 
 -----------------------------------------------------------------------
 
-The topic is located at <topic_url>
-
 You can unsubscribe by going to <unsubscribe_url>
 
 --
-<board_mailer>
+<board_mailer> Mailer
 (Do not reply to this message)
diff --git a/lang/English/mail_templates/new_user.tpl b/lang/English/mail_templates/new_user.tpl
new file mode 100644
index 0000000..5725b1c
--- /dev/null
+++ b/lang/English/mail_templates/new_user.tpl
@@ -0,0 +1,9 @@
+Subject: Alert - New registration
+
+User '<username>' registered in the forums at <base_url>
+
+User profile: <profile_url>
+
+--
+<board_mailer> Mailer
+(Do not reply to this message)
diff --git a/lang/English/mail_templates/rename.tpl b/lang/English/mail_templates/rename.tpl
index f1ea88c..3cba50d 100644
--- a/lang/English/mail_templates/rename.tpl
+++ b/lang/English/mail_templates/rename.tpl
@@ -8,5 +8,5 @@ New username: <new_username>
 We apologise for any inconvenience caused.
 
 --
-<board_mailer>
+<board_mailer> Mailer
 (Do not reply to this message)
diff --git a/lang/English/mail_templates/welcome.tpl b/lang/English/mail_templates/welcome.tpl
index 0110042..779a574 100644
--- a/lang/English/mail_templates/welcome.tpl
+++ b/lang/English/mail_templates/welcome.tpl
@@ -8,5 +8,5 @@ Password: <password>
 Login at <login_url> to activate the account.
 
 --
-<board_mailer>
+<board_mailer> Mailer
 (Do not reply to this message)
diff --git a/lang/English/post.php b/lang/English/post.php
index 2b3c678..b89804b 100644
--- a/lang/English/post.php
+++ b/lang/English/post.php
@@ -9,7 +9,7 @@ $lang_post = array(
 'Too long subject'	=>	'Subjects cannot be longer than 70 characters.',
 'No message'		=>	'You must enter a message.',
 'No message after censoring'	=>	'You must enter a message. After applying censoring filters, your message was empty.',
-'Too long message'	=>	'Posts cannot be longer that %s bytes.',
+'Too long message'	=>	'Posts cannot be longer than %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.',
diff --git a/lang/English/prof_reg.php b/lang/English/prof_reg.php
index 1cbbec6..7a0a6f6 100644
--- a/lang/English/prof_reg.php
+++ b/lang/English/prof_reg.php
@@ -53,27 +53,27 @@ $lang_prof_reg = array(
 'UTC'						=>	'(UTC) Western European, Greenwich',
 'UTC+01:00'					=>	'(UTC+01:00) Central European, West African',
 'UTC+02:00'					=>	'(UTC+02:00) Eastern European, Central African',
-'UTC+03:00'					=>	'(UTC+03:00) Moscow, Eastern African',
+'UTC+03:00'					=>	'(UTC+03:00) Eastern African',
 'UTC+03:30'					=>	'(UTC+03:30) Iran',
-'UTC+04:00'					=>	'(UTC+04:00) Gulf, Samara',
+'UTC+04:00'					=>	'(UTC+04:00) Moscow, Gulf, Samara',
 'UTC+04:30'					=>	'(UTC+04:30) Afghanistan',
-'UTC+05:00'					=>	'(UTC+05:00) Pakistan, Yekaterinburg',
+'UTC+05:00'					=>	'(UTC+05:00) Pakistan',
 'UTC+05:30'					=>	'(UTC+05:30) India, Sri Lanka',
 'UTC+05:45'					=>	'(UTC+05:45) Nepal',
-'UTC+06:00'					=>	'(UTC+06:00) Bangladesh, Bhutan, Novosibirsk',
+'UTC+06:00'					=>	'(UTC+06:00) Bangladesh, Bhutan, Yekaterinburg',
 'UTC+06:30'					=>	'(UTC+06:30) Cocos Islands, Myanmar',
-'UTC+07:00'					=>	'(UTC+07:00) Indochina, Krasnoyarsk',
-'UTC+08:00'					=>	'(UTC+08:00) Greater China, Australian Western, Irkutsk',
+'UTC+07:00'					=>	'(UTC+07:00) Indochina, Novosibirsk',
+'UTC+08:00'					=>	'(UTC+08:00) Greater China, Australian Western, Krasnoyarsk',
 'UTC+08:45'					=>	'(UTC+08:45) Southeastern Western Australia',
-'UTC+09:00'					=>	'(UTC+09:00) Japan, Korea, Chita',
+'UTC+09:00'					=>	'(UTC+09:00) Japan, Korea, Chita, Irkutsk',
 'UTC+09:30'					=>	'(UTC+09:30) Australian Central',
-'UTC+10:00'					=>	'(UTC+10:00) Australian Eastern, Vladivostok',
+'UTC+10:00'					=>	'(UTC+10:00) Australian Eastern',
 'UTC+10:30'					=>	'(UTC+10:30) Lord Howe',
-'UTC+11:00'					=>	'(UTC+11:00) Solomon Island, Magadan',
+'UTC+11:00'					=>	'(UTC+11:00) Solomon Island, Vladivostok',
 'UTC+11:30'					=>	'(UTC+11:30) Norfolk Island',
-'UTC+12:00'					=>	'(UTC+12:00) New Zealand, Fiji, Kamchatka',
+'UTC+12:00'					=>	'(UTC+12:00) New Zealand, Fiji, Magadan',
 'UTC+12:45'					=>	'(UTC+12:45) Chatham Islands',
-'UTC+13:00'					=>	'(UTC+13:00) Tonga, Phoenix Islands',
+'UTC+13:00'					=>	'(UTC+13:00) Tonga, Phoenix Islands, Kamchatka',
 'UTC+14:00'					=>	'(UTC+14:00) Line Islands'
 
 );
diff --git a/lang/English/profile.php b/lang/English/profile.php
index b1aa3ab..5687e35 100644
--- a/lang/English/profile.php
+++ b/lang/English/profile.php
@@ -81,6 +81,7 @@ $lang_profile = array(
 'Realname'						=>	'Real name',
 'Location'						=>	'Location',
 'Website'						=>	'Website',
+'Invalid website URL'					=>	'The website URL you entered is invalid.',
 'Jabber'						=>	'Jabber',
 'ICQ'							=>	'ICQ',
 'MSN'							=>	'MSN Messenger',
diff --git a/lang/English/search.php b/lang/English/search.php
index 1fe47cf..b0f7fba 100644
--- a/lang/English/search.php
+++ b/lang/English/search.php
@@ -14,6 +14,7 @@ $lang_search = array(
 'Author search'						=>	'Author search',
 'Search in legend'					=>	'Select where to search',
 'Search in info'					=>	'Choose in which forum you would like to search and if you want to search in topic subjects, message text or both.',
+'Search multiple forums info'		=>	'If no forums are selected, all forums will be searched.',
 'Forum search'						=>	'Forum',
 'All forums'						=>	'All forums',
 'Search in'							=>	'Search in',
@@ -35,22 +36,21 @@ $lang_search = array(
 'Show as posts'						=>	'Posts',
 
 // Results
+'Search'							=>	'Search',
 'Search results'					=>	'Search results',
-'Search topics'						=>	'Search topics',
-'Search posts'						=>	'Search posts',
 'Quick search show_new'				=>	'New',
 'Quick search show_recent'			=>	'Active',
 'Quick search show_unanswered'		=>	'Unanswered',
 'Quick search show_replies'			=>	'Posted',
-'Quick search show_user_topics'		=>	'By %s',
-'Quick search show_user_posts'		=>	'By %s',
+'Quick search show_user_topics'		=>	'Topics by %s',
+'Quick search show_user_posts'		=>	'Posts by %s',
 'Quick search show_subscriptions'	=>	'Subscribed by %s',
-'By keywords show as topics'		=>	'With posts with keywords \'%s\'',
-'By keywords show as posts'			=>	'With keywords \'%s\'',
-'By user show as topics'			=>	'With posts by %s',
-'By user show as posts'				=>	'By %s',
-'By both show as topics'			=>	'With keywords \'%s\' in posts by %s',
-'By both show as posts'				=>	'With keywords \'%s\' by %s',
+'By keywords show as topics'		=>	'Topics with posts containing \'%s\'',
+'By keywords show as posts'			=>	'Posts containing \'%s\'',
+'By user show as topics'			=>	'Topics with posts by %s',
+'By user show as posts'				=>	'Posts by %s',
+'By both show as topics'			=>	'Topics with posts containing \'%s\', by %s',
+'By both show as posts'				=>	'Posts containing \'%s\', by %s',
 '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.',
diff --git a/lang/English/update.php b/lang/English/update.php
index d4e64b6..437e7fe 100644
--- a/lang/English/update.php
+++ b/lang/English/update.php
@@ -12,6 +12,9 @@ $lang_update = array(
 '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',
+'Maintenance'					=>	'Maintenance',
+'Maintenance message info'		=>	'The message that will be displayed to users during the updating process. This text will not be parsed like regular posts and thus may contain HTML.',
+'Maintenance message'		    =>	'Maintenance message',
 '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.',
@@ -70,8 +73,4 @@ $lang_update = array(
 '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 efcd94d..0497ad6 100644
--- a/login.php
+++ b/login.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -122,7 +122,7 @@ else if ($action == 'forget' || $action == 'forget_2')
 		require PUN_ROOT.'include/email.php';
 
 		// Validate the email address
-		$email = strtolower(trim($_POST['req_email']));
+		$email = strtolower(pun_trim($_POST['req_email']));
 		if (!is_valid_email($email))
 			$errors[] = $lang_common['Invalid email'];
 
@@ -143,7 +143,7 @@ else if ($action == 'forget' || $action == 'forget_2')
 
 				// Do the generic replacements first (they apply to all emails sent out here)
 				$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);
+				$mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'], $mail_message);
 
 				// Loop through users we found
 				while ($cur_hit = $db->fetch_assoc($result))
@@ -251,7 +251,7 @@ if (!empty($_SERVER['HTTP_REFERER']))
 	if (!isset($valid['path']))
 		$valid['path'] = '';
 
-	if ($referrer['host'] == $valid['host'] && preg_match('#^'.preg_quote($valid['path']).'/(.*?)\.php#i', $referrer['path']))
+	if ($referrer['host'] == $valid['host'] && preg_match('%^'.preg_quote($valid['path'], '%').'/(.*?)\.php%i', $referrer['path']))
 		$redirect_url = $_SERVER['HTTP_REFERER'];
 }
 
@@ -283,11 +283,11 @@ require PUN_ROOT.'header.php';
 						</div>
 
 						<p class="clearb"><?php echo $lang_login['Login info'] ?></p>
-						<p class="actions"><span><a href="register.php" tabindex="4"><?php echo $lang_login['Not registered'] ?></a></span> <span><a href="login.php?action=forget" tabindex="5"><?php echo $lang_login['Forgotten pass'] ?></a></span></p>
+						<p class="actions"><span><a href="register.php" tabindex="5"><?php echo $lang_login['Not registered'] ?></a></span> <span><a href="login.php?action=forget" tabindex="6"><?php echo $lang_login['Forgotten pass'] ?></a></span></p>
 					</div>
 				</fieldset>
 			</div>
-			<p class="buttons"><input type="submit" name="login" value="<?php echo $lang_common['Login'] ?>" tabindex="3" /></p>
+			<p class="buttons"><input type="submit" name="login" value="<?php echo $lang_common['Login'] ?>" tabindex="4" /></p>
 		</form>
 	</div>
 </div>
diff --git a/misc.php b/misc.php
index 8468313..46d2707 100644
--- a/misc.php
+++ b/misc.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -49,7 +49,7 @@ if ($action == 'rules')
 else if ($action == 'markread')
 {
 	if ($pun_user['is_guest'])
-		message($lang_common['No permission']);
+		message($lang_common['No permission'], false, '403 Forbidden');
 
 	$db->query('UPDATE '.$db->prefix.'users SET last_visit='.$pun_user['logged'].' WHERE id='.$pun_user['id']) or error('Unable to update user last visit data', __FILE__, __LINE__, $db->error());
 
@@ -64,7 +64,7 @@ else if ($action == 'markread')
 else if ($action == 'markforumread')
 {
 	if ($pun_user['is_guest'])
-		message($lang_common['No permission']);
+		message($lang_common['No permission'], false, '403 Forbidden');
 
 	$fid = isset($_GET['fid']) ? intval($_GET['fid']) : 0;
 	if ($fid < 1)
@@ -81,7 +81,7 @@ else if ($action == 'markforumread')
 else if (isset($_GET['email']))
 {
 	if ($pun_user['is_guest'] || $pun_user['g_send_email'] == '0')
-		message($lang_common['No permission']);
+		message($lang_common['No permission'], false, '403 Forbidden');
 
 	$recipient_id = intval($_GET['email']);
 	if ($recipient_id < 2)
@@ -125,7 +125,7 @@ else if (isset($_GET['email']))
 		$mail_message = str_replace('<sender>', $pun_user['username'], $mail_message);
 		$mail_message = str_replace('<board_title>', $pun_config['o_board_title'], $mail_message);
 		$mail_message = str_replace('<mail_message>', $message, $mail_message);
-		$mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'].' '.$lang_common['Mailer'], $mail_message);
+		$mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'], $mail_message);
 
 		require_once PUN_ROOT.'include/email.php';
 
@@ -145,12 +145,20 @@ else if (isset($_GET['email']))
 		if (strpos($referrer['host'], 'www.') === 0)
 			$referrer['host'] = substr($referrer['host'], 4);
 
+		// Make sure the path component exists
+		if (!isset($referrer['path']))
+			$referrer['path'] = '';
+
 		$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']))
+		// Make sure the path component exists
+		if (!isset($valid['path']))
+			$valid['path'] = '';
+
+		if ($referrer['host'] == $valid['host'] && preg_match('%^'.preg_quote($valid['path'], '%').'/(.*?)\.php%i', $referrer['path']))
 			$redirect_url = $_SERVER['HTTP_REFERER'];
 	}
 
@@ -210,8 +218,8 @@ else if (isset($_GET['report']))
 		else if (strlen($reason) > 65535) // TEXT field can only hold 65535 bytes
 			message($lang_misc['Reason too long']);
 
-		if ($pun_user['last_email_sent'] != '' && (time() - $pun_user['last_email_sent']) < $pun_user['g_email_flood'] && (time() - $pun_user['last_email_sent']) >= 0)
-			message(sprintf($lang_misc['Report flood'], $pun_user['g_email_flood']));
+		if ($pun_user['last_report_sent'] != '' && (time() - $pun_user['last_report_sent']) < $pun_user['g_report_flood'] && (time() - $pun_user['last_report_sent']) >= 0)
+			message(sprintf($lang_misc['Report flood'], $pun_user['g_report_flood']));
 
 		// Get the topic ID
 		$result = $db->query('SELECT topic_id FROM '.$db->prefix.'posts WHERE id='.$post_id) or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
@@ -237,10 +245,20 @@ else if (isset($_GET['report']))
 			// We send it to the complete mailing-list in one swoop
 			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'], 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'];
+				// Load the "new report" template
+				$mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$pun_user['language'].'/mail_templates/new_report.tpl'));
+
+				// The first row contains the 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));
+
+				$mail_subject = str_replace('<forum_id>', $forum_id, $mail_subject);
+				$mail_subject = str_replace('<topic_subject>', $subject, $mail_subject);
+				$mail_message = str_replace('<username>', $pun_user['username'], $mail_message);
+				$mail_message = str_replace('<post_url>', get_base_url().'/viewtopic.php?pid='.$post_id.'#p'.$post_id, $mail_message);
+				$mail_message = str_replace('<reason>', $reason, $mail_message);
+				$mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'], $mail_message);
 
 				require PUN_ROOT.'include/email.php';
 
@@ -248,7 +266,7 @@ else if (isset($_GET['report']))
 			}
 		}
 
-		$db->query('UPDATE '.$db->prefix.'users SET last_email_sent='.time().' WHERE id='.$pun_user['id']) or error('Unable to update user', __FILE__, __LINE__, $db->error());
+		$db->query('UPDATE '.$db->prefix.'users SET last_report_sent='.time().' WHERE id='.$pun_user['id']) or error('Unable to update user', __FILE__, __LINE__, $db->error());
 
 		redirect('viewtopic.php?pid='.$post_id.'#p'.$post_id, $lang_misc['Report redirect']);
 	}
diff --git a/moderate.php b/moderate.php
index 77b45e2..4555e31 100644
--- a/moderate.php
+++ b/moderate.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -15,10 +15,10 @@ require PUN_ROOT.'include/common.php';
 if (isset($_GET['get_host']))
 {
 	if (!$pun_user['is_admmod'])
-		message($lang_common['No permission']);
+		message($lang_common['No permission'], false, '403 Forbidden');
 
 	// Is get_host an IP address or a post ID?
-	if (@preg_match('/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', $_GET['get_host']) || @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}:))$/', $_GET['get_host']))
+	if (@preg_match('%^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$%', $_GET['get_host']) || @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}:))$%', $_GET['get_host']))
 		$ip = $_GET['get_host'];
 	else
 	{
@@ -28,7 +28,7 @@ if (isset($_GET['get_host']))
 
 		$result = $db->query('SELECT poster_ip FROM '.$db->prefix.'posts WHERE id='.$get_host) or error('Unable to fetch post IP address', __FILE__, __LINE__, $db->error());
 		if (!$db->num_rows($result))
-			message($lang_common['Bad request']);
+			message($lang_common['Bad request'], false, '404 Not Found');
 
 		$ip = $db->result($result);
 	}
@@ -43,7 +43,7 @@ if (isset($_GET['get_host']))
 // All other functions require moderator/admin access
 $fid = isset($_GET['fid']) ? intval($_GET['fid']) : 0;
 if ($fid < 1)
-	message($lang_common['Bad request']);
+	message($lang_common['Bad request'], false, '404 Not Found');
 
 $result = $db->query('SELECT moderators FROM '.$db->prefix.'forums WHERE id='.$fid) or error('Unable to fetch forum info', __FILE__, __LINE__, $db->error());
 
@@ -51,7 +51,7 @@ $moderators = $db->result($result);
 $mods_array = ($moderators != '') ? unserialize($moderators) : array();
 
 if ($pun_user['g_id'] != PUN_ADMIN && ($pun_user['g_moderator'] == '0' || !array_key_exists($pun_user['username'], $mods_array)))
-	message($lang_common['No permission']);
+	message($lang_common['No permission'], false, '403 Forbidden');
 
 // Get topic/forum tracking data
 if (!$pun_user['is_guest'])
@@ -66,12 +66,12 @@ if (isset($_GET['tid']))
 {
 	$tid = intval($_GET['tid']);
 	if ($tid < 1)
-		message($lang_common['Bad request']);
+		message($lang_common['Bad request'], false, '404 Not Found');
 
 	// 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.'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']);
+		message($lang_common['Bad request'], false, '404 Not Found');
 
 	$cur_topic = $db->fetch_assoc($result);
 
@@ -86,7 +86,7 @@ if (isset($_GET['tid']))
 		{
 			confirm_referrer('moderate.php');
 
-			if (@preg_match('/[^0-9,]/', $posts))
+			if (@preg_match('%[^0-9,]%', $posts))
 				message($lang_common['Bad request']);
 
 			// Verify that the post IDs are valid
@@ -153,7 +153,7 @@ if (isset($_GET['tid']))
 		{
 			confirm_referrer('moderate.php');
 
-			if (@preg_match('/[^0-9,]/', $posts))
+			if (@preg_match('%[^0-9,]%', $posts))
 				message($lang_common['Bad request']);
 
 			$move_to_forum = isset($_POST['move_to_forum']) ? intval($_POST['move_to_forum']) : 0;
@@ -195,6 +195,9 @@ if (isset($_GET['tid']))
 			// Move the posts to the new topic
 			$db->query('UPDATE '.$db->prefix.'posts SET topic_id='.$new_tid.' WHERE id IN('.$posts.')') or error('Unable to move posts into new topic', __FILE__, __LINE__, $db->error());
 
+			// Apply every subscription to both topics
+			$db->query('INSERT INTO '.$db->prefix.'topic_subscriptions (user_id, topic_id) SELECT user_id, '.$new_tid.' FROM '.$db->prefix.'topic_subscriptions WHERE topic_id='.$tid) or error('Unable to copy existing subscriptions', __FILE__, __LINE__, $db->error());
+
 			// Get last_post, last_post_id, and last_poster from the topic and update it
 			$result = $db->query('SELECT id, poster, posted FROM '.$db->prefix.'posts WHERE topic_id='.$tid.' ORDER BY id DESC LIMIT 1') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
 			$last_post_data = $db->fetch_assoc($result);
@@ -420,7 +423,7 @@ if (isset($_REQUEST['move_topics']) || isset($_POST['move_topics_to']))
 	{
 		confirm_referrer('moderate.php');
 
-		if (@preg_match('/[^0-9,]/', $_POST['topics']))
+		if (@preg_match('%[^0-9,]%', $_POST['topics']))
 			message($lang_common['Bad request']);
 
 		$topics = explode(',', $_POST['topics']);
@@ -548,21 +551,19 @@ else if (isset($_POST['merge_topics']) || isset($_POST['merge_topics_comply']))
 	{
 		confirm_referrer('moderate.php');
 
-		if (@preg_match('/[^0-9,]/', $_POST['topics']))
+		if (@preg_match('%[^0-9,]%', $_POST['topics']))
 			message($lang_common['Bad request']);
 
 		$topics = explode(',', $_POST['topics']);
 		if (count($topics) < 2)
 			message($lang_misc['Not enough topics selected']);
 
-		// Verify that the topic IDs are valid (moved topics can not be merged?)
-		// $result = $db->query('SELECT 1 FROM '.$db->prefix.'topics WHERE id IN('.implode(',', $topics).') AND moved_to IS NULL AND forum_id='.$fid) or error('Unable to check topics', __FILE__, __LINE__, $db->error());
-		$result = $db->query('SELECT 1 FROM '.$db->prefix.'topics WHERE id IN('.implode(',', $topics).') AND forum_id='.$fid) or error('Unable to check topics', __FILE__, __LINE__, $db->error());
+		// Verify that the topic IDs are valid (redirect links will point to the merged topic after the merge)
+		$result = $db->query('SELECT id FROM '.$db->prefix.'topics WHERE id IN('.implode(',', $topics).') AND forum_id='.$fid.' ORDER BY id ASC') or error('Unable to check topics', __FILE__, __LINE__, $db->error());
 		if ($db->num_rows($result) != count($topics))
 			message($lang_common['Bad request']);
 
-		// Fetch the topic that we're merging into
-		$result = $db->query('SELECT MIN(t.id) FROM '.$db->prefix.'topics AS t WHERE t.id IN('.implode(',', $topics).')') or error('Unable to get topic', __FILE__, __LINE__, $db->error());
+		// The topic that we are merging into is the one with the smallest ID
 		$merge_to_tid = $db->result($result);
 
 		// Make any redirect topics point to our new, merged topic
@@ -577,8 +578,18 @@ else if (isset($_POST['merge_topics']) || isset($_POST['merge_topics_comply']))
 		// Merge the posts into the topic
 		$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.'topic_subscriptions WHERE topic_id IN('.implode(',', $topics).') AND topic_id != '.$merge_to_tid) or error('Unable to delete subscriptions', __FILE__, __LINE__, $db->error());
+		// Update any subscriptions
+		$result = $db->query('SELECT user_id FROM '.$db->prefix.'topic_subscriptions WHERE topic_id IN ('.implode(',', $topics).')') or error('Unable to fetch subscriptions of merged topics', __FILE__, __LINE__, $db->error());
+
+		$subscribed_users = array();
+		while ($cur_user_id = $db->result($result))
+			$subscribed_users[] = $cur_user_id;
+		$subscribed_users = array_unique($subscribed_users);
+
+		$db->query('DELETE FROM '.$db->prefix.'topic_subscriptions WHERE topic_id IN ('.implode(',', $topics).')') or error('Unable to delete subscriptions of merged topics', __FILE__, __LINE__, $db->error());
+
+		foreach ($subscribed_users as $cur_user_id)
+			$db->query('INSERT INTO '.$db->prefix.'topic_subscriptions (topic_id, user_id) VALUES ('.$merge_to_tid.', '.$cur_user_id.')') or error('Unable to re-enter subscriptions for merge topic', __FILE__, __LINE__, $db->error());
 
 		// Without redirection the old topics are removed
 		if (!isset($_POST['with_redirect']))
@@ -644,7 +655,7 @@ else if (isset($_POST['delete_topics']) || isset($_POST['delete_topics_comply'])
 	{
 		confirm_referrer('moderate.php');
 
-		if (@preg_match('/[^0-9,]/', $topics))
+		if (@preg_match('%[^0-9,]%', $topics))
 			message($lang_common['Bad request']);
 
 		require PUN_ROOT.'include/search_idx.php';
@@ -783,7 +794,7 @@ 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.num_topics, f.sort_by 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());
 if (!$db->num_rows($result))
-	message($lang_common['Bad request']);
+	message($lang_common['Bad request'], false, '404 Not Found');
 
 $cur_forum = $db->fetch_assoc($result);
 
@@ -877,7 +888,7 @@ if ($db->num_rows($result))
 		$item_status = ($topic_count % 2 == 0) ? 'roweven' : 'rowodd';
 		$icon_type = 'icon';
 
-		if ($cur_topic['moved_to'] == null)
+		if (is_null($cur_topic['moved_to']))
 		{
 			$last_post = '<a href="viewtopic.php?pid='.$cur_topic['last_post_id'].'#p'.$cur_topic['last_post_id'].'">'.format_time($cur_topic['last_post']).'</a> <span class="byuser">'.$lang_common['by'].' '.pun_htmlspecialchars($cur_topic['last_poster']).'</span>';
 			$ghost_topic = false;
diff --git a/post.php b/post.php
index c849b14..a01fc1c 100644
--- a/post.php
+++ b/post.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -11,13 +11,13 @@ require PUN_ROOT.'include/common.php';
 
 
 if ($pun_user['g_read_board'] == '0')
-	message($lang_common['No view']);
+	message($lang_common['No view'], false, '403 Forbidden');
 
 
 $tid = isset($_GET['tid']) ? intval($_GET['tid']) : 0;
 $fid = isset($_GET['fid']) ? intval($_GET['fid']) : 0;
 if ($tid < 1 && $fid < 1 || $tid > 0 && $fid > 0)
-	message($lang_common['Bad request']);
+	message($lang_common['Bad request'], false, '404 Not Found');
 
 // Fetch some info about the topic and/or the forum
 if ($tid)
@@ -26,7 +26,7 @@ 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());
 
 if (!$db->num_rows($result))
-	message($lang_common['Bad request']);
+	message($lang_common['Bad request'], false, '404 Not Found');
 
 $cur_posting = $db->fetch_assoc($result);
 $is_subscribed = $tid && $cur_posting['is_subscribed'];
@@ -47,7 +47,7 @@ if ((($tid && (($cur_posting['post_replies'] == '' && $pun_user['g_post_replies'
 	($fid && (($cur_posting['post_topics'] == '' && $pun_user['g_post_topics'] == '0') || $cur_posting['post_topics'] == '0')) ||
 	(isset($cur_posting['closed']) && $cur_posting['closed'] == '1')) &&
 	!$is_admmod)
-	message($lang_common['No permission']);
+	message($lang_common['No permission'], false, '403 Forbidden');
 
 // Load the post.php language file
 require PUN_ROOT.'lang/'.$pun_user['language'].'/post.php';
@@ -59,10 +59,6 @@ $errors = array();
 // Did someone just hit "Submit" or "Preview"?
 if (isset($_POST['form_sent']))
 {
-	// Make sure form_user is correct
-	if (($pun_user['is_guest'] && $_POST['form_user'] != 'Guest') || (!$pun_user['is_guest'] && $_POST['form_user'] != $pun_user['username']))
-		message($lang_common['Bad request']);
-
 	// Flood protection
 	if (!isset($_POST['preview']) && $pun_user['last_post'] != '' && (time() - $pun_user['last_post']) < $pun_user['g_post_flood'])
 		$errors[] = $lang_post['Flood start'].' '.$pun_user['g_post_flood'].' '.$lang_post['flood end'];
@@ -95,9 +91,10 @@ if (isset($_POST['form_sent']))
 	else
 	{
 		$username = pun_trim($_POST['req_username']);
-		$email = strtolower(trim(($pun_config['p_force_guest_email'] == '1') ? $_POST['req_email'] : $_POST['email']));
+		$email = strtolower(pun_trim(($pun_config['p_force_guest_email'] == '1') ? $_POST['req_email'] : $_POST['email']));
+		$banned_email = false;
 
-		// Load the register.php/profile.php language files
+		// Load the register.php/prof_reg.php language files
 		require PUN_ROOT.'lang/'.$pun_user['language'].'/prof_reg.php';
 		require PUN_ROOT.'lang/'.$pun_user['language'].'/register.php';
 
@@ -119,8 +116,6 @@ if (isset($_POST['form_sent']))
 
 				$banned_email = true; // Used later when we send an alert email
 			}
-			else
-				$banned_email = false;
 		}
 	}
 
@@ -157,6 +152,9 @@ if (isset($_POST['form_sent']))
 	$hide_smilies = isset($_POST['hide_smilies']) ? '1' : '0';
 	$subscribe = isset($_POST['subscribe']) ? '1' : '0';
 	$stick_topic = isset($_POST['stick_topic']) && $is_admmod ? '1' : '0';
+	
+	// Replace four-byte characters (MySQL cannot handle them)
+	$message = strip_bad_multibyte_chars($message);
 
 	$now = time();
 
@@ -188,17 +186,13 @@ if (isset($_POST['form_sent']))
 			else
 			{
 				// It's a guest. Insert the new post
-				$email_sql = ($pun_config['p_force_guest_email'] == '1' || $email != '') ? '\''.$email.'\'' : 'NULL';
+				$email_sql = ($pun_config['p_force_guest_email'] == '1' || $email != '') ? '\''.$db->escape($email).'\'' : 'NULL';
 				$db->query('INSERT INTO '.$db->prefix.'posts (poster, poster_ip, poster_email, message, hide_smilies, posted, topic_id) VALUES(\''.$db->escape($username).'\', \''.get_remote_address().'\', '.$email_sql.', \''.$db->escape($message).'\', '.$hide_smilies.', '.$now.', '.$tid.')') or error('Unable to create post', __FILE__, __LINE__, $db->error());
 				$new_pid = $db->insert_id();
 			}
 
-			// Count number of replies in the topic
-			$result = $db->query('SELECT COUNT(id) FROM '.$db->prefix.'posts WHERE topic_id='.$tid) or error('Unable to fetch post count for topic', __FILE__, __LINE__, $db->error());
-			$num_replies = $db->result($result, 0) - 1;
-
 			// Update topic
-			$db->query('UPDATE '.$db->prefix.'topics SET num_replies='.$num_replies.', last_post='.$now.', last_post_id='.$new_pid.', last_poster=\''.$db->escape($username).'\' WHERE id='.$tid) or error('Unable to update topic', __FILE__, __LINE__, $db->error());
+			$db->query('UPDATE '.$db->prefix.'topics SET num_replies=num_replies+1, last_post='.$now.', last_post_id='.$new_pid.', last_poster=\''.$db->escape($username).'\' WHERE id='.$tid) or error('Unable to update topic', __FILE__, __LINE__, $db->error());
 
 			update_search_index('post', $new_pid, $message);
 
@@ -219,6 +213,11 @@ if (isset($_POST['form_sent']))
 
 					$notification_emails = array();
 
+					if ($pun_config['o_censoring'] == '1')
+						$cleaned_message = bbcode2email($censored_message, -1);
+					else
+						$cleaned_message = bbcode2email($message, -1);
+
 					// Loop through subscribed users and send emails
 					while ($cur_subscriber = $db->fetch_assoc($result))
 					{
@@ -247,15 +246,15 @@ if (isset($_POST['form_sent']))
 								$mail_message = str_replace('<replier>', $username, $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_message = str_replace('<board_mailer>', $pun_config['o_board_title'], $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>', $pun_config['o_censoring'] == '1' ? $censored_message : $message, $mail_message_full);
+								$mail_message_full = str_replace('<message>', $cleaned_message, $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);
+								$mail_message_full = str_replace('<board_mailer>', $pun_config['o_board_title'], $mail_message_full);
 
 								$notification_emails[$cur_subscriber['language']][0] = $mail_subject;
 								$notification_emails[$cur_subscriber['language']][1] = $mail_message;
@@ -275,6 +274,8 @@ if (isset($_POST['form_sent']))
 								pun_mail($cur_subscriber['email'], $notification_emails[$cur_subscriber['language']][2], $notification_emails[$cur_subscriber['language']][3]);
 						}
 					}
+
+					unset($cleaned_message);
 				}
 			}
 		}
@@ -297,7 +298,7 @@ if (isset($_POST['form_sent']))
 			else
 			{
 				// Create the post ("topic post")
-				$email_sql = ($pun_config['p_force_guest_email'] == '1' || $email != '') ? '\''.$email.'\'' : 'NULL';
+				$email_sql = ($pun_config['p_force_guest_email'] == '1' || $email != '') ? '\''.$db->escape($email).'\'' : 'NULL';
 				$db->query('INSERT INTO '.$db->prefix.'posts (poster, poster_ip, poster_email, message, hide_smilies, posted, topic_id) VALUES(\''.$db->escape($username).'\', \''.get_remote_address().'\', '.$email_sql.', \''.$db->escape($message).'\', '.$hide_smilies.', '.$now.', '.$new_tid.')') or error('Unable to create post', __FILE__, __LINE__, $db->error());
 			}
 			$new_pid = $db->insert_id();
@@ -320,6 +321,11 @@ if (isset($_POST['form_sent']))
 
 					$notification_emails = array();
 
+					if ($pun_config['o_censoring'] == '1')
+						$cleaned_message = bbcode2email($censored_message, -1);
+					else
+						$cleaned_message = bbcode2email($message, -1);
+
 					// Loop through subscribed users and send emails
 					while ($cur_subscriber = $db->fetch_assoc($result))
 					{
@@ -349,16 +355,16 @@ if (isset($_POST['form_sent']))
 								$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_message = str_replace('<board_mailer>', $pun_config['o_board_title'], $mail_message);
 
 								$mail_subject_full = str_replace('<forum_name>', $cur_posting['forum_name'], $mail_subject_full);
 								$mail_message_full = str_replace('<topic_subject>', $pun_config['o_censoring'] == '1' ? $censored_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>', $pun_config['o_censoring'] == '1' ? $censored_message : $message, $mail_message_full);
+								$mail_message_full = str_replace('<message>', $cleaned_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);
+								$mail_message_full = str_replace('<board_mailer>', $pun_config['o_board_title'], $mail_message_full);
 
 								$notification_emails[$cur_subscriber['language']][0] = $mail_subject;
 								$notification_emails[$cur_subscriber['language']][1] = $mail_message;
@@ -378,6 +384,8 @@ if (isset($_POST['form_sent']))
 								pun_mail($cur_subscriber['email'], $notification_emails[$cur_subscriber['language']][2], $notification_emails[$cur_subscriber['language']][3]);
 						}
 					}
+
+					unset($cleaned_message);
 				}
 			}
 		}
@@ -385,10 +393,18 @@ if (isset($_POST['form_sent']))
 		// If we previously found out that the email was banned
 		if ($pun_user['is_guest'] && $banned_email && $pun_config['o_mailing_list'] != '')
 		{
-			$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'], get_base_url().'/viewtopic.php?pid='.$new_pid.'#p'.$new_pid)."\n";
-			$mail_message .= "\n".'--'."\n".$lang_common['Email signature'];
+			// Load the "banned email post" template
+			$mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$pun_user['language'].'/mail_templates/banned_email_post.tpl'));
+
+			// The first row contains the 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));
+
+			$mail_message = str_replace('<username>', $username, $mail_message);
+			$mail_message = str_replace('<email>', $email, $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('<board_mailer>', $pun_config['o_board_title'], $mail_message);
 
 			pun_mail($pun_config['o_mailing_list'], $mail_subject, $mail_message);
 		}
@@ -423,21 +439,18 @@ if ($tid)
 	{
 		$qid = intval($_GET['qid']);
 		if ($qid < 1)
-			message($lang_common['Bad request']);
+			message($lang_common['Bad request'], false, '404 Not Found');
 
 		$result = $db->query('SELECT poster, message FROM '.$db->prefix.'posts WHERE id='.$qid.' AND topic_id='.$tid) or error('Unable to fetch quote info', __FILE__, __LINE__, $db->error());
 		if (!$db->num_rows($result))
-			message($lang_common['Bad request']);
+			message($lang_common['Bad request'], false, '404 Not Found');
 
 		list($q_poster, $q_message) = $db->fetch_row($result);
 
 		// 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]);
+			list($inside, $outside) = split_text($q_message, '[code]', '[/code]');
 
 			$q_message = implode("\1", $outside);
 		}
@@ -596,7 +609,6 @@ $cur_index = 1;
 					<legend><?php echo $lang_common['Write message legend'] ?></legend>
 					<div class="infldset txtarea">
 						<input type="hidden" name="form_sent" value="1" />
-						<input type="hidden" name="form_user" value="<?php echo (!$pun_user['is_guest']) ? pun_htmlspecialchars($pun_user['username']) : 'Guest'; ?>" />
 <?php
 
 if ($pun_user['is_guest'])
diff --git a/profile.php b/profile.php
index d9e9ae8..bd9b7b3 100644
--- a/profile.php
+++ b/profile.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -18,14 +18,14 @@ $action = isset($_GET['action']) ? $_GET['action'] : null;
 $section = isset($_GET['section']) ? $_GET['section'] : null;
 $id = isset($_GET['id']) ? intval($_GET['id']) : 0;
 if ($id < 2)
-	message($lang_common['Bad request']);
+	message($lang_common['Bad request'], false, '404 Not Found');
 
 if ($action != 'change_pass' || !isset($_GET['key']))
 {
 	if ($pun_user['g_read_board'] == '0')
-		message($lang_common['No view']);
+		message($lang_common['No view'], false, '403 Forbidden');
 	else if ($pun_user['g_view_users'] == '0' && ($pun_user['is_guest'] || $pun_user['id'] != $id))
-		message($lang_common['No permission']);
+		message($lang_common['No permission'], false, '403 Forbidden');
 }
 
 // Load the profile.php/register.php language file
@@ -65,17 +65,17 @@ if ($action == 'change_pass')
 	if ($pun_user['id'] != $id)
 	{
 		if (!$pun_user['is_admmod']) // A regular user trying to change another users password?
-			message($lang_common['No permission']);
+			message($lang_common['No permission'], false, '403 Forbidden');
 		else if ($pun_user['g_moderator'] == '1') // A moderator trying to change a users password?
 		{
 			$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());
 			if (!$db->num_rows($result))
-				message($lang_common['Bad request']);
+				message($lang_common['Bad request'], false, '404 Not Found');
 
 			list($group_id, $is_moderator) = $db->fetch_row($result);
 
 			if ($pun_user['g_mod_edit_users'] == '0' || $pun_user['g_mod_change_passwords'] == '0' || $group_id == PUN_ADMIN || $is_moderator == '1')
-				message($lang_common['No permission']);
+				message($lang_common['No permission'], false, '403 Forbidden');
 		}
 	}
 
@@ -161,17 +161,17 @@ else if ($action == 'change_email')
 	if ($pun_user['id'] != $id)
 	{
 		if (!$pun_user['is_admmod']) // A regular user trying to change another users email?
-			message($lang_common['No permission']);
+			message($lang_common['No permission'], false, '403 Forbidden');
 		else if ($pun_user['g_moderator'] == '1') // A moderator trying to change a users email?
 		{
 			$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());
 			if (!$db->num_rows($result))
-				message($lang_common['Bad request']);
+				message($lang_common['Bad request'], false, '404 Not Found');
 
 			list($group_id, $is_moderator) = $db->fetch_row($result);
 
 			if ($pun_user['g_mod_edit_users'] == '0' || $group_id == PUN_ADMIN || $is_moderator == '1')
-				message($lang_common['No permission']);
+				message($lang_common['No permission'], false, '403 Forbidden');
 		}
 	}
 
@@ -199,7 +199,7 @@ else if ($action == 'change_email')
 		require PUN_ROOT.'include/email.php';
 
 		// Validate the email address
-		$new_email = strtolower(trim($_POST['req_new_email']));
+		$new_email = strtolower(pun_trim($_POST['req_new_email']));
 		if (!is_valid_email($new_email))
 			message($lang_common['Invalid email']);
 
@@ -210,10 +210,18 @@ else if ($action == 'change_email')
 				message($lang_prof_reg['Banned email']);
 			else if ($pun_config['o_mailing_list'] != '')
 			{
-				$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'], get_base_url().'/profile.php?id='.$id)."\n";
-				$mail_message .= "\n".'--'."\n".$lang_common['Email signature'];
+				// Load the "banned email change" template
+				$mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$pun_user['language'].'/mail_templates/banned_email_change.tpl'));
+
+				// The first row contains the 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));
+
+				$mail_message = str_replace('<username>', $pun_user['username'], $mail_message);
+				$mail_message = str_replace('<email>', $new_email, $mail_message);
+				$mail_message = str_replace('<profile_url>', get_base_url().'/profile.php?id='.$id, $mail_message);
+				$mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'], $mail_message);
 
 				pun_mail($pun_config['o_mailing_list'], $mail_subject, $mail_message);
 			}
@@ -230,10 +238,18 @@ else if ($action == 'change_email')
 				while ($cur_dupe = $db->fetch_assoc($result))
 					$dupe_list[] = $cur_dupe['username'];
 
-				$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'], get_base_url().'/profile.php?id='.$id)."\n";
-				$mail_message .= "\n".'--'."\n".$lang_common['Email signature'];
+				// Load the "dupe email change" template
+				$mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$pun_user['language'].'/mail_templates/dupe_email_change.tpl'));
+
+				// The first row contains the 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));
+
+				$mail_message = str_replace('<username>', $pun_user['username'], $mail_message);
+				$mail_message = str_replace('<dupe_list>', implode(', ', $dupe_list), $mail_message);
+				$mail_message = str_replace('<profile_url>', get_base_url().'/profile.php?id='.$id, $mail_message);
+				$mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'], $mail_message);
 
 				pun_mail($pun_config['o_mailing_list'], $mail_subject, $mail_message);
 			}
@@ -255,7 +271,7 @@ else if ($action == 'change_email')
 		$mail_message = str_replace('<username>', $pun_user['username'], $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);
+		$mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'], $mail_message);
 
 		pun_mail($new_email, $mail_subject, $mail_message);
 
@@ -300,7 +316,7 @@ else if ($action == 'upload_avatar' || $action == 'upload_avatar2')
 		message($lang_profile['Avatars disabled']);
 
 	if ($pun_user['id'] != $id && !$pun_user['is_admmod'])
-		message($lang_common['No permission']);
+		message($lang_common['No permission'], false, '403 Forbidden');
 
 	if (isset($_POST['form_sent']))
 	{
@@ -423,7 +439,7 @@ else if ($action == 'upload_avatar' || $action == 'upload_avatar2')
 else if ($action == 'delete_avatar')
 {
 	if ($pun_user['id'] != $id && !$pun_user['is_admmod'])
-		message($lang_common['No permission']);
+		message($lang_common['No permission'], false, '403 Forbidden');
 
 	confirm_referrer('profile.php');
 
@@ -436,7 +452,7 @@ else if ($action == 'delete_avatar')
 else if (isset($_POST['update_group_membership']))
 {
 	if ($pun_user['g_id'] > PUN_ADMIN)
-		message($lang_common['No permission']);
+		message($lang_common['No permission'], false, '403 Forbidden');
 
 	confirm_referrer('profile.php');
 
@@ -480,7 +496,7 @@ else if (isset($_POST['update_group_membership']))
 else if (isset($_POST['update_forums']))
 {
 	if ($pun_user['g_id'] > PUN_ADMIN)
-		message($lang_common['No permission']);
+		message($lang_common['No permission'], false, '403 Forbidden');
 
 	confirm_referrer('profile.php');
 
@@ -521,16 +537,28 @@ else if (isset($_POST['update_forums']))
 else if (isset($_POST['ban']))
 {
 	if ($pun_user['g_id'] != PUN_ADMIN && ($pun_user['g_moderator'] != '1' || $pun_user['g_mod_ban_users'] == '0'))
-		message($lang_common['No permission']);
+		message($lang_common['No permission'], false, '403 Forbidden');
+
+	// Get the username of the user we are banning
+	$result = $db->query('SELECT username FROM '.$db->prefix.'users WHERE id='.$id) or error('Unable to fetch username', __FILE__, __LINE__, $db->error());
+	$username = $db->result($result);
 
-	redirect('admin_bans.php?add_ban='.$id, $lang_profile['Ban redirect']);
+	// Check whether user is already banned
+	$result = $db->query('SELECT id FROM '.$db->prefix.'bans WHERE username = \''.$db->escape($username).'\' ORDER BY expire IS NULL DESC, expire DESC LIMIT 1') or error('Unable to fetch ban ID', __FILE__, __LINE__, $db->error());
+	if ($db->num_rows($result))
+	{
+		$ban_id = $db->result($result);
+		redirect('admin_bans.php?edit_ban='.$ban_id.'&amp;exists', $lang_profile['Ban redirect']);
+	}
+	else
+		redirect('admin_bans.php?add_ban='.$id, $lang_profile['Ban redirect']);
 }
 
 
 else if (isset($_POST['delete_user']) || isset($_POST['delete_user_comply']))
 {
 	if ($pun_user['g_id'] > PUN_ADMIN)
-		message($lang_common['No permission']);
+		message($lang_common['No permission'], false, '403 Forbidden');
 
 	confirm_referrer('profile.php');
 
@@ -651,7 +679,7 @@ else if (isset($_POST['form_sent']))
 	// Fetch the user group of the user we are editing
 	$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']);
+		message($lang_common['Bad request'], false, '404 Not Found');
 
 	list($old_username, $group_id, $is_moderator) = $db->fetch_row($result);
 
@@ -661,7 +689,7 @@ else if (isset($_POST['form_sent']))
 		($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']);
+		message($lang_common['No permission'], false, '403 Forbidden');
 
 	if ($pun_user['is_admmod'])
 		confirm_referrer('profile.php');
@@ -686,7 +714,7 @@ else if (isset($_POST['form_sent']))
 				$languages = forum_list_langs();
 				$form['language'] = pun_trim($_POST['form']['language']);
 				if (!in_array($form['language'], $languages))
-					message($lang_common['Bad request']);
+					message($lang_common['Bad request'], false, '404 Not Found');
 			}
 
 			if ($pun_user['is_admmod'])
@@ -722,7 +750,7 @@ else if (isset($_POST['form_sent']))
 				require PUN_ROOT.'include/email.php';
 
 				// Validate the email address
-				$form['email'] = strtolower(trim($_POST['req_email']));
+				$form['email'] = strtolower(pun_trim($_POST['req_email']));
 				if (!is_valid_email($form['email']))
 					message($lang_common['Invalid email']);
 			}
@@ -739,8 +767,15 @@ else if (isset($_POST['form_sent']))
 			);
 
 			// Add http:// if the URL doesn't contain it already (while allowing https://, too)
-			if ($form['url'] != '' && !preg_match('#^https?://#i', $form['url']))
-				$form['url'] = 'http://'.$form['url'];
+			if ($form['url'] != '')
+			{
+				$url = url_valid($form['url']);
+
+				if ($url === false)
+					message($lang_profile['Invalid website URL']);
+
+				$form['url'] = $url['url'];
+			}
 
 			if ($pun_user['g_id'] == PUN_ADMIN)
 				$form['title'] = pun_trim($_POST['title']);
@@ -773,7 +808,7 @@ else if (isset($_POST['form_sent']))
 			);
 
 			// If the ICQ UIN contains anything other than digits it's invalid
-			if (preg_match('/[^0-9]/', $form['icq']))
+			if (preg_match('%[^0-9]%', $form['icq']))
 				message($lang_prof_reg['Bad ICQ']);
 
 			break;
@@ -849,7 +884,7 @@ else if (isset($_POST['form_sent']))
 				$styles = forum_list_styles();
 				$form['style'] = pun_trim($_POST['form']['style']);
 				if (!in_array($form['style'], $styles))
-					message($lang_common['Bad request']);
+					message($lang_common['Bad request'], false, '404 Not Found');
 			}
 
 			break;
@@ -892,6 +927,10 @@ else if (isset($_POST['form_sent']))
 	// If we changed the username we have to update some stuff
 	if ($username_updated)
 	{
+		$db->query('UPDATE '.$db->prefix.'bans SET username=\''.$db->escape($form['username']).'\' WHERE username=\''.$db->escape($old_username).'\'') or error('Unable to update bans', __FILE__, __LINE__, $db->error());
+		// If any bans were updated, we will need to know because the cache will need to be regenerated.
+		if ($db->affected_rows() > 0)
+			$bans_updated = true;
 		$db->query('UPDATE '.$db->prefix.'posts SET poster=\''.$db->escape($form['username']).'\' WHERE poster_id='.$id) or error('Unable to update posts', __FILE__, __LINE__, $db->error());
 		$db->query('UPDATE '.$db->prefix.'posts SET edited_by=\''.$db->escape($form['username']).'\' WHERE edited_by=\''.$db->escape($old_username).'\'') or error('Unable to update posts', __FILE__, __LINE__, $db->error());
 		$db->query('UPDATE '.$db->prefix.'topics SET poster=\''.$db->escape($form['username']).'\' WHERE poster=\''.$db->escape($old_username).'\'') or error('Unable to update topics', __FILE__, __LINE__, $db->error());
@@ -924,6 +963,16 @@ else if (isset($_POST['form_sent']))
 				}
 			}
 		}
+
+		// Regenerate the users info cache
+		if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+			require PUN_ROOT.'include/cache.php';
+
+		generate_users_info_cache();
+
+		// Check if the bans table was updated and regenerate the bans cache when needed
+		if (isset($bans_updated))
+			generate_bans_cache();
 	}
 
 	redirect('profile.php?section='.$section.'&amp;id='.$id, $lang_profile['Profile redirect']);
@@ -932,7 +981,7 @@ else if (isset($_POST['form_sent']))
 
 $result = $db->query('SELECT u.username, u.email, u.title, u.realname, u.url, u.jabber, u.icq, u.msn, u.aim, u.yahoo, u.location, u.signature, u.disp_topics, u.disp_posts, u.email_setting, u.notify_with_post, u.auto_notify, u.show_smilies, u.show_img, u.show_img_sig, u.show_avatars, u.show_sig, u.timezone, u.dst, u.language, u.style, u.num_posts, u.last_post, u.registered, u.registration_ip, u.admin_note, u.date_format, u.time_format, u.last_visit, g.g_id, g.g_user_title, g.g_moderator FROM '.$db->prefix.'users AS u LEFT 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']);
+	message($lang_common['Bad request'], false, '404 Not Found');
 
 $user = $db->fetch_assoc($result);
 
@@ -1625,7 +1674,7 @@ else
 	else if ($section == 'admin')
 	{
 		if (!$pun_user['is_admmod'] || ($pun_user['g_moderator'] == '1' && $pun_user['g_mod_ban_users'] == '0'))
-			message($lang_common['Bad request']);
+			message($lang_common['Bad request'], false, '403 Forbidden');
 
 		$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Profile'], $lang_profile['Section admin']);
 		define('PUN_ACTIVE_PAGE', 'profile');
diff --git a/register.php b/register.php
index 980eaf1..815eec2 100644
--- a/register.php
+++ b/register.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -73,11 +73,11 @@ if (isset($_POST['form_sent']))
 
 
 	$username = pun_trim($_POST['req_user']);
-	$email1 = strtolower(trim($_POST['req_email1']));
+	$email1 = strtolower(pun_trim($_POST['req_email1']));
 
 	if ($pun_config['o_regs_verify'] == '1')
 	{
-		$email2 = strtolower(trim($_POST['req_email2']));
+		$email2 = strtolower(pun_trim($_POST['req_email2']));
 
 		$password1 = random_pass(8);
 		$password2 = $password1;
@@ -131,7 +131,7 @@ if (isset($_POST['form_sent']))
 	// Make sure we got a valid language string
 	if (isset($_POST['language']))
 	{
-		$language = preg_replace('#[\.\\\/]#', '', $_POST['language']);
+		$language = preg_replace('%[\.\\\/]%', '', $_POST['language']);
 		if (!file_exists(PUN_ROOT.'lang/'.$language.'/common.php'))
 			message($lang_common['Bad request']);
 	}
@@ -165,10 +165,18 @@ if (isset($_POST['form_sent']))
 			// If we previously found out that the email was banned
 			if ($banned_email)
 			{
-				$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'], get_base_url().'/profile.php?id='.$new_uid)."\n";
-				$mail_message .= "\n".'--'."\n".$lang_common['Email signature'];
+				// Load the "banned email register" template
+				$mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$pun_user['language'].'/mail_templates/banned_email_register.tpl'));
+
+				// The first row contains the 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));
+
+				$mail_message = str_replace('<username>', $username, $mail_message);
+				$mail_message = str_replace('<email>', $email1, $mail_message);
+				$mail_message = str_replace('<profile_url>', get_base_url().'/profile.php?id='.$new_uid, $mail_message);
+				$mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'], $mail_message);
 
 				pun_mail($pun_config['o_mailing_list'], $mail_subject, $mail_message);
 			}
@@ -176,10 +184,18 @@ if (isset($_POST['form_sent']))
 			// If we previously found out that the email was a dupe
 			if (!empty($dupe_list))
 			{
-				$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'], get_base_url().'/profile.php?id='.$new_uid)."\n";
-				$mail_message .= "\n".'--'."\n".$lang_common['Email signature'];
+				// Load the "dupe email register" template
+				$mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$pun_user['language'].'/mail_templates/dupe_email_register.tpl'));
+
+				// The first row contains the 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));
+
+				$mail_message = str_replace('<username>', $username, $mail_message);
+				$mail_message = str_replace('<dupe_list>', implode(', ', $dupe_list), $mail_message);
+				$mail_message = str_replace('<profile_url>', get_base_url().'/profile.php?id='.$new_uid, $mail_message);
+				$mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'], $mail_message);
 
 				pun_mail($pun_config['o_mailing_list'], $mail_subject, $mail_message);
 			}
@@ -187,10 +203,18 @@ if (isset($_POST['form_sent']))
 			// Should we alert people on the admin mailing list that a new user has registered?
 			if ($pun_config['o_regs_report'] == '1')
 			{
-				$mail_subject = $lang_common['New user notification'];
-				$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'];
+				// Load the "new user" template
+				$mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$pun_user['language'].'/mail_templates/new_user.tpl'));
+
+				// The first row contains the 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));
+
+				$mail_message = str_replace('<username>', $username, $mail_message);
+				$mail_message = str_replace('<base_url>', get_base_url().'/', $mail_message);
+				$mail_message = str_replace('<profile_url>', get_base_url().'/profile.php?id='.$new_uid, $mail_message);
+				$mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'], $mail_message);
 
 				pun_mail($pun_config['o_mailing_list'], $mail_subject, $mail_message);
 			}
@@ -212,7 +236,7 @@ if (isset($_POST['form_sent']))
 			$mail_message = str_replace('<username>', $username, $mail_message);
 			$mail_message = str_replace('<password>', $password1, $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);
+			$mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'], $mail_message);
 
 			pun_mail($email1, $mail_subject, $mail_message);
 
@@ -242,9 +266,6 @@ $timezone = isset($timezone) ? $timezone : $pun_config['o_default_timezone'];
 $dst = isset($dst) ? $dst : $pun_config['o_default_dst'];
 $email_setting = isset($email_setting) ? $email_setting : $pun_config['o_default_email_setting'];
 
-?>
-<?php
-
 // If there are errors, we display them
 if (!empty($errors))
 {
@@ -280,8 +301,6 @@ if (!empty($errors))
 					<p><?php echo $lang_register['Desc 1'] ?></p>
 					<p><?php echo $lang_register['Desc 2'] ?></p>
 				</div>
-			</div>
-			<div class="inform">
 				<fieldset>
 					<legend><?php echo $lang_register['Username legend'] ?></legend>
 					<div class="infldset">
diff --git a/search.php b/search.php
index bdbd71b..8d092af 100644
--- a/search.php
+++ b/search.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -18,9 +18,9 @@ require PUN_ROOT.'lang/'.$pun_user['language'].'/forum.php';
 
 
 if ($pun_user['g_read_board'] == '0')
-	message($lang_common['No view']);
+	message($lang_common['No view'], false, '403 Forbidden');
 else if ($pun_user['g_search'] == '0')
-	message($lang_search['No search permission']);
+	message($lang_search['No search permission'], false, '403 Forbidden');
 
 require PUN_ROOT.'include/search_idx.php';
 
@@ -28,9 +28,11 @@ require PUN_ROOT.'include/search_idx.php';
 if (isset($_GET['action']) || isset($_GET['search_id']))
 {
 	$action = (isset($_GET['action'])) ? $_GET['action'] : null;
-	$forum = (isset($_GET['forum'])) ? intval($_GET['forum']) : -1;
+	$forums = isset($_GET['forums']) ? (is_array($_GET['forums']) ? $_GET['forums'] : array_filter(explode(',', $_GET['forums']))) : (isset($_GET['forum']) ? array($_GET['forum']) : array());
 	$sort_dir = (isset($_GET['sort_dir']) && $_GET['sort_dir'] == 'DESC') ? 'DESC' : 'ASC';
 
+	$forums = array_map('intval', $forums);
+
 	// Allow the old action names for backwards compatibility reasons
 	if ($action == 'show_user')
 		$action = 'show_user_posts';
@@ -42,7 +44,7 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 	{
 		$search_id = intval($_GET['search_id']);
 		if ($search_id < 1)
-			message($lang_common['Bad request']);
+			message($lang_common['Bad request'], false, '404 Not Found');
 	}
 	// If it's a regular search (keywords and/or author)
 	else if ($action == 'search')
@@ -50,10 +52,10 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 		$keywords = (isset($_GET['keywords'])) ? utf8_strtolower(pun_trim($_GET['keywords'])) : null;
 		$author = (isset($_GET['author'])) ? utf8_strtolower(pun_trim($_GET['author'])) : null;
 
-		if (preg_match('#^[\*%]+$#', $keywords) || (pun_strlen(str_replace(array('*', '%'), '', $keywords)) < PUN_SEARCH_MIN_WORD && !is_cjk($keywords)))
+		if (preg_match('%^[\*\%]+$%', $keywords) || (pun_strlen(str_replace(array('*', '%'), '', $keywords)) < PUN_SEARCH_MIN_WORD && !is_cjk($keywords)))
 			$keywords = '';
 
-		if (preg_match('#^[\*%]+$#', $author) || pun_strlen(str_replace(array('*', '%'), '', $author)) < 2)
+		if (preg_match('%^[\*\%]+$%', $author) || pun_strlen(str_replace(array('*', '%'), '', $author)) < 2)
 			$author = '';
 
 		if (!$keywords && !$author)
@@ -62,20 +64,20 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 		if ($author)
 			$author = str_replace('*', '%', $author);
 
-		$show_as = (isset($_GET['show_as'])) ? $_GET['show_as'] : 'posts';
-		$sort_by = (isset($_GET['sort_by'])) ? intval($_GET['sort_by']) : null;
-		$search_in = (!isset($_GET['search_in']) || $_GET['search_in'] == 'all') ? 0 : (($_GET['search_in'] == 'message') ? 1 : -1);
+		$show_as = (isset($_GET['show_as']) && $_GET['show_as'] == 'topics') ? 'topics' : 'posts';
+		$sort_by = (isset($_GET['sort_by'])) ? intval($_GET['sort_by']) : 0;
+		$search_in = (!isset($_GET['search_in']) || $_GET['search_in'] == '0') ? 0 : (($_GET['search_in'] == '1') ? 1 : -1);
 	}
 	// If it's a user search (by ID)
 	else if ($action == 'show_user_posts' || $action == 'show_user_topics' || $action == 'show_subscriptions')
 	{
 		$user_id = (isset($_GET['user_id'])) ? intval($_GET['user_id']) : $pun_user['id'];
 		if ($user_id < 2)
-			message($lang_common['Bad request']);
+			message($lang_common['Bad request'], false, '404 Not Found');
 
 		// 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']);
+			message($lang_common['No permission'], false, '403 Forbidden');
 	}
 	else if ($action == 'show_recent')
 		$interval = isset($_GET['value']) ? intval($_GET['value']) : 86400;
@@ -115,7 +117,7 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 		$keyword_results = $author_results = array();
 
 		// Search a specific forum?
-		$forum_sql = ($forum != -1 || ($forum == -1 && $pun_config['o_search_all_forums'] == '0' && !$pun_user['is_admmod'])) ? ' AND t.forum_id = '.$forum : '';
+		$forum_sql = (!empty($forums) || (empty($forums) && $pun_config['o_search_all_forums'] == '0' && !$pun_user['is_admmod'])) ? ' AND t.forum_id IN ('.implode(',', $forums).')' : '';
 
 		if (!empty($author) || !empty($keywords))
 		{
@@ -283,17 +285,17 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 			if ($author && $keywords)
 			{
 				$search_ids = array_intersect_assoc($keyword_results, $author_results);
-				$search_type = array('both', array($keywords, pun_trim($_GET['author'])), $forum, isset($_GET['search_in']) ? $_GET['search_in'] : '');
+				$search_type = array('both', array($keywords, pun_trim($_GET['author'])), implode(',', $forums), $search_in);
 			}
 			else if ($keywords)
 			{
 				$search_ids = $keyword_results;
-				$search_type = array('keywords', $keywords, $forum, isset($_GET['search_in']) ? $_GET['search_in'] : '');
+				$search_type = array('keywords', $keywords, implode(',', $forums), $search_in);
 			}
 			else
 			{
 				$search_ids = $author_results;
-				$search_type = array('author', pun_trim($_GET['author']), $forum, isset($_GET['search_in']) ? $_GET['search_in'] : '');
+				$search_type = array('author', pun_trim($_GET['author']), implode(',', $forums), $search_in);
 			}
 
 			unset($keyword_results, $author_results);
@@ -321,7 +323,7 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 			if ($action == 'show_new')
 			{
 				if ($pun_user['is_guest'])
-					message($lang_common['No permission']);
+					message($lang_common['No permission'], false, '403 Forbidden');
 
 				$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>'.$pun_user['last_visit'].' 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);
@@ -498,7 +500,7 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 			$search_set[] = $row;
 
 		$crumbs_text = array();
-		$crumbs_text['show_as'] = $show_as == 'topics' ? $lang_search['Search topics'] : $lang_search['Search posts'];
+		$crumbs_text['show_as'] = $lang_search['Search'];
 
 		if ($search_type[0] == 'action')
 		{
@@ -515,12 +517,12 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 				if ($db->num_rows($result))
 					$subscriber_name = $db->result($result);
 				else
-					message($lang_common['Bad request']);
+					message($lang_common['Bad request'], false, '404 Not Found');
 
 				$crumbs_text['search_type'] = '<a href="search.php?action=show_subscriptions&amp;user_id='.$subscriber_id.'">'.sprintf($lang_search['Quick search show_subscriptions'], pun_htmlspecialchars($subscriber_name)).'</a>';
 			}
 			else
-				$crumbs_text['search_type'] = '<a href="search.php?action='.pun_htmlspecialchars($search_type[1]).'">'.$lang_search['Quick search '.$search_type[1]].'</a>';
+				$crumbs_text['search_type'] = '<a href="search.php?action='.$search_type[1].'">'.$lang_search['Quick search '.$search_type[1]].'</a>';
 		}
 		else
 		{
@@ -542,7 +544,7 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 				$crumbs_text['search_type'] = sprintf($lang_search['By user show as '.$show_as], pun_htmlspecialchars($author));
 			}
 
-			$crumbs_text['search_type'] = '<a href="search.php?action=search&amp;keywords='.pun_htmlspecialchars($keywords).'&amp;author='.pun_htmlspecialchars($author).'&amp;forum='.pun_htmlspecialchars($search_type[2]).'&amp;search_in='.pun_htmlspecialchars($search_type[3]).'&amp;sort_by='.pun_htmlspecialchars($sort_by).'&amp;sort_dir='.pun_htmlspecialchars($sort_dir).'&amp;show_as='.pun_htmlspecialchars($show_as).'">'.$crumbs_text['search_type'].'</a>';
+			$crumbs_text['search_type'] = '<a href="search.php?action=search&amp;keywords='.urlencode($keywords).'&amp;author='.urlencode($author).'&amp;forums='.$search_type[2].'&amp;search_in='.$search_type[3].'&amp;sort_by='.$sort_by.'&amp;sort_dir='.$sort_dir.'&amp;show_as='.$show_as.'">'.$crumbs_text['search_type'].'</a>';
 		}
 
 		$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_search['Search results']);
@@ -796,42 +798,73 @@ require PUN_ROOT.'header.php';
 				<fieldset>
 					<legend><?php echo $lang_search['Search in legend'] ?></legend>
 					<div class="infldset">
-						<label class="conl"><?php echo $lang_search['Forum search']."\n" ?>
-						<br /><select id="forum" name="forum">
 <?php
 
+$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='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND f.redirect_url IS NULL ORDER BY c.disp_position, c.id, f.disp_position', true) or error('Unable to fetch category/forum list', __FILE__, __LINE__, $db->error());
+
+// We either show a list of forums of which multiple can be selected
 if ($pun_config['o_search_all_forums'] == '1' || $pun_user['is_admmod'])
-	echo "\t\t\t\t\t\t\t".'<option value="-1">'.$lang_search['All forums'].'</option>'."\n";
+{
+	echo "\t\t\t\t\t\t".'<div class="conl multiselect">'.$lang_search['Forum search']."\n";
+	echo "\t\t\t\t\t\t".'<br />'."\n";
+	echo "\t\t\t\t\t\t".'<div class="checklist">'."\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='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND f.redirect_url IS NULL ORDER BY c.disp_position, c.id, f.disp_position', true) or error('Unable to fetch category/forum list', __FILE__, __LINE__, $db->error());
+	$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)
+				echo "\t\t\t\t\t\t\t".'</fieldset>'."\n";
 
-$cur_category = 0;
-while ($cur_forum = $db->fetch_assoc($result))
+			echo "\t\t\t\t\t\t\t".'<fieldset><legend><span>'.pun_htmlspecialchars($cur_forum['cat_name']).'</span></legend>'."\n";
+			$cur_category = $cur_forum['cid'];
+		}
+
+		echo "\t\t\t\t\t\t\t\t".'<div class="checklist-item"><span class="fld-input"><input type="checkbox" name="forums[]" id="forum-'.$cur_forum['fid'].'" value="'.$cur_forum['fid'].'" /></span> <label for="forum-'.$cur_forum['fid'].'">'.pun_htmlspecialchars($cur_forum['forum_name']).'</label></div>'."\n";
+	}
+
+	echo "\t\t\t\t\t\t\t".'</fieldset>'."\n";
+	echo "\t\t\t\t\t\t".'</div>'."\n";
+	echo "\t\t\t\t\t\t".'</div>'."\n";
+}
+// ... or a simple select list for one forum only
+else
 {
-	if ($cur_forum['cid'] != $cur_category) // A new category since last iteration?
+	echo "\t\t\t\t\t\t".'<label class="conl">'.$lang_search['Forum search']."\n";
+	echo "\t\t\t\t\t\t".'<br />'."\n";
+	echo "\t\t\t\t\t\t".'<select id="forum" name="forum">'."\n";
+
+	$cur_category = 0;
+	while ($cur_forum = $db->fetch_assoc($result))
 	{
-		if ($cur_category)
-			echo "\t\t\t\t\t\t\t".'</optgroup>'."\n";
+		if ($cur_forum['cid'] != $cur_category) // A new category since last iteration?
+		{
+			if ($cur_category)
+				echo "\t\t\t\t\t\t\t".'</optgroup>'."\n";
+
+			echo "\t\t\t\t\t\t\t".'<optgroup label="'.pun_htmlspecialchars($cur_forum['cat_name']).'">'."\n";
+			$cur_category = $cur_forum['cid'];
+		}
 
-		echo "\t\t\t\t\t\t\t".'<optgroup label="'.pun_htmlspecialchars($cur_forum['cat_name']).'">'."\n";
-		$cur_category = $cur_forum['cid'];
+		echo "\t\t\t\t\t\t\t\t".'<option value="'.$cur_forum['fid'].'">'.pun_htmlspecialchars($cur_forum['forum_name']).'</option>'."\n";
 	}
 
-	echo "\t\t\t\t\t\t\t\t".'<option value="'.$cur_forum['fid'].'">'.pun_htmlspecialchars($cur_forum['forum_name']).'</option>'."\n";
+	echo "\t\t\t\t\t\t\t".'</optgroup>'."\n";
+	echo "\t\t\t\t\t\t".'</select>'."\n";
+	echo "\t\t\t\t\t\t".'<br /></label>'."\n";
 }
 
 ?>
-							</optgroup>
-						</select>
-						<br /></label>
 						<label class="conl"><?php echo $lang_search['Search in']."\n" ?>
 						<br /><select id="search_in" name="search_in">
-							<option value="all"><?php echo $lang_search['Message and subject'] ?></option>
-							<option value="message"><?php echo $lang_search['Message only'] ?></option>
-							<option value="topic"><?php echo $lang_search['Topic only'] ?></option>
+							<option value="0"><?php echo $lang_search['Message and subject'] ?></option>
+							<option value="1"><?php echo $lang_search['Message only'] ?></option>
+							<option value="-1"><?php echo $lang_search['Topic only'] ?></option>
 						</select>
 						<br /></label>
-						<p class="clearb"><?php echo $lang_search['Search in info'] ?></p>
+						<p class="clearl"><?php echo $lang_search['Search in info'] ?></p>
+<?php echo ($pun_config['o_search_all_forums'] == '1' || $pun_user['is_admmod'] ? '<p>'.$lang_search['Search multiple forums info'].'</p>' : '') ?>
 					</div>
 				</fieldset>
 			</div>
diff --git a/style/Air.css b/style/Air.css
index 59614e9..cab49c9 100644
--- a/style/Air.css
+++ b/style/Air.css
@@ -151,6 +151,11 @@ html, body, .pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3,
 	clear: none;
 }
 
+.clearl {
+	clear: left;
+}
+
+
 /*****************************************************************
 2. COMMON STYLES
 *****************************************************************/
@@ -767,8 +772,8 @@ MAIN POSTS
 }
 
 .pun .blockpost .postleft, .pun .blockpost .postfootleft {
-	width: 206px;
-	padding: 7px 0 7px 12px;
+	width: 194px;
+	padding: 7px 12px 7px 12px;
 	float: left;
 	margin-left: -218px;
 	position: relative;
@@ -1275,6 +1280,52 @@ MAIN FORMS
 	padding: 7px 12px;
 }
 
+.pun .multiselect {
+	float: left;
+	padding-bottom: 7px;
+}
+
+.pun .checklist {
+	border-width: 1px;
+	border-style: solid;
+	max-height: 9em;
+	width: 20em;
+	overflow: auto;
+	padding: 0.25em 0.5em;
+	margin: 0.25em 16px 0 0.15em;
+}
+
+.pun .checklist legend {
+	padding: 0;
+}
+
+.pun .checklist legend span {
+	width: auto;
+	max-width: 25em;
+}
+
+.pun .checklist .checklist-item {
+	position: relative;
+}
+
+.pun .checklist .checklist-item label {
+	padding: 0 0 0 1.75em;
+}
+
+.pun .checklist .checklist-item .fld-input {
+	position: absolute;
+	left: 0;
+	top: 0;
+	padding: 0;
+	margin: 0;
+}
+
+.pun .checklist .checklist-item input {
+	margin: 0;
+	height: 1.55em;
+	width: 1.55em;
+}
+
 /*****************************************************************
 PROFILES (+ ADMIN MENU)
 *****************************************************************/
@@ -1609,6 +1660,15 @@ html, body, .pun {
 	background: #d59b9b;
 }
 
+.pun .multiselect {
+	color: #357082;
+}
+
+.pun .checklist {
+	background: white;
+	border-color: #ccc;
+}
+
 /* Status Indicators
 ----------------------------------------------------------------*/
 
diff --git a/style/Air/base_admin.css b/style/Air/base_admin.css
index 45f192a..2948b81 100644
--- a/style/Air/base_admin.css
+++ b/style/Air/base_admin.css
@@ -123,25 +123,41 @@
 	text-align: center;
 }
 
-#users2 .tcl, #users2 .tc3, #users2 .tc5, #bans1 .tcl, #bans1 .tc3, #bans1 .tc5, #bans1 .tc6 {
-	width: 15%;
+#users2 .tcl, #bans1 .tcl {
+	width: auto;
 	text-align: left;
 }
 
 #users2 .tc2, #bans1 .tc2 {
-	width: 22%;
+	width: 18%;
+	text-align: left;
+}
+
+#users2 .tc3, #users2 .tc5, #bans1 .tc3, #bans1 .tc5, #bans1 .tc6 {
+	width: 12%;
 	text-align: left;
 }
 
 #users2 .tc4, #bans1 .tc4 {
-	width: 8%;
+	width: 10%;
 	text-align: center;
 }
 
-#users2 .tcr, #bans1 .tcr {
+#users2 .tcr {
+	width: 20%;
+	white-space: nowrap;
+}
+
+#bans1 .tcr {
+	width: 15%;
 	white-space: nowrap;
 }
 
+#users2 .tcmod {
+	width: 10%;
+	text-align: center;
+}
+
 .plugin p {
 	padding: 12px 18px 0;
 }
diff --git a/style/Cobalt.css b/style/Cobalt.css
index 0c52912..6698bff 100644
--- a/style/Cobalt.css
+++ b/style/Cobalt.css
@@ -48,6 +48,9 @@
 	min-height: 1px
 	}
 
+.clearl {
+	clear: left;
+	}
 
 /* Hidden Elements
 ----------------------------------------------------------------*/
@@ -72,6 +75,7 @@
 
 .pun {
 	font: 68.75%/1.4545em Verdana, Helvetica, Arial, sans-serif;
+	line-height: normal;
 	}
 
 .pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun samp, .pun legend {
@@ -108,7 +112,7 @@
 .pun h2 {
 	font-size: 1em;
 	font-weight: normal;
-	padding: 4px 7px;
+	padding: 4px 6px;
 	}
 
 .pun h3 {
@@ -209,6 +213,11 @@
 /* Page Layout
 ----------------------------------------------------------------*/
 
+html, body {
+	margin: 0;
+	padding: 0
+	}
+
 .pun {
 	margin: 12px 20px
 	}
@@ -225,6 +234,10 @@
 	margin: 0 0 12px 0;
 	}
 
+#brdtitle p {
+	padding-top: 0px
+	}
+
 #announce, #brdstats {
 	margin: 12px 0 12px 0;
 	}
@@ -281,21 +294,11 @@
 ----------------------------------------------------------------*/
 
 #brdtitle h1 {
-	font-size: 1.454em;
+	font-size: 1.4em;
 	font-weight: bold;
-	line-height: 1em;
 	padding: 3px 0 0 0;
 	}
 
-#brddesc {
-	padding: 3px 0;
-	}
-
-#brddesc * {
-	padding-top: 0;
-	padding-bottom: 0;
-	}
-
 #brdmenu li {
 	display: inline;
 	margin-right: 12px;
@@ -435,12 +438,11 @@
 
 .pun td, .pun th {
 	padding: 4px 6px;
-	line-height: 1.273em;
 	text-align: left;
 	font-weight: normal;
 	}
 
-.pun td {
+.pun td, .pun th {
 	border-style: solid none none solid;
 	border-width: 1px;
 	}
@@ -525,7 +527,8 @@
 	}
 
 .pun .icon {
-	border-width: 7px;
+	margin: 0.1em 0 0 0.2em;
+	border-width: 0.6em;
 	border-style: solid;
 	height: 0;
 	width: 0;
@@ -598,14 +601,6 @@
 	margin-right: 10px
 	}
 
-.pun select {
-	padding-top: 1px;
-	padding-bottom: 1px;
-	}
-
-.pun fieldset .rbox {
-	}
-
 .pun fieldset .rbox br {
 	display: none;
 	}
@@ -665,6 +660,58 @@
 	margin-right: 12px;
 	}
 
+.pun .multiselect {
+	float: left;
+	padding-bottom: 7px;
+	}
+
+.pun .checklist {
+	border-width: 1px;
+	border-style: solid;
+	max-height: 9em;
+	width: 20em;
+	overflow: auto;
+	padding: 0.3em 0.5em;
+	margin: 0.25em 16px 0 0.15em;
+	}
+
+.pun .checklist fieldset {
+	border: 0;
+	padding: 0;
+	}
+
+.pun .checklist legend {
+	padding: 0;
+	}
+
+.pun .checklist legend span {
+	width: auto;
+	max-width: 25em;
+	}
+
+.pun .checklist .checklist-item {
+	position: relative;
+	margin: .2em 0;
+	}
+
+.pun .checklist .checklist-item label {
+	padding: 0 0 0 2.1em;
+	}
+
+.pun .checklist .checklist-item .fld-input {
+	position: absolute;
+	left: 0;
+	top: 0;
+	padding: 0;
+	margin: 0;
+	}
+
+.pun .checklist .checklist-item input {
+	margin: 0;
+	height: 1.15em;
+	width: 1.55em;
+	}
+
 /*****************************************************************
 7. PROFILES AND ADMIN
 *****************************************************************/
@@ -674,12 +721,12 @@
 	}
 
 .pun .block2col .blockform, .pun .block2col .block {
-	margin-left: 17em
+	margin-left: 14em
 	}
 
 .pun .blockmenu {
 	float:left;
-	width: 16em
+	width: 13em
 	}
 
 .pun .blockmenu li {
@@ -756,7 +803,7 @@
 	}
 
 .pun .postleft dl {
-	padding: 0.75em 6px;
+	padding: 6px;
 	}
 
 .pun .postleft .usercontacts, .pun .postleft .icon {
@@ -807,12 +854,19 @@
 
 .pun .postfootright ul, .pun .postfootright div, .pun .postfootright p,
 .pun .postfootleft p {
-	padding: 6px 6px 6px 6px;
+	padding: 10px 6px 5px 6px;
 	}
 
 .pun .postfootright li {
 	display: inline;
-	margin-left: 12px;
+	}
+
+.pun .postfootright li:before {
+	content: " | ";
+	}
+
+.pun .postfootright li:first-child:before {
+	content: "";
 	}
 
 .pun .postfootright a:link, .pun .postfootright a:visited {
@@ -926,6 +980,14 @@
 	text-align: center;
 	}
 
+#brdwelcome, #brdfooter dl a, div.blockmenu li, div.rbox input {
+	line-height: 1.4em
+	}
+
+#announce div.inbox div {
+	padding: 3px 0
+	}
+
 /*****************************************************************
 COLOUR SCHEME
 *****************************************************************/
@@ -978,6 +1040,14 @@ body {
 	color: #d4d4d4
 	}
 
+.pun .multiselect, .pun .checklist {
+	color: #D4D4D4;
+	}
+
+.pun .checklist {
+	border-color: #666;
+	}
+
 /* Posts
 ----------------------------------------------------------------*/
 
@@ -1021,10 +1091,14 @@ body {
 	border-color: #565656
 	}
 
-.pun th, .pun fieldset {
+.pun th {
 	border-color: #484848
 	}
 
+.pun fieldset {
+	border-color: #606060
+	}
+
 #adminconsole td, #adminconsole th {
 	border-color: #383838
 	}
diff --git a/style/Earth.css b/style/Earth.css
index 7c20aec..d886bbe 100644
--- a/style/Earth.css
+++ b/style/Earth.css
@@ -151,6 +151,10 @@ html, body, .pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3,
 	clear: none;
 }
 
+.clearl {
+	clear: left;
+}
+
 /*****************************************************************
 2. COMMON STYLES
 *****************************************************************/
@@ -767,8 +771,8 @@ MAIN POSTS
 }
 
 .pun .blockpost .postleft, .pun .blockpost .postfootleft {
-	width: 206px;
-	padding: 7px 0 7px 12px;
+	width: 194px;
+	padding: 7px 12px 7px 12px;
 	float: left;
 	margin-left: -218px;
 	position: relative;
@@ -1275,6 +1279,52 @@ MAIN FORMS
 	padding: 7px 12px;
 }
 
+.pun .multiselect {
+	float: left;
+	padding-bottom: 7px;
+}
+
+.pun .checklist {
+	border-width: 1px;
+	border-style: solid;
+	max-height: 9em;
+	width: 20em;
+	overflow: auto;
+	padding: 0.25em 0.5em;
+	margin: 0.25em 16px 0 0.15em;
+}
+
+.pun .checklist legend {
+	padding: 0;
+}
+
+.pun .checklist legend span {
+	width: auto;
+	max-width: 25em;
+}
+
+.pun .checklist .checklist-item {
+	position: relative;
+}
+
+.pun .checklist .checklist-item label {
+	padding: 0 0 0 1.75em;
+}
+
+.pun .checklist .checklist-item .fld-input {
+	position: absolute;
+	left: 0;
+	top: 0;
+	padding: 0;
+	margin: 0;
+}
+
+.pun .checklist .checklist-item input {
+	margin: 0;
+	height: 1.55em;
+	width: 1.55em;
+}
+
 /*****************************************************************
 PROFILES (+ ADMIN MENU)
 *****************************************************************/
@@ -1609,6 +1659,15 @@ html, body, .pun {
 	background: #d59b9b;
 }
 
+.pun .multiselect {
+	color: #83866A;
+}
+
+.pun .checklist {
+	background: white;
+	border-color: #ccc;
+}
+
 /* Status Indicators
 ----------------------------------------------------------------*/
 
diff --git a/style/Earth/base_admin.css b/style/Earth/base_admin.css
index 45f192a..2948b81 100644
--- a/style/Earth/base_admin.css
+++ b/style/Earth/base_admin.css
@@ -123,25 +123,41 @@
 	text-align: center;
 }
 
-#users2 .tcl, #users2 .tc3, #users2 .tc5, #bans1 .tcl, #bans1 .tc3, #bans1 .tc5, #bans1 .tc6 {
-	width: 15%;
+#users2 .tcl, #bans1 .tcl {
+	width: auto;
 	text-align: left;
 }
 
 #users2 .tc2, #bans1 .tc2 {
-	width: 22%;
+	width: 18%;
+	text-align: left;
+}
+
+#users2 .tc3, #users2 .tc5, #bans1 .tc3, #bans1 .tc5, #bans1 .tc6 {
+	width: 12%;
 	text-align: left;
 }
 
 #users2 .tc4, #bans1 .tc4 {
-	width: 8%;
+	width: 10%;
 	text-align: center;
 }
 
-#users2 .tcr, #bans1 .tcr {
+#users2 .tcr {
+	width: 20%;
+	white-space: nowrap;
+}
+
+#bans1 .tcr {
+	width: 15%;
 	white-space: nowrap;
 }
 
+#users2 .tcmod {
+	width: 10%;
+	text-align: center;
+}
+
 .plugin p {
 	padding: 12px 18px 0;
 }
diff --git a/style/Fire.css b/style/Fire.css
index c4951dd..33ee9f7 100644
--- a/style/Fire.css
+++ b/style/Fire.css
@@ -151,6 +151,10 @@ html, body, .pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3,
 	clear: none;
 }
 
+.clearl {
+	clear: left;
+}
+
 /*****************************************************************
 2. COMMON STYLES
 *****************************************************************/
@@ -767,8 +771,8 @@ MAIN POSTS
 }
 
 .pun .blockpost .postleft, .pun .blockpost .postfootleft {
-	width: 206px;
-	padding: 7px 0 7px 12px;
+	width: 194px;
+	padding: 7px 12px 7px 12px;
 	float: left;
 	margin-left: -218px;
 	position: relative;
@@ -1275,6 +1279,52 @@ MAIN FORMS
 	padding: 7px 12px;
 }
 
+.pun .multiselect {
+	float: left;
+	padding-bottom: 7px;
+}
+
+.pun .checklist {
+	border-width: 1px;
+	border-style: solid;
+	max-height: 9em;
+	width: 20em;
+	overflow: auto;
+	padding: 0.25em 0.5em;
+	margin: 0.25em 16px 0 0.15em;
+}
+
+.pun .checklist legend {
+	padding: 0;
+}
+
+.pun .checklist legend span {
+	width: auto;
+	max-width: 25em;
+}
+
+.pun .checklist .checklist-item {
+	position: relative;
+}
+
+.pun .checklist .checklist-item label {
+	padding: 0 0 0 1.75em;
+}
+
+.pun .checklist .checklist-item .fld-input {
+	position: absolute;
+	left: 0;
+	top: 0;
+	padding: 0;
+	margin: 0;
+}
+
+.pun .checklist .checklist-item input {
+	margin: 0;
+	height: 1.55em;
+	width: 1.55em;
+}
+
 /*****************************************************************
 PROFILES (+ ADMIN MENU)
 *****************************************************************/
@@ -1609,6 +1659,15 @@ html, body, .pun {
 	background: #d59b9b;
 }
 
+.pun .multiselect {
+	color: #836359;
+}
+
+.pun .checklist {
+	background: white;
+	border-color: #ccc;
+}
+
 /* Status Indicators
 ----------------------------------------------------------------*/
 
diff --git a/style/Fire/base_admin.css b/style/Fire/base_admin.css
index 45f192a..2948b81 100644
--- a/style/Fire/base_admin.css
+++ b/style/Fire/base_admin.css
@@ -123,25 +123,41 @@
 	text-align: center;
 }
 
-#users2 .tcl, #users2 .tc3, #users2 .tc5, #bans1 .tcl, #bans1 .tc3, #bans1 .tc5, #bans1 .tc6 {
-	width: 15%;
+#users2 .tcl, #bans1 .tcl {
+	width: auto;
 	text-align: left;
 }
 
 #users2 .tc2, #bans1 .tc2 {
-	width: 22%;
+	width: 18%;
+	text-align: left;
+}
+
+#users2 .tc3, #users2 .tc5, #bans1 .tc3, #bans1 .tc5, #bans1 .tc6 {
+	width: 12%;
 	text-align: left;
 }
 
 #users2 .tc4, #bans1 .tc4 {
-	width: 8%;
+	width: 10%;
 	text-align: center;
 }
 
-#users2 .tcr, #bans1 .tcr {
+#users2 .tcr {
+	width: 20%;
+	white-space: nowrap;
+}
+
+#bans1 .tcr {
+	width: 15%;
 	white-space: nowrap;
 }
 
+#users2 .tcmod {
+	width: 10%;
+	text-align: center;
+}
+
 .plugin p {
 	padding: 12px 18px 0;
 }
diff --git a/style/Lithium.css b/style/Lithium.css
index f9d50e3..01c6082 100644
--- a/style/Lithium.css
+++ b/style/Lithium.css
@@ -48,6 +48,9 @@
 	min-height: 1px
 	}
 
+.clearl {
+	clear: left;
+	}
 
 /* Hidden Elements
 ----------------------------------------------------------------*/
@@ -72,6 +75,7 @@
 
 .pun {
 	font: 68.75%/1.4545em Verdana, Helvetica, Arial, sans-serif;
+	line-height: normal;
 	}
 
 .pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun samp, .pun legend {
@@ -108,7 +112,7 @@
 .pun h2 {
 	font-size: 1em;
 	font-weight: normal;
-	padding: 4px 7px;
+	padding: 4px 6px;
 	}
 
 .pun h3 {
@@ -209,6 +213,11 @@
 /* Page Layout
 ----------------------------------------------------------------*/
 
+html, body {
+	margin: 0;
+	padding: 0
+	}
+
 .pun {
 	margin: 12px 20px
 	}
@@ -225,6 +234,10 @@
 	margin: 0 0 12px 0;
 	}
 
+#brdtitle p {
+	padding-top: 0px
+	}
+
 #announce, #brdstats {
 	margin: 12px 0 12px 0;
 	}
@@ -281,21 +294,11 @@
 ----------------------------------------------------------------*/
 
 #brdtitle h1 {
-	font-size: 1.454em;
+	font-size: 1.4em;
 	font-weight: bold;
-	line-height: 1em;
 	padding: 3px 0 0 0;
 	}
 
-#brddesc {
-	padding: 3px 0;
-	}
-
-#brddesc * {
-	padding-top: 0;
-	padding-bottom: 0;
-	}
-
 #brdmenu li {
 	display: inline;
 	margin-right: 12px;
@@ -435,12 +438,11 @@
 
 .pun td, .pun th {
 	padding: 4px 6px;
-	line-height: 1.273em;
 	text-align: left;
 	font-weight: normal;
 	}
 
-.pun td {
+.pun td, .pun th {
 	border-style: solid none none solid;
 	border-width: 1px;
 	}
@@ -525,7 +527,8 @@
 	}
 
 .pun .icon {
-	border-width: 7px;
+	margin: 0.1em 0 0 0.2em;
+	border-width: 0.6em;
 	border-style: solid;
 	height: 0;
 	width: 0;
@@ -598,14 +601,6 @@
 	margin-right: 10px
 	}
 
-.pun select {
-	padding-top: 1px;
-	padding-bottom: 1px;
-	}
-
-.pun fieldset .rbox {
-	}
-
 .pun fieldset .rbox br {
 	display: none;
 	}
@@ -665,6 +660,58 @@
 	margin-right: 12px;
 	}
 
+.pun .multiselect {
+	float: left;
+	padding-bottom: 7px;
+	}
+
+.pun .checklist {
+	border-width: 1px;
+	border-style: solid;
+	max-height: 9em;
+	width: 20em;
+	overflow: auto;
+	padding: 0.3em 0.5em;
+	margin: 0.25em 16px 0 0.15em;
+	}
+
+.pun .checklist fieldset {
+	border: 0;
+	padding: 0;
+	}
+
+.pun .checklist legend {
+	padding: 0;
+	}
+
+.pun .checklist legend span {
+	width: auto;
+	max-width: 25em;
+	}
+
+.pun .checklist .checklist-item {
+	position: relative;
+	margin: .2em 0;
+	}
+
+.pun .checklist .checklist-item label {
+	padding: 0 0 0 2.1em;
+	}
+
+.pun .checklist .checklist-item .fld-input {
+	position: absolute;
+	left: 0;
+	top: 0;
+	padding: 0;
+	margin: 0;
+	}
+
+.pun .checklist .checklist-item input {
+	margin: 0;
+	height: 1.15em;
+	width: 1.55em;
+	}
+
 /*****************************************************************
 7. PROFILES AND ADMIN
 *****************************************************************/
@@ -674,12 +721,12 @@
 	}
 
 .pun .block2col .blockform, .pun .block2col .block {
-	margin-left: 17em
+	margin-left: 14em
 	}
 
 .pun .blockmenu {
 	float:left;
-	width: 16em
+	width: 13em
 	}
 
 .pun .blockmenu li {
@@ -756,7 +803,7 @@
 	}
 
 .pun .postleft dl {
-	padding: 0.75em 6px;
+	padding: 6px;
 	}
 
 .pun .postleft .usercontacts, .pun .postleft .icon {
@@ -807,12 +854,19 @@
 
 .pun .postfootright ul, .pun .postfootright div, .pun .postfootright p,
 .pun .postfootleft p {
-	padding: 6px 6px 6px 6px;
+	padding: 10px 6px 5px 6px;
 	}
 
 .pun .postfootright li {
 	display: inline;
-	margin-left: 12px;
+	}
+
+.pun .postfootright li:before {
+	content: " | ";
+	}
+
+.pun .postfootright li:first-child:before {
+	content: "";
 	}
 
 .pun .postfootright a:link, .pun .postfootright a:visited {
@@ -926,6 +980,14 @@
 	text-align: center;
 	}
 
+#brdwelcome, #brdfooter dl a, div.blockmenu li, div.rbox input {
+	line-height: 1.4em
+	}
+
+#announce div.inbox div {
+	padding: 3px 0
+	}
+
 /*****************************************************************
 COLOUR SCHEME
 *****************************************************************/
@@ -973,6 +1035,14 @@ body {
 	color: #333
 	}
 
+.pun .multiselect, .pun .checklist {
+	color: #333;
+	}
+
+.pun .checklist {
+	border-color: #ACA899;
+	}
+
 /* posts
 ----------------------------------------------------------------*/
 
@@ -1020,10 +1090,14 @@ body {
 	border-color: #cedeb9
 	}
 
-.pun th, .pun fieldset {
+.pun th {
 	border-color: #d1d1d1
 	}
 
+.pun fieldset {
+	border-color: #aca899
+	}
+
 #adminconsole td, #adminconsole th {
 	border-color: #f1f1f1
 	}
diff --git a/style/Mercury.css b/style/Mercury.css
index 93cea7e..cffebb1 100644
--- a/style/Mercury.css
+++ b/style/Mercury.css
@@ -48,6 +48,9 @@
 	min-height: 1px
 	}
 
+.clearl {
+	clear: left;
+	}
 
 /* Hidden Elements
 ----------------------------------------------------------------*/
@@ -72,6 +75,7 @@
 
 .pun {
 	font: 68.75%/1.4545em Verdana, Helvetica, Arial, sans-serif;
+	line-height: normal;
 	}
 
 .pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun samp, .pun legend {
@@ -108,7 +112,7 @@
 .pun h2 {
 	font-size: 1em;
 	font-weight: normal;
-	padding: 4px 7px;
+	padding: 4px 6px;
 	}
 
 .pun h3 {
@@ -209,6 +213,11 @@
 /* Page Layout
 ----------------------------------------------------------------*/
 
+html, body {
+	margin: 0;
+	padding: 0
+	}
+
 .pun {
 	margin: 12px 20px
 	}
@@ -225,6 +234,10 @@
 	margin: 0 0 12px 0;
 	}
 
+#brdtitle p {
+	padding-top: 0px
+	}
+
 #announce, #brdstats {
 	margin: 12px 0 12px 0;
 	}
@@ -281,21 +294,11 @@
 ----------------------------------------------------------------*/
 
 #brdtitle h1 {
-	font-size: 1.454em;
+	font-size: 1.4em;
 	font-weight: bold;
-	line-height: 1em;
 	padding: 3px 0 0 0;
 	}
 
-#brddesc {
-	padding: 3px 0;
-	}
-
-#brddesc * {
-	padding-top: 0;
-	padding-bottom: 0;
-	}
-
 #brdmenu li {
 	display: inline;
 	margin-right: 12px;
@@ -435,12 +438,11 @@
 
 .pun td, .pun th {
 	padding: 4px 6px;
-	line-height: 1.273em;
 	text-align: left;
 	font-weight: normal;
 	}
 
-.pun td {
+.pun td, .pun th {
 	border-style: solid none none solid;
 	border-width: 1px;
 	}
@@ -525,7 +527,8 @@
 	}
 
 .pun .icon {
-	border-width: 7px;
+	margin: 0.1em 0 0 0.2em;
+	border-width: 0.6em;
 	border-style: solid;
 	height: 0;
 	width: 0;
@@ -598,14 +601,6 @@
 	margin-right: 10px
 	}
 
-.pun select {
-	padding-top: 1px;
-	padding-bottom: 1px;
-	}
-
-.pun fieldset .rbox {
-	}
-
 .pun fieldset .rbox br {
 	display: none;
 	}
@@ -665,6 +660,58 @@
 	margin-right: 12px;
 	}
 
+.pun .multiselect {
+	float: left;
+	padding-bottom: 7px;
+	}
+
+.pun .checklist {
+	border-width: 1px;
+	border-style: solid;
+	max-height: 9em;
+	width: 20em;
+	overflow: auto;
+	padding: 0.3em 0.5em;
+	margin: 0.25em 16px 0 0.15em;
+	}
+
+.pun .checklist fieldset {
+	border: 0;
+	padding: 0;
+	}
+
+.pun .checklist legend {
+	padding: 0;
+	}
+
+.pun .checklist legend span {
+	width: auto;
+	max-width: 25em;
+	}
+
+.pun .checklist .checklist-item {
+	position: relative;
+	margin: .2em 0;
+	}
+
+.pun .checklist .checklist-item label {
+	padding: 0 0 0 2.1em;
+	}
+
+.pun .checklist .checklist-item .fld-input {
+	position: absolute;
+	left: 0;
+	top: 0;
+	padding: 0;
+	margin: 0;
+	}
+
+.pun .checklist .checklist-item input {
+	margin: 0;
+	height: 1.15em;
+	width: 1.55em;
+	}
+
 /*****************************************************************
 7. PROFILES AND ADMIN
 *****************************************************************/
@@ -674,12 +721,12 @@
 	}
 
 .pun .block2col .blockform, .pun .block2col .block {
-	margin-left: 17em
+	margin-left: 14em
 	}
 
 .pun .blockmenu {
 	float:left;
-	width: 16em
+	width: 13em
 	}
 
 .pun .blockmenu li {
@@ -756,7 +803,7 @@
 	}
 
 .pun .postleft dl {
-	padding: 0.75em 6px;
+	padding: 6px;
 	}
 
 .pun .postleft .usercontacts, .pun .postleft .icon {
@@ -807,12 +854,19 @@
 
 .pun .postfootright ul, .pun .postfootright div, .pun .postfootright p,
 .pun .postfootleft p {
-	padding: 6px 6px 6px 6px;
+	padding: 10px 6px 5px 6px;
 	}
 
 .pun .postfootright li {
 	display: inline;
-	margin-left: 12px;
+	}
+
+.pun .postfootright li:before {
+	content: " | ";
+	}
+
+.pun .postfootright li:first-child:before {
+	content: "";
 	}
 
 .pun .postfootright a:link, .pun .postfootright a:visited {
@@ -926,6 +980,14 @@
 	text-align: center;
 	}
 
+#brdwelcome, #brdfooter dl a, div.blockmenu li, div.rbox input {
+	line-height: 1.4em
+	}
+
+#announce div.inbox div {
+	padding: 3px 0
+	}
+
 /*****************************************************************
 COLOUR SCHEME
 *****************************************************************/
@@ -978,6 +1040,14 @@ body {
 	color: #d4d4d4
 	}
 
+.pun .multiselect, .pun .checklist {
+	color: #D4D4D4;
+	}
+
+.pun .checklist {
+	border-color: #666;
+	}
+
 /* Posts
 ----------------------------------------------------------------*/
 
@@ -1021,10 +1091,14 @@ body {
 	border-color: #565656
 	}
 
-.pun th, .pun fieldset {
+.pun th {
 	border-color: #484848
 	}
 
+.pun fieldset {
+	border-color: #565656
+	}
+
 #adminconsole td, #adminconsole th {
 	border-color: #383838
 	}
diff --git a/style/Oxygen.css b/style/Oxygen.css
index 1af7310..4705bfb 100644
--- a/style/Oxygen.css
+++ b/style/Oxygen.css
@@ -48,6 +48,9 @@
 	min-height: 1px
 	}
 
+.clearl {
+	clear: left;
+	}
 
 /* Hidden Elements
 ----------------------------------------------------------------*/
@@ -72,6 +75,7 @@
 
 .pun {
 	font: 68.75%/1.4545em Verdana, Helvetica, Arial, sans-serif;
+	line-height: normal;
 	}
 
 .pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun small, .pun samp, .pun legend {
@@ -108,7 +112,7 @@
 .pun h2 {
 	font-size: 1em;
 	font-weight: normal;
-	padding: 4px 7px;
+	padding: 4px 6px;
 	}
 
 .pun h3 {
@@ -210,6 +214,11 @@
 /* Page Layout
 ----------------------------------------------------------------*/
 
+html, body {
+	margin: 0;
+	padding: 0
+	}
+
 .pun {
 	margin: 12px 20px
 	}
@@ -226,6 +235,10 @@
 	margin: 0 0 12px 0;
 	}
 
+#brdtitle p {
+	padding-top: 0px
+	}
+
 #announce, #brdstats {
 	margin: 12px 0 12px 0;
 	}
@@ -282,21 +295,11 @@
 ----------------------------------------------------------------*/
 
 #brdtitle h1 {
-	font-size: 1.454em;
+	font-size: 1.4em;
 	font-weight: bold;
-	line-height: 1em;
 	padding: 3px 0 0 0;
 	}
 
-#brddesc {
-	padding: 3px 0;
-	}
-
-#brddesc * {
-	padding-top: 0;
-	padding-bottom: 0;
-	}
-
 #brdmenu li {
 	display: inline;
 	margin-right: 12px;
@@ -436,12 +439,11 @@
 
 .pun td, .pun th {
 	padding: 4px 6px;
-	line-height: 1.273em;
 	text-align: left;
 	font-weight: normal;
 	}
 
-.pun td {
+.pun td, .pun th {
 	border-style: solid none none solid;
 	border-width: 1px;
 	}
@@ -526,7 +528,8 @@
 	}
 
 .pun .icon {
-	border-width: 7px;
+	margin: 0.1em 0 0 0.2em;
+	border-width: 0.6em;
 	border-style: solid;
 	height: 0;
 	width: 0;
@@ -599,14 +602,6 @@
 	margin-right: 10px
 	}
 
-.pun select {
-	padding-top: 1px;
-	padding-bottom: 1px;
-	}
-
-.pun fieldset .rbox {
-	}
-
 .pun fieldset .rbox br {
 	display: none;
 	}
@@ -666,6 +661,58 @@
 	margin-right: 12px;
 	}
 
+.pun .multiselect {
+	float: left;
+	padding-bottom: 7px;
+	}
+
+.pun .checklist {
+	border-width: 1px;
+	border-style: solid;
+	max-height: 9em;
+	width: 20em;
+	overflow: auto;
+	padding: 0.3em 0.5em;
+	margin: 0.25em 16px 0 0.15em;
+	}
+
+.pun .checklist fieldset {
+	border: 0;
+	padding: 0;
+	}
+
+.pun .checklist legend {
+	padding: 0;
+	}
+
+.pun .checklist legend span {
+	width: auto;
+	max-width: 25em;
+	}
+
+.pun .checklist .checklist-item {
+	position: relative;
+	margin: .2em 0;
+	}
+
+.pun .checklist .checklist-item label {
+	padding: 0 0 0 2.1em;
+	}
+
+.pun .checklist .checklist-item .fld-input {
+	position: absolute;
+	left: 0;
+	top: 0;
+	padding: 0;
+	margin: 0;
+	}
+
+.pun .checklist .checklist-item input {
+	margin: 0;
+	height: 1.15em;
+	width: 1.55em;
+	}
+
 /*****************************************************************
 7. PROFILES AND ADMIN
 *****************************************************************/
@@ -675,12 +722,12 @@
 	}
 
 .pun .block2col .blockform, .pun .block2col .block {
-	margin-left: 17em
+	margin-left: 14em
 	}
 
 .pun .blockmenu {
 	float:left;
-	width: 16em
+	width: 13em
 	}
 
 .pun .blockmenu li {
@@ -757,7 +804,7 @@
 	}
 
 .pun .postleft dl {
-	padding: 0.75em 6px;
+	padding: 6px;
 	}
 
 .pun .postleft .usercontacts, .pun .postleft .icon {
@@ -808,12 +855,19 @@
 
 .pun .postfootright ul, .pun .postfootright div, .pun .postfootright p,
 .pun .postfootleft p {
-	padding: 6px 6px 6px 6px;
+	padding: 10px 6px 5px 6px;
 	}
 
 .pun .postfootright li {
 	display: inline;
-	margin-right: 12px;
+	}
+
+.pun .postfootright li:before {
+	content: " | ";
+	}
+
+.pun .postfootright li:first-child:before {
+	content: "";
 	}
 
 .pun .postfootright a:link, .pun .postfootright a:visited {
@@ -927,6 +981,14 @@
 	text-align: center;
 	}
 
+#brdwelcome, #brdfooter dl a, div.blockmenu li, div.rbox input {
+	line-height: 1.4em
+	}
+
+#announce div.inbox div {
+	padding: 3px 0
+	}
+
 /*****************************************************************
 COLOUR SCHEME
 *****************************************************************/
@@ -974,6 +1036,14 @@ body {
 	color: #333
 	}
 
+.pun .multiselect, .pun .checklist {
+	color: #333;
+	}
+
+.pun .checklist {
+	border-color: #ACA899;
+	}
+
 /* Posts
 ----------------------------------------------------------------*/
 
@@ -1021,10 +1091,14 @@ body {
 	border-color: #bbcede
 	}
 
-.pun th, .pun fieldset {
+.pun th {
 	border-color: #d1d1d1
 	}
 
+.pun fieldset {
+	border-color: #aca899
+	}
+
 #adminconsole td, #adminconsole th {
 	border-color: #f1f1f1
 	}
diff --git a/style/Radium.css b/style/Radium.css
index babe283..8b43a10 100644
--- a/style/Radium.css
+++ b/style/Radium.css
@@ -48,6 +48,9 @@
 	min-height: 1px
 	}
 
+.clearl {
+	clear: left;
+	}
 
 /* Hidden Elements
 ----------------------------------------------------------------*/
@@ -72,6 +75,7 @@
 
 .pun {
 	font: 68.75%/1.4545em Verdana, Helvetica, Arial, sans-serif;
+	line-height: normal;
 	}
 
 .pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun samp, .pun legend {
@@ -108,7 +112,7 @@
 .pun h2 {
 	font-size: 1em;
 	font-weight: normal;
-	padding: 4px 7px;
+	padding: 4px 6px;
 	}
 
 .pun h3 {
@@ -209,6 +213,11 @@
 /* Page Layout
 ----------------------------------------------------------------*/
 
+html, body {
+	margin: 0;
+	padding: 0
+	}
+
 .pun {
 	margin: 12px 20px
 	}
@@ -225,6 +234,10 @@
 	margin: 0 0 12px 0;
 	}
 
+#brdtitle p {
+	padding-top: 0px
+	}
+
 #announce, #brdstats {
 	margin: 12px 0 12px 0;
 	}
@@ -281,21 +294,11 @@
 ----------------------------------------------------------------*/
 
 #brdtitle h1 {
-	font-size: 1.454em;
+	font-size: 1.4em;
 	font-weight: bold;
-	line-height: 1em;
 	padding: 3px 0 0 0;
 	}
 
-#brddesc {
-	padding: 3px 0;
-	}
-
-#brddesc * {
-	padding-top: 0;
-	padding-bottom: 0;
-	}
-
 #brdmenu li {
 	display: inline;
 	margin-right: 12px;
@@ -435,12 +438,11 @@
 
 .pun td, .pun th {
 	padding: 4px 6px;
-	line-height: 1.273em;
 	text-align: left;
 	font-weight: normal;
 	}
 
-.pun td {
+.pun td, .pun th {
 	border-style: solid none none solid;
 	border-width: 1px;
 	}
@@ -525,7 +527,8 @@
 	}
 
 .pun .icon {
-	border-width: 7px;
+	margin: 0.1em 0 0 0.2em;
+	border-width: 0.6em;
 	border-style: solid;
 	height: 0;
 	width: 0;
@@ -598,14 +601,6 @@
 	margin-right: 10px
 	}
 
-.pun select {
-	padding-top: 1px;
-	padding-bottom: 1px;
-	}
-
-.pun fieldset .rbox {
-	}
-
 .pun fieldset .rbox br {
 	display: none;
 	}
@@ -665,6 +660,58 @@
 	margin-right: 12px;
 	}
 
+.pun .multiselect {
+	float: left;
+	padding-bottom: 7px;
+	}
+
+.pun .checklist {
+	border-width: 1px;
+	border-style: solid;
+	max-height: 9em;
+	width: 20em;
+	overflow: auto;
+	padding: 0.3em 0.5em;
+	margin: 0.25em 16px 0 0.15em;
+	}
+
+.pun .checklist fieldset {
+	border: 0;
+	padding: 0;
+	}
+
+.pun .checklist legend {
+	padding: 0;
+	}
+
+.pun .checklist legend span {
+	width: auto;
+	max-width: 25em;
+	}
+
+.pun .checklist .checklist-item {
+	position: relative;
+	margin: .2em 0;
+	}
+
+.pun .checklist .checklist-item label {
+	padding: 0 0 0 2.1em;
+	}
+
+.pun .checklist .checklist-item .fld-input {
+	position: absolute;
+	left: 0;
+	top: 0;
+	padding: 0;
+	margin: 0;
+	}
+
+.pun .checklist .checklist-item input {
+	margin: 0;
+	height: 1.15em;
+	width: 1.55em;
+	}
+
 /*****************************************************************
 7. PROFILES AND ADMIN
 *****************************************************************/
@@ -674,12 +721,12 @@
 	}
 
 .pun .block2col .blockform, .pun .block2col .block {
-	margin-left: 17em
+	margin-left: 14em
 	}
 
 .pun .blockmenu {
 	float:left;
-	width: 16em
+	width: 13em
 	}
 
 .pun .blockmenu li {
@@ -756,7 +803,7 @@
 	}
 
 .pun .postleft dl {
-	padding: 0.75em 6px;
+	padding: 6px;
 	}
 
 .pun .postleft .usercontacts, .pun .postleft .icon {
@@ -807,12 +854,19 @@
 
 .pun .postfootright ul, .pun .postfootright div, .pun .postfootright p,
 .pun .postfootleft p {
-	padding: 6px 6px 6px 6px;
+	padding: 10px 6px 5px 6px;
 	}
 
 .pun .postfootright li {
 	display: inline;
-	margin-left: 12px;
+	}
+
+.pun .postfootright li:before {
+	content: " | ";
+	}
+
+.pun .postfootright li:first-child:before {
+	content: "";
 	}
 
 .pun .postfootright a:link, .pun .postfootright a:visited {
@@ -926,6 +980,14 @@
 	text-align: center;
 	}
 
+#brdwelcome, #brdfooter dl a, div.blockmenu li, div.rbox input {
+	line-height: 1.4em
+	}
+
+#announce div.inbox div {
+	padding: 3px 0
+	}
+
 /*****************************************************************
 COLOUR SCHEME
 *****************************************************************/
@@ -978,6 +1040,14 @@ body {
 	color: #d4d4d4
 	}
 
+.pun .multiselect, .pun .checklist {
+	color: #D4D4D4;
+	}
+
+.pun .checklist {
+	border-color: #666;
+	}
+
 /* Posts
 ----------------------------------------------------------------*/
 
@@ -1021,10 +1091,14 @@ body {
 	border-color: #565656
 	}
 
-.pun th, .pun fieldset {
+.pun th {
 	border-color: #484848
 	}
 
+.pun fieldset {
+	border-color: #606060
+	}
+
 #adminconsole td, #adminconsole th {
 	border-color: #383838
 	}
diff --git a/style/Sulfur.css b/style/Sulfur.css
index e9d7e5a..b275248 100644
--- a/style/Sulfur.css
+++ b/style/Sulfur.css
@@ -48,6 +48,9 @@
 	min-height: 1px
 	}
 
+.clearl {
+	clear: left;
+	}
 
 /* Hidden Elements
 ----------------------------------------------------------------*/
@@ -72,6 +75,7 @@
 
 .pun {
 	font: 68.75%/1.4545em Verdana, Helvetica, Arial, sans-serif;
+	line-height: normal;
 	}
 
 .pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun samp, .pun legend {
@@ -108,7 +112,7 @@
 .pun h2 {
 	font-size: 1em;
 	font-weight: normal;
-	padding: 4px 7px;
+	padding: 4px 6px;
 	}
 
 .pun h3 {
@@ -209,6 +213,11 @@
 /* Page Layout
 ----------------------------------------------------------------*/
 
+html, body {
+	margin: 0;
+	padding: 0
+	}
+
 .pun {
 	margin: 12px 20px
 	}
@@ -225,6 +234,10 @@
 	margin: 0 0 12px 0;
 	}
 
+#brdtitle p {
+	padding-top: 0px
+	}
+
 #announce, #brdstats {
 	margin: 12px 0 12px 0;
 	}
@@ -281,21 +294,11 @@
 ----------------------------------------------------------------*/
 
 #brdtitle h1 {
-	font-size: 1.454em;
+	font-size: 1.4em;
 	font-weight: bold;
-	line-height: 1em;
 	padding: 3px 0 0 0;
 	}
 
-#brddesc {
-	padding: 3px 0;
-	}
-
-#brddesc * {
-	padding-top: 0;
-	padding-bottom: 0;
-	}
-
 #brdmenu li {
 	display: inline;
 	margin-right: 12px;
@@ -435,12 +438,11 @@
 
 .pun td, .pun th {
 	padding: 4px 6px;
-	line-height: 1.273em;
 	text-align: left;
 	font-weight: normal;
 	}
 
-.pun td {
+.pun td, .pun th {
 	border-style: solid none none solid;
 	border-width: 1px;
 	}
@@ -525,7 +527,8 @@
 	}
 
 .pun .icon {
-	border-width: 7px;
+	margin: 0.1em 0 0 0.2em;
+	border-width: 0.6em;
 	border-style: solid;
 	height: 0;
 	width: 0;
@@ -598,14 +601,6 @@
 	margin-right: 10px
 	}
 
-.pun select {
-	padding-top: 1px;
-	padding-bottom: 1px;
-	}
-
-.pun fieldset .rbox {
-	}
-
 .pun fieldset .rbox br {
 	display: none;
 	}
@@ -665,6 +660,58 @@
 	margin-right: 12px;
 	}
 
+.pun .multiselect {
+	float: left;
+	padding-bottom: 7px;
+	}
+
+.pun .checklist {
+	border-width: 1px;
+	border-style: solid;
+	max-height: 9em;
+	width: 20em;
+	overflow: auto;
+	padding: 0.3em 0.5em;
+	margin: 0.25em 16px 0 0.15em;
+	}
+
+.pun .checklist fieldset {
+	border: 0;
+	padding: 0;
+	}
+
+.pun .checklist legend {
+	padding: 0;
+	}
+
+.pun .checklist legend span {
+	width: auto;
+	max-width: 25em;
+	}
+
+.pun .checklist .checklist-item {
+	position: relative;
+	margin: .2em 0;
+	}
+
+.pun .checklist .checklist-item label {
+	padding: 0 0 0 2.1em;
+	}
+
+.pun .checklist .checklist-item .fld-input {
+	position: absolute;
+	left: 0;
+	top: 0;
+	padding: 0;
+	margin: 0;
+	}
+
+.pun .checklist .checklist-item input {
+	margin: 0;
+	height: 1.15em;
+	width: 1.55em;
+	}
+
 /*****************************************************************
 7. PROFILES AND ADMIN
 *****************************************************************/
@@ -674,12 +721,12 @@
 	}
 
 .pun .block2col .blockform, .pun .block2col .block {
-	margin-left: 17em
+	margin-left: 14em
 	}
 
 .pun .blockmenu {
 	float:left;
-	width: 16em
+	width: 13em
 	}
 
 .pun .blockmenu li {
@@ -756,7 +803,7 @@
 	}
 
 .pun .postleft dl {
-	padding: 0.75em 6px;
+	padding: 6px;
 	}
 
 .pun .postleft .usercontacts, .pun .postleft .icon {
@@ -807,12 +854,19 @@
 
 .pun .postfootright ul, .pun .postfootright div, .pun .postfootright p,
 .pun .postfootleft p {
-	padding: 6px 6px 6px 6px;
+	padding: 10px 6px 5px 6px;
 	}
 
 .pun .postfootright li {
 	display: inline;
-	margin-left: 12px;
+	}
+
+.pun .postfootright li:before {
+	content: " | ";
+	}
+
+.pun .postfootright li:first-child:before {
+	content: "";
 	}
 
 .pun .postfootright a:link, .pun .postfootright a:visited {
@@ -926,6 +980,14 @@
 	text-align: center;
 	}
 
+#brdwelcome, #brdfooter dl a, div.blockmenu li, div.rbox input {
+	line-height: 1.4em
+	}
+
+#announce div.inbox div {
+	padding: 3px 0
+	}
+
 /*****************************************************************
 COLOUR SCHEME
 *****************************************************************/
@@ -973,6 +1035,14 @@ body {
 	color: #333
 	}
 
+.pun .multiselect, .pun .checklist {
+	color: #333;
+	}
+
+.pun .checklist {
+	border-color: #ACA899;
+	}
+
 /* Posts
 ----------------------------------------------------------------*/
 
@@ -1020,10 +1090,14 @@ body {
 	border-color: #e1c3c3
 	}
 
-.pun th, .pun fieldset {
+.pun th {
 	border-color: #d1d1d1
 	}
 
+.pun fieldset {
+	border-color: #aca899
+	}
+
 #adminconsole td, #adminconsole th {
 	border-color: #f1f1f1
 	}
diff --git a/style/Technetium.css b/style/Technetium.css
index 765e11b..d8fd7be 100644
--- a/style/Technetium.css
+++ b/style/Technetium.css
@@ -63,6 +63,9 @@ body {
 	min-height: 1px
 	}
 
+.clearl {
+	clear: left;
+	}
 
 /* Hidden Elements
 ----------------------------------------------------------------*/
@@ -776,6 +779,51 @@ body {
 	padding: 6px 15px;
 	}
 
+.pun .multiselect {
+	float: left;
+	padding-bottom: 7px;
+	}
+
+.pun .checklist {
+	border-width: 1px;
+	border-style: solid;
+	max-height: 9em;
+	width: 20em;
+	overflow: auto;
+	padding: 0.25em 0.5em;
+	margin: 0.25em 16px 0 0.15em;
+	}
+
+.pun .checklist legend {
+	padding: 0;
+	}
+
+.pun .checklist legend span {
+	width: auto;
+	max-width: 25em;
+	}
+
+.pun .checklist .checklist-item {
+	position: relative;
+	}
+
+.pun .checklist .checklist-item label {
+	padding: 0 0 0 1.75em;
+	}
+
+.pun .checklist .checklist-item .fld-input {
+	position: absolute;
+	left: 0;
+	top: 0;
+	padding: 0;
+	margin: 0;
+	}
+
+.pun .checklist .checklist-item input {
+	margin: 0;
+	height: 1.55em;
+	width: 1.55em;
+	}
 
 /*****************************************************************
 7. PROFILES AND ADMIN
@@ -921,6 +969,14 @@ body {
 	display: inline;
 	}
 
+.pun .postfootright li:before {
+	content: " | ";
+	}
+
+.pun .postfootright li:first-child:before {
+	content: "";
+	}
+
 .pun .postfootright a:link, .pun .postfootright a:visited {
 	text-decoration: none
 	}
@@ -973,7 +1029,7 @@ body {
 	overflow-y: auto
 	}
 
-.pun .postmsg img.postimg, .pun .postmsg a img.postimg {
+.pun .postmsg .postimg img, .pun .postmsg a .postimg img {
 	max-width: 100%;
 	vertical-align: middle;
 	}
@@ -1170,6 +1226,14 @@ body {
 	padding-left: 16px;
 	}
 
+.pun .multiselect {
+	color: #122434;
+	}
+
+.pun .checklist {
+	background: white;
+	border-color: #A2B5CC;
+	}
 
 /* Posts
 ----------------------------------------------------------------*/
diff --git a/style/imports/base_admin.css b/style/imports/base_admin.css
index 3c3c966..26a29c4 100644
--- a/style/imports/base_admin.css
+++ b/style/imports/base_admin.css
@@ -38,10 +38,13 @@ TABLE#forumperms .atcl {TEXT-ALIGN: left; WIDTH: 15em; WHITE-SPACE: nowrap}
 /*** User/Ban Search Result Tables ***/
 #users2 TH, #bans1 TH {TEXT-ALIGN: left}
 #users2 TH.tcmod {TEXT-ALIGN: center}
-#users2 .tcl, #users2 .tc3, #users2 .tc5, #bans1 .tcl, #bans1 .tc3, #bans1 .tc5, #bans1 .tc6 {WIDTH: 15%; TEXT-ALIGN: left; PADDING: 4px 6px}
-#users2 .tc2, #bans1 .tc2 {WIDTH: 22%; TEXT-ALIGN: left; PADDING: 4px 6px}
-#users2 .tc4, #bans1 .tc4 {WIDTH: 8%; TEXT-ALIGN: center}
-#users2 .tcr, #bans1 .tcr {WHITE-SPACE: nowrap}
+#users2 .tcl, #bans1 .tcl {WIDTH: auto; TEXT-ALIGN: left; PADDING: 4px 6px}
+#users2 .tc2, #bans1 .tc2 {WIDTH: 18%; TEXT-ALIGN: left; PADDING: 4px 6px}
+#users2 .tc3, #users2 .tc5, #bans1 .tc3, #bans1 .tc5, #bans1 .tc6 {WIDTH: 12%; TEXT-ALIGN: left; PADDING: 4px 6px}
+#users2 .tc4, #bans1 .tc4 {WIDTH: 10%; TEXT-ALIGN: center}
+#users2 .tcr {WIDTH: 20%; WHITE-SPACE: nowrap}
+#bans1 .tcr {WIDTH: 15%; WHITE-SPACE: nowrap}
+#users2 .tcmod {WIDTH: 10%; TEXT-ALIGN: center}
 #adminconsole #linkst, #adminconsole #linksb A {FONT-WEIGHT: bold}
 
 /*** Plugins ***/
diff --git a/style/imports/minmax.js b/style/imports/minmax.js
index 65fddca..c8cb073 100644
--- a/style/imports/minmax.js
+++ b/style/imports/minmax.js
@@ -25,6 +25,16 @@ function minmax_bind(el) {
 		minmax_elements[minmax_elements.length]= el;
 		minmax_delayout();
 	}
+
+	if (cs['min-width'])
+		st['minWidth']= cs['min-width'];
+
+	ms= cs['minWidth'];
+	if (ms && ms!='auto' && ms!='none' && ms!='0' && ms!='') {
+		st.minmaxWidth= cs.width;
+		minmax_elements[minmax_elements.length]= el;
+		minmax_delayout();
+	}
 }
 
 var minmax_delaying= false;
@@ -81,4 +91,4 @@ minmax_scan();
 minmax_scanner= window.setInterval(minmax_scan, minmax_SCANDELAY);
 window.attachEvent('onload', minmax_stop);
 
-@end @*/
+@end @*/
\ No newline at end of file
diff --git a/userlist.php b/userlist.php
index c37242a..0f1480d 100644
--- a/userlist.php
+++ b/userlist.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -11,9 +11,9 @@ require PUN_ROOT.'include/common.php';
 
 
 if ($pun_user['g_read_board'] == '0')
-	message($lang_common['No view']);
+	message($lang_common['No view'], false, '403 Forbidden');
 else if ($pun_user['g_view_users'] == '0')
-	message($lang_common['No permission']);
+	message($lang_common['No permission'], false, '403 Forbidden');
 
 // Load the userlist.php language file
 require PUN_ROOT.'lang/'.$pun_user['language'].'/userlist.php';
@@ -63,7 +63,7 @@ require PUN_ROOT.'header.php';
 
 ?>
 <div class="blockform">
-	<h2><span><?php echo $lang_common['User list'] ?></span></h2>
+	<h2><span><?php echo $lang_search['User search'] ?></span></h2>
 	<div class="box">
 		<form id="userlist" method="get" action="userlist.php">
 			<div class="inform">
@@ -119,6 +119,7 @@ while ($cur_group = $db->fetch_assoc($result))
 </div>
 
 <div id="users1" class="blocktable">
+	<h2><span><?php echo $lang_common['User list'] ?></span></h2>
 	<div class="box">
 		<div class="inbox">
 			<table cellspacing="0">
diff --git a/viewforum.php b/viewforum.php
index 7a464f0..b629019 100644
--- a/viewforum.php
+++ b/viewforum.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -11,12 +11,12 @@ require PUN_ROOT.'include/common.php';
 
 
 if ($pun_user['g_read_board'] == '0')
-	message($lang_common['No view']);
+	message($lang_common['No view'], false, '403 Forbidden');
 
 
 $id = isset($_GET['id']) ? intval($_GET['id']) : 0;
 if ($id < 1)
-	message($lang_common['Bad request']);
+	message($lang_common['Bad request'], false, '404 Not Found');
 
 // Load the viewforum.php language file
 require PUN_ROOT.'lang/'.$pun_user['language'].'/forum.php';
@@ -28,7 +28,7 @@ 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']);
+	message($lang_common['Bad request'], false, '404 Not Found');
 
 $cur_forum = $db->fetch_assoc($result);
 
@@ -78,29 +78,10 @@ $start_from = $pun_user['disp_topics'] * ($p - 1);
 // Generate paging links
 $paging_links = '<span class="pages-label">'.$lang_common['Pages'].' </span>'.paginate($num_pages, $p, 'viewforum.php?id='.$id);
 
-
-// Add relationship meta tags
-$page_head = array();
-$page_head['up'] = '<link rel="up" href="index.php" title="'.$lang_common['Forum index'].'" />';
-
-if ($num_pages > 1)
-{
-	if ($p > 1)
-	{
-		$page_head['first'] = '<link rel="first" href="viewforum.php?id='.$id.'&amp;p=1" title="'.sprintf($lang_common['Page'], 1).'" />';
-		$page_head['prev'] = '<link rel="prev" href="viewforum.php?id='.$id.'&amp;p='.($p-1).'" title="'.sprintf($lang_common['Page'], $p-1).'" />';
-	}
-	if ($p < $num_pages)
-	{
-		$page_head['next'] = '<link rel="next" href="viewforum.php?id='.$id.'&amp;p='.($p+1).'" title="'.sprintf($lang_common['Page'], $p+1).'" />';
-		$page_head['last'] = '<link rel="last" href="viewforum.php?id='.$id.'&amp;p='.$num_pages.'" title="'.sprintf($lang_common['Page'], $num_pages).'" />';
-	}
-}
-
 if ($pun_config['o_feed_type'] == '1')
-	$page_head['feed'] = '<link rel="alternate" type="application/rss+xml" href="extern.php?action=feed&amp;fid='.$id.'&amp;type=rss" title="'.$lang_common['RSS forum feed'].'" />';
+	$page_head = array('feed' => '<link rel="alternate" type="application/rss+xml" href="extern.php?action=feed&amp;fid='.$id.'&amp;type=rss" title="'.$lang_common['RSS forum feed'].'" />');
 else if ($pun_config['o_feed_type'] == '2')
-	$page_head['feed'] = '<link rel="alternate" type="application/atom+xml" href="extern.php?action=feed&amp;fid='.$id.'&amp;type=atom" title="'.$lang_common['Atom forum feed'].'" />';
+	$page_head = array('feed' => '<link rel="alternate" type="application/atom+xml" href="extern.php?action=feed&amp;fid='.$id.'&amp;type=atom" title="'.$lang_common['Atom forum feed'].'" />');
 
 $forum_actions = array();
 
@@ -188,7 +169,7 @@ if ($db->num_rows($result))
 		$item_status = ($topic_count % 2 == 0) ? 'roweven' : 'rowodd';
 		$icon_type = 'icon';
 
-		if ($cur_topic['moved_to'] == null)
+		if (is_null($cur_topic['moved_to']))
 			$last_post = '<a href="viewtopic.php?pid='.$cur_topic['last_post_id'].'#p'.$cur_topic['last_post_id'].'">'.format_time($cur_topic['last_post']).'</a> <span class="byuser">'.$lang_common['by'].' '.pun_htmlspecialchars($cur_topic['last_poster']).'</span>';
 		else
 			$last_post = '- - -';
@@ -217,7 +198,7 @@ if ($db->num_rows($result))
 			$item_status .= ' iclosed';
 		}
 
-		if (!$pun_user['is_guest'] && $cur_topic['last_post'] > $pun_user['last_visit'] && (!isset($tracked_topics['topics'][$cur_topic['id']]) || $tracked_topics['topics'][$cur_topic['id']] < $cur_topic['last_post']) && (!isset($tracked_topics['forums'][$id]) || $tracked_topics['forums'][$id] < $cur_topic['last_post']) && $cur_topic['moved_to'] == null)
+		if (!$pun_user['is_guest'] && $cur_topic['last_post'] > $pun_user['last_visit'] && (!isset($tracked_topics['topics'][$cur_topic['id']]) || $tracked_topics['topics'][$cur_topic['id']] < $cur_topic['last_post']) && (!isset($tracked_topics['forums'][$id]) || $tracked_topics['forums'][$id] < $cur_topic['last_post']) && is_null($cur_topic['moved_to']))
 		{
 			$item_status .= ' inew';
 			$icon_type = 'icon icon-new';
@@ -264,8 +245,8 @@ if ($db->num_rows($result))
 							</div>
 						</div>
 					</td>
-					<td class="tc2"><?php echo ($cur_topic['moved_to'] == null) ? forum_number_format($cur_topic['num_replies']) : '-' ?></td>
-<?php if ($pun_config['o_topic_views'] == '1'): ?>					<td class="tc3"><?php echo ($cur_topic['moved_to'] == null) ? forum_number_format($cur_topic['num_views']) : '-' ?></td>
+					<td class="tc2"><?php echo (is_null($cur_topic['moved_to'])) ? forum_number_format($cur_topic['num_replies']) : '-' ?></td>
+<?php if ($pun_config['o_topic_views'] == '1'): ?>					<td class="tc3"><?php echo (is_null($cur_topic['moved_to'])) ? forum_number_format($cur_topic['num_views']) : '-' ?></td>
 <?php endif; ?>					<td class="tcr"><?php echo $last_post ?></td>
 				</tr>
 <?php
diff --git a/viewtopic.php b/viewtopic.php
index 1bd9127..02c9aec 100644
--- a/viewtopic.php
+++ b/viewtopic.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Copyright (C) 2008-2011 FluxBB
+ * Copyright (C) 2008-2012 FluxBB
  * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  */
@@ -11,14 +11,14 @@ require PUN_ROOT.'include/common.php';
 
 
 if ($pun_user['g_read_board'] == '0')
-	message($lang_common['No view']);
+	message($lang_common['No view'], false, '403 Forbidden');
 
 
 $action = isset($_GET['action']) ? $_GET['action'] : null;
 $id = isset($_GET['id']) ? intval($_GET['id']) : 0;
 $pid = isset($_GET['pid']) ? intval($_GET['pid']) : 0;
 if ($id < 1 && $pid < 1)
-	message($lang_common['Bad request']);
+	message($lang_common['Bad request'], false, '404 Not Found');
 
 // Load the viewtopic.php language file
 require PUN_ROOT.'lang/'.$pun_user['language'].'/topic.php';
@@ -29,7 +29,7 @@ if ($pid)
 {
 	$result = $db->query('SELECT topic_id, posted FROM '.$db->prefix.'posts WHERE id='.$pid) or error('Unable to fetch topic ID', __FILE__, __LINE__, $db->error());
 	if (!$db->num_rows($result))
-		message($lang_common['Bad request']);
+		message($lang_common['Bad request'], false, '404 Not Found');
 
 	list($id, $posted) = $db->fetch_row($result);
 
@@ -85,7 +85,7 @@ 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 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']);
+	message($lang_common['Bad request'], false, '404 Not Found');
 
 $cur_topic = $db->fetch_assoc($result);
 
@@ -131,25 +131,6 @@ $start_from = $pun_user['disp_posts'] * ($p - 1);
 $paging_links = '<span class="pages-label">'.$lang_common['Pages'].' </span>'.paginate($num_pages, $p, 'viewtopic.php?id='.$id);
 
 
-// Add relationship meta tags
-$page_head = array();
-$page_head['up'] = '<link rel="up" href="viewforum.php?id='.$cur_topic['forum_id'].'" title="'.pun_htmlspecialchars($cur_topic['forum_name']).'" />';
-
-if ($num_pages > 1)
-{
-	if ($p > 1)
-	{
-		$page_head['first'] = '<link rel="first" href="viewtopic.php?id='.$id.'&amp;p=1" title="'.sprintf($lang_common['Page'], 1).'" />';
-		$page_head['prev'] = '<link rel="prev" href="viewtopic.php?id='.$id.'&amp;p='.($p-1).'" title="'.sprintf($lang_common['Page'], $p-1).'" />';
-	}
-	if ($p < $num_pages)
-	{
-		$page_head['next'] = '<link rel="next" href="viewtopic.php?id='.$id.'&amp;p='.($p+1).'" title="'.sprintf($lang_common['Page'], $p+1).'" />';
-		$page_head['last'] = '<link rel="last" href="viewtopic.php?id='.$id.'&amp;p='.$num_pages.'" title="'.sprintf($lang_common['Page'], $num_pages).'" />';
-	}
-}
-
-
 if ($pun_config['o_censoring'] == '1')
 	$cur_topic['subject'] = censor_words($cur_topic['subject']);
 
@@ -162,7 +143,13 @@ if ($pun_config['o_quickpost'] == '1' &&
 	// Load the post.php language file
 	require PUN_ROOT.'lang/'.$pun_user['language'].'/post.php';
 
-	$required_fields = array('req_email' => $lang_common['Email'], 'req_message' => $lang_common['Message']);
+	$required_fields = array('req_message' => $lang_common['Message']);
+	if ($pun_user['is_guest'])
+	{
+		$required_fields['req_username'] = $lang_post['Guest name'];
+		if ($pun_config['p_force_guest_email'] == '1')
+			$required_fields['req_email'] = $lang_common['Email'];
+	}
 	$quickpost = true;
 }
 
@@ -178,9 +165,9 @@ else
 	$subscraction = '';
 
 if ($pun_config['o_feed_type'] == '1')
-	$page_head['feed'] = '<link rel="alternate" type="application/rss+xml" href="extern.php?action=feed&amp;tid='.$id.'&amp;type=rss" title="'.$lang_common['RSS topic feed'].'" />';
+	$page_head = array('feed' => '<link rel="alternate" type="application/rss+xml" href="extern.php?action=feed&amp;tid='.$id.'&amp;type=rss" title="'.$lang_common['RSS topic feed'].'" />');
 else if ($pun_config['o_feed_type'] == '2')
-	$page_head['feed'] = '<link rel="alternate" type="application/atom+xml" href="extern.php?action=feed&amp;tid='.$id.'&amp;type=atom" title="'.$lang_common['Atom topic feed'].'" />';
+	$page_head = array('feed' => '<link rel="alternate" type="application/atom+xml" href="extern.php?action=feed&amp;tid='.$id.'&amp;type=atom" title="'.$lang_common['Atom topic feed'].'" />');
 
 $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), pun_htmlspecialchars($cur_topic['forum_name']), pun_htmlspecialchars($cur_topic['subject']));
 define('PUN_ALLOW_INDEX', 1);
@@ -424,7 +411,6 @@ $cur_index = 1;
 					<legend><?php echo $lang_common['Write message legend'] ?></legend>
 					<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_topic_subscriptions'] == '1' && ($pun_user['auto_notify'] == '1' || $cur_topic['is_subscribed'])): ?>						<input type="hidden" name="subscribe" value="1" />
 <?php endif; ?>
 <?php
