diff --git a/lib/punbb/admin_forums.php b/lib/punbb/admin_forums.php
index a68d96e390fdeafe140df581f564329d4923bf22..b8dd7f7758c1e784d80ab5a3be2f4ff400c37d51 100644
--- a/lib/punbb/admin_forums.php
+++ b/lib/punbb/admin_forums.php
@@ -385,8 +385,13 @@ generate_admin_menu('forums');
 <?php
 
 	$result = $db->query('SELECT id, cat_name FROM '.$db->prefix.'categories ORDER BY disp_position') or error('Unable to fetch category list', __FILE__, __LINE__, $db->error());
-	while ($cur_cat = $db->fetch_assoc($result))
-		echo "\t\t\t\t\t\t\t\t\t".'<option value="'.$cur_cat['id'].'">'.pun_htmlspecialchars($cur_cat['cat_name']).'</option>'."\n";
+	if ($db->num_rows($result) > 0)
+	{
+		while ($cur_cat = $db->fetch_assoc($result))
+			echo "\t\t\t\t\t\t\t\t\t".'<option value="'.$cur_cat['id'].'">'.pun_htmlspecialchars($cur_cat['cat_name']).'</option>'."\n";
+	}
+	else
+		echo "\t\t\t\t\t\t\t\t\t".'<option value="0" disabled="disabled">No categories exist</option>'."\n";
 
 ?>
 										</select>
@@ -399,7 +404,15 @@ generate_admin_menu('forums');
 				</div>
 			</form>
 		</div>
+<?php
+
+// Display all the categories and forums
+$result = $db->query('SELECT c.id AS cid, c.cat_name, f.id AS fid, f.forum_name, f.disp_position FROM '.$db->prefix.'categories AS c INNER JOIN '.$db->prefix.'forums AS f ON c.id=f.cat_id ORDER BY c.disp_position, c.id, f.disp_position') or error('Unable to fetch category/forum list', __FILE__, __LINE__, $db->error());
+
+if ($db->num_rows($result) > 0)
+{
 
+?>
 		<h2 class="block2"><span>Edit forums</span></h2>
 		<div class="box">
 			<form id="edforum" method="post" action="admin_forums.php?action=edit">
@@ -408,9 +421,6 @@ generate_admin_menu('forums');
 
 $tabindex_count = 4;
 
-// Display all the categories and forums
-$result = $db->query('SELECT c.id AS cid, c.cat_name, f.id AS fid, f.forum_name, f.disp_position FROM '.$db->prefix.'categories AS c INNER JOIN '.$db->prefix.'forums AS f ON c.id=f.cat_id ORDER BY c.disp_position, c.id, f.disp_position') or error('Unable to fetch category/forum list', __FILE__, __LINE__, $db->error());
-
 $cur_category = 0;
 while ($cur_forum = $db->fetch_assoc($result))
 {
@@ -449,6 +459,11 @@ while ($cur_forum = $db->fetch_assoc($result))
 				<p class="submitend"><input type="submit" name="update_positions" value="Update positions" tabindex="<?php echo $tabindex_count ?>" /></p>
 			</form>
 		</div>
+<?php
+
+}
+
+?>
 	</div>
 	<div class="clearer"></div>
 </div>
diff --git a/lib/punbb/admin_maintenance.php b/lib/punbb/admin_maintenance.php
index 34e43533b057543a28306dea462fc430f4864797..adda7811232d2a3aeb478a57f3fcdd600c5ca3b5 100644
--- a/lib/punbb/admin_maintenance.php
+++ b/lib/punbb/admin_maintenance.php
@@ -69,8 +69,6 @@ if (isset($_GET['i_per_page']) && isset($_GET['i_start_at']))
 		}
 	}
 
-	$end_at = $start_at + $per_page;
-
 ?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
@@ -95,7 +93,7 @@ Rebuilding index &hellip; This might be a good time to put on some coffee :-)<br
 	require PUN_ROOT.'include/search_idx.php';
 
 	// Fetch posts to process
-	$result = $db->query('SELECT DISTINCT t.id, p.id, p.message FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'posts AS p ON t.id=p.topic_id WHERE t.id>='.$start_at.' AND t.id<'.$end_at.' ORDER BY t.id') or error('Unable to fetch topic/post info', __FILE__, __LINE__, $db->error());
+	$result = $db->query('SELECT DISTINCT t.id, p.id, p.message FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'posts AS p ON t.id=p.topic_id WHERE t.id>='.$start_at.' ORDER BY t.id LIMIT '.$per_page) or error('Unable to fetch topic/post info', __FILE__, __LINE__, $db->error());
 
 	$cur_topic = 0;
 	while ($cur_post = $db->fetch_row($result))
@@ -118,9 +116,9 @@ Rebuilding index &hellip; This might be a good time to put on some coffee :-)<br
 	}
 
 	// Check if there is more work to do
-	$result = $db->query('SELECT id FROM '.$db->prefix.'topics WHERE id>'.$end_at) or error('Unable to fetch topic info', __FILE__, __LINE__, $db->error());
+	$result = $db->query('SELECT id FROM '.$db->prefix.'topics WHERE id>'.$cur_topic.' ORDER BY id ASC LIMIT 1') or error('Unable to fetch topic info', __FILE__, __LINE__, $db->error());
 
-	$query_str = ($db->num_rows($result)) ? '?i_per_page='.$per_page.'&i_start_at='.$end_at : '';
+	$query_str = ($db->num_rows($result)) ? '?i_per_page='.$per_page.'&i_start_at='.$db->result($result) : '';
 
 	$db->end_transaction();
 	$db->close();
diff --git a/lib/punbb/edit.php b/lib/punbb/edit.php
index 861ef3ba456509abfbc69ea3f363964fcee2b9a3..59d683f3c6dfcb8b4a6653e1d5a3450047988100 100644
--- a/lib/punbb/edit.php
+++ b/lib/punbb/edit.php
@@ -197,7 +197,7 @@ else if (isset($_POST['preview']))
 
 ?>
 <div class="blockform">
-	<h2><?php echo $lang_post['Edit post'] ?></h2>
+	<h2><span><?php echo $lang_post['Edit post'] ?></span></h2>
 	<div class="box">
 		<form id="edit" method="post" action="edit.php?id=<?php echo $id ?>&amp;action=edit" onsubmit="return process_form(this)">
 			<div class="inform">
diff --git a/lib/punbb/header.php b/lib/punbb/header.php
index add4cd060600e73515c0edbf1b27130257e0253a..ff4bdcecf9f9407578014bbf853a63a209dc6768 100644
--- a/lib/punbb/header.php
+++ b/lib/punbb/header.php
@@ -147,7 +147,7 @@ if (isset($focus_element))
 
 // START SUBST - <pun_page>
 $tpl_main = str_replace('<pun_page>', htmlspecialchars(basename($_SERVER['PHP_SELF'], '.php')), $tpl_main);
-// END SUBST - <pun_title>
+// END SUBST - <pun_page>
 
 
 // START SUBST - <pun_title>
diff --git a/lib/punbb/include/common.php b/lib/punbb/include/common.php
index 1a1292fcf27b564c23d0a7cbc633cc02ace0642d..4669abc6115f6e227c1a4ba70fb296b490340a63 100644
--- a/lib/punbb/include/common.php
+++ b/lib/punbb/include/common.php
@@ -70,8 +70,9 @@ if (get_magic_quotes_gpc())
 	$_COOKIE = stripslashes_array($_COOKIE);
 }
 
-// Seed the random number generator
-mt_srand((double)microtime()*1000000);
+// Seed the random number generator (PHP <4.2.0 only)
+if (version_compare(PHP_VERSION, '4.2.0', '<'))
+	mt_srand((double)microtime()*1000000);
 
 // If a cookie name is not specified in config.php, we use the default (punbb_cookie)
 if (empty($cookie_name))
diff --git a/lib/punbb/include/functions.php b/lib/punbb/include/functions.php
index 7f402e6b625eaa1c737ab5e1b41fbff65540e6dc..56b28464d21028c2a6570c291a1d4c56d5b61f4a 100644
--- a/lib/punbb/include/functions.php
+++ b/lib/punbb/include/functions.php
@@ -121,7 +121,7 @@ function check_cookie(&$pun_user)
 		// If user authorisation failed
 		if (!isset($pun_user['id']) || md5($cookie_seed.$pun_user['password']) !== $cookie['password_hash'])
 		{
-			pun_setcookie(0, random_pass(8), $expire);
+			pun_setcookie(1, md5(uniqid(rand(), true)), $expire);
 			set_default_user();
 
 			return;
@@ -368,14 +368,14 @@ function generate_navlinks()
 				$links[] = '<li id="navsearch"><a href="search.php">'.$lang_common['Search'].'</a>';
 
 			$links[] = '<li id="navprofile"><a href="profile.php?id='.$pun_user['id'].'">'.$lang_common['Profile'].'</a>';
-//			$links[] = '<li id="navlogout"><a href="login.php?action=out&amp;id='.$pun_user['id'].'">'.$lang_common['Logout'].'</a>';
+//			$links[] = '<li id="navlogout"><a href="login.php?action=out&amp;id='.$pun_user['id'].'&amp;csrf_token='.sha1($pun_user['id'].sha1(get_remote_address())).'">'.$lang_common['Logout'].'</a>';
 		}
 		else
 		{
 			$links[] = '<li id="navsearch"><a href="search.php">'.$lang_common['Search'].'</a>';
 			$links[] = '<li id="navprofile"><a href="profile.php?id='.$pun_user['id'].'">'.$lang_common['Profile'].'</a>';
 			$links[] = '<li id="navadmin"><a href="admin_index.php">'.$lang_common['Admin'].'</a>';
-//			$links[] = '<li id="navlogout"><a href="login.php?action=out&amp;id='.$pun_user['id'].'">'.$lang_common['Logout'].'</a>';
+//			$links[] = '<li id="navlogout"><a href="login.php?action=out&amp;id='.$pun_user['id'].'&amp;csrf_token='.sha1($pun_user['id'].sha1(get_remote_address())).'">'.$lang_common['Logout'].'</a>';
 		}
 	}
 
@@ -922,8 +922,8 @@ function redirect($destination_url, $message)
 {
 	global $db, $pun_config, $lang_common, $pun_user;
 
-	// Prefix with o_base_url (unless it's there already)
-	if (strpos($destination_url, $pun_config['o_base_url']) !== 0)
+	// Prefix with o_base_url (unless there's already a valid URI)
+	if (strpos($destination_url, 'http://') !== 0 && strpos($destination_url, 'https://') !== 0 && strpos($destination_url, '/') !== 0)
 		$destination_url = $pun_config['o_base_url'].'/'.$destination_url;
 
 	// Do a little spring cleaning
diff --git a/lib/punbb/install.php b/lib/punbb/install.php
index 1a89dff6a935b904ce4906359cd3100e6164de6c..33c477b53da39ce8e61feb350716e03e03898a6a 100644
--- a/lib/punbb/install.php
+++ b/lib/punbb/install.php
@@ -27,7 +27,7 @@ pun_exit();
 
 
 // The PunBB version this script installs
-$punbb_version = '1.2.16';
+$punbb_version = '1.2.17';
 
 
 define('PUN_ROOT', './');
@@ -1404,7 +1404,7 @@ else
 
 
 	/// Display config.php and give further instructions
-	$config = '<?php'."\n\n".'$db_type = \''.$db_type."';\n".'$db_host = \''.$db_host."';\n".'$db_name = \''.$db_name."';\n".'$db_username = \''.$db_username."';\n".'$db_password = \''.$db_password."';\n".'$db_prefix = \''.$db_prefix."';\n".'$p_connect = false;'."\n\n".'$cookie_name = '."'punbb_cookie';\n".'$cookie_domain = '."'';\n".'$cookie_path = '."'/';\n".'$cookie_secure = 0;'."\n".'$cookie_seed = \''.substr(md5(time()), -8)."';\n\ndefine('PUN', 1);";
+	$config = '<?php'."\n\n".'$db_type = \''.$db_type."';\n".'$db_host = \''.$db_host."';\n".'$db_name = \''.$db_name."';\n".'$db_username = \''.$db_username."';\n".'$db_password = \''.$db_password."';\n".'$db_prefix = \''.$db_prefix."';\n".'$p_connect = false;'."\n\n".'$cookie_name = '."'punbb_cookie';\n".'$cookie_domain = '."'';\n".'$cookie_path = '."'/';\n".'$cookie_secure = 0;'."\n".'$cookie_seed = \''.substr(sha1(uniqid(rand(), true)), 0, 16)."';\n\ndefine('PUN', 1);";
 
 
 ?>
diff --git a/lib/punbb/login.php b/lib/punbb/login.php
index cc6fc8a8b27c4a4573f504c7eb538a7dd6c208eb..7257dbb2c5b0cbba4f80d3aa6b9cc5db3b828283 100644
--- a/lib/punbb/login.php
+++ b/lib/punbb/login.php
@@ -84,7 +84,7 @@ if (isset($_POST['form_sent']) && $action == 'in')
 
 else if ($action == 'out')
 {
-	if ($pun_user['is_guest'] || !isset($_GET['id']) || $_GET['id'] != $pun_user['id'])
+	if ($pun_user['is_guest'] || !isset($_GET['id']) || $_GET['id'] != $pun_user['id'] || !isset($_GET['csrf_token']) || $_GET['csrf_token'] != sha1($pun_user['id'].sha1(get_remote_address())))
 	{
 		header('Location: index.php');
 		pun_exit();
@@ -97,7 +97,7 @@ else if ($action == 'out')
 	if (isset($pun_user['logged']))
 		$db->query('UPDATE '.$db->prefix.'users SET last_visit='.$pun_user['logged'].' WHERE id='.$pun_user['id']) or error('Unable to update user visit data', __FILE__, __LINE__, $db->error());
 
-	pun_setcookie(1, random_pass(8), time() + 31536000);
+	pun_setcookie(1, md5(uniqid(rand(), true)), time() + 31536000);
 
 	redirect('index.php', $lang_login['Logout redirect']);
 }
diff --git a/lib/punbb/misc.php b/lib/punbb/misc.php
index 77fd2fba310d26a55e6ee2887de3b0de6c39d371..637ea6a887812bbf45e0b69c48a909d857a7bc0e 100644
--- a/lib/punbb/misc.php
+++ b/lib/punbb/misc.php
@@ -252,6 +252,11 @@ else if (isset($_GET['subscribe']))
 	if ($topic_id < 1)
 		message($lang_common['Bad request']);
 
+	// Make sure the user can view the topic
+	$result = $db->query('SELECT 1 FROM '.$db->prefix.'topics AS t LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id=1) WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND t.id='.$topic_id.' AND t.moved_to IS NULL') or error('Unable to fetch topic info', __FILE__, __LINE__, $db->error());
+	if (!$db->num_rows($result))
+		message($lang_common['Bad request']);
+
 	$result = $db->query('SELECT 1 FROM '.$db->prefix.'subscriptions WHERE user_id='.$pun_user['id'].' AND topic_id='.$topic_id) or error('Unable to fetch subscription info', __FILE__, __LINE__, $db->error());
 	if ($db->num_rows($result))
 		message($lang_misc['Already subscribed']);
diff --git a/lib/punbb/moderate.php b/lib/punbb/moderate.php
index 36fc4383f4f6d1796d65604f3c9248b779ea3480..780d5076bd3fa5851ab86c663469da80b79a311f 100644
--- a/lib/punbb/moderate.php
+++ b/lib/punbb/moderate.php
@@ -35,7 +35,7 @@ if (isset($_GET['get_host']))
 		message($lang_common['No permission']);
 
 	// 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']))
+	if (@preg_match('/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', $_GET['get_host']))
 		$ip = $_GET['get_host'];
 	else
 	{
@@ -295,10 +295,10 @@ if (isset($_REQUEST['move_topics']) || isset($_POST['move_topics_to']))
 		if (empty($topics) || $move_to_forum < 1)
 			message($lang_common['Bad request']);
 
-		// Verify that the topic IDs are valid
-		$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());
-
-		if ($db->num_rows($result) != count($topics))
+		// Verify that the topic IDs are valid
+		$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());
+
+		if ($db->num_rows($result) != count($topics))
 			message($lang_common['Bad request']);
 
 		// Delete any redirect topics if there are any (only if we moved/copied the topic back to where it where it was once moved from)
@@ -417,10 +417,10 @@ if (isset($_REQUEST['delete_topics']) || isset($_POST['delete_topics_comply']))
 
 		require PUN_ROOT.'include/search_idx.php';
 
-		// Verify that the topic IDs are valid
-		$result = $db->query('SELECT 1 FROM '.$db->prefix.'topics WHERE id IN('.$topics.') AND forum_id='.$fid) or error('Unable to check topics', __FILE__, __LINE__, $db->error());
-
-		if ($db->num_rows($result) != substr_count($topics, ',') + 1)
+		// Verify that the topic IDs are valid
+		$result = $db->query('SELECT 1 FROM '.$db->prefix.'topics WHERE id IN('.$topics.') AND forum_id='.$fid) or error('Unable to check topics', __FILE__, __LINE__, $db->error());
+
+		if ($db->num_rows($result) != substr_count($topics, ',') + 1)
 			message($lang_common['Bad request']);
 
 		// Delete the topics and any redirect topics
diff --git a/lib/punbb/search.php b/lib/punbb/search.php
index bcba0df7c8bd0bb88d5f95767c43639cd2c97ef2..82385b168849221f69c08fc8e95dd52d19335f7b 100644
--- a/lib/punbb/search.php
+++ b/lib/punbb/search.php
@@ -122,7 +122,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')) ? ' AND t.forum_id = '.$forum : '';
+		$forum_sql = ($forum != -1 || ($forum == -1 && $pun_config['o_search_all_forums'] == '0' && $pun_user['g_id'] >= PUN_GUEST)) ? ' AND t.forum_id = '.$forum : '';
 
 		if (!empty($author) || !empty($keywords))
 		{
@@ -160,7 +160,7 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 					{
 						$num_chars = pun_strlen($word);
 
-						if ($num_chars < 3 || $num_chars > 20 || in_array($word, $stopwords))
+						if ($word !== 'or' && ($num_chars < 3 || $num_chars > 20 || in_array($word, $stopwords)))
 							unset($keywords_array[$i]);
 					}
 
@@ -199,7 +199,7 @@ if (isset($_GET['action']) || isset($_GET['search_id']))
 							}
 							else
 							{
-								$cur_word = str_replace('*', '%', $cur_word);
+								$cur_word = $db->escape(str_replace('*', '%', $cur_word));
 								$sql = 'SELECT m.post_id FROM '.$db->prefix.'search_words AS w INNER JOIN '.$db->prefix.'search_matches AS m ON m.word_id = w.id WHERE w.word LIKE \''.$cur_word.'\''.$search_in_cond;
 							}