Browse Source

Merge branch 'release-2.1' of https://github.com/SimpleMachines/SMF2.1 into release-2.1

emanuele 11 years ago
parent
commit
e6bd3bf50f
100 changed files with 1986 additions and 1032 deletions
  1. 1 1
      SSI.php
  2. 1 1
      Sources/Admin.php
  3. 1 1
      Sources/Avatar.php
  4. 4 2
      Sources/BoardIndex.php
  5. 1 1
      Sources/Calendar.php
  6. 1 1
      Sources/Class-BrowserDetect.php
  7. 1 1
      Sources/Class-CurlFetchWeb.php
  8. 1 1
      Sources/Class-Graphics.php
  9. 1 1
      Sources/Class-Package.php
  10. 1 1
      Sources/DbExtra-mysql.php
  11. 1 1
      Sources/DbExtra-postgresql.php
  12. 1 1
      Sources/DbExtra-sqlite.php
  13. 1 1
      Sources/DbPackages-mysql.php
  14. 1 1
      Sources/DbPackages-postgresql.php
  15. 1 1
      Sources/DbPackages-sqlite.php
  16. 1 1
      Sources/DbSearch-mysql.php
  17. 1 1
      Sources/DbSearch-postgresql.php
  18. 1 1
      Sources/DbSearch-sqlite.php
  19. 11 1
      Sources/Display.php
  20. 1 1
      Sources/Drafts.php
  21. 0 231
      Sources/DumpDatabase.php
  22. 1 1
      Sources/Errors.php
  23. 11 3
      Sources/Groups.php
  24. 1 1
      Sources/Help.php
  25. 1 1
      Sources/Karma.php
  26. 67 11
      Sources/Load.php
  27. 1 1
      Sources/LogInOut.php
  28. 1 1
      Sources/Logging.php
  29. 1 1
      Sources/ManageAttachments.php
  30. 1 1
      Sources/ManageBans.php
  31. 30 1
      Sources/ManageBoards.php
  32. 1 1
      Sources/ManageCalendar.php
  33. 1 1
      Sources/ManageErrors.php
  34. 1 1
      Sources/ManageLanguages.php
  35. 1 1
      Sources/ManageMail.php
  36. 5 71
      Sources/ManageMaintenance.php
  37. 16 1
      Sources/ManageMembergroups.php
  38. 1 1
      Sources/ManageMembers.php
  39. 1 1
      Sources/ManageNews.php
  40. 1 1
      Sources/ManagePaid.php
  41. 1 1
      Sources/ManagePermissions.php
  42. 2 2
      Sources/ManagePosts.php
  43. 1 1
      Sources/ManageRegistration.php
  44. 1 1
      Sources/ManageScheduledTasks.php
  45. 2 2
      Sources/ManageSearch.php
  46. 1 1
      Sources/ManageSearchEngines.php
  47. 1 1
      Sources/ManageServer.php
  48. 3 1
      Sources/ManageSettings.php
  49. 1 1
      Sources/ManageSmileys.php
  50. 1 1
      Sources/Memberlist.php
  51. 14 3
      Sources/MessageIndex.php
  52. 1 1
      Sources/ModerationCenter.php
  53. 1 1
      Sources/Modlog.php
  54. 1 1
      Sources/MoveTopic.php
  55. 1 1
      Sources/News.php
  56. 1 1
      Sources/Notify.php
  57. 1 1
      Sources/PackageGet.php
  58. 1 1
      Sources/Packages.php
  59. 1 1
      Sources/PersonalMessage.php
  60. 1 1
      Sources/Poll.php
  61. 1 1
      Sources/Post.php
  62. 9 4
      Sources/PostModeration.php
  63. 1 1
      Sources/Printpage.php
  64. 1 1
      Sources/Profile-Actions.php
  65. 2 2
      Sources/Profile-Modify.php
  66. 8 5
      Sources/Profile-View.php
  67. 3 4
      Sources/Profile.php
  68. 1 1
      Sources/QueryString.php
  69. 1 1
      Sources/Recent.php
  70. 1 1
      Sources/Register.php
  71. 1 1
      Sources/Reminder.php
  72. 1 1
      Sources/RemoveTopic.php
  73. 1 1
      Sources/RepairBoards.php
  74. 58 1
      Sources/Reports.php
  75. 2 2
      Sources/ScheduledTasks.php
  76. 1 1
      Sources/Search.php
  77. 2 2
      Sources/SearchAPI-Custom.php
  78. 2 2
      Sources/SearchAPI-Fulltext.php
  79. 1 1
      Sources/SearchAPI-Standard.php
  80. 33 6
      Sources/Security.php
  81. 22 1
      Sources/SendTopic.php
  82. 1 1
      Sources/Session.php
  83. 1 1
      Sources/SplitTopics.php
  84. 1 1
      Sources/Stats.php
  85. 1 1
      Sources/Subs-Admin.php
  86. 1 1
      Sources/Subs-Attachments.php
  87. 17 1
      Sources/Subs-Auth.php
  88. 22 1
      Sources/Subs-BoardIndex.php
  89. 79 2
      Sources/Subs-Boards.php
  90. 1 1
      Sources/Subs-Calendar.php
  91. 1 1
      Sources/Subs-Categories.php
  92. 591 591
      Sources/Subs-Charset.php
  93. 1 1
      Sources/Subs-Compat.php
  94. 1 1
      Sources/Subs-Db-mysql.php
  95. 839 0
      Sources/Subs-Db-mysqli.php
  96. 1 1
      Sources/Subs-Db-postgresql.php
  97. 1 1
      Sources/Subs-Db-sqlite.php
  98. 53 1
      Sources/Subs-Editor.php
  99. 8 7
      Sources/Subs-Graphics.php
  100. 1 1
      Sources/Subs-List.php

+ 1 - 1
SSI.php

@@ -5,7 +5,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/Admin.php

@@ -8,7 +8,7 @@
  * @package SMF
  * @author Simple Machines
  *
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/Avatar.php

@@ -8,7 +8,7 @@
  * @package SMF
  * @author Simple Machines
  *
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 4 - 2
Sources/BoardIndex.php

@@ -8,7 +8,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1
@@ -32,11 +32,13 @@ function BoardIndex()
 	if (WIRELESS)
 		$context['sub_template'] = WIRELESS_PROTOCOL . '_boardindex';
 	else
+	{
 		loadTemplate('BoardIndex');
+		$context['template_layers'][] = 'boardindex_outer';
+	}
 
 	// Set a canonical URL for this page.
 	$context['canonical_url'] = $scripturl;
-	$context['template_layers'][] = 'boardindex_outer';
 
 	// Do not let search engines index anything if there is a random thing in $_GET.
 	if (!empty($_GET))

+ 1 - 1
Sources/Calendar.php

@@ -8,7 +8,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/Class-BrowserDetect.php

@@ -5,7 +5,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/Class-CurlFetchWeb.php

@@ -4,7 +4,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/Class-Graphics.php

@@ -13,7 +13,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/Class-Package.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/DbExtra-mysql.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/DbExtra-postgresql.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/DbExtra-sqlite.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/DbPackages-mysql.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/DbPackages-postgresql.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/DbPackages-sqlite.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/DbSearch-mysql.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/DbSearch-postgresql.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/DbSearch-sqlite.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 11 - 1
Sources/Display.php

@@ -8,7 +8,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1
@@ -482,13 +482,23 @@ function Display()
 
 	// Build a list of this board's moderators.
 	$context['moderators'] = &$board_info['moderators'];
+	$context['moderator_groups'] = &$board_info['moderator_groups'];
 	$context['link_moderators'] = array();
 	if (!empty($board_info['moderators']))
 	{
 		// Add a link for each moderator...
 		foreach ($board_info['moderators'] as $mod)
 			$context['link_moderators'][] = '<a href="' . $scripturl . '?action=profile;u=' . $mod['id'] . '" title="' . $txt['board_moderator'] . '">' . $mod['name'] . '</a>';
+	}
+	if (!empty($board_info['moderator_groups']))
+	{
+		// Add a link for each moderator group as well...
+		foreach ($board_info['moderator_groups'] as $mod_group)
+			$context['link_moderators'][] = '<a href="' . $scripturl . '?action=groups;sa=viewmemberes;group=' . $mod_group['id'] . '" title="' . $txt['board_moderator'] . '">' . $mod_group['name'] . '</a>';
+	}
 
+	if (!empty($context['link_moderators']))
+	{
 		// And show it after the board's name.
 		$context['linktree'][count($context['linktree']) - 2]['extra_after'] = '<span class="board_moderators"> (' . (count($context['link_moderators']) == 1 ? $txt['moderator'] : $txt['moderators']) . ': ' . implode(', ', $context['link_moderators']) . ')</span>';
 	}

+ 1 - 1
Sources/Drafts.php

@@ -8,7 +8,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 0 - 231
Sources/DumpDatabase.php

@@ -1,231 +0,0 @@
-<?php
-
-/**
- * This file has a single job - database backup.
- *
- * Simple Machines Forum (SMF)
- *
- * @package SMF
- * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
- * @license http://www.simplemachines.org/about/smf/license.php BSD
- *
- * @version 2.1 Alpha 1
- */
-
-if (!defined('SMF'))
-	die('No direct access...');
-
-/**
- * Dumps the database.
- * It writes all of the database to standard output.
- * It uses gzip compression if compress is set in the URL/post data.
- * It may possibly time out, and mess up badly if you were relying on it. :P
- * The data dumped depends on whether "struct" and "data" are passed.
- * It requires an administrator and the session hash by post.
- * It is called from ManageMaintenance.php.
- */
-function DumpDatabase2()
-{
-	global $db_name, $scripturl, $modSettings, $crlf, $smcFunc, $db_prefix, $db_show_debug;
-
-	// Administrators only!
-	if (!allowedTo('admin_forum'))
-		fatal_lang_error('no_dump_database', 'critical');
-
-	// We don't need debug when dumping the database
-	$modSettings['disableQueryCheck'] = true;
-	$db_show_debug = false;
-
-	// You can't dump nothing!
-	if (!isset($_REQUEST['struct']) && !isset($_REQUEST['data']))
-		$_REQUEST['data'] = true;
-
-	checkSession('post');
-
-	// We will need this, badly!
-	db_extend();
-
-	// Attempt to stop from dying...
-	@set_time_limit(600);
-	$time_limit = ini_get('max_execution_time');
-	$start_time = time();
-
-	// @todo ... fail on not getting the requested memory?
-	setMemoryLimit('256M');
-	$memory_limit = memoryReturnBytes(ini_get('memory_limit')) / 4;
-	$current_used_memory = 0;
-	$db_backup = '';
-	$output_function = 'un_compressed';
-
-	@ob_end_clean();
-
-	// Start saving the output... (don't do it otherwise for memory reasons.)
-	if (isset($_REQUEST['compress']) && function_exists('gzencode'))
-	{
-		$output_function = 'gzencode';
-
-		// Send faked headers so it will just save the compressed output as a gzip.
-		header('Content-Type: application/x-gzip');
-		header('Accept-Ranges: bytes');
-		header('Content-Encoding: none');
-
-		// Gecko browsers... don't like this. (Mozilla, Firefox, etc.)
-		if (!isBrowser('gecko'))
-			header('Content-Transfer-Encoding: binary');
-
-		// The file extension will include .gz...
-		$extension = '.sql.gz';
-	}
-	else
-	{
-		// Get rid of the gzipping alreading being done.
-		if (!empty($modSettings['enableCompressedOutput']))
-			@ob_end_clean();
-		// If we can, clean anything already sent from the output buffer...
-		elseif (ob_get_length() != 0)
-			ob_clean();
-
-		// Tell the client to save this file, even though it's text.
-		header('Content-Type: ' . (isBrowser('ie') || isBrowser('opera') ? 'application/octetstream' : 'application/octet-stream'));
-		header('Content-Encoding: none');
-
-		// This time the extension should just be .sql.
-		$extension = '.sql';
-	}
-
-	// This should turn off the session URL parser.
-	$scripturl = '';
-
-	// If this database is flat file and has a handler function pass it to that.
-	if (!empty($smcFunc['db_get_backup']))
-	{
-		$smcFunc['db_get_backup']();
-		exit;
-	}
-
-	// Send the proper headers to let them download this file.
-	header('Content-Disposition: attachment; filename="' . $db_name . '-' . (empty($_REQUEST['struct']) ? 'data' : (empty($_REQUEST['data']) ? 'structure' : 'complete')) . '_' . strftime('%Y-%m-%d') . $extension . '"');
-	header('Cache-Control: private');
-	header('Connection: close');
-
-	// This makes things simpler when using it so very very often.
-	$crlf = "\r\n";
-
-	// SQL Dump Header.
-	$db_chunks =
-		'-- ==========================================================' . $crlf .
-		'--' . $crlf .
-		'-- Database dump of tables in `' . $db_name . '`' . $crlf .
-		'-- ' . timeformat(time(), false) . $crlf .
-		'--' . $crlf .
-		'-- ==========================================================' . $crlf .
-		$crlf;
-
-	// Get all tables in the database....
-	if (preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) != 0)
-	{
-		$db = strtr($match[1], array('`' => ''));
-		$dbp = str_replace('_', '\_', $match[2]);
-	}
-	else
-	{
-		$db = false;
-		$dbp = $db_prefix;
-	}
-
-	// Dump each table.
-	$tables = $smcFunc['db_list_tables'](false, $db_prefix . '%');
-	foreach ($tables as $tableName)
-	{
-		// Are we dumping the structures?
-		if (isset($_REQUEST['struct']))
-		{
-			$db_chunks .=
-				$crlf .
-				'--' . $crlf .
-				'-- Table structure for table `' . $tableName . '`' . $crlf .
-				'--' . $crlf .
-				$crlf .
-				$smcFunc['db_table_sql']($tableName) . ';' . $crlf;
-		}
-		else
-			// This is needed to speedup things later
-			$smcFunc['db_table_sql']($tableName);
-
-		// How about the data?
-		if (!isset($_REQUEST['data']) || substr($tableName, -10) == 'log_errors')
-			continue;
-
-		$first_round = true;
-		$close_table = false;
-
-		// Are there any rows in this table?
-		while ($get_rows = $smcFunc['db_insert_sql']($tableName, $first_round))
-		{
-			if (empty($get_rows))
-				break;
-
-			// Time is what we need here!
-			if (function_exists('apache_reset_timeout'))
-				@apache_reset_timeout();
-			elseif (!empty($time_limit) && ($start_time + $time_limit - 20 > time()))
-			{
-				$start_time = time();
-				@set_time_limit(150);
-			}
-
-			if ($first_round)
-			{
-				$db_chunks .=
-					$crlf .
-					'--' . $crlf .
-					'-- Dumping data in `' . $tableName . '`' . $crlf .
-					'--' . $crlf .
-					$crlf;
-				$first_round = false;
-			}
-			$db_chunks .=
-				$get_rows;
-			$current_used_memory += $smcFunc['strlen']($db_chunks);
-
-			$db_backup .= $db_chunks;
-			unset($db_chunks);
-			$db_chunks = '';
-			if ($current_used_memory > $memory_limit)
-			{
-				echo $output_function($db_backup);
-				$current_used_memory = 0;
-				// This is probably redundant
-				unset($db_backup);
-				$db_backup = '';
-			}
-			$close_table = true;
-		}
-
-		// No rows to get - skip it.
-		if ($close_table)
-			$db_backup .=
-			'-- --------------------------------------------------------' . $crlf;
-	}
-
-	$db_backup .=
-		$crlf .
-		'-- Done' . $crlf;
-
-	echo $output_function($db_backup);
-
-	exit;
-}
-
-/**
- * Dummy/helper function, it simply returns the string passed as argument
- * @param $string, a string
- * @return the string passed
- */
-function un_compressed($string = '')
-{
-	return $string;
-}
-
-?>

+ 1 - 1
Sources/Errors.php

@@ -9,7 +9,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 11 - 3
Sources/Groups.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1
@@ -102,7 +102,15 @@ function GroupList()
 						else
 						{
 							$color_style = empty($rowData[\'online_color\']) ? \'\' : sprintf(\' style="color: %1$s;"\', $rowData[\'online_color\']);
-							$group_name = sprintf(\'<a href="%1$s?action=admin;area=membergroups;sa=members;group=%2$d"%3$s>%4$s</a>\', $scripturl, $rowData[\'id_group\'], $color_style, $rowData[\'group_name\']);
+							
+							if (allowedTo(\'manage_membergroups\'))
+							{
+								$group_name = sprintf(\'<a href="%1$s?action=admin;area=membergroups;sa=members;group=%2$d"%3$s>%4$s</a>\', $scripturl, $rowData[\'id_group\'], $color_style, $rowData[\'group_name\']);
+							}
+							else
+							{
+								$group_name = sprintf(\'<a href="%1$s?action=groups;sa=members;group=%2$d"%3$s>%4$s</a>\', $scripturl, $rowData[\'id_group\'], $color_style, $rowData[\'group_name\']);
+							}
 						}
 
 						// Add a help option for moderator and administrator.
@@ -213,7 +221,7 @@ function MembergroupMembers()
 
 	// Fix the membergroup icons.
 	$context['group']['icons'] = explode('#', $context['group']['icons']);
-	$context['group']['icons'] = !empty($context['group']['icons'][0]) && !empty($context['group']['icons'][1]) ? str_repeat('<img src="' . $settings['images_url'] . '/' . $context['group']['icons'][1] . '" alt="*" />', $context['group']['icons'][0]) : '';
+	$context['group']['icons'] = !empty($context['group']['icons'][0]) && !empty($context['group']['icons'][1]) ? str_repeat('<img src="' . $settings['images_url'] . '/membericons/' . $context['group']['icons'][1] . '" alt="*" />', $context['group']['icons'][0]) : '';
 	$context['group']['can_moderate'] = allowedTo('manage_membergroups') && (allowedTo('admin_forum') || $context['group']['group_type'] != 1);
 
 	$context['linktree'][] = array(

+ 1 - 1
Sources/Help.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/Karma.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 67 - 11
Sources/Load.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1
@@ -543,7 +543,7 @@ function loadBoard()
 	// Load this board only if it is specified.
 	if (empty($board) && empty($topic))
 	{
-		$board_info = array('moderators' => array());
+		$board_info = array('moderators' => array(), 'moderator_groups' => array());
 		return;
 	}
 
@@ -567,13 +567,16 @@ function loadBoard()
 		$request = $smcFunc['db_query']('', '
 			SELECT
 				c.id_cat, b.name AS bname, b.description, b.num_topics, b.member_groups, b.deny_member_groups,
-				b.id_parent, c.name AS cname, IFNULL(mem.id_member, 0) AS id_moderator,
+				b.id_parent, c.name AS cname, IFNULL(mg.id_group, 0) AS id_moderator_group, mg.group_name,
+				IFNULL(mem.id_member, 0) AS id_moderator,
 				mem.real_name' . (!empty($topic) ? ', b.id_board' : '') . ', b.child_level,
 				b.id_theme, b.override_theme, b.count_posts, b.id_profile, b.redirect,
 				b.unapproved_topics, b.unapproved_posts' . (!empty($topic) ? ', t.approved, t.id_member_started' : '') . '
 			FROM {db_prefix}boards AS b' . (!empty($topic) ? '
 				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic})' : '') . '
 				LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat)
+				LEFT JOIN {db_prefix}moderator_groups AS modgs ON (modgs.id_board = {raw:board_link})
+				LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = modgs.id_group)
 				LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = {raw:board_link})
 				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)
 			WHERE b.id_board = {raw:board_link}',
@@ -595,6 +598,7 @@ function loadBoard()
 			$board_info = array(
 				'id' => $board,
 				'moderators' => array(),
+				'moderator_groups' => array(),
 				'cat' => array(
 					'id' => $row['id_cat'],
 					'name' => $row['cname']
@@ -630,6 +634,14 @@ function loadBoard()
 						'href' => $scripturl . '?action=profile;u=' . $row['id_moderator'],
 						'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_moderator'] . '">' . $row['real_name'] . '</a>'
 					);
+
+				if (!empty($row['id_moderator_group']))
+					$board_info['moderator_groups'][$row['id_moderator_group']] = array(
+						'id' => $row['id_moderator_group'],
+						'name' => $row['group_name'],
+						'href' => $scripturl . '?action=groups;sa=members;group=' . $row['id_moderator_group'],
+						'link' => '<a href="' . $scripturl . '?action=groups;sa=members;group=' . $row['id_moderator_group'] . '">' . $row['group_name'] . '</a>'
+					);
 			}
 			while ($row = $smcFunc['db_fetch_assoc']($request));
 
@@ -671,6 +683,7 @@ function loadBoard()
 			// Otherwise the topic is invalid, there are no moderators, etc.
 			$board_info = array(
 				'moderators' => array(),
+				'moderator_groups' => array(),
 				'error' => 'exist'
 			);
 			$topic = null;
@@ -684,8 +697,11 @@ function loadBoard()
 
 	if (!empty($board))
 	{
+		// Get this into an array of keys for array_intersect
+		$moderator_groups = array_keys($board_info['moderator_groups']);
+		
 		// Now check if the user is a moderator.
-		$user_info['is_mod'] = isset($board_info['moderators'][$user_info['id']]);
+		$user_info['is_mod'] = isset($board_info['moderators'][$user_info['id']]) || count(array_intersect($user_info['groups'], $moderator_groups)) != 0;
 
 		if (count(array_intersect($user_info['groups'], $board_info['groups'])) == 0 && !$user_info['is_admin'])
 			$board_info['error'] = 'access';
@@ -925,11 +941,11 @@ function loadMemberData($users, $is_name = false, $set = 'normal')
 	switch ($set)
 	{
 		case 'normal':
-			$select_columns .= ', mem.buddy_list';
+			$select_columns .= ', mem.buddy_list,  mem.additional_groups';
 			break;
 		case 'profile':
-			$select_columns .= ', mem.openid_uri, mem.id_theme, mem.pm_ignore_list, mem.pm_email_notify, mem.pm_receive_from,
-			mem.time_format, mem.secret_question,  mem.additional_groups, mem.smiley_set,
+			$select_columns .= ', mem.additional_groups, mem.openid_uri, mem.id_theme, mem.pm_ignore_list, mem.pm_email_notify, mem.pm_receive_from,
+			mem.time_format, mem.secret_question, mem.smiley_set,
 			mem.total_time_logged_in, mem.notify_announcements, mem.notify_regularity, mem.notify_send_body,
 			mem.notify_types, lo.url, mem.ignore_boards, mem.password_salt, mem.pm_prefs, mem.buddy_list';
 			break;
@@ -984,6 +1000,27 @@ function loadMemberData($users, $is_name = false, $set = 'normal')
 		$smcFunc['db_free_result']($request);
 	}
 
+	$additional_mods = array();
+	
+	// Are any of these users in groups assigned to moderate this board?
+	if (!empty($loaded_ids) && !empty($board_info['moderator_groups']) && $set === 'normal')
+	{
+		foreach ($loaded_ids as $a_member)
+		{
+			if (!empty($user_profile[$a_member]['additional_groups']))
+				$groups = array_merge(array($user_profile[$a_member]['id_group']), explode(',', $user_profile[$a_member]['additional_groups']));
+			else
+				$groups = array($user_profile[$a_member]['id_group']);
+			
+			$temp = array_intersect($groups, array_keys($board_info['moderator_groups']));
+			
+			if (!empty($temp))
+			{
+				$additional_mods[] = $a_member;
+			}
+		}
+	}
+
 	if (!empty($new_loaded_ids) && !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 3)
 	{
 		for ($i = 0, $n = count($new_loaded_ids); $i < $n; $i++)
@@ -991,7 +1028,7 @@ function loadMemberData($users, $is_name = false, $set = 'normal')
 	}
 
 	// Are we loading any moderators?  If so, fix their group data...
-	if (!empty($loaded_ids) && !empty($board_info['moderators']) && $set === 'normal' && count($temp_mods = array_intersect($loaded_ids, array_keys($board_info['moderators']))) !== 0)
+	if (!empty($loaded_ids) && (!empty($board_info['moderators']) || !empty($board_info['moderator_groups'])) && $set === 'normal' && count($temp_mods = array_merge(array_intersect($loaded_ids, array_keys($board_info['moderators'])), $additional_mods)) !== 0)
 	{
 		if (($row = cache_get_data('moderator_group_info', 480)) == null)
 		{
@@ -2100,7 +2137,7 @@ function addInlineJavascript($javascript, $defer = false)
 function loadLanguage($template_name, $lang = '', $fatal = true, $force_reload = false)
 {
 	global $user_info, $language, $settings, $context, $modSettings;
-	global $db_show_debug, $sourcedir, $txt;
+	global $db_show_debug, $sourcedir, $txt, $birthdayEmails, $txtBirthdayEmails;
 	static $already_loaded = array();
 
 	// Default to the user's language.
@@ -2186,6 +2223,10 @@ function loadLanguage($template_name, $lang = '', $fatal = true, $force_reload =
 			}
 			$txt['emails'] = array();
 		}
+		// For sake of backward compatibility: $birthdayEmails is supposed to be 
+		// empty in a normal install. If it isn't it means the forum is using 
+		// something "old" (it may be the translation, it may be a mod) and this
+		// code (like the piece above) takes care of converting it to the new format
 		if (!empty($birthdayEmails))
 		{
 			foreach ($birthdayEmails as $key => $value)
@@ -2233,10 +2274,12 @@ function getBoardParents($id_parent)
 			$result = $smcFunc['db_query']('', '
 				SELECT
 					b.id_parent, b.name, {int:board_parent} AS id_board, IFNULL(mem.id_member, 0) AS id_moderator,
-					mem.real_name, b.child_level
+					mem.real_name, b.child_level, IFNULL(mg.id_group, 0) AS id_moderator_group, mg.group_name
 				FROM {db_prefix}boards AS b
 					LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board)
 					LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)
+					LEFT JOIN {db_prefix}moderator_groups AS modgs ON (modgs.id_board = b.id_board)
+					LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = modgs.id_group)
 				WHERE b.id_board = {int:board_parent}',
 				array(
 					'board_parent' => $id_parent,
@@ -2254,7 +2297,8 @@ function getBoardParents($id_parent)
 						'url' => $scripturl . '?board=' . $row['id_board'] . '.0',
 						'name' => $row['name'],
 						'level' => $row['child_level'],
-						'moderators' => array()
+						'moderators' => array(),
+						'moderator_groups' => array()
 					);
 				}
 				// If a moderator exists for this board, add that moderator for all children too.
@@ -2268,6 +2312,18 @@ function getBoardParents($id_parent)
 							'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_moderator'] . '">' . $row['real_name'] . '</a>'
 						);
 					}
+				
+				// If a moderator group exists for this board, add that moderator group for all children too
+				if (!empty($row['id_moderator_group']))
+					foreach ($boards as $id => $dummy)
+					{
+						$boards[$id]['moderator_groups'][$row['id_moderator_group']] = array(
+							'id' => $row['id_moderator_group'],
+							'name' => $row['group_name'],
+							'href' => $scripturl . '?action=groups;sa=members;group=' . $row['id_moderator_group'],
+							'link' => '<a href="' . $scripturl . '?action=groups;sa=members;group=' . $row['id_moderator_group'] . '">' . $row['group_name'] . '</a>'
+						);					
+					}
 			}
 			$smcFunc['db_free_result']($result);
 		}

+ 1 - 1
Sources/LogInOut.php

@@ -8,7 +8,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/Logging.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/ManageAttachments.php

@@ -8,7 +8,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/ManageBans.php

@@ -8,7 +8,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 30 - 1
Sources/ManageBoards.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1
@@ -547,6 +547,25 @@ function EditBoard()
 	if (!empty($context['board']['moderators']))
 		list ($context['board']['last_moderator_id']) = array_slice(array_keys($context['board']['moderators']), -1);
 
+	// Get all the groups assigned as moderators
+	$request = $smcFunc['db_query']('', '
+		SELECT id_group
+		FROM {db_prefix}moderator_groups
+		WHERE id_board = {int:current_board}',
+		array(
+			'current_board' => $_REQUEST['boardid'],
+		)
+	);
+	$context['board']['moderator_groups'] = array();
+	while ($row = $smcFunc['db_fetch_assoc']($request))
+		$context['board']['moderator_groups'][$row['id_group']] = $context['groups'][$row['id_group']]['name'];
+	$smcFunc['db_free_result']($request);
+	
+	$context['board']['moderator_groups_list'] = empty($context['board']['moderator_groups']) ? '' : '&quot;' . implode('&quot;, &qout;', $context['board']['moderator_groups']) . '&quot;';
+
+	if (!empty($context['board']['moderator_groups']))
+		list ($context['board']['last_moderator_group_id']) = array_slice(array_keys($context['board']['moderator_groups']), -1);
+
 	// Get all the themes...
 	$request = $smcFunc['db_query']('', '
 		SELECT id_theme AS id, value AS name
@@ -650,6 +669,16 @@ function EditBoard2()
 			$boardOptions['moderators'] = $moderators;
 		}
 
+		$boardOptions['moderator_group_string'] = $_POST['moderator_groups'];
+		
+		if (isset($_POST['moderator_group_list']) && is_array($_POST['moderator_group_list']))
+		{
+			$moderator_groups = array();
+			foreach ($_POST['moderator_group_list'] as $moderator_group)
+				$moderator_groups[(int) $moderator_group] = (int) $moderator_group;
+			$boardOptions['moderator_groups'] = $moderator_groups;
+		}
+
 		// Are they doing redirection?
 		$boardOptions['redirect'] = !empty($_POST['redirect_enable']) && isset($_POST['redirect_address']) && trim($_POST['redirect_address']) != '' ? trim($_POST['redirect_address']) : '';
 

+ 1 - 1
Sources/ManageCalendar.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/ManageErrors.php

@@ -8,7 +8,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/ManageLanguages.php

@@ -8,7 +8,7 @@
  * @package SMF
  * @author Simple Machines
  *
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/ManageMail.php

@@ -9,7 +9,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 5 - 71
Sources/ManageMaintenance.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1
@@ -61,7 +61,6 @@ function ManageMaintenance()
 			'template' => 'maintain_database',
 			'activities' => array(
 				'optimize' => 'OptimizeTables',
-				'backup' => 'MaintainDownloadBackup',
 				'convertentities' => 'ConvertEntities',
 				'convertutf8' => 'ConvertUtf8',
 				'convertmsgbody' => 'ConvertMsgBody',
@@ -131,10 +130,10 @@ function MaintainDatabase()
 	global $context, $db_type, $db_character_set, $modSettings, $smcFunc, $txt, $maintenance;
 
 	// Show some conversion options?
-	$context['convert_utf8'] = $db_type == 'mysql' && (!isset($db_character_set) || $db_character_set !== 'utf8' || empty($modSettings['global_character_set']) || $modSettings['global_character_set'] !== 'UTF-8') && version_compare('4.1.2', preg_replace('~\-.+?$~', '', $smcFunc['db_server_info']()), '<=');
-	$context['convert_entities'] = $db_type == 'mysql' && isset($db_character_set, $modSettings['global_character_set']) && $db_character_set === 'utf8' && $modSettings['global_character_set'] === 'UTF-8';
+	$context['convert_utf8'] = ($db_type == 'mysql' || $db_type == 'mysqli') && (!isset($db_character_set) || $db_character_set !== 'utf8' || empty($modSettings['global_character_set']) || $modSettings['global_character_set'] !== 'UTF-8') && version_compare('4.1.2', preg_replace('~\-.+?$~', '', $smcFunc['db_server_info']()), '<=');
+	$context['convert_entities'] = ($db_type == 'mysql' || $db_type == 'mysqli') && isset($db_character_set, $modSettings['global_character_set']) && $db_character_set === 'utf8' && $modSettings['global_character_set'] === 'UTF-8';
 
-	if ($db_type == 'mysql')
+	if ($db_type == 'mysql' || $db_type == 'mysqli')
 	{
 		db_extend('packages');
 
@@ -147,58 +146,6 @@ function MaintainDatabase()
 		$context['convert_to_suggest'] = ($body_type != 'text' && !empty($modSettings['max_messageLength']) && $modSettings['max_messageLength'] < 65536);
 	}
 
-	// Check few things to give advices before make a backup
-	// If safe mod is enable the external tool is *always* the best (and probably the only) solution
-	$context['safe_mode_enable'] = @ini_get('safe_mode');
-	// This is just a...guess
-	$result = $smcFunc['db_query']('', '
-		SELECT COUNT(*)
-		FROM {db_prefix}messages',
-		array()
-	);
-	list($messages) = $smcFunc['db_fetch_row']($result);
-	$smcFunc['db_free_result']($result);
-
-	// 256 is what we use in the backup script
-	setMemoryLimit('256M');
-	$memory_limit = memoryReturnBytes(ini_get('memory_limit')) / (1024 * 1024);
-	// Zip limit is set to more or less 1/4th the size of the available memory * 1500
-	// 1500 is an estimate of the number of messages that generates a database of 1 MB (yeah I know IT'S AN ESTIMATION!!!)
-	// Why that? Because the only reliable zip package is the one sent out the first time,
-	// so when the backup takes 1/5th (just to stay on the safe side) of the memory available
-	$zip_limit = $memory_limit * 1500 / 5;
-	// Here is more tricky: it depends on many factors, but the main idea is that
-	// if it takes "too long" the backup is not reliable. So, I know that on my computer it take
-	// 20 minutes to backup 2.5 GB, of course my computer is not representative, so I'll multiply by 4 the time.
-	// I would consider "too long" 5 minutes (I know it can be a long time, but let's start with that):
-	// 80 minutes for a 2.5 GB and a 5 minutes limit means 160 MB approx
-	$plain_limit = 240000;
-	// Last thing: are we able to gain time?
-	$current_time_limit = ini_get('max_execution_time');
-	@set_time_limit(159); //something strange just to be sure
-	$new_time_limit = ini_get('max_execution_time');
-
-	$context['use_maintenance'] = 0;
-
-	// External tool if:
-	//  * safe_mode enable OR
-	//  * cannot change the execution time OR
-	//  * cannot reset timeout
-	if ($context['safe_mode_enable'] || empty($new_time_limit) || ($current_time_limit == $new_time_limit && !function_exists('apache_reset_timeout')))
-		$context['suggested_method'] = 'use_external_tool';
-	elseif ($zip_limit < $plain_limit && $messages < $zip_limit)
-		$context['suggested_method'] = 'zipped_file';
-	elseif ($zip_limit > $plain_limit || ($zip_limit < $plain_limit && $plain_limit < $messages))
-	{
-		$context['suggested_method'] = 'use_external_tool';
-		$context['use_maintenance'] = empty($maintenance) ? 2 : 0;
-	}
-	else
-	{
-		$context['use_maintenance'] = 1;
-		$context['suggested_method'] = 'plain_text';
-	}
-
 	if (isset($_GET['done']) && $_GET['done'] == 'convertutf8')
 		$context['maintenance_finished'] = $txt['utf8_title'];
 	if (isset($_GET['done']) && $_GET['done'] == 'convertentities')
@@ -775,7 +722,7 @@ function ConvertMsgBody()
 	// Show me your badge!
 	isAllowedTo('admin_forum');
 
-	if ($db_type != 'mysql')
+	if ($db_type != 'mysql' && $db_type != 'mysqli')
 		return;
 
 	db_extend('packages');
@@ -1772,19 +1719,6 @@ function MaintainReattributePosts()
 	$context['maintenance_finished'] = $txt['maintain_reattribute_posts'];
 }
 
-/**
- * Handling function for the backup stuff.
- */
-function MaintainDownloadBackup()
-{
-	global $sourcedir;
-
-	validateToken('admin-maint');
-
-	require_once($sourcedir . '/DumpDatabase.php');
-	DumpDatabase2();
-}
-
 /**
  * Removing old members. Done and out!
  * @todo refactor

+ 16 - 1
Sources/ManageMembergroups.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1
@@ -659,6 +659,21 @@ function EditMembergroup()
 	if (empty($_REQUEST['group']))
 		fatal_lang_error('membergroup_does_not_exist', false);
 
+	// Can this group moderate any boards?
+	$request = $smcFunc['db_query']('', '
+		SELECT COUNT(id_board)
+		FROM {db_prefix}moderator_groups
+		WHERE id_group = {int:current_group}',
+		array(
+			'current_group' => $_REQUEST['group'],
+		)
+	);
+	
+	// Why don't we have a $smcFunc['db_result'] function?
+	$result = $smcFunc['db_fetch_row']($request);
+	$context['is_moderator_group'] = ($result[0] > 0);
+	$smcFunc['db_free_result']($request);
+
 	// The delete this membergroup button was pressed.
 	if (isset($_POST['delete']))
 	{

+ 1 - 1
Sources/ManageMembers.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/ManageNews.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/ManagePaid.php

@@ -8,7 +8,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/ManagePermissions.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 2 - 2
Sources/ManagePosts.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1
@@ -218,7 +218,7 @@ function ModifyPostSettings($return_config = false)
 		checkSession();
 
 		// If we're changing the message length (and we are using MySQL) let's check the column is big enough.
-		if (isset($_POST['max_messageLength']) && $_POST['max_messageLength'] != $modSettings['max_messageLength'] && $db_type == 'mysql')
+		if (isset($_POST['max_messageLength']) && $_POST['max_messageLength'] != $modSettings['max_messageLength'] && ($db_type == 'mysql' || $db_type == 'mysqli'))
 		{
 			db_extend('packages');
 

+ 1 - 1
Sources/ManageRegistration.php

@@ -8,7 +8,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/ManageScheduledTasks.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 2 - 2
Sources/ManageSearch.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1
@@ -308,7 +308,7 @@ function EditSearchMethod()
 	);
 
 	// Get some info about the messages table, to show its size and index size.
-	if ($db_type == 'mysql')
+	if ($db_type == 'mysql' || $db_type == 'mysqli')
 	{
 		if (preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) !== 0)
 			$request = $smcFunc['db_query']('', '

+ 1 - 1
Sources/ManageSearchEngines.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/ManageServer.php

@@ -50,7 +50,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 3 - 1
Sources/ManageSettings.php

@@ -8,7 +8,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1
@@ -605,6 +605,8 @@ function ModifyGeneralSecuritySettings($return_config = false)
 		'',
 			// Reporting of personal messages?
 			array('check', 'enableReportPM'),
+		'',
+			array('select', 'frame_security', array('SAMEORIGIN' => $txt['setting_frame_security_SAMEORIGIN'], 'DENY' => $txt['setting_frame_security_DENY'], 'DISABLE' => $txt['setting_frame_security_DISABLE'])),
 	);
 
 	call_integration_hook('integrate_general_security_settings', array(&$config_vars));

+ 1 - 1
Sources/ManageSmileys.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/Memberlist.php

@@ -8,7 +8,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 14 - 3
Sources/MessageIndex.php

@@ -8,7 +8,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1
@@ -111,13 +111,24 @@ function MessageIndex()
 
 	// Build a list of the board's moderators.
 	$context['moderators'] = &$board_info['moderators'];
+	$context['moderator_groups'] = &$board_info['moderator_groups'];
 	$context['link_moderators'] = array();
 	if (!empty($board_info['moderators']))
 	{
 		foreach ($board_info['moderators'] as $mod)
 			$context['link_moderators'][] ='<a href="' . $scripturl . '?action=profile;u=' . $mod['id'] . '" title="' . $txt['board_moderator'] . '">' . $mod['name'] . '</a>';
-
-		$context['linktree'][count($context['linktree']) - 1]['extra_after'] = '<span class="board_moderators"> (' . (count($context['link_moderators']) == 1 ? $txt['moderator'] : $txt['moderators']) . ': ' . implode(', ', $context['link_moderators']) . ')</span>';
+	}
+	if (!empty($board_info['moderator_groups']))
+	{
+		// By default just tack the moderator groups onto the end of the members
+		foreach ($board_info['moderator_groups'] as $mod_group)
+			$context['link_moderators'][] = '<a href="' . $scripturl . '?action=groups;sa=members;group=' . $mod_group['id'] . '" title="' . $txt['board_moderator'] . '">' . $mod_group['name'] . '</a>';
+	}
+	
+	// Now we tack the info onto the end of the linktree
+	if (!empty($context['link_moderators']))
+	{
+	 	$context['linktree'][count($context['linktree']) - 1]['extra_after'] = '<span class="board_moderators"> (' . (count($context['link_moderators']) == 1 ? $txt['moderator'] : $txt['moderators']) . ': ' . implode(', ', $context['link_moderators']) . ')</span>';
 	}
 
 	// Mark current and parent boards as seen.

+ 1 - 1
Sources/ModerationCenter.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/Modlog.php

@@ -8,7 +8,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/MoveTopic.php

@@ -8,7 +8,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/News.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/Notify.php

@@ -8,7 +8,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/PackageGet.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/Packages.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/PersonalMessage.php

@@ -9,7 +9,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/Poll.php

@@ -8,7 +8,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/Post.php

@@ -8,7 +8,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 9 - 4
Sources/PostModeration.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1
@@ -50,7 +50,7 @@ function PostModerationMain()
  */
 function UnapprovedPosts()
 {
-	global $txt, $scripturl, $context, $user_info, $smcFunc;
+	global $txt, $scripturl, $context, $user_info, $smcFunc, $options, $modSettings;
 
 	$context['current_view'] = isset($_GET['sa']) && $_GET['sa'] == 'topics' ? 'topics' : 'replies';
 	$context['page_title'] = $txt['mc_unapproved_posts'];
@@ -213,8 +213,11 @@ function UnapprovedPosts()
 	);
 	list ($context['total_unapproved_topics']) = $smcFunc['db_fetch_row']($request);
 	$smcFunc['db_free_result']($request);
+	
+	// Limit to how many? (obey the user setting)
+	$limit = !empty($options['messages_per_page']) ? $options['messages_per_page'] : $modSettings['defaultMaxMessages'];
 
-	$context['page_index'] = constructPageIndex($scripturl . '?action=moderate;area=postmod;sa=' . $context['current_view'] . (isset($_REQUEST['brd']) ? ';brd=' . (int) $_REQUEST['brd'] : ''), $_GET['start'], $context['current_view'] == 'topics' ? $context['total_unapproved_topics'] : $context['total_unapproved_posts'], 10);
+	$context['page_index'] = constructPageIndex($scripturl . '?action=moderate;area=postmod;sa=' . $context['current_view'] . (isset($_REQUEST['brd']) ? ';brd=' . (int) $_REQUEST['brd'] : ''), $_GET['start'], $context['current_view'] == 'topics' ? $context['total_unapproved_topics'] : $context['total_unapproved_posts'], $limit);
 	$context['start'] = $_GET['start'];
 
 	// We have enough to make some pretty tabs!
@@ -249,9 +252,11 @@ function UnapprovedPosts()
 			AND t.id_first_msg ' . ($context['current_view'] == 'topics' ? '=' : '!=') . ' m.id_msg
 			AND {query_see_board}
 			' . $approve_query . '
-		LIMIT ' . $context['start'] . ', 10',
+		LIMIT {int:start}, {int:limit}',
 		array(
 			'not_approved' => 0,
+			'start' => $context['start'],
+			'limit' => $limit,
 		)
 	);
 	$context['unapproved_items'] = array();

+ 1 - 1
Sources/Printpage.php

@@ -8,7 +8,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/Profile-Actions.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 2 - 2
Sources/Profile-Modify.php

@@ -9,7 +9,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1
@@ -639,7 +639,7 @@ function loadProfileFields($force_reload = false)
 			'input_validate' => create_function('&$value', '
 				global $smcFunc;
 
-				if ($smcFunc[\'strlen\'] > 50)
+				if ($smcFunc[\'strlen\']($value) > 50)
 					return \'user_title_too_long\';
 
 				return true;

+ 8 - 5
Sources/Profile-View.php

@@ -5,7 +5,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1
@@ -2218,12 +2218,14 @@ function showPermissions($memID)
 
 	// Load a list of boards for the jump box - except the defaults.
 	$request = $smcFunc['db_query']('order_by_board_order', '
-		SELECT b.id_board, b.name, b.id_profile, b.member_groups, IFNULL(mods.id_member, 0) AS is_mod
+		SELECT b.id_board, b.name, b.id_profile, b.member_groups, IFNULL(mods.id_member, IFNULL(modgs.id_group, 0)) AS is_mod
 		FROM {db_prefix}boards AS b
 			LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board AND mods.id_member = {int:current_member})
+			LEFT JOIN {db_prefix}moderator_groups AS modgs ON (modgs.id_board = b.id_board AND modgs.id_group IN ({array_int:current_groups}))
 		WHERE {query_see_board}',
 		array(
 			'current_member' => $memID,
+			'current_groups' => $curGroups,
 		)
 	);
 	$context['boards'] = array();
@@ -2313,14 +2315,15 @@ function showPermissions($memID)
 	$request = $smcFunc['db_query']('', '
 		SELECT
 			bp.add_deny, bp.permission, bp.id_group, mg.group_name' . (empty($board) ? '' : ',
-			b.id_profile, CASE WHEN mods.id_member IS NULL THEN 0 ELSE 1 END AS is_moderator') . '
+			b.id_profile, CASE WHEN (mods.id_member IS NULL AND modgs.id_group IS NULL) THEN 0 ELSE 1 END AS is_moderator') . '
 		FROM {db_prefix}board_permissions AS bp' . (empty($board) ? '' : '
 			INNER JOIN {db_prefix}boards AS b ON (b.id_board = {int:current_board})
-			LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board AND mods.id_member = {int:current_member})') . '
+			LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board AND mods.id_member = {int:current_member})
+			LEFT JOIN {db_prefix}moderator_groups AS modgs ON (modgs.id_board = b.id_board AND modgs.id_group IN ({array_int:group_list}))') . '
 			LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = bp.id_group)
 		WHERE bp.id_profile = {raw:current_profile}
 			AND bp.id_group IN ({array_int:group_list}' . (empty($board) ? ')' : ', {int:moderator_group})
-			AND (mods.id_member IS NOT NULL OR bp.id_group != {int:moderator_group})'),
+			AND (mods.id_member IS NOT NULL OR modgs.id_group IS NOT NULL OR bp.id_group != {int:moderator_group})'),
 		array(
 			'current_board' => $board,
 			'group_list' => $curGroups,

+ 3 - 4
Sources/Profile.php

@@ -9,7 +9,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1
@@ -299,7 +299,6 @@ function ModifyProfile($post_errors = array())
 					'file' => 'Profile-Actions.php',
 					'function' => 'issueWarning',
 					'token' => 'profile-iw%u',
-					'enabled' => !$context['user']['is_owner'],
 					'permission' => array(
 						'own' => array('issue_warning'),
 						'any' => array('issue_warning'),
@@ -460,12 +459,12 @@ function ModifyProfile($post_errors = array())
 	unset($profile_areas);
 
 	// Now the context is setup have we got any security checks to carry out additional to that above?
-	if (isset($security_checks['validateToken']))
-		validateToken($token_name, $token_type);
 	if (isset($security_checks['session']))
 		checkSession($security_checks['session']);
 	if (isset($security_checks['validate']))
 		validateSession();
+	if (isset($security_checks['validateToken']))
+		validateToken($token_name, $token_type);
 	if (isset($security_checks['permission']))
 		isAllowedTo($security_checks['permission']);
 

+ 1 - 1
Sources/QueryString.php

@@ -8,7 +8,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/Recent.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/Register.php

@@ -9,7 +9,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/Reminder.php

@@ -6,7 +6,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/RemoveTopic.php

@@ -8,7 +8,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.0

+ 1 - 1
Sources/RepairBoards.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 58 - 1
Sources/Reports.php

@@ -15,7 +15,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1
@@ -156,6 +156,19 @@ function BoardReport()
 		$moderators[$row['id_board']][] = $row['real_name'];
 	$smcFunc['db_free_result']($request);
 
+	// Get every moderator gruop.
+	$request = $smcFunc['db_query']('', '
+		SELECT modgs.id_board, modgs.id_group, memg.group_name
+		FROM {db_prefix}moderator_groups AS modgs
+			INNER JOIN {db_prefix}membergroups AS memg ON (memg.id_group = modgs.id_group)',
+		array(
+		)
+	);
+	$moderator_groups = array();
+	while ($row = $smcFunc['db_fetch_assoc']($request))
+		$moderator_groups[$row['id_board']][] = $row['group_name'];
+	$smcFunc['db_free_result']($request);
+
 	// Get all the possible membergroups!
 	$request = $smcFunc['db_query']('', '
 		SELECT id_group, group_name, online_color
@@ -179,6 +192,7 @@ function BoardReport()
 		'override_theme' => $txt['board_override_theme'],
 		'profile' => $txt['board_profile'],
 		'moderators' => $txt['board_moderators'],
+		'moderator_groups' => $txt['board_moderator_groups'],
 		'groups' => $txt['board_groups'],
 	);
 	if (!empty($modSettings['deny_boards_access']))
@@ -223,6 +237,7 @@ function BoardReport()
 			'profile' => $profile_name,
 			'override_theme' => $row['override_theme'] ? $txt['yes'] : $txt['no'],
 			'moderators' => empty($moderators[$row['id_board']]) ? $txt['none'] : implode(', ', $moderators[$row['id_board']]),
+			'moderator_groups' => empty($moderator_groups[$row['id_board']]) ? $txt['none'] : implode(', ', $moderator_groups[$row['id_board']]),
 		);
 
 		// Work out the membergroups who can and cannot access it (but only if enabled).
@@ -309,10 +324,26 @@ function BoardPermissionsReport()
 		$boards[$row['id_board']] = array(
 			'name' => $row['name'],
 			'profile' => $row['id_profile'],
+			'mod_groups' => array(),
 		);
 		$profiles[] = $row['id_profile'];
 	}
 	$smcFunc['db_free_result']($request);
+	
+	// Get the ids of any groups allowed to moderate this board
+	// Limit it to any boards and/or groups we're looking at
+	$request = $smcFunc['db_query']('', '
+		SELECT id_board, id_group
+		FROM {db_prefix}moderator_groups
+		WHERE ' . $board_clause .' AND ' . $group_clause,
+		array(
+		)
+	);
+	while ($row = $smcFunc['db_fetch_assoc']($request))
+	{
+		$boards[$row['id_board']]['mod_groups'][] = $row['id_group'];
+	}
+	$smcFunc['db_free_result']($request);
 
 	// Get all the possible membergroups, except admin!
 	$request = $smcFunc['db_query']('', '
@@ -409,6 +440,11 @@ function BoardPermissionsReport()
 					// Set the data for this group to be the local permission.
 					$curData[$id_group] = $group_permissions[$ID_PERM];
 				}
+				// Is it inherited from Moderator?
+				elseif (in_array($id_group, $boards[$board]['mod_groups']) && !empty($groups[3]) && isset($groups[3][$ID_PERM]))
+				{
+					$curData[$id_group] = $groups[3][$ID_PERM];
+				}
 				// Otherwise means it's set to disallow..
 				else
 				{
@@ -702,6 +738,27 @@ function StaffReport()
 	}
 	$smcFunc['db_free_result']($request);
 
+	// Get any additional boards they can moderate through group-based board moderation
+	$request = $smcFunc['db_query']('', '
+		SELECT mem.id_member, modgs.id_board
+		FROM {db_prefix}members AS mem
+			INNER JOIN {db_prefix}moderator_groups AS modgs ON (modgs.id_group = mem.id_group OR FIND_IN_SET(modgs.id_group, mem.additional_groups) != 0)',
+		array(
+		)
+	);
+	
+	// Add each board/member to the arrays, but only if they aren't already there
+	while ($row = $smcFunc['db_fetch_assoc']($request))
+	{
+		// Either we don't have them as a moderator at all or at least not as a moderator of this board
+		if (!array_key_exists($row['id_member'], $moderators) || !in_array($row['id_board'], $moderators[$row['id_member']]))
+			$moderators[$row['id_member']][] = $row['id_board'];
+		
+		// We don't have them listed as a moderator yet
+		if (!array_key_exists($row['id_member'], $local_mods))
+			$local_mods[$row['id_member']] = $row['id_member'];
+	}
+
 	// Get a list of global moderators (i.e. members with moderation powers).
 	$global_mods = array_intersect(membersAllowedTo('moderate_board', 0), membersAllowedTo('approve_posts', 0), membersAllowedTo('remove_any', 0), membersAllowedTo('modify_any', 0));
 

+ 2 - 2
Sources/ScheduledTasks.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1
@@ -434,7 +434,7 @@ function scheduled_daily_maintenance()
 
 	// Check the database version - for some buggy MySQL version.
 	$server_version = $smcFunc['db_server_info']();
-	if ($db_type == 'mysql' && in_array(substr($server_version, 0, 6), array('5.0.50', '5.0.51')))
+	if (($db_type == 'mysql' || $db_type == 'mysqli') && in_array(substr($server_version, 0, 6), array('5.0.50', '5.0.51')))
 		updateSettings(array('db_mysql_group_by_fix' => '1'));
 	elseif (!empty($modSettings['db_mysql_group_by_fix']))
 		$smcFunc['db_query']('', '

+ 1 - 1
Sources/Search.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 2 - 2
Sources/SearchAPI-Custom.php

@@ -5,7 +5,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1
@@ -59,7 +59,7 @@ class custom_search
 	 * What databases support the custom index?
 	 * @var type
 	 */
-	protected $supported_databases = array('mysql', 'postgresql', 'sqlite');
+	protected $supported_databases = array('mysql', 'mysqli', 'postgresql', 'sqlite');
 
 	/**
 	 * constructor function

+ 2 - 2
Sources/SearchAPI-Fulltext.php

@@ -5,7 +5,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1
@@ -54,7 +54,7 @@ class fulltext_search
 	 * What databases support the fulltext index?
 	 * @var type
 	 */
-	protected $supported_databases = array('mysql');
+	protected $supported_databases = array('mysql', 'mysqli');
 
 	/**
 	 * fulltext_search::__construct()

+ 1 - 1
Sources/SearchAPI-Standard.php

@@ -5,7 +5,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 33 - 6
Sources/Security.php

@@ -8,7 +8,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1
@@ -951,10 +951,11 @@ function allowedTo($permission, $boards = null)
 		FROM {db_prefix}boards AS b
 			INNER JOIN {db_prefix}board_permissions AS bp ON (bp.id_profile = b.id_profile)
 			LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board AND mods.id_member = {int:current_member})
+			LEFT JOIN {db_prefix}moderator_groups AS modgs ON (modgs.id_board = b.id_board AND b.id_group IN ({array_int:group_list})
 		WHERE b.id_board IN ({array_int:board_list})
 			AND bp.id_group IN ({array_int:group_list}, {int:moderator_group})
 			AND bp.permission {raw:permission_list}
-			AND (mods.id_member IS NOT NULL OR bp.id_group != {int:moderator_group})
+			AND (mods.id_member IS NOT NULL OR modgs.id_group IS NOT NULL OR bp.id_group != {int:moderator_group})
 		GROUP BY b.id_board',
 		array(
 			'current_member' => $user_info['id'],
@@ -1087,9 +1088,10 @@ function boardsAllowedTo($permissions, $check_access = true, $simple = true)
 		FROM {db_prefix}board_permissions AS bp
 			INNER JOIN {db_prefix}boards AS b ON (b.id_profile = bp.id_profile)
 			LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board AND mods.id_member = {int:current_member})
+			LEFT JOIN {db_prefix}moderator_groups AS modgs ON (modgs.id_board = b.id_board AND modgs.id_group IN ({array_int:group_list}))
 		WHERE bp.id_group IN ({array_int:group_list}, {int:moderator_group})
 			AND bp.permission IN ({array_string:permissions})
-			AND (mods.id_member IS NOT NULL OR bp.id_group != {int:moderator_group})' .
+			AND (mods.id_member IS NOT NULL OR modgs.id_group IS NOT NULL OR bp.id_group != {int:moderator_group})' .
 			($check_access ? ' AND {query_see_board}' : ''),
 		array(
 			'current_member' => $user_info['id'],
@@ -1284,7 +1286,7 @@ RemoveHandler .php .php3 .phtml .cgi .fcgi .pl .fpl .shtml';
 	{
 		$fh = @fopen($path . '/index.php', 'w');
 		if ($fh) {
-			fwrite($fh, '<?php
+			fwrite($fh, '<' . '?php
 
 /**
  * This file is here solely to protect your ' . $directoryname . ' directory.
@@ -1301,7 +1303,7 @@ if (file_exists(dirname(dirname(__FILE__)) . \'/Settings.php\'))
 else
 	exit;
 
-?>');
+?'. '>');
 			fclose($fh);
 		}
 		$errors[] = 'index-php_cannot_create_file';
@@ -1352,4 +1354,29 @@ function constructBanQueryIP($fullip)
 	return $ban_query;
 }
 
-?>
+/**
+* This sets the X-Frame-Options header.
+*
+* @param string $option the frame option, defaults to deny.
+* @return void.
+* @since 2.1
+*/
+function frameOptionsHeader($override = null)
+{
+	global $modSettings;
+
+	$option = 'SAMEORIGIN';
+	if (is_null($override) && !empty($modSettings['frame_security']))
+		$option = $modSettings['frame_security'];
+	elseif (in_array($override, array('SAMEORIGIN', 'DENY', 'SAMEORIGIN')))
+		$option = $override;
+
+	// Don't bother setting the header if we have disabled it.
+	if ($option == 'DISABLE')
+		return;
+
+	// Finally set it.
+	header('X-Frame-Options: ' . $option);
+}
+
+?>

+ 22 - 1
Sources/SendTopic.php

@@ -6,7 +6,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1
@@ -572,7 +572,28 @@ function ReportToModerator2()
 	while ($row = $smcFunc['db_fetch_assoc']($request2))
 		$real_mods[] = $row['id_member'];
 	$smcFunc['db_free_result']($request2);
+	
+	// Get any additional members who are in groups assigned to moderate this board
+	$request3 = $smcFunc['db_query']('', '
+		SELECT mem.id_member
+		FROM {db_prefix}members AS mem, {db_prefix}moderator_groups AS bm
+		WHERE bm.id_board = {int:current_board}
+			AND(
+				mem.id_group = bm.id_group
+				OR FIND_IN_SET(bm.id_group, mem.additional_groups) != 0
+			)',
+		array(
+			'current_board' => $board,
+		)
+	);
 
+	while ($row = $smcFunc['db_fetch_assoc']($request3))
+		$real_mods[] = $row['id_member'];
+	$smcFunc['db_free_result']($request3);
+	
+	// Make sure we don't have any duplicates
+	$real_mods = array_unique($real_mods);
+	
 	// Send every moderator an email.
 	while ($row = $smcFunc['db_fetch_assoc']($request))
 	{

+ 1 - 1
Sources/Session.php

@@ -11,7 +11,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/SplitTopics.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/Stats.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/Subs-Admin.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/Subs-Attachments.php

@@ -9,7 +9,7 @@
  * @package SMF
  * @author Simple Machines
  *
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 17 - 1
Sources/Subs-Auth.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1
@@ -736,6 +736,22 @@ function rebuildModCache()
 		while ($row = $smcFunc['db_fetch_assoc']($request))
 			$boards_mod[] = $row['id_board'];
 		$smcFunc['db_free_result']($request);
+		
+		// Can any of the groups they're in moderate any of the boards?
+		$request = $smcFunc['db_query']('', '
+			SELECT id_board
+			FROM {db_prefix}moderator_groups
+			WHERE id_group IN({array_int:groups})',
+			array(
+				'groups' => $user_info['groups'],
+			)
+		);
+		while ($row = $smcFunc['db_fetch_assoc']($request))
+			$boards_mod[] = $row['id_board'];
+		$smcFunc['db_free_result']($request);
+		
+		// Just in case we've got duplicates here...
+		$boards_mod = array_unique($boards_mod);
 	}
 
 	$mod_query = empty($boards_mod) ? '0=1' : 'b.id_board IN (' . implode(',', $boards_mod) . ')';

+ 22 - 1
Sources/Subs-BoardIndex.php

@@ -8,7 +8,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1
@@ -54,6 +54,7 @@ function getBoardIndex($boardIndexOptions)
 			(IFNULL(lb.id_msg, 0) >= b.id_msg_updated) AS is_read, IFNULL(lb.id_msg, -1) + 1 AS new_from,' . ($boardIndexOptions['include_categories'] ? '
 			c.can_collapse, IFNULL(cc.id_member, 0) AS is_collapsed,' : '')) . '
 			IFNULL(mem.id_member, 0) AS id_member, mem.avatar, m.id_msg,
+			IFNULL(mods_grp.id_group, 0) AS id_moderator_group, mods_grp.group_name AS mod_group_name,
 			IFNULL(mods_mem.id_member, 0) AS id_moderator, mods_mem.real_name AS mod_real_name' . (!empty($settings['avatars_on_indexes']) ? ',
 			IFNULL(a.id_attach, 0) AS id_attach, a.filename, a.attachment_type' : '') . '
 		FROM {db_prefix}boards AS b' . ($boardIndexOptions['include_categories'] ? '
@@ -63,6 +64,8 @@ function getBoardIndex($boardIndexOptions)
 			LEFT JOIN {db_prefix}log_boards AS lb ON (lb.id_board = b.id_board AND lb.id_member = {int:current_member})' . ($boardIndexOptions['include_categories'] ? '
 			LEFT JOIN {db_prefix}collapsed_categories AS cc ON (cc.id_cat = c.id_cat AND cc.id_member = {int:current_member})' : '')) . '
 			LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board)
+			LEFT JOIN {db_prefix}moderator_groups AS mods_g ON (mods_g.id_board = b.id_board)
+			LEFT JOIN {db_prefix}membergroups AS mods_grp ON (mods_grp.id_group = mods_g.id_group)
 			LEFT JOIN {db_prefix}members AS mods_mem ON (mods_mem.id_member = mods.id_member)' . (!empty($settings['avatars_on_indexes']) ? '
 			LEFT JOIN {db_prefix}attachments AS a ON (a.id_member = m.id_member)' : '') . '
 		WHERE {query_see_board}' . (empty($boardIndexOptions['countChildPosts']) ? (empty($boardIndexOptions['base_level']) ? '' : '
@@ -137,7 +140,9 @@ function getBoardIndex($boardIndexOptions)
 					'name' => $row_board['board_name'],
 					'description' => $row_board['description'],
 					'moderators' => array(),
+					'moderator_groups' => array(),
 					'link_moderators' => array(),
+					'link_moderator_groups' => array(),
 					'children' => array(),
 					'link_children' => array(),
 					'children_new' => false,
@@ -161,6 +166,22 @@ function getBoardIndex($boardIndexOptions)
 				);
 				$this_category[$row_board['id_board']]['link_moderators'][] = '<a href="' . $scripturl . '?action=profile;u=' . $row_board['id_moderator'] . '" title="' . $txt['board_moderator'] . '">' . $row_board['mod_real_name'] . '</a>';
 			}
+			if (!empty($row_board['id_moderator_group']))
+			{
+				$this_category[$row_board['id_board']]['moderator_groups'][$row_board['id_moderator_group']] = array(
+					'id' => $row_board['id_moderator_group'],
+					'name' => $row_board['mod_group_name'],
+					'href' => $scripturl . '?action=groups;sa=members;group=' . $row_board['id_moderator_group'],
+					'link' => '<a href="' . $scripturl . '?action=groups;sa=members;group=' . $row_board['id_moderator_group'] . '" title="' . $txt['board_moderator'] . '">' . $row_board['mod_group_name'] . '</a>'
+				);
+				$this_category[$row_board['id_board']]['link_moderator_groups'][] = '<a href="' . $scripturl . '?action=groups;sa=members;group=' . $row_board['id_moderator_group'] . '" title="' . $txt['board_moderator'] . '">' . $row_board['mod_group_name'] . '</a>';
+			}
+			
+			// Merge the two lists of moderators
+			if (!empty($this_category[$row_board['id_board']]['link_moderator_groups']))
+			{
+				$this_category[$row_board['id_board']]['link_moderators'] = array_merge($this_category[$row_board['id_board']]['link_moderators'], $this_category[$row_board['id_board']]['link_moderator_groups']);
+			}
 		}
 		// Found a child board.... make sure we've found its parent and the child hasn't been set already.
 		elseif (isset($this_category[$row_board['id_parent']]['children']) && !isset($this_category[$row_board['id_parent']]['children'][$row_board['id_board']]))

+ 79 - 2
Sources/Subs-Boards.php

@@ -8,7 +8,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1
@@ -657,7 +657,7 @@ function modifyBoard($board_id, &$boardOptions)
 		);
 
 	// Set moderators of this board.
-	if (isset($boardOptions['moderators']) || isset($boardOptions['moderator_string']))
+	if (isset($boardOptions['moderators']) || isset($boardOptions['moderator_string']) || isset($boardOptions['moderator_groups']) || isset($boardOptions['moderator_group_string']))
 	{
 		// Reset current moderators for this board - if there are any!
 		$smcFunc['db_query']('', '
@@ -718,6 +718,74 @@ function modifyBoard($board_id, &$boardOptions)
 			);
 		}
 
+		// Reset current moderator groups for this board - if there are any!
+		$smcFunc['db_query']('', '
+			DELETE FROM {db_prefix}moderator_groups
+			WHERE id_board = {int:board_list}',
+			array(
+				'board_list' => $board_id,
+			)
+		);
+
+		// Validate and get the IDs of the new moderator groups.
+		if (isset($boardOptions['moderator_group_string']) && trim($boardOptions['moderator_group_string']) != '')
+		{
+			// Divvy out the group names, remove extra space.
+			$moderator_group_string = strtr($smcFunc['htmlspecialchars']($boardOptions['moderator_group_string'], ENT_QUOTES), array('&quot;' => '"'));
+			preg_match_all('~"([^"]+)"~', $moderator_group_string, $matches);
+			$moderator_groups = array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $moderator_group_string)));
+			for ($k = 0, $n = count($moderator_groups); $k < $n; $k++)
+			{
+				$moderator_groups[$k] = trim($moderator_groups[$k]);
+
+				if (strlen($moderator_groups[$k]) == 0)
+					unset($moderator_groups[$k]);
+			}
+
+			/* 	Find all the id_group's for all the group names in the list
+				But skip any invalid ones (invisible/post groups/Administrator/Moderator) */
+			if (empty($boardOptions['moderator_groups']))
+				$boardOptions['moderator_groups'] = array();
+			if (!empty($moderator_groups))
+			{
+				$request = $smcFunc['db_query']('', '
+					SELECT id_group
+					FROM {db_prefix}membergroups
+					WHERE group_name IN ({array_string:moderator_group_list})
+						AND hidden = {int:visible}
+						AND min_posts = {int:negative_one}
+						AND id_group NOT IN ({array_int:invalid_groups})
+					LIMIT ' . count($moderator_groups),
+					array(
+						'visible' => 0,
+						'negative_one' => -1,
+						'invalid_groups' => array(1,3),
+						'moderator_group_list' => $moderator_groups,
+					)
+				);
+				while ($row = $smcFunc['db_fetch_assoc']($request))
+				{
+					$boardOptions['moderator_groups'][] = $row['id_group'];
+				}
+				$smcFunc['db_free_result']($request);
+			}
+		}
+
+		// Add the moderator groups to the board.
+		if (!empty($boardOptions['moderator_groups']))
+		{
+			$inserts = array();
+			foreach ($boardOptions['moderator_groups'] as $moderator_group)
+				$inserts[] = array($board_id, $moderator_group);
+
+			$smcFunc['db_insert']('insert',
+				'{db_prefix}moderator_groups',
+				array('id_board' => 'int', 'id_group' => 'int'),
+				$inserts,
+				array('id_board', 'id_group')
+			);
+		}
+
 		// Note that caches can now be wrong!
 		updateSettings(array('settings_updated' => time()));
 	}
@@ -928,6 +996,15 @@ function deleteBoards($boards_to_remove, $moveChildrenTo = null)
 		)
 	);
 
+	// Delete this board's moderator groups.
+	$smcFunc['db_query']('', '
+		DELETE FROM {db_prefix}moderator_groups
+		WHERE id_board IN ({array_int:boards_to_remove})',
+		array(
+			'boards_to_remove' => $boards_to_remove,
+		)
+	);
+
 	// Delete any extra events in the calendar.
 	$smcFunc['db_query']('', '
 		DELETE FROM {db_prefix}calendar

+ 1 - 1
Sources/Subs-Calendar.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/Subs-Categories.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 591 - 591
Sources/Subs-Charset.php

@@ -1,592 +1,592 @@
-<?php
-
-/**
- * Simple Machines Forum (SMF)
- *
- * @package SMF
- * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
- * @license http://www.simplemachines.org/about/smf/license.php BSD
- *
- * @version 2.1 Alpha 1
- */
-
-if (!defined('SMF'))
-	die('No direct access...');
-
-/**
- * Converts the given UTF-8 string into lowercase.
- * equivalent to mb_strtolower($string, 'UTF-8')
- *
- * @param string $string
- * @return string
- */
-function utf8_strtolower($string)
-{
-	static $case_folding = array(
-		'A' => 'a',		'B' => 'b',		'C' => 'c',		'D' => 'd',
-		'E' => 'e',		'F' => 'f',		'G' => 'g',		'H' => 'h',
-		'I' => 'i',		'J' => 'j',		'K' => 'k',		'L' => 'l',
-		'M' => 'm',		'N' => 'n',		'O' => 'o',		'P' => 'p',
-		'Q' => 'q',		'R' => 'r',		'S' => 's',		'T' => 't',
-		'U' => 'u',		'V' => 'v',		'W' => 'w',		'X' => 'x',
-		'Y' => 'y',		'Z' => 'z',		'µ' => 'μ',		'À' => 'à',
-		'Á' => 'á',		'Â' => 'â',		'Ã' => 'ã',		'Ä' => 'ä',
-		'Å' => 'å',		'Æ' => 'æ',		'Ç' => 'ç',		'È' => 'è',
-		'É' => 'é',		'Ê' => 'ê',		'Ë' => 'ë',		'Ì' => 'ì',
-		'Í' => 'í',		'Î' => 'î',		'Ï' => 'ï',		'Ð' => 'ð',
-		'Ñ' => 'ñ',		'Ò' => 'ò',		'Ó' => 'ó',		'Ô' => 'ô',
-		'Õ' => 'õ',		'Ö' => 'ö',		'Ø' => 'ø',		'Ù' => 'ù',
-		'Ú' => 'ú',		'Û' => 'û',		'Ü' => 'ü',		'Ý' => 'ý',
-		'Þ' => 'þ',		'ß' => 'ss',	'Ā' => 'ā',		'Ă' => 'ă',
-		'Ą' => 'ą',		'Ć' => 'ć',		'Ĉ' => 'ĉ',		'Ċ' => 'ċ',
-		'Č' => 'č',		'Ď' => 'ď',		'Đ' => 'đ',		'Ē' => 'ē',
-		'Ĕ' => 'ĕ',		'Ė' => 'ė',		'Ę' => 'ę',		'Ě' => 'ě',
-		'Ĝ' => 'ĝ',		'Ğ' => 'ğ',		'Ġ' => 'ġ',		'Ģ' => 'ģ',
-		'Ĥ' => 'ĥ',		'Ħ' => 'ħ',		'Ĩ' => 'ĩ',		'Ī' => 'ī',
-		'Ĭ' => 'ĭ',		'Į' => 'į',		'İ' => 'i̇',		'IJ' => 'ij',
-		'Ĵ' => 'ĵ',		'Ķ' => 'ķ',		'Ĺ' => 'ĺ',		'Ļ' => 'ļ',
-		'Ľ' => 'ľ',		'Ŀ' => 'ŀ',		'Ł' => 'ł',		'Ń' => 'ń',
-		'Ņ' => 'ņ',		'Ň' => 'ň',		'ʼn' => 'ʼn',	'Ŋ' => 'ŋ',
-		'Ō' => 'ō',		'Ŏ' => 'ŏ',		'Ő' => 'ő',		'Œ' => 'œ',
-		'Ŕ' => 'ŕ',		'Ŗ' => 'ŗ',		'Ř' => 'ř',		'Ś' => 'ś',
-		'Ŝ' => 'ŝ',		'Ş' => 'ş',		'Š' => 'š',		'Ţ' => 'ţ',
-		'Ť' => 'ť',		'Ŧ' => 'ŧ',		'Ũ' => 'ũ',		'Ū' => 'ū',
-		'Ŭ' => 'ŭ',		'Ů' => 'ů',		'Ű' => 'ű',		'Ų' => 'ų',
-		'Ŵ' => 'ŵ',		'Ŷ' => 'ŷ',		'Ÿ' => 'ÿ',		'Ź' => 'ź',
-		'Ż' => 'ż',		'Ž' => 'ž',		'ſ' => 's',		'Ɓ' => 'ɓ',
-		'Ƃ' => 'ƃ',		'Ƅ' => 'ƅ',		'Ɔ' => 'ɔ',		'Ƈ' => 'ƈ',
-		'Ɖ' => 'ɖ',		'Ɗ' => 'ɗ',		'Ƌ' => 'ƌ',		'Ǝ' => 'ǝ',
-		'Ə' => 'ə',		'Ɛ' => 'ɛ',		'Ƒ' => 'ƒ',		'Ɠ' => 'ɠ',
-		'Ɣ' => 'ɣ',		'Ɩ' => 'ɩ',		'Ɨ' => 'ɨ',		'Ƙ' => 'ƙ',
-		'Ɯ' => 'ɯ',		'Ɲ' => 'ɲ',		'Ɵ' => 'ɵ',		'Ơ' => 'ơ',
-		'Ƣ' => 'ƣ',		'Ƥ' => 'ƥ',		'Ʀ' => 'ʀ',		'Ƨ' => 'ƨ',
-		'Ʃ' => 'ʃ',		'Ƭ' => 'ƭ',		'Ʈ' => 'ʈ',		'Ư' => 'ư',
-		'Ʊ' => 'ʊ',		'Ʋ' => 'ʋ',		'Ƴ' => 'ƴ',		'Ƶ' => 'ƶ',
-		'Ʒ' => 'ʒ',		'Ƹ' => 'ƹ',		'Ƽ' => 'ƽ',		'DŽ' => 'dž',
-		'Dž' => 'dž',		'LJ' => 'lj',		'Lj' => 'lj',		'NJ' => 'nj',
-		'Nj' => 'nj',		'Ǎ' => 'ǎ',		'Ǐ' => 'ǐ',		'Ǒ' => 'ǒ',
-		'Ǔ' => 'ǔ',		'Ǖ' => 'ǖ',		'Ǘ' => 'ǘ',		'Ǚ' => 'ǚ',
-		'Ǜ' => 'ǜ',		'Ǟ' => 'ǟ',		'Ǡ' => 'ǡ',		'Ǣ' => 'ǣ',
-		'Ǥ' => 'ǥ',		'Ǧ' => 'ǧ',		'Ǩ' => 'ǩ',		'Ǫ' => 'ǫ',
-		'Ǭ' => 'ǭ',		'Ǯ' => 'ǯ',		'ǰ' => 'ǰ',		'DZ' => 'dz',
-		'Dz' => 'dz',		'Ǵ' => 'ǵ',		'Ƕ' => 'ƕ',		'Ƿ' => 'ƿ',
-		'Ǹ' => 'ǹ',		'Ǻ' => 'ǻ',		'Ǽ' => 'ǽ',		'Ǿ' => 'ǿ',
-		'Ȁ' => 'ȁ',		'Ȃ' => 'ȃ',		'Ȅ' => 'ȅ',		'Ȇ' => 'ȇ',
-		'Ȉ' => 'ȉ',		'Ȋ' => 'ȋ',		'Ȍ' => 'ȍ',		'Ȏ' => 'ȏ',
-		'Ȑ' => 'ȑ',		'Ȓ' => 'ȓ',		'Ȕ' => 'ȕ',		'Ȗ' => 'ȗ',
-		'Ș' => 'ș',		'Ț' => 'ț',		'Ȝ' => 'ȝ',		'Ȟ' => 'ȟ',
-		'Ƞ' => 'ƞ',		'Ȣ' => 'ȣ',		'Ȥ' => 'ȥ',		'Ȧ' => 'ȧ',
-		'Ȩ' => 'ȩ',		'Ȫ' => 'ȫ',		'Ȭ' => 'ȭ',		'Ȯ' => 'ȯ',
-		'Ȱ' => 'ȱ',		'Ȳ' => 'ȳ',		'Ⱥ' => 'ⱥ',		'Ȼ' => 'ȼ',
-		'Ƚ' => 'ƚ',		'Ⱦ' => 'ⱦ',		'Ɂ' => 'ɂ',		'Ƀ' => 'ƀ',
-		'Ʉ' => 'ʉ',		'Ʌ' => 'ʌ',		'Ɇ' => 'ɇ',		'Ɉ' => 'ɉ',
-		'Ɋ' => 'ɋ',		'Ɍ' => 'ɍ',		'Ɏ' => 'ɏ',		'ͅ' => 'ι',
-		'Ά' => 'ά',		'Έ' => 'έ',		'Ή' => 'ή',		'Ί' => 'ί',
-		'Ό' => 'ό',		'Ύ' => 'ύ',		'Ώ' => 'ώ',		'ΐ' => 'ΐ',
-		'Α' => 'α',		'Β' => 'β',		'Γ' => 'γ',		'Δ' => 'δ',
-		'Ε' => 'ε',		'Ζ' => 'ζ',		'Η' => 'η',		'Θ' => 'θ',
-		'Ι' => 'ι',		'Κ' => 'κ',		'Λ' => 'λ',		'Μ' => 'μ',
-		'Ν' => 'ν',		'Ξ' => 'ξ',		'Ο' => 'ο',		'Π' => 'π',
-		'Ρ' => 'ρ',		'Σ' => 'σ',		'Τ' => 'τ',		'Υ' => 'υ',
-		'Φ' => 'φ',		'Χ' => 'χ',		'Ψ' => 'ψ',		'Ω' => 'ω',
-		'Ϊ' => 'ϊ',		'Ϋ' => 'ϋ',		'ΰ' => 'ΰ',	'ς' => 'σ',
-		'ϐ' => 'β',		'ϑ' => 'θ',		'ϕ' => 'φ',		'ϖ' => 'π',
-		'Ϙ' => 'ϙ',		'Ϛ' => 'ϛ',		'Ϝ' => 'ϝ',		'Ϟ' => 'ϟ',
-		'Ϡ' => 'ϡ',		'Ϣ' => 'ϣ',		'Ϥ' => 'ϥ',		'Ϧ' => 'ϧ',
-		'Ϩ' => 'ϩ',		'Ϫ' => 'ϫ',		'Ϭ' => 'ϭ',		'Ϯ' => 'ϯ',
-		'ϰ' => 'κ',		'ϱ' => 'ρ',		'ϴ' => 'θ',		'ϵ' => 'ε',
-		'Ϸ' => 'ϸ',		'Ϲ' => 'ϲ',		'Ϻ' => 'ϻ',		'Ͻ' => 'ͻ',
-		'Ͼ' => 'ͼ',		'Ͽ' => 'ͽ',		'Ѐ' => 'ѐ',		'Ё' => 'ё',
-		'Ђ' => 'ђ',		'Ѓ' => 'ѓ',		'Є' => 'є',		'Ѕ' => 'ѕ',
-		'І' => 'і',		'Ї' => 'ї',		'Ј' => 'ј',		'Љ' => 'љ',
-		'Њ' => 'њ',		'Ћ' => 'ћ',		'Ќ' => 'ќ',		'Ѝ' => 'ѝ',
-		'Ў' => 'ў',		'Џ' => 'џ',		'А' => 'а',		'Б' => 'б',
-		'В' => 'в',		'Г' => 'г',		'Д' => 'д',		'Е' => 'е',
-		'Ж' => 'ж',		'З' => 'з',		'И' => 'и',		'Й' => 'й',
-		'К' => 'к',		'Л' => 'л',		'М' => 'м',		'Н' => 'н',
-		'О' => 'о',		'П' => 'п',		'Р' => 'р',		'С' => 'с',
-		'Т' => 'т',		'У' => 'у',		'Ф' => 'ф',		'Х' => 'х',
-		'Ц' => 'ц',		'Ч' => 'ч',		'Ш' => 'ш',		'Щ' => 'щ',
-		'Ъ' => 'ъ',		'Ы' => 'ы',		'Ь' => 'ь',		'Э' => 'э',
-		'Ю' => 'ю',		'Я' => 'я',		'Ѡ' => 'ѡ',		'Ѣ' => 'ѣ',
-		'Ѥ' => 'ѥ',		'Ѧ' => 'ѧ',		'Ѩ' => 'ѩ',		'Ѫ' => 'ѫ',
-		'Ѭ' => 'ѭ',		'Ѯ' => 'ѯ',		'Ѱ' => 'ѱ',		'Ѳ' => 'ѳ',
-		'Ѵ' => 'ѵ',		'Ѷ' => 'ѷ',		'Ѹ' => 'ѹ',		'Ѻ' => 'ѻ',
-		'Ѽ' => 'ѽ',		'Ѿ' => 'ѿ',		'Ҁ' => 'ҁ',		'Ҋ' => 'ҋ',
-		'Ҍ' => 'ҍ',		'Ҏ' => 'ҏ',		'Ґ' => 'ґ',		'Ғ' => 'ғ',
-		'Ҕ' => 'ҕ',		'Җ' => 'җ',		'Ҙ' => 'ҙ',		'Қ' => 'қ',
-		'Ҝ' => 'ҝ',		'Ҟ' => 'ҟ',		'Ҡ' => 'ҡ',		'Ң' => 'ң',
-		'Ҥ' => 'ҥ',		'Ҧ' => 'ҧ',		'Ҩ' => 'ҩ',		'Ҫ' => 'ҫ',
-		'Ҭ' => 'ҭ',		'Ү' => 'ү',		'Ұ' => 'ұ',		'Ҳ' => 'ҳ',
-		'Ҵ' => 'ҵ',		'Ҷ' => 'ҷ',		'Ҹ' => 'ҹ',		'Һ' => 'һ',
-		'Ҽ' => 'ҽ',		'Ҿ' => 'ҿ',		'Ӏ' => 'ӏ',		'Ӂ' => 'ӂ',
-		'Ӄ' => 'ӄ',		'Ӆ' => 'ӆ',		'Ӈ' => 'ӈ',		'Ӊ' => 'ӊ',
-		'Ӌ' => 'ӌ',		'Ӎ' => 'ӎ',		'Ӑ' => 'ӑ',		'Ӓ' => 'ӓ',
-		'Ӕ' => 'ӕ',		'Ӗ' => 'ӗ',		'Ә' => 'ә',		'Ӛ' => 'ӛ',
-		'Ӝ' => 'ӝ',		'Ӟ' => 'ӟ',		'Ӡ' => 'ӡ',		'Ӣ' => 'ӣ',
-		'Ӥ' => 'ӥ',		'Ӧ' => 'ӧ',		'Ө' => 'ө',		'Ӫ' => 'ӫ',
-		'Ӭ' => 'ӭ',		'Ӯ' => 'ӯ',		'Ӱ' => 'ӱ',		'Ӳ' => 'ӳ',
-		'Ӵ' => 'ӵ',		'Ӷ' => 'ӷ',		'Ӹ' => 'ӹ',		'Ӻ' => 'ӻ',
-		'Ӽ' => 'ӽ',		'Ӿ' => 'ӿ',		'Ԁ' => 'ԁ',		'Ԃ' => 'ԃ',
-		'Ԅ' => 'ԅ',		'Ԇ' => 'ԇ',		'Ԉ' => 'ԉ',		'Ԋ' => 'ԋ',
-		'Ԍ' => 'ԍ',		'Ԏ' => 'ԏ',		'Ԑ' => 'ԑ',		'Ԓ' => 'ԓ',
-		'Ա' => 'ա',		'Բ' => 'բ',		'Գ' => 'գ',		'Դ' => 'դ',
-		'Ե' => 'ե',		'Զ' => 'զ',		'Է' => 'է',		'Ը' => 'ը',
-		'Թ' => 'թ',		'Ժ' => 'ժ',		'Ի' => 'ի',		'Լ' => 'լ',
-		'Խ' => 'խ',		'Ծ' => 'ծ',		'Կ' => 'կ',		'Հ' => 'հ',
-		'Ձ' => 'ձ',		'Ղ' => 'ղ',		'Ճ' => 'ճ',		'Մ' => 'մ',
-		'Յ' => 'յ',		'Ն' => 'ն',		'Շ' => 'շ',		'Ո' => 'ո',
-		'Չ' => 'չ',		'Պ' => 'պ',		'Ջ' => 'ջ',		'Ռ' => 'ռ',
-		'Ս' => 'ս',		'Վ' => 'վ',		'Տ' => 'տ',		'Ր' => 'ր',
-		'Ց' => 'ց',		'Ւ' => 'ւ',		'Փ' => 'փ',		'Ք' => 'ք',
-		'Օ' => 'օ',		'Ֆ' => 'ֆ',		'և' => 'եւ',		'Ⴀ' => 'ⴀ',
-		'Ⴁ' => 'ⴁ',		'Ⴂ' => 'ⴂ',		'Ⴃ' => 'ⴃ',		'Ⴄ' => 'ⴄ',
-		'Ⴅ' => 'ⴅ',		'Ⴆ' => 'ⴆ',		'Ⴇ' => 'ⴇ',		'Ⴈ' => 'ⴈ',
-		'Ⴉ' => 'ⴉ',		'Ⴊ' => 'ⴊ',		'Ⴋ' => 'ⴋ',		'Ⴌ' => 'ⴌ',
-		'Ⴍ' => 'ⴍ',		'Ⴎ' => 'ⴎ',		'Ⴏ' => 'ⴏ',		'Ⴐ' => 'ⴐ',
-		'Ⴑ' => 'ⴑ',		'Ⴒ' => 'ⴒ',		'Ⴓ' => 'ⴓ',		'Ⴔ' => 'ⴔ',
-		'Ⴕ' => 'ⴕ',		'Ⴖ' => 'ⴖ',		'Ⴗ' => 'ⴗ',		'Ⴘ' => 'ⴘ',
-		'Ⴙ' => 'ⴙ',		'Ⴚ' => 'ⴚ',		'Ⴛ' => 'ⴛ',		'Ⴜ' => 'ⴜ',
-		'Ⴝ' => 'ⴝ',		'Ⴞ' => 'ⴞ',		'Ⴟ' => 'ⴟ',		'Ⴠ' => 'ⴠ',
-		'Ⴡ' => 'ⴡ',		'Ⴢ' => 'ⴢ',		'Ⴣ' => 'ⴣ',		'Ⴤ' => 'ⴤ',
-		'Ⴥ' => 'ⴥ',		'Ḁ' => 'ḁ',		'Ḃ' => 'ḃ',		'Ḅ' => 'ḅ',
-		'Ḇ' => 'ḇ',		'Ḉ' => 'ḉ',		'Ḋ' => 'ḋ',		'Ḍ' => 'ḍ',
-		'Ḏ' => 'ḏ',		'Ḑ' => 'ḑ',		'Ḓ' => 'ḓ',		'Ḕ' => 'ḕ',
-		'Ḗ' => 'ḗ',		'Ḙ' => 'ḙ',		'Ḛ' => 'ḛ',		'Ḝ' => 'ḝ',
-		'Ḟ' => 'ḟ',		'Ḡ' => 'ḡ',		'Ḣ' => 'ḣ',		'Ḥ' => 'ḥ',
-		'Ḧ' => 'ḧ',		'Ḩ' => 'ḩ',		'Ḫ' => 'ḫ',		'Ḭ' => 'ḭ',
-		'Ḯ' => 'ḯ',		'Ḱ' => 'ḱ',		'Ḳ' => 'ḳ',		'Ḵ' => 'ḵ',
-		'Ḷ' => 'ḷ',		'Ḹ' => 'ḹ',		'Ḻ' => 'ḻ',		'Ḽ' => 'ḽ',
-		'Ḿ' => 'ḿ',		'Ṁ' => 'ṁ',		'Ṃ' => 'ṃ',		'Ṅ' => 'ṅ',
-		'Ṇ' => 'ṇ',		'Ṉ' => 'ṉ',		'Ṋ' => 'ṋ',		'Ṍ' => 'ṍ',
-		'Ṏ' => 'ṏ',		'Ṑ' => 'ṑ',		'Ṓ' => 'ṓ',		'Ṕ' => 'ṕ',
-		'Ṗ' => 'ṗ',		'Ṙ' => 'ṙ',		'Ṛ' => 'ṛ',		'Ṝ' => 'ṝ',
-		'Ṟ' => 'ṟ',		'Ṡ' => 'ṡ',		'Ṣ' => 'ṣ',		'Ṥ' => 'ṥ',
-		'Ṧ' => 'ṧ',		'Ṩ' => 'ṩ',		'Ṫ' => 'ṫ',		'Ṭ' => 'ṭ',
-		'Ṯ' => 'ṯ',		'Ṱ' => 'ṱ',		'Ṳ' => 'ṳ',		'Ṵ' => 'ṵ',
-		'Ṷ' => 'ṷ',		'Ṹ' => 'ṹ',		'Ṻ' => 'ṻ',		'Ṽ' => 'ṽ',
-		'Ṿ' => 'ṿ',		'Ẁ' => 'ẁ',		'Ẃ' => 'ẃ',		'Ẅ' => 'ẅ',
-		'Ẇ' => 'ẇ',		'Ẉ' => 'ẉ',		'Ẋ' => 'ẋ',		'Ẍ' => 'ẍ',
-		'Ẏ' => 'ẏ',		'Ẑ' => 'ẑ',		'Ẓ' => 'ẓ',		'Ẕ' => 'ẕ',
-		'ẖ' => 'ẖ',		'ẗ' => 'ẗ',		'ẘ' => 'ẘ',		'ẙ' => 'ẙ',
-		'ẚ' => 'aʾ',	'ẛ' => 'ṡ',		'Ạ' => 'ạ',		'Ả' => 'ả',
-		'Ấ' => 'ấ',		'Ầ' => 'ầ',		'Ẩ' => 'ẩ',		'Ẫ' => 'ẫ',
-		'Ậ' => 'ậ',		'Ắ' => 'ắ',		'Ằ' => 'ằ',		'Ẳ' => 'ẳ',
-		'Ẵ' => 'ẵ',		'Ặ' => 'ặ',		'Ẹ' => 'ẹ',		'Ẻ' => 'ẻ',
-		'Ẽ' => 'ẽ',		'Ế' => 'ế',		'Ề' => 'ề',		'Ể' => 'ể',
-		'Ễ' => 'ễ',		'Ệ' => 'ệ',		'Ỉ' => 'ỉ',		'Ị' => 'ị',
-		'Ọ' => 'ọ',		'Ỏ' => 'ỏ',		'Ố' => 'ố',		'Ồ' => 'ồ',
-		'Ổ' => 'ổ',		'Ỗ' => 'ỗ',		'Ộ' => 'ộ',		'Ớ' => 'ớ',
-		'Ờ' => 'ờ',		'Ở' => 'ở',		'Ỡ' => 'ỡ',		'Ợ' => 'ợ',
-		'Ụ' => 'ụ',		'Ủ' => 'ủ',		'Ứ' => 'ứ',		'Ừ' => 'ừ',
-		'Ử' => 'ử',		'Ữ' => 'ữ',		'Ự' => 'ự',		'Ỳ' => 'ỳ',
-		'Ỵ' => 'ỵ',		'Ỷ' => 'ỷ',		'Ỹ' => 'ỹ',		'Ἀ' => 'ἀ',
-		'Ἁ' => 'ἁ',		'Ἂ' => 'ἂ',		'Ἃ' => 'ἃ',		'Ἄ' => 'ἄ',
-		'Ἅ' => 'ἅ',		'Ἆ' => 'ἆ',		'Ἇ' => 'ἇ',		'Ἐ' => 'ἐ',
-		'Ἑ' => 'ἑ',		'Ἒ' => 'ἒ',		'Ἓ' => 'ἓ',		'Ἔ' => 'ἔ',
-		'Ἕ' => 'ἕ',		'Ἠ' => 'ἠ',		'Ἡ' => 'ἡ',		'Ἢ' => 'ἢ',
-		'Ἣ' => 'ἣ',		'Ἤ' => 'ἤ',		'Ἥ' => 'ἥ',		'Ἦ' => 'ἦ',
-		'Ἧ' => 'ἧ',		'Ἰ' => 'ἰ',		'Ἱ' => 'ἱ',		'Ἲ' => 'ἲ',
-		'Ἳ' => 'ἳ',		'Ἴ' => 'ἴ',		'Ἵ' => 'ἵ',		'Ἶ' => 'ἶ',
-		'Ἷ' => 'ἷ',		'Ὀ' => 'ὀ',		'Ὁ' => 'ὁ',		'Ὂ' => 'ὂ',
-		'Ὃ' => 'ὃ',		'Ὄ' => 'ὄ',		'Ὅ' => 'ὅ',		'ὐ' => 'ὐ',
-		'ὒ' => 'ὒ',	'ὔ' => 'ὔ',	'ὖ' => 'ὖ',		'Ὑ' => 'ὑ',
-		'Ὓ' => 'ὓ',		'Ὕ' => 'ὕ',		'Ὗ' => 'ὗ',		'Ὠ' => 'ὠ',
-		'Ὡ' => 'ὡ',		'Ὢ' => 'ὢ',		'Ὣ' => 'ὣ',		'Ὤ' => 'ὤ',
-		'Ὥ' => 'ὥ',		'Ὦ' => 'ὦ',		'Ὧ' => 'ὧ',		'ᾀ' => 'ἀι',
-		'ᾁ' => 'ἁι',	'ᾂ' => 'ἂι',	'ᾃ' => 'ἃι',	'ᾄ' => 'ἄι',
-		'ᾅ' => 'ἅι',	'ᾆ' => 'ἆι',	'ᾇ' => 'ἇι',	'ᾈ' => 'ᾀ',
-		'ᾉ' => 'ᾁ',		'ᾊ' => 'ᾂ',		'ᾋ' => 'ᾃ',		'ᾌ' => 'ᾄ',
-		'ᾍ' => 'ᾅ',		'ᾎ' => 'ᾆ',		'ᾏ' => 'ᾇ',		'ᾐ' => 'ἠι',
-		'ᾑ' => 'ἡι',	'ᾒ' => 'ἢι',	'ᾓ' => 'ἣι',	'ᾔ' => 'ἤι',
-		'ᾕ' => 'ἥι',	'ᾖ' => 'ἦι',	'ᾗ' => 'ἧι',	'ᾘ' => 'ᾐ',
-		'ᾙ' => 'ᾑ',		'ᾚ' => 'ᾒ',		'ᾛ' => 'ᾓ',		'ᾜ' => 'ᾔ',
-		'ᾝ' => 'ᾕ',		'ᾞ' => 'ᾖ',		'ᾟ' => 'ᾗ',		'ᾠ' => 'ὠι',
-		'ᾡ' => 'ὡι',	'ᾢ' => 'ὢι',	'ᾣ' => 'ὣι',	'ᾤ' => 'ὤι',
-		'ᾥ' => 'ὥι',	'ᾦ' => 'ὦι',	'ᾧ' => 'ὧι',	'ᾨ' => 'ᾠ',
-		'ᾩ' => 'ᾡ',		'ᾪ' => 'ᾢ',		'ᾫ' => 'ᾣ',		'ᾬ' => 'ᾤ',
-		'ᾭ' => 'ᾥ',		'ᾮ' => 'ᾦ',		'ᾯ' => 'ᾧ',		'ᾲ' => 'ὰι',
-		'ᾳ' => 'αι',	'ᾴ' => 'άι',	'ᾶ' => 'ᾶ',		'ᾷ' => 'ᾶι',
-		'Ᾰ' => 'ᾰ',		'Ᾱ' => 'ᾱ',		'Ὰ' => 'ὰ',		'Ά' => 'ά',
-		'ᾼ' => 'ᾳ',		'ι' => 'ι',		'ῂ' => 'ὴι',	'ῃ' => 'ηι',
-		'ῄ' => 'ήι',	'ῆ' => 'ῆ',		'ῇ' => 'ῆι',	'Ὲ' => 'ὲ',
-		'Έ' => 'έ',		'Ὴ' => 'ὴ',		'Ή' => 'ή',		'ῌ' => 'ῃ',
-		'ῒ' => 'ῒ',	'ΐ' => 'ΐ',	'ῖ' => 'ῖ',		'ῗ' => 'ῗ',
-		'Ῐ' => 'ῐ',		'Ῑ' => 'ῑ',		'Ὶ' => 'ὶ',		'Ί' => 'ί',
-		'ῢ' => 'ῢ',	'ΰ' => 'ΰ',	'ῤ' => 'ῤ',		'ῦ' => 'ῦ',
-		'ῧ' => 'ῧ',		'Ῠ' => 'ῠ',		'Ῡ' => 'ῡ',		'Ὺ' => 'ὺ',
-		'Ύ' => 'ύ',		'Ῥ' => 'ῥ',		'ῲ' => 'ὼι',	'ῳ' => 'ωι',
-		'ῴ' => 'ώι',	'ῶ' => 'ῶ',		'ῷ' => 'ῶι',	'Ὸ' => 'ὸ',
-		'Ό' => 'ό',		'Ὼ' => 'ὼ',		'Ώ' => 'ώ',		'ῼ' => 'ῳ',
-		'Ω' => 'ω',		'K' => 'k',		'Å' => 'å',		'Ⅎ' => 'ⅎ',
-		'Ⅰ' => 'ⅰ',		'Ⅱ' => 'ⅱ',		'Ⅲ' => 'ⅲ',		'Ⅳ' => 'ⅳ',
-		'Ⅴ' => 'ⅴ',		'Ⅵ' => 'ⅵ',		'Ⅶ' => 'ⅶ',		'Ⅷ' => 'ⅷ',
-		'Ⅸ' => 'ⅸ',		'Ⅹ' => 'ⅹ',		'Ⅺ' => 'ⅺ',		'Ⅻ' => 'ⅻ',
-		'Ⅼ' => 'ⅼ',		'Ⅽ' => 'ⅽ',		'Ⅾ' => 'ⅾ',		'Ⅿ' => 'ⅿ',
-		'Ↄ' => 'ↄ',		'Ⓐ' => 'ⓐ',		'Ⓑ' => 'ⓑ',		'Ⓒ' => 'ⓒ',
-		'Ⓓ' => 'ⓓ',		'Ⓔ' => 'ⓔ',		'Ⓕ' => 'ⓕ',		'Ⓖ' => 'ⓖ',
-		'Ⓗ' => 'ⓗ',		'Ⓘ' => 'ⓘ',		'Ⓙ' => 'ⓙ',		'Ⓚ' => 'ⓚ',
-		'Ⓛ' => 'ⓛ',		'Ⓜ' => 'ⓜ',		'Ⓝ' => 'ⓝ',		'Ⓞ' => 'ⓞ',
-		'Ⓟ' => 'ⓟ',		'Ⓠ' => 'ⓠ',		'Ⓡ' => 'ⓡ',		'Ⓢ' => 'ⓢ',
-		'Ⓣ' => 'ⓣ',		'Ⓤ' => 'ⓤ',		'Ⓥ' => 'ⓥ',		'Ⓦ' => 'ⓦ',
-		'Ⓧ' => 'ⓧ',		'Ⓨ' => 'ⓨ',		'Ⓩ' => 'ⓩ',		'Ⰰ' => 'ⰰ',
-		'Ⰱ' => 'ⰱ',		'Ⰲ' => 'ⰲ',		'Ⰳ' => 'ⰳ',		'Ⰴ' => 'ⰴ',
-		'Ⰵ' => 'ⰵ',		'Ⰶ' => 'ⰶ',		'Ⰷ' => 'ⰷ',		'Ⰸ' => 'ⰸ',
-		'Ⰹ' => 'ⰹ',		'Ⰺ' => 'ⰺ',		'Ⰻ' => 'ⰻ',		'Ⰼ' => 'ⰼ',
-		'Ⰽ' => 'ⰽ',		'Ⰾ' => 'ⰾ',		'Ⰿ' => 'ⰿ',		'Ⱀ' => 'ⱀ',
-		'Ⱁ' => 'ⱁ',		'Ⱂ' => 'ⱂ',		'Ⱃ' => 'ⱃ',		'Ⱄ' => 'ⱄ',
-		'Ⱅ' => 'ⱅ',		'Ⱆ' => 'ⱆ',		'Ⱇ' => 'ⱇ',		'Ⱈ' => 'ⱈ',
-		'Ⱉ' => 'ⱉ',		'Ⱊ' => 'ⱊ',		'Ⱋ' => 'ⱋ',		'Ⱌ' => 'ⱌ',
-		'Ⱍ' => 'ⱍ',		'Ⱎ' => 'ⱎ',		'Ⱏ' => 'ⱏ',		'Ⱐ' => 'ⱐ',
-		'Ⱑ' => 'ⱑ',		'Ⱒ' => 'ⱒ',		'Ⱓ' => 'ⱓ',		'Ⱔ' => 'ⱔ',
-		'Ⱕ' => 'ⱕ',		'Ⱖ' => 'ⱖ',		'Ⱗ' => 'ⱗ',		'Ⱘ' => 'ⱘ',
-		'Ⱙ' => 'ⱙ',		'Ⱚ' => 'ⱚ',		'Ⱛ' => 'ⱛ',		'Ⱜ' => 'ⱜ',
-		'Ⱝ' => 'ⱝ',		'Ⱞ' => 'ⱞ',		'Ⱡ' => 'ⱡ',		'Ɫ' => 'ɫ',
-		'Ᵽ' => 'ᵽ',		'Ɽ' => 'ɽ',		'Ⱨ' => 'ⱨ',		'Ⱪ' => 'ⱪ',
-		'Ⱬ' => 'ⱬ',		'Ⱶ' => 'ⱶ',		'Ⲁ' => 'ⲁ',		'Ⲃ' => 'ⲃ',
-		'Ⲅ' => 'ⲅ',		'Ⲇ' => 'ⲇ',		'Ⲉ' => 'ⲉ',		'Ⲋ' => 'ⲋ',
-		'Ⲍ' => 'ⲍ',		'Ⲏ' => 'ⲏ',		'Ⲑ' => 'ⲑ',		'Ⲓ' => 'ⲓ',
-		'Ⲕ' => 'ⲕ',		'Ⲗ' => 'ⲗ',		'Ⲙ' => 'ⲙ',		'Ⲛ' => 'ⲛ',
-		'Ⲝ' => 'ⲝ',		'Ⲟ' => 'ⲟ',		'Ⲡ' => 'ⲡ',		'Ⲣ' => 'ⲣ',
-		'Ⲥ' => 'ⲥ',		'Ⲧ' => 'ⲧ',		'Ⲩ' => 'ⲩ',		'Ⲫ' => 'ⲫ',
-		'Ⲭ' => 'ⲭ',		'Ⲯ' => 'ⲯ',		'Ⲱ' => 'ⲱ',		'Ⲳ' => 'ⲳ',
-		'Ⲵ' => 'ⲵ',		'Ⲷ' => 'ⲷ',		'Ⲹ' => 'ⲹ',		'Ⲻ' => 'ⲻ',
-		'Ⲽ' => 'ⲽ',		'Ⲿ' => 'ⲿ',		'Ⳁ' => 'ⳁ',		'Ⳃ' => 'ⳃ',
-		'Ⳅ' => 'ⳅ',		'Ⳇ' => 'ⳇ',		'Ⳉ' => 'ⳉ',		'Ⳋ' => 'ⳋ',
-		'Ⳍ' => 'ⳍ',		'Ⳏ' => 'ⳏ',		'Ⳑ' => 'ⳑ',		'Ⳓ' => 'ⳓ',
-		'Ⳕ' => 'ⳕ',		'Ⳗ' => 'ⳗ',		'Ⳙ' => 'ⳙ',		'Ⳛ' => 'ⳛ',
-		'Ⳝ' => 'ⳝ',		'Ⳟ' => 'ⳟ',		'Ⳡ' => 'ⳡ',		'Ⳣ' => 'ⳣ',
-		'ff' => 'ff',	'fi' => 'fi',	'fl' => 'fl',	'ffi' => 'ffi',
-		'ffl' => 'ffl',	'ſt' => 'st',	'st' => 'st',	'ﬓ' => 'մն',
-		'ﬔ' => 'մե',	'ﬕ' => 'մի',	'ﬖ' => 'վն',	'ﬗ' => 'մխ',
-		'A' => 'a',		'B' => 'b',		'C' => 'c',		'D' => 'd',
-		'E' => 'e',		'F' => 'f',		'G' => 'g',		'H' => 'h',
-		'I' => 'i',		'J' => 'j',		'K' => 'k',		'L' => 'l',
-		'M' => 'm',	'N' => 'n',		'O' => 'o',		'P' => 'p',
-		'Q' => 'q',		'R' => 'r',		'S' => 's',		'T' => 't',
-		'U' => 'u',		'V' => 'v',		'W' => 'w',	'X' => 'x',
-		'Y' => 'y',		'Z' => 'z',		'𐐀' => '𐐨',	'𐐁' => '𐐩',
-		'𐐂' => '𐐪',	'𐐃' => '𐐫',	'𐐄' => '𐐬',	'𐐅' => '𐐭',
-		'𐐆' => '𐐮',	'𐐇' => '𐐯',	'𐐈' => '𐐰',	'𐐉' => '𐐱',
-		'𐐊' => '𐐲',	'𐐋' => '𐐳',	'𐐌' => '𐐴',	'𐐍' => '𐐵',
-		'𐐎' => '𐐶',	'𐐏' => '𐐷',	'𐐐' => '𐐸',	'𐐑' => '𐐹',
-		'𐐒' => '𐐺',	'𐐓' => '𐐻',	'𐐔' => '𐐼',	'𐐕' => '𐐽',
-		'𐐖' => '𐐾',	'𐐗' => '𐐿',	'𐐘' => '𐑀',	'𐐙' => '𐑁',
-		'𐐚' => '𐑂',	'𐐛' => '𐑃',	'𐐜' => '𐑄',	'𐐝' => '𐑅',
-		'𐐞' => '𐑆',	'𐐟' => '𐑇',	'𐐠' => '𐑈',	'𐐡' => '𐑉',
-		'𐐢' => '𐑊',	'𐐣' => '𐑋',	'𐐤' => '𐑌',	'𐐥' => '𐑍',
-		'𐑎' => '𐐦',	'𐑏' => '𐐧',
-	);
-
-	return strtr($string, $case_folding);
-}
-
-// Convert the given UTF-8 string to uppercase.
-/**
- * Convert the given UTF-8 string to uppercase.
- * equivalent to mb_strtoupper($string, 'UTF-8')
- *
- * @param string $string
- * @return string
- */
-function utf8_strtoupper($string)
-{
-	static $case_folding = array(
-		'a' => 'A',		'b' => 'B',		'c' => 'C',		'd' => 'D',
-		'e' => 'E',		'f' => 'F',		'g' => 'G',		'h' => 'H',
-		'i' => 'I',		'j' => 'J',		'k' => 'K',		'l' => 'L',
-		'm' => 'M',		'n' => 'N',		'o' => 'O',		'p' => 'P',
-		'q' => 'Q',		'r' => 'R',		's' => 'S',		't' => 'T',
-		'u' => 'U',		'v' => 'V',		'w' => 'W',		'x' => 'X',
-		'y' => 'Y',		'z' => 'Z',		'μ' => 'µ',		'à' => 'À',
-		'á' => 'Á',		'â' => 'Â',		'ã' => 'Ã',		'ä' => 'Ä',
-		'å' => 'Å',		'æ' => 'Æ',		'ç' => 'Ç',		'è' => 'È',
-		'é' => 'É',		'ê' => 'Ê',		'ë' => 'Ë',		'ì' => 'Ì',
-		'í' => 'Í',		'î' => 'Î',		'ï' => 'Ï',		'ð' => 'Ð',
-		'ñ' => 'Ñ',		'ò' => 'Ò',		'ó' => 'Ó',		'ô' => 'Ô',
-		'õ' => 'Õ',		'ö' => 'Ö',		'ø' => 'Ø',		'ù' => 'Ù',
-		'ú' => 'Ú',		'û' => 'Û',		'ü' => 'Ü',		'ý' => 'Ý',
-		'þ' => 'Þ',		'ss' => 'ß',	'ā' => 'Ā',		'ă' => 'Ă',
-		'ą' => 'Ą',		'ć' => 'Ć',		'ĉ' => 'Ĉ',		'ċ' => 'Ċ',
-		'č' => 'Č',		'ď' => 'Ď',		'đ' => 'Đ',		'ē' => 'Ē',
-		'ĕ' => 'Ĕ',		'ė' => 'Ė',		'ę' => 'Ę',		'ě' => 'Ě',
-		'ĝ' => 'Ĝ',		'ğ' => 'Ğ',		'ġ' => 'Ġ',		'ģ' => 'Ģ',
-		'ĥ' => 'Ĥ',		'ħ' => 'Ħ',		'ĩ' => 'Ĩ',		'ī' => 'Ī',
-		'ĭ' => 'Ĭ',		'į' => 'Į',		'i̇' => 'İ',		'ij' => 'IJ',
-		'ĵ' => 'Ĵ',		'ķ' => 'Ķ',		'ĺ' => 'Ĺ',		'ļ' => 'Ļ',
-		'ľ' => 'Ľ',		'ŀ' => 'Ŀ',		'ł' => 'Ł',		'ń' => 'Ń',
-		'ņ' => 'Ņ',		'ň' => 'Ň',		'ʼn' => 'ʼn',	'ŋ' => 'Ŋ',
-		'ō' => 'Ō',		'ŏ' => 'Ŏ',		'ő' => 'Ő',		'œ' => 'Œ',
-		'ŕ' => 'Ŕ',		'ŗ' => 'Ŗ',		'ř' => 'Ř',		'ś' => 'Ś',
-		'ŝ' => 'Ŝ',		'ş' => 'Ş',		'š' => 'Š',		'ţ' => 'Ţ',
-		'ť' => 'Ť',		'ŧ' => 'Ŧ',		'ũ' => 'Ũ',		'ū' => 'Ū',
-		'ŭ' => 'Ŭ',		'ů' => 'Ů',		'ű' => 'Ű',		'ų' => 'Ų',
-		'ŵ' => 'Ŵ',		'ŷ' => 'Ŷ',		'ÿ' => 'Ÿ',		'ź' => 'Ź',
-		'ż' => 'Ż',		'ž' => 'Ž',		's' => 'ſ',		'ɓ' => 'Ɓ',
-		'ƃ' => 'Ƃ',		'ƅ' => 'Ƅ',		'ɔ' => 'Ɔ',		'ƈ' => 'Ƈ',
-		'ɖ' => 'Ɖ',		'ɗ' => 'Ɗ',		'ƌ' => 'Ƌ',		'ǝ' => 'Ǝ',
-		'ə' => 'Ə',		'ɛ' => 'Ɛ',		'ƒ' => 'Ƒ',		'ɠ' => 'Ɠ',
-		'ɣ' => 'Ɣ',		'ɩ' => 'Ɩ',		'ɨ' => 'Ɨ',		'ƙ' => 'Ƙ',
-		'ɯ' => 'Ɯ',		'ɲ' => 'Ɲ',		'ɵ' => 'Ɵ',		'ơ' => 'Ơ',
-		'ƣ' => 'Ƣ',		'ƥ' => 'Ƥ',		'ʀ' => 'Ʀ',		'ƨ' => 'Ƨ',
-		'ʃ' => 'Ʃ',		'ƭ' => 'Ƭ',		'ʈ' => 'Ʈ',		'ư' => 'Ư',
-		'ʊ' => 'Ʊ',		'ʋ' => 'Ʋ',		'ƴ' => 'Ƴ',		'ƶ' => 'Ƶ',
-		'ʒ' => 'Ʒ',		'ƹ' => 'Ƹ',		'ƽ' => 'Ƽ',		'dž' => 'DŽ',
-		'dž' => 'Dž',		'lj' => 'LJ',		'lj' => 'Lj',		'nj' => 'NJ',
-		'nj' => 'Nj',		'ǎ' => 'Ǎ',		'ǐ' => 'Ǐ',		'ǒ' => 'Ǒ',
-		'ǔ' => 'Ǔ',		'ǖ' => 'Ǖ',		'ǘ' => 'Ǘ',		'ǚ' => 'Ǚ',
-		'ǜ' => 'Ǜ',		'ǟ' => 'Ǟ',		'ǡ' => 'Ǡ',		'ǣ' => 'Ǣ',
-		'ǥ' => 'Ǥ',		'ǧ' => 'Ǧ',		'ǩ' => 'Ǩ',		'ǫ' => 'Ǫ',
-		'ǭ' => 'Ǭ',		'ǯ' => 'Ǯ',		'ǰ' => 'ǰ',		'dz' => 'DZ',
-		'dz' => 'Dz',		'ǵ' => 'Ǵ',		'ƕ' => 'Ƕ',		'ƿ' => 'Ƿ',
-		'ǹ' => 'Ǹ',		'ǻ' => 'Ǻ',		'ǽ' => 'Ǽ',		'ǿ' => 'Ǿ',
-		'ȁ' => 'Ȁ',		'ȃ' => 'Ȃ',		'ȅ' => 'Ȅ',		'ȇ' => 'Ȇ',
-		'ȉ' => 'Ȉ',		'ȋ' => 'Ȋ',		'ȍ' => 'Ȍ',		'ȏ' => 'Ȏ',
-		'ȑ' => 'Ȑ',		'ȓ' => 'Ȓ',		'ȕ' => 'Ȕ',		'ȗ' => 'Ȗ',
-		'ș' => 'Ș',		'ț' => 'Ț',		'ȝ' => 'Ȝ',		'ȟ' => 'Ȟ',
-		'ƞ' => 'Ƞ',		'ȣ' => 'Ȣ',		'ȥ' => 'Ȥ',		'ȧ' => 'Ȧ',
-		'ȩ' => 'Ȩ',		'ȫ' => 'Ȫ',		'ȭ' => 'Ȭ',		'ȯ' => 'Ȯ',
-		'ȱ' => 'Ȱ',		'ȳ' => 'Ȳ',		'ⱥ' => 'Ⱥ',		'ȼ' => 'Ȼ',
-		'ƚ' => 'Ƚ',		'ⱦ' => 'Ⱦ',		'ɂ' => 'Ɂ',		'ƀ' => 'Ƀ',
-		'ʉ' => 'Ʉ',		'ʌ' => 'Ʌ',		'ɇ' => 'Ɇ',		'ɉ' => 'Ɉ',
-		'ɋ' => 'Ɋ',		'ɍ' => 'Ɍ',		'ɏ' => 'Ɏ',		'ι' => 'ͅ',
-		'ά' => 'Ά',		'έ' => 'Έ',		'ή' => 'Ή',		'ί' => 'Ί',
-		'ό' => 'Ό',		'ύ' => 'Ύ',		'ώ' => 'Ώ',		'ΐ' => 'ΐ',
-		'α' => 'Α',		'β' => 'Β',		'γ' => 'Γ',		'δ' => 'Δ',
-		'ε' => 'Ε',		'ζ' => 'Ζ',		'η' => 'Η',		'θ' => 'Θ',
-		'ι' => 'Ι',		'κ' => 'Κ',		'λ' => 'Λ',		'μ' => 'Μ',
-		'ν' => 'Ν',		'ξ' => 'Ξ',		'ο' => 'Ο',		'π' => 'Π',
-		'ρ' => 'Ρ',		'σ' => 'Σ',		'τ' => 'Τ',		'υ' => 'Υ',
-		'φ' => 'Φ',		'χ' => 'Χ',		'ψ' => 'Ψ',		'ω' => 'Ω',
-		'ϊ' => 'Ϊ',		'ϋ' => 'Ϋ',		'ΰ' => 'ΰ',	'σ' => 'ς',
-		'β' => 'ϐ',		'θ' => 'ϑ',		'φ' => 'ϕ',		'π' => 'ϖ',
-		'ϙ' => 'Ϙ',		'ϛ' => 'Ϛ',		'ϝ' => 'Ϝ',		'ϟ' => 'Ϟ',
-		'ϡ' => 'Ϡ',		'ϣ' => 'Ϣ',		'ϥ' => 'Ϥ',		'ϧ' => 'Ϧ',
-		'ϩ' => 'Ϩ',		'ϫ' => 'Ϫ',		'ϭ' => 'Ϭ',		'ϯ' => 'Ϯ',
-		'κ' => 'ϰ',		'ρ' => 'ϱ',		'θ' => 'ϴ',		'ε' => 'ϵ',
-		'ϸ' => 'Ϸ',		'ϲ' => 'Ϲ',		'ϻ' => 'Ϻ',		'ͻ' => 'Ͻ',
-		'ͼ' => 'Ͼ',		'ͽ' => 'Ͽ',		'ѐ' => 'Ѐ',		'ё' => 'Ё',
-		'ђ' => 'Ђ',		'ѓ' => 'Ѓ',		'є' => 'Є',		'ѕ' => 'Ѕ',
-		'і' => 'І',		'ї' => 'Ї',		'ј' => 'Ј',		'љ' => 'Љ',
-		'њ' => 'Њ',		'ћ' => 'Ћ',		'ќ' => 'Ќ',		'ѝ' => 'Ѝ',
-		'ў' => 'Ў',		'џ' => 'Џ',		'а' => 'А',		'б' => 'Б',
-		'в' => 'В',		'г' => 'Г',		'д' => 'Д',		'е' => 'Е',
-		'ж' => 'Ж',		'з' => 'З',		'и' => 'И',		'й' => 'Й',
-		'к' => 'К',		'л' => 'Л',		'м' => 'М',		'н' => 'Н',
-		'о' => 'О',		'п' => 'П',		'р' => 'Р',		'с' => 'С',
-		'т' => 'Т',		'у' => 'У',		'ф' => 'Ф',		'х' => 'Х',
-		'ц' => 'Ц',		'ч' => 'Ч',		'ш' => 'Ш',		'щ' => 'Щ',
-		'ъ' => 'Ъ',		'ы' => 'Ы',		'ь' => 'Ь',		'э' => 'Э',
-		'ю' => 'Ю',		'я' => 'Я',		'ѡ' => 'Ѡ',		'ѣ' => 'Ѣ',
-		'ѥ' => 'Ѥ',		'ѧ' => 'Ѧ',		'ѩ' => 'Ѩ',		'ѫ' => 'Ѫ',
-		'ѭ' => 'Ѭ',		'ѯ' => 'Ѯ',		'ѱ' => 'Ѱ',		'ѳ' => 'Ѳ',
-		'ѵ' => 'Ѵ',		'ѷ' => 'Ѷ',		'ѹ' => 'Ѹ',		'ѻ' => 'Ѻ',
-		'ѽ' => 'Ѽ',		'ѿ' => 'Ѿ',		'ҁ' => 'Ҁ',		'ҋ' => 'Ҋ',
-		'ҍ' => 'Ҍ',		'ҏ' => 'Ҏ',		'ґ' => 'Ґ',		'ғ' => 'Ғ',
-		'ҕ' => 'Ҕ',		'җ' => 'Җ',		'ҙ' => 'Ҙ',		'қ' => 'Қ',
-		'ҝ' => 'Ҝ',		'ҟ' => 'Ҟ',		'ҡ' => 'Ҡ',		'ң' => 'Ң',
-		'ҥ' => 'Ҥ',		'ҧ' => 'Ҧ',		'ҩ' => 'Ҩ',		'ҫ' => 'Ҫ',
-		'ҭ' => 'Ҭ',		'ү' => 'Ү',		'ұ' => 'Ұ',		'ҳ' => 'Ҳ',
-		'ҵ' => 'Ҵ',		'ҷ' => 'Ҷ',		'ҹ' => 'Ҹ',		'һ' => 'Һ',
-		'ҽ' => 'Ҽ',		'ҿ' => 'Ҿ',		'ӏ' => 'Ӏ',		'ӂ' => 'Ӂ',
-		'ӄ' => 'Ӄ',		'ӆ' => 'Ӆ',		'ӈ' => 'Ӈ',		'ӊ' => 'Ӊ',
-		'ӌ' => 'Ӌ',		'ӎ' => 'Ӎ',		'ӑ' => 'Ӑ',		'ӓ' => 'Ӓ',
-		'ӕ' => 'Ӕ',		'ӗ' => 'Ӗ',		'ә' => 'Ә',		'ӛ' => 'Ӛ',
-		'ӝ' => 'Ӝ',		'ӟ' => 'Ӟ',		'ӡ' => 'Ӡ',		'ӣ' => 'Ӣ',
-		'ӥ' => 'Ӥ',		'ӧ' => 'Ӧ',		'ө' => 'Ө',		'ӫ' => 'Ӫ',
-		'ӭ' => 'Ӭ',		'ӯ' => 'Ӯ',		'ӱ' => 'Ӱ',		'ӳ' => 'Ӳ',
-		'ӵ' => 'Ӵ',		'ӷ' => 'Ӷ',		'ӹ' => 'Ӹ',		'ӻ' => 'Ӻ',
-		'ӽ' => 'Ӽ',		'ӿ' => 'Ӿ',		'ԁ' => 'Ԁ',		'ԃ' => 'Ԃ',
-		'ԅ' => 'Ԅ',		'ԇ' => 'Ԇ',		'ԉ' => 'Ԉ',		'ԋ' => 'Ԋ',
-		'ԍ' => 'Ԍ',		'ԏ' => 'Ԏ',		'ԑ' => 'Ԑ',		'ԓ' => 'Ԓ',
-		'ա' => 'Ա',		'բ' => 'Բ',		'գ' => 'Գ',		'դ' => 'Դ',
-		'ե' => 'Ե',		'զ' => 'Զ',		'է' => 'Է',		'ը' => 'Ը',
-		'թ' => 'Թ',		'ժ' => 'Ժ',		'ի' => 'Ի',		'լ' => 'Լ',
-		'խ' => 'Խ',		'ծ' => 'Ծ',		'կ' => 'Կ',		'հ' => 'Հ',
-		'ձ' => 'Ձ',		'ղ' => 'Ղ',		'ճ' => 'Ճ',		'մ' => 'Մ',
-		'յ' => 'Յ',		'ն' => 'Ն',		'շ' => 'Շ',		'ո' => 'Ո',
-		'չ' => 'Չ',		'պ' => 'Պ',		'ջ' => 'Ջ',		'ռ' => 'Ռ',
-		'ս' => 'Ս',		'վ' => 'Վ',		'տ' => 'Տ',		'ր' => 'Ր',
-		'ց' => 'Ց',		'ւ' => 'Ւ',		'փ' => 'Փ',		'ք' => 'Ք',
-		'օ' => 'Օ',		'ֆ' => 'Ֆ',		'եւ' => 'և',		'ⴀ' => 'Ⴀ',
-		'ⴁ' => 'Ⴁ',		'ⴂ' => 'Ⴂ',		'ⴃ' => 'Ⴃ',		'ⴄ' => 'Ⴄ',
-		'ⴅ' => 'Ⴅ',		'ⴆ' => 'Ⴆ',		'ⴇ' => 'Ⴇ',		'ⴈ' => 'Ⴈ',
-		'ⴉ' => 'Ⴉ',		'ⴊ' => 'Ⴊ',		'ⴋ' => 'Ⴋ',		'ⴌ' => 'Ⴌ',
-		'ⴍ' => 'Ⴍ',		'ⴎ' => 'Ⴎ',		'ⴏ' => 'Ⴏ',		'ⴐ' => 'Ⴐ',
-		'ⴑ' => 'Ⴑ',		'ⴒ' => 'Ⴒ',		'ⴓ' => 'Ⴓ',		'ⴔ' => 'Ⴔ',
-		'ⴕ' => 'Ⴕ',		'ⴖ' => 'Ⴖ',		'ⴗ' => 'Ⴗ',		'ⴘ' => 'Ⴘ',
-		'ⴙ' => 'Ⴙ',		'ⴚ' => 'Ⴚ',		'ⴛ' => 'Ⴛ',		'ⴜ' => 'Ⴜ',
-		'ⴝ' => 'Ⴝ',		'ⴞ' => 'Ⴞ',		'ⴟ' => 'Ⴟ',		'ⴠ' => 'Ⴠ',
-		'ⴡ' => 'Ⴡ',		'ⴢ' => 'Ⴢ',		'ⴣ' => 'Ⴣ',		'ⴤ' => 'Ⴤ',
-		'ⴥ' => 'Ⴥ',		'ḁ' => 'Ḁ',		'ḃ' => 'Ḃ',		'ḅ' => 'Ḅ',
-		'ḇ' => 'Ḇ',		'ḉ' => 'Ḉ',		'ḋ' => 'Ḋ',		'ḍ' => 'Ḍ',
-		'ḏ' => 'Ḏ',		'ḑ' => 'Ḑ',		'ḓ' => 'Ḓ',		'ḕ' => 'Ḕ',
-		'ḗ' => 'Ḗ',		'ḙ' => 'Ḙ',		'ḛ' => 'Ḛ',		'ḝ' => 'Ḝ',
-		'ḟ' => 'Ḟ',		'ḡ' => 'Ḡ',		'ḣ' => 'Ḣ',		'ḥ' => 'Ḥ',
-		'ḧ' => 'Ḧ',		'ḩ' => 'Ḩ',		'ḫ' => 'Ḫ',		'ḭ' => 'Ḭ',
-		'ḯ' => 'Ḯ',		'ḱ' => 'Ḱ',		'ḳ' => 'Ḳ',		'ḵ' => 'Ḵ',
-		'ḷ' => 'Ḷ',		'ḹ' => 'Ḹ',		'ḻ' => 'Ḻ',		'ḽ' => 'Ḽ',
-		'ḿ' => 'Ḿ',		'ṁ' => 'Ṁ',		'ṃ' => 'Ṃ',		'ṅ' => 'Ṅ',
-		'ṇ' => 'Ṇ',		'ṉ' => 'Ṉ',		'ṋ' => 'Ṋ',		'ṍ' => 'Ṍ',
-		'ṏ' => 'Ṏ',		'ṑ' => 'Ṑ',		'ṓ' => 'Ṓ',		'ṕ' => 'Ṕ',
-		'ṗ' => 'Ṗ',		'ṙ' => 'Ṙ',		'ṛ' => 'Ṛ',		'ṝ' => 'Ṝ',
-		'ṟ' => 'Ṟ',		'ṡ' => 'Ṡ',		'ṣ' => 'Ṣ',		'ṥ' => 'Ṥ',
-		'ṧ' => 'Ṧ',		'ṩ' => 'Ṩ',		'ṫ' => 'Ṫ',		'ṭ' => 'Ṭ',
-		'ṯ' => 'Ṯ',		'ṱ' => 'Ṱ',		'ṳ' => 'Ṳ',		'ṵ' => 'Ṵ',
-		'ṷ' => 'Ṷ',		'ṹ' => 'Ṹ',		'ṻ' => 'Ṻ',		'ṽ' => 'Ṽ',
-		'ṿ' => 'Ṿ',		'ẁ' => 'Ẁ',		'ẃ' => 'Ẃ',		'ẅ' => 'Ẅ',
-		'ẇ' => 'Ẇ',		'ẉ' => 'Ẉ',		'ẋ' => 'Ẋ',		'ẍ' => 'Ẍ',
-		'ẏ' => 'Ẏ',		'ẑ' => 'Ẑ',		'ẓ' => 'Ẓ',		'ẕ' => 'Ẕ',
-		'ẖ' => 'ẖ',		'ẗ' => 'ẗ',		'ẘ' => 'ẘ',		'ẙ' => 'ẙ',
-		'aʾ' => 'ẚ',	'ṡ' => 'ẛ',		'ạ' => 'Ạ',		'ả' => 'Ả',
-		'ấ' => 'Ấ',		'ầ' => 'Ầ',		'ẩ' => 'Ẩ',		'ẫ' => 'Ẫ',
-		'ậ' => 'Ậ',		'ắ' => 'Ắ',		'ằ' => 'Ằ',		'ẳ' => 'Ẳ',
-		'ẵ' => 'Ẵ',		'ặ' => 'Ặ',		'ẹ' => 'Ẹ',		'ẻ' => 'Ẻ',
-		'ẽ' => 'Ẽ',		'ế' => 'Ế',		'ề' => 'Ề',		'ể' => 'Ể',
-		'ễ' => 'Ễ',		'ệ' => 'Ệ',		'ỉ' => 'Ỉ',		'ị' => 'Ị',
-		'ọ' => 'Ọ',		'ỏ' => 'Ỏ',		'ố' => 'Ố',		'ồ' => 'Ồ',
-		'ổ' => 'Ổ',		'ỗ' => 'Ỗ',		'ộ' => 'Ộ',		'ớ' => 'Ớ',
-		'ờ' => 'Ờ',		'ở' => 'Ở',		'ỡ' => 'Ỡ',		'ợ' => 'Ợ',
-		'ụ' => 'Ụ',		'ủ' => 'Ủ',		'ứ' => 'Ứ',		'ừ' => 'Ừ',
-		'ử' => 'Ử',		'ữ' => 'Ữ',		'ự' => 'Ự',		'ỳ' => 'Ỳ',
-		'ỵ' => 'Ỵ',		'ỷ' => 'Ỷ',		'ỹ' => 'Ỹ',		'ἀ' => 'Ἀ',
-		'ἁ' => 'Ἁ',		'ἂ' => 'Ἂ',		'ἃ' => 'Ἃ',		'ἄ' => 'Ἄ',
-		'ἅ' => 'Ἅ',		'ἆ' => 'Ἆ',		'ἇ' => 'Ἇ',		'ἐ' => 'Ἐ',
-		'ἑ' => 'Ἑ',		'ἒ' => 'Ἒ',		'ἓ' => 'Ἓ',		'ἔ' => 'Ἔ',
-		'ἕ' => 'Ἕ',		'ἠ' => 'Ἠ',		'ἡ' => 'Ἡ',		'ἢ' => 'Ἢ',
-		'ἣ' => 'Ἣ',		'ἤ' => 'Ἤ',		'ἥ' => 'Ἥ',		'ἦ' => 'Ἦ',
-		'ἧ' => 'Ἧ',		'ἰ' => 'Ἰ',		'ἱ' => 'Ἱ',		'ἲ' => 'Ἲ',
-		'ἳ' => 'Ἳ',		'ἴ' => 'Ἴ',		'ἵ' => 'Ἵ',		'ἶ' => 'Ἶ',
-		'ἷ' => 'Ἷ',		'ὀ' => 'Ὀ',		'ὁ' => 'Ὁ',		'ὂ' => 'Ὂ',
-		'ὃ' => 'Ὃ',		'ὄ' => 'Ὄ',		'ὅ' => 'Ὅ',		'ὐ' => 'ὐ',
-		'ὒ' => 'ὒ',	'ὔ' => 'ὔ',	'ὖ' => 'ὖ',		'ὑ' => 'Ὑ',
-		'ὓ' => 'Ὓ',		'ὕ' => 'Ὕ',		'ὗ' => 'Ὗ',		'ὠ' => 'Ὠ',
-		'ὡ' => 'Ὡ',		'ὢ' => 'Ὢ',		'ὣ' => 'Ὣ',		'ὤ' => 'Ὤ',
-		'ὥ' => 'Ὥ',		'ὦ' => 'Ὦ',		'ὧ' => 'Ὧ',		'ἀι' => 'ᾀ',
-		'ἁι' => 'ᾁ',	'ἂι' => 'ᾂ',	'ἃι' => 'ᾃ',	'ἄι' => 'ᾄ',
-		'ἅι' => 'ᾅ',	'ἆι' => 'ᾆ',	'ἇι' => 'ᾇ',	'ᾀ' => 'ᾈ',
-		'ᾁ' => 'ᾉ',		'ᾂ' => 'ᾊ',		'ᾃ' => 'ᾋ',		'ᾄ' => 'ᾌ',
-		'ᾅ' => 'ᾍ',		'ᾆ' => 'ᾎ',		'ᾇ' => 'ᾏ',		'ἠι' => 'ᾐ',
-		'ἡι' => 'ᾑ',	'ἢι' => 'ᾒ',	'ἣι' => 'ᾓ',	'ἤι' => 'ᾔ',
-		'ἥι' => 'ᾕ',	'ἦι' => 'ᾖ',	'ἧι' => 'ᾗ',	'ᾐ' => 'ᾘ',
-		'ᾑ' => 'ᾙ',		'ᾒ' => 'ᾚ',		'ᾓ' => 'ᾛ',		'ᾔ' => 'ᾜ',
-		'ᾕ' => 'ᾝ',		'ᾖ' => 'ᾞ',		'ᾗ' => 'ᾟ',		'ὠι' => 'ᾠ',
-		'ὡι' => 'ᾡ',	'ὢι' => 'ᾢ',	'ὣι' => 'ᾣ',	'ὤι' => 'ᾤ',
-		'ὥι' => 'ᾥ',	'ὦι' => 'ᾦ',	'ὧι' => 'ᾧ',	'ᾠ' => 'ᾨ',
-		'ᾡ' => 'ᾩ',		'ᾢ' => 'ᾪ',		'ᾣ' => 'ᾫ',		'ᾤ' => 'ᾬ',
-		'ᾥ' => 'ᾭ',		'ᾦ' => 'ᾮ',		'ᾧ' => 'ᾯ',		'ὰι' => 'ᾲ',
-		'αι' => 'ᾳ',	'άι' => 'ᾴ',	'ᾶ' => 'ᾶ',		'ᾶι' => 'ᾷ',
-		'ᾰ' => 'Ᾰ',		'ᾱ' => 'Ᾱ',		'ὰ' => 'Ὰ',		'ά' => 'Ά',
-		'ᾳ' => 'ᾼ',		'ι' => 'ι',		'ὴι' => 'ῂ',	'ηι' => 'ῃ',
-		'ήι' => 'ῄ',	'ῆ' => 'ῆ',		'ῆι' => 'ῇ',	'ὲ' => 'Ὲ',
-		'έ' => 'Έ',		'ὴ' => 'Ὴ',		'ή' => 'Ή',		'ῃ' => 'ῌ',
-		'ῒ' => 'ῒ',	'ΐ' => 'ΐ',	'ῖ' => 'ῖ',		'ῗ' => 'ῗ',
-		'ῐ' => 'Ῐ',		'ῑ' => 'Ῑ',		'ὶ' => 'Ὶ',		'ί' => 'Ί',
-		'ῢ' => 'ῢ',	'ΰ' => 'ΰ',	'ῤ' => 'ῤ',		'ῦ' => 'ῦ',
-		'ῧ' => 'ῧ',		'ῠ' => 'Ῠ',		'ῡ' => 'Ῡ',		'ὺ' => 'Ὺ',
-		'ύ' => 'Ύ',		'ῥ' => 'Ῥ',		'ὼι' => 'ῲ',	'ωι' => 'ῳ',
-		'ώι' => 'ῴ',	'ῶ' => 'ῶ',		'ῶι' => 'ῷ',	'ὸ' => 'Ὸ',
-		'ό' => 'Ό',		'ὼ' => 'Ὼ',		'ώ' => 'Ώ',		'ῳ' => 'ῼ',
-		'ω' => 'Ω',		'k' => 'K',		'å' => 'Å',		'ⅎ' => 'Ⅎ',
-		'ⅰ' => 'Ⅰ',		'ⅱ' => 'Ⅱ',		'ⅲ' => 'Ⅲ',		'ⅳ' => 'Ⅳ',
-		'ⅴ' => 'Ⅴ',		'ⅵ' => 'Ⅵ',		'ⅶ' => 'Ⅶ',		'ⅷ' => 'Ⅷ',
-		'ⅸ' => 'Ⅸ',		'ⅹ' => 'Ⅹ',		'ⅺ' => 'Ⅺ',		'ⅻ' => 'Ⅻ',
-		'ⅼ' => 'Ⅼ',		'ⅽ' => 'Ⅽ',		'ⅾ' => 'Ⅾ',		'ⅿ' => 'Ⅿ',
-		'ↄ' => 'Ↄ',		'ⓐ' => 'Ⓐ',		'ⓑ' => 'Ⓑ',		'ⓒ' => 'Ⓒ',
-		'ⓓ' => 'Ⓓ',		'ⓔ' => 'Ⓔ',		'ⓕ' => 'Ⓕ',		'ⓖ' => 'Ⓖ',
-		'ⓗ' => 'Ⓗ',		'ⓘ' => 'Ⓘ',		'ⓙ' => 'Ⓙ',		'ⓚ' => 'Ⓚ',
-		'ⓛ' => 'Ⓛ',		'ⓜ' => 'Ⓜ',		'ⓝ' => 'Ⓝ',		'ⓞ' => 'Ⓞ',
-		'ⓟ' => 'Ⓟ',		'ⓠ' => 'Ⓠ',		'ⓡ' => 'Ⓡ',		'ⓢ' => 'Ⓢ',
-		'ⓣ' => 'Ⓣ',		'ⓤ' => 'Ⓤ',		'ⓥ' => 'Ⓥ',		'ⓦ' => 'Ⓦ',
-		'ⓧ' => 'Ⓧ',		'ⓨ' => 'Ⓨ',		'ⓩ' => 'Ⓩ',		'ⰰ' => 'Ⰰ',
-		'ⰱ' => 'Ⰱ',		'ⰲ' => 'Ⰲ',		'ⰳ' => 'Ⰳ',		'ⰴ' => 'Ⰴ',
-		'ⰵ' => 'Ⰵ',		'ⰶ' => 'Ⰶ',		'ⰷ' => 'Ⰷ',		'ⰸ' => 'Ⰸ',
-		'ⰹ' => 'Ⰹ',		'ⰺ' => 'Ⰺ',		'ⰻ' => 'Ⰻ',		'ⰼ' => 'Ⰼ',
-		'ⰽ' => 'Ⰽ',		'ⰾ' => 'Ⰾ',		'ⰿ' => 'Ⰿ',		'ⱀ' => 'Ⱀ',
-		'ⱁ' => 'Ⱁ',		'ⱂ' => 'Ⱂ',		'ⱃ' => 'Ⱃ',		'ⱄ' => 'Ⱄ',
-		'ⱅ' => 'Ⱅ',		'ⱆ' => 'Ⱆ',		'ⱇ' => 'Ⱇ',		'ⱈ' => 'Ⱈ',
-		'ⱉ' => 'Ⱉ',		'ⱊ' => 'Ⱊ',		'ⱋ' => 'Ⱋ',		'ⱌ' => 'Ⱌ',
-		'ⱍ' => 'Ⱍ',		'ⱎ' => 'Ⱎ',		'ⱏ' => 'Ⱏ',		'ⱐ' => 'Ⱐ',
-		'ⱑ' => 'Ⱑ',		'ⱒ' => 'Ⱒ',		'ⱓ' => 'Ⱓ',		'ⱔ' => 'Ⱔ',
-		'ⱕ' => 'Ⱕ',		'ⱖ' => 'Ⱖ',		'ⱗ' => 'Ⱗ',		'ⱘ' => 'Ⱘ',
-		'ⱙ' => 'Ⱙ',		'ⱚ' => 'Ⱚ',		'ⱛ' => 'Ⱛ',		'ⱜ' => 'Ⱜ',
-		'ⱝ' => 'Ⱝ',		'ⱞ' => 'Ⱞ',		'ⱡ' => 'Ⱡ',		'ɫ' => 'Ɫ',
-		'ᵽ' => 'Ᵽ',		'ɽ' => 'Ɽ',		'ⱨ' => 'Ⱨ',		'ⱪ' => 'Ⱪ',
-		'ⱬ' => 'Ⱬ',		'ⱶ' => 'Ⱶ',		'ⲁ' => 'Ⲁ',		'ⲃ' => 'Ⲃ',
-		'ⲅ' => 'Ⲅ',		'ⲇ' => 'Ⲇ',		'ⲉ' => 'Ⲉ',		'ⲋ' => 'Ⲋ',
-		'ⲍ' => 'Ⲍ',		'ⲏ' => 'Ⲏ',		'ⲑ' => 'Ⲑ',		'ⲓ' => 'Ⲓ',
-		'ⲕ' => 'Ⲕ',		'ⲗ' => 'Ⲗ',		'ⲙ' => 'Ⲙ',		'ⲛ' => 'Ⲛ',
-		'ⲝ' => 'Ⲝ',		'ⲟ' => 'Ⲟ',		'ⲡ' => 'Ⲡ',		'ⲣ' => 'Ⲣ',
-		'ⲥ' => 'Ⲥ',		'ⲧ' => 'Ⲧ',		'ⲩ' => 'Ⲩ',		'ⲫ' => 'Ⲫ',
-		'ⲭ' => 'Ⲭ',		'ⲯ' => 'Ⲯ',		'ⲱ' => 'Ⲱ',		'ⲳ' => 'Ⲳ',
-		'ⲵ' => 'Ⲵ',		'ⲷ' => 'Ⲷ',		'ⲹ' => 'Ⲹ',		'ⲻ' => 'Ⲻ',
-		'ⲽ' => 'Ⲽ',		'ⲿ' => 'Ⲿ',		'ⳁ' => 'Ⳁ',		'ⳃ' => 'Ⳃ',
-		'ⳅ' => 'Ⳅ',		'ⳇ' => 'Ⳇ',		'ⳉ' => 'Ⳉ',		'ⳋ' => 'Ⳋ',
-		'ⳍ' => 'Ⳍ',		'ⳏ' => 'Ⳏ',		'ⳑ' => 'Ⳑ',		'ⳓ' => 'Ⳓ',
-		'ⳕ' => 'Ⳕ',		'ⳗ' => 'Ⳗ',		'ⳙ' => 'Ⳙ',		'ⳛ' => 'Ⳛ',
-		'ⳝ' => 'Ⳝ',		'ⳟ' => 'Ⳟ',		'ⳡ' => 'Ⳡ',		'ⳣ' => 'Ⳣ',
-		'ff' => 'ff',	'fi' => 'fi',	'fl' => 'fl',	'ffi' => 'ffi',
-		'ffl' => 'ffl',	'st' => 'ſt',	'st' => 'st',	'մն' => 'ﬓ',
-		'մե' => 'ﬔ',	'մի' => 'ﬕ',	'վն' => 'ﬖ',	'մխ' => 'ﬗ',
-		'a' => 'A',		'b' => 'B',		'c' => 'C',		'd' => 'D',
-		'e' => 'E',		'f' => 'F',		'g' => 'G',		'h' => 'H',
-		'i' => 'I',		'j' => 'J',		'k' => 'K',		'l' => 'L',
-		'm' => 'M',	'n' => 'N',		'o' => 'O',		'p' => 'P',
-		'q' => 'Q',		'r' => 'R',		's' => 'S',		't' => 'T',
-		'u' => 'U',		'v' => 'V',		'w' => 'W',	'x' => 'X',
-		'y' => 'Y',		'z' => 'Z',		'𐐨' => '𐐀',	'𐐩' => '𐐁',
-		'𐐪' => '𐐂',	'𐐫' => '𐐃',	'𐐬' => '𐐄',	'𐐭' => '𐐅',
-		'𐐮' => '𐐆',	'𐐯' => '𐐇',	'𐐰' => '𐐈',	'𐐱' => '𐐉',
-		'𐐲' => '𐐊',	'𐐳' => '𐐋',	'𐐴' => '𐐌',	'𐐵' => '𐐍',
-		'𐐶' => '𐐎',	'𐐷' => '𐐏',	'𐐸' => '𐐐',	'𐐹' => '𐐑',
-		'𐐺' => '𐐒',	'𐐻' => '𐐓',	'𐐼' => '𐐔',	'𐐽' => '𐐕',
-		'𐐾' => '𐐖',	'𐐿' => '𐐗',	'𐑀' => '𐐘',	'𐑁' => '𐐙',
-		'𐑂' => '𐐚',	'𐑃' => '𐐛',	'𐑄' => '𐐜',	'𐑅' => '𐐝',
-		'𐑆' => '𐐞',	'𐑇' => '𐐟',	'𐑈' => '𐐠',	'𐑉' => '𐐡',
-		'𐑊' => '𐐢',	'𐑋' => '𐐣',	'𐑌' => '𐐤',	'𐑍' => '𐐥',
-		'𐐦' => '𐑎',	'𐐧' => '𐑏',
-	);
-
-	return strtr($string, $case_folding);
-}
-
-/**
- * Fixes corrupted serialized strings after a character set conversion.
- */
-function fix_serialized_columns()
-{
-	global $smcFunc;
-
-	$request = $smcFunc['db_query']('', '
-		SELECT id_action, extra
-		FROM {db_prefix}log_actions
-		WHERE action IN ({string:remove}, {string:delete})',
-		array(
-			'remove' => 'remove',
-			'delete' => 'delete',
-		)
-	);
-	while ($row = $smcFunc['db_fetch_assoc']($request))
-	{
-		if (@unserialize($row['extra']) === false && preg_match('~^(a:3:{s:5:"topic";i:\d+;s:7:"subject";s:)(\d+):"(.+)"(;s:6:"member";s:5:"\d+";})$~', $row['extra'], $matches) === 1)
-			$smcFunc['db_query']('', '
-				UPDATE {db_prefix}log_actions
-				SET extra = {string:extra}
-				WHERE id_action = {int:current_action}',
-				array(
-					'current_action' => $row['id_action'],
-					'extra' => $matches[1] . strlen($matches[3]) . ':"' . $matches[3] . '"' . $matches[4],
-				)
-			);
-	}
-	$smcFunc['db_free_result']($request);
-
-	// Refresh some cached data.
-	updateSettings(array(
-		'memberlist_updated' => time(),
-	));
-
-}
-
+<?php
+
+/**
+ * Simple Machines Forum (SMF)
+ *
+ * @package SMF
+ * @author Simple Machines http://www.simplemachines.org
+ * @copyright 2013 Simple Machines and individual contributors
+ * @license http://www.simplemachines.org/about/smf/license.php BSD
+ *
+ * @version 2.1 Alpha 1
+ */
+
+if (!defined('SMF'))
+	die('No direct access...');
+
+/**
+ * Converts the given UTF-8 string into lowercase.
+ * equivalent to mb_strtolower($string, 'UTF-8')
+ *
+ * @param string $string
+ * @return string
+ */
+function utf8_strtolower($string)
+{
+	static $case_folding = array(
+		'A' => 'a',		'B' => 'b',		'C' => 'c',		'D' => 'd',
+		'E' => 'e',		'F' => 'f',		'G' => 'g',		'H' => 'h',
+		'I' => 'i',		'J' => 'j',		'K' => 'k',		'L' => 'l',
+		'M' => 'm',		'N' => 'n',		'O' => 'o',		'P' => 'p',
+		'Q' => 'q',		'R' => 'r',		'S' => 's',		'T' => 't',
+		'U' => 'u',		'V' => 'v',		'W' => 'w',		'X' => 'x',
+		'Y' => 'y',		'Z' => 'z',		'µ' => 'μ',		'À' => 'à',
+		'Á' => 'á',		'Â' => 'â',		'Ã' => 'ã',		'Ä' => 'ä',
+		'Å' => 'å',		'Æ' => 'æ',		'Ç' => 'ç',		'È' => 'è',
+		'É' => 'é',		'Ê' => 'ê',		'Ë' => 'ë',		'Ì' => 'ì',
+		'Í' => 'í',		'Î' => 'î',		'Ï' => 'ï',		'Ð' => 'ð',
+		'Ñ' => 'ñ',		'Ò' => 'ò',		'Ó' => 'ó',		'Ô' => 'ô',
+		'Õ' => 'õ',		'Ö' => 'ö',		'Ø' => 'ø',		'Ù' => 'ù',
+		'Ú' => 'ú',		'Û' => 'û',		'Ü' => 'ü',		'Ý' => 'ý',
+		'Þ' => 'þ',		'ß' => 'ss',	'Ā' => 'ā',		'Ă' => 'ă',
+		'Ą' => 'ą',		'Ć' => 'ć',		'Ĉ' => 'ĉ',		'Ċ' => 'ċ',
+		'Č' => 'č',		'Ď' => 'ď',		'Đ' => 'đ',		'Ē' => 'ē',
+		'Ĕ' => 'ĕ',		'Ė' => 'ė',		'Ę' => 'ę',		'Ě' => 'ě',
+		'Ĝ' => 'ĝ',		'Ğ' => 'ğ',		'Ġ' => 'ġ',		'Ģ' => 'ģ',
+		'Ĥ' => 'ĥ',		'Ħ' => 'ħ',		'Ĩ' => 'ĩ',		'Ī' => 'ī',
+		'Ĭ' => 'ĭ',		'Į' => 'į',		'İ' => 'i̇',		'IJ' => 'ij',
+		'Ĵ' => 'ĵ',		'Ķ' => 'ķ',		'Ĺ' => 'ĺ',		'Ļ' => 'ļ',
+		'Ľ' => 'ľ',		'Ŀ' => 'ŀ',		'Ł' => 'ł',		'Ń' => 'ń',
+		'Ņ' => 'ņ',		'Ň' => 'ň',		'ʼn' => 'ʼn',	'Ŋ' => 'ŋ',
+		'Ō' => 'ō',		'Ŏ' => 'ŏ',		'Ő' => 'ő',		'Œ' => 'œ',
+		'Ŕ' => 'ŕ',		'Ŗ' => 'ŗ',		'Ř' => 'ř',		'Ś' => 'ś',
+		'Ŝ' => 'ŝ',		'Ş' => 'ş',		'Š' => 'š',		'Ţ' => 'ţ',
+		'Ť' => 'ť',		'Ŧ' => 'ŧ',		'Ũ' => 'ũ',		'Ū' => 'ū',
+		'Ŭ' => 'ŭ',		'Ů' => 'ů',		'Ű' => 'ű',		'Ų' => 'ų',
+		'Ŵ' => 'ŵ',		'Ŷ' => 'ŷ',		'Ÿ' => 'ÿ',		'Ź' => 'ź',
+		'Ż' => 'ż',		'Ž' => 'ž',		'ſ' => 's',		'Ɓ' => 'ɓ',
+		'Ƃ' => 'ƃ',		'Ƅ' => 'ƅ',		'Ɔ' => 'ɔ',		'Ƈ' => 'ƈ',
+		'Ɖ' => 'ɖ',		'Ɗ' => 'ɗ',		'Ƌ' => 'ƌ',		'Ǝ' => 'ǝ',
+		'Ə' => 'ə',		'Ɛ' => 'ɛ',		'Ƒ' => 'ƒ',		'Ɠ' => 'ɠ',
+		'Ɣ' => 'ɣ',		'Ɩ' => 'ɩ',		'Ɨ' => 'ɨ',		'Ƙ' => 'ƙ',
+		'Ɯ' => 'ɯ',		'Ɲ' => 'ɲ',		'Ɵ' => 'ɵ',		'Ơ' => 'ơ',
+		'Ƣ' => 'ƣ',		'Ƥ' => 'ƥ',		'Ʀ' => 'ʀ',		'Ƨ' => 'ƨ',
+		'Ʃ' => 'ʃ',		'Ƭ' => 'ƭ',		'Ʈ' => 'ʈ',		'Ư' => 'ư',
+		'Ʊ' => 'ʊ',		'Ʋ' => 'ʋ',		'Ƴ' => 'ƴ',		'Ƶ' => 'ƶ',
+		'Ʒ' => 'ʒ',		'Ƹ' => 'ƹ',		'Ƽ' => 'ƽ',		'DŽ' => 'dž',
+		'Dž' => 'dž',		'LJ' => 'lj',		'Lj' => 'lj',		'NJ' => 'nj',
+		'Nj' => 'nj',		'Ǎ' => 'ǎ',		'Ǐ' => 'ǐ',		'Ǒ' => 'ǒ',
+		'Ǔ' => 'ǔ',		'Ǖ' => 'ǖ',		'Ǘ' => 'ǘ',		'Ǚ' => 'ǚ',
+		'Ǜ' => 'ǜ',		'Ǟ' => 'ǟ',		'Ǡ' => 'ǡ',		'Ǣ' => 'ǣ',
+		'Ǥ' => 'ǥ',		'Ǧ' => 'ǧ',		'Ǩ' => 'ǩ',		'Ǫ' => 'ǫ',
+		'Ǭ' => 'ǭ',		'Ǯ' => 'ǯ',		'ǰ' => 'ǰ',		'DZ' => 'dz',
+		'Dz' => 'dz',		'Ǵ' => 'ǵ',		'Ƕ' => 'ƕ',		'Ƿ' => 'ƿ',
+		'Ǹ' => 'ǹ',		'Ǻ' => 'ǻ',		'Ǽ' => 'ǽ',		'Ǿ' => 'ǿ',
+		'Ȁ' => 'ȁ',		'Ȃ' => 'ȃ',		'Ȅ' => 'ȅ',		'Ȇ' => 'ȇ',
+		'Ȉ' => 'ȉ',		'Ȋ' => 'ȋ',		'Ȍ' => 'ȍ',		'Ȏ' => 'ȏ',
+		'Ȑ' => 'ȑ',		'Ȓ' => 'ȓ',		'Ȕ' => 'ȕ',		'Ȗ' => 'ȗ',
+		'Ș' => 'ș',		'Ț' => 'ț',		'Ȝ' => 'ȝ',		'Ȟ' => 'ȟ',
+		'Ƞ' => 'ƞ',		'Ȣ' => 'ȣ',		'Ȥ' => 'ȥ',		'Ȧ' => 'ȧ',
+		'Ȩ' => 'ȩ',		'Ȫ' => 'ȫ',		'Ȭ' => 'ȭ',		'Ȯ' => 'ȯ',
+		'Ȱ' => 'ȱ',		'Ȳ' => 'ȳ',		'Ⱥ' => 'ⱥ',		'Ȼ' => 'ȼ',
+		'Ƚ' => 'ƚ',		'Ⱦ' => 'ⱦ',		'Ɂ' => 'ɂ',		'Ƀ' => 'ƀ',
+		'Ʉ' => 'ʉ',		'Ʌ' => 'ʌ',		'Ɇ' => 'ɇ',		'Ɉ' => 'ɉ',
+		'Ɋ' => 'ɋ',		'Ɍ' => 'ɍ',		'Ɏ' => 'ɏ',		'ͅ' => 'ι',
+		'Ά' => 'ά',		'Έ' => 'έ',		'Ή' => 'ή',		'Ί' => 'ί',
+		'Ό' => 'ό',		'Ύ' => 'ύ',		'Ώ' => 'ώ',		'ΐ' => 'ΐ',
+		'Α' => 'α',		'Β' => 'β',		'Γ' => 'γ',		'Δ' => 'δ',
+		'Ε' => 'ε',		'Ζ' => 'ζ',		'Η' => 'η',		'Θ' => 'θ',
+		'Ι' => 'ι',		'Κ' => 'κ',		'Λ' => 'λ',		'Μ' => 'μ',
+		'Ν' => 'ν',		'Ξ' => 'ξ',		'Ο' => 'ο',		'Π' => 'π',
+		'Ρ' => 'ρ',		'Σ' => 'σ',		'Τ' => 'τ',		'Υ' => 'υ',
+		'Φ' => 'φ',		'Χ' => 'χ',		'Ψ' => 'ψ',		'Ω' => 'ω',
+		'Ϊ' => 'ϊ',		'Ϋ' => 'ϋ',		'ΰ' => 'ΰ',	'ς' => 'σ',
+		'ϐ' => 'β',		'ϑ' => 'θ',		'ϕ' => 'φ',		'ϖ' => 'π',
+		'Ϙ' => 'ϙ',		'Ϛ' => 'ϛ',		'Ϝ' => 'ϝ',		'Ϟ' => 'ϟ',
+		'Ϡ' => 'ϡ',		'Ϣ' => 'ϣ',		'Ϥ' => 'ϥ',		'Ϧ' => 'ϧ',
+		'Ϩ' => 'ϩ',		'Ϫ' => 'ϫ',		'Ϭ' => 'ϭ',		'Ϯ' => 'ϯ',
+		'ϰ' => 'κ',		'ϱ' => 'ρ',		'ϴ' => 'θ',		'ϵ' => 'ε',
+		'Ϸ' => 'ϸ',		'Ϲ' => 'ϲ',		'Ϻ' => 'ϻ',		'Ͻ' => 'ͻ',
+		'Ͼ' => 'ͼ',		'Ͽ' => 'ͽ',		'Ѐ' => 'ѐ',		'Ё' => 'ё',
+		'Ђ' => 'ђ',		'Ѓ' => 'ѓ',		'Є' => 'є',		'Ѕ' => 'ѕ',
+		'І' => 'і',		'Ї' => 'ї',		'Ј' => 'ј',		'Љ' => 'љ',
+		'Њ' => 'њ',		'Ћ' => 'ћ',		'Ќ' => 'ќ',		'Ѝ' => 'ѝ',
+		'Ў' => 'ў',		'Џ' => 'џ',		'А' => 'а',		'Б' => 'б',
+		'В' => 'в',		'Г' => 'г',		'Д' => 'д',		'Е' => 'е',
+		'Ж' => 'ж',		'З' => 'з',		'И' => 'и',		'Й' => 'й',
+		'К' => 'к',		'Л' => 'л',		'М' => 'м',		'Н' => 'н',
+		'О' => 'о',		'П' => 'п',		'Р' => 'р',		'С' => 'с',
+		'Т' => 'т',		'У' => 'у',		'Ф' => 'ф',		'Х' => 'х',
+		'Ц' => 'ц',		'Ч' => 'ч',		'Ш' => 'ш',		'Щ' => 'щ',
+		'Ъ' => 'ъ',		'Ы' => 'ы',		'Ь' => 'ь',		'Э' => 'э',
+		'Ю' => 'ю',		'Я' => 'я',		'Ѡ' => 'ѡ',		'Ѣ' => 'ѣ',
+		'Ѥ' => 'ѥ',		'Ѧ' => 'ѧ',		'Ѩ' => 'ѩ',		'Ѫ' => 'ѫ',
+		'Ѭ' => 'ѭ',		'Ѯ' => 'ѯ',		'Ѱ' => 'ѱ',		'Ѳ' => 'ѳ',
+		'Ѵ' => 'ѵ',		'Ѷ' => 'ѷ',		'Ѹ' => 'ѹ',		'Ѻ' => 'ѻ',
+		'Ѽ' => 'ѽ',		'Ѿ' => 'ѿ',		'Ҁ' => 'ҁ',		'Ҋ' => 'ҋ',
+		'Ҍ' => 'ҍ',		'Ҏ' => 'ҏ',		'Ґ' => 'ґ',		'Ғ' => 'ғ',
+		'Ҕ' => 'ҕ',		'Җ' => 'җ',		'Ҙ' => 'ҙ',		'Қ' => 'қ',
+		'Ҝ' => 'ҝ',		'Ҟ' => 'ҟ',		'Ҡ' => 'ҡ',		'Ң' => 'ң',
+		'Ҥ' => 'ҥ',		'Ҧ' => 'ҧ',		'Ҩ' => 'ҩ',		'Ҫ' => 'ҫ',
+		'Ҭ' => 'ҭ',		'Ү' => 'ү',		'Ұ' => 'ұ',		'Ҳ' => 'ҳ',
+		'Ҵ' => 'ҵ',		'Ҷ' => 'ҷ',		'Ҹ' => 'ҹ',		'Һ' => 'һ',
+		'Ҽ' => 'ҽ',		'Ҿ' => 'ҿ',		'Ӏ' => 'ӏ',		'Ӂ' => 'ӂ',
+		'Ӄ' => 'ӄ',		'Ӆ' => 'ӆ',		'Ӈ' => 'ӈ',		'Ӊ' => 'ӊ',
+		'Ӌ' => 'ӌ',		'Ӎ' => 'ӎ',		'Ӑ' => 'ӑ',		'Ӓ' => 'ӓ',
+		'Ӕ' => 'ӕ',		'Ӗ' => 'ӗ',		'Ә' => 'ә',		'Ӛ' => 'ӛ',
+		'Ӝ' => 'ӝ',		'Ӟ' => 'ӟ',		'Ӡ' => 'ӡ',		'Ӣ' => 'ӣ',
+		'Ӥ' => 'ӥ',		'Ӧ' => 'ӧ',		'Ө' => 'ө',		'Ӫ' => 'ӫ',
+		'Ӭ' => 'ӭ',		'Ӯ' => 'ӯ',		'Ӱ' => 'ӱ',		'Ӳ' => 'ӳ',
+		'Ӵ' => 'ӵ',		'Ӷ' => 'ӷ',		'Ӹ' => 'ӹ',		'Ӻ' => 'ӻ',
+		'Ӽ' => 'ӽ',		'Ӿ' => 'ӿ',		'Ԁ' => 'ԁ',		'Ԃ' => 'ԃ',
+		'Ԅ' => 'ԅ',		'Ԇ' => 'ԇ',		'Ԉ' => 'ԉ',		'Ԋ' => 'ԋ',
+		'Ԍ' => 'ԍ',		'Ԏ' => 'ԏ',		'Ԑ' => 'ԑ',		'Ԓ' => 'ԓ',
+		'Ա' => 'ա',		'Բ' => 'բ',		'Գ' => 'գ',		'Դ' => 'դ',
+		'Ե' => 'ե',		'Զ' => 'զ',		'Է' => 'է',		'Ը' => 'ը',
+		'Թ' => 'թ',		'Ժ' => 'ժ',		'Ի' => 'ի',		'Լ' => 'լ',
+		'Խ' => 'խ',		'Ծ' => 'ծ',		'Կ' => 'կ',		'Հ' => 'հ',
+		'Ձ' => 'ձ',		'Ղ' => 'ղ',		'Ճ' => 'ճ',		'Մ' => 'մ',
+		'Յ' => 'յ',		'Ն' => 'ն',		'Շ' => 'շ',		'Ո' => 'ո',
+		'Չ' => 'չ',		'Պ' => 'պ',		'Ջ' => 'ջ',		'Ռ' => 'ռ',
+		'Ս' => 'ս',		'Վ' => 'վ',		'Տ' => 'տ',		'Ր' => 'ր',
+		'Ց' => 'ց',		'Ւ' => 'ւ',		'Փ' => 'փ',		'Ք' => 'ք',
+		'Օ' => 'օ',		'Ֆ' => 'ֆ',		'և' => 'եւ',		'Ⴀ' => 'ⴀ',
+		'Ⴁ' => 'ⴁ',		'Ⴂ' => 'ⴂ',		'Ⴃ' => 'ⴃ',		'Ⴄ' => 'ⴄ',
+		'Ⴅ' => 'ⴅ',		'Ⴆ' => 'ⴆ',		'Ⴇ' => 'ⴇ',		'Ⴈ' => 'ⴈ',
+		'Ⴉ' => 'ⴉ',		'Ⴊ' => 'ⴊ',		'Ⴋ' => 'ⴋ',		'Ⴌ' => 'ⴌ',
+		'Ⴍ' => 'ⴍ',		'Ⴎ' => 'ⴎ',		'Ⴏ' => 'ⴏ',		'Ⴐ' => 'ⴐ',
+		'Ⴑ' => 'ⴑ',		'Ⴒ' => 'ⴒ',		'Ⴓ' => 'ⴓ',		'Ⴔ' => 'ⴔ',
+		'Ⴕ' => 'ⴕ',		'Ⴖ' => 'ⴖ',		'Ⴗ' => 'ⴗ',		'Ⴘ' => 'ⴘ',
+		'Ⴙ' => 'ⴙ',		'Ⴚ' => 'ⴚ',		'Ⴛ' => 'ⴛ',		'Ⴜ' => 'ⴜ',
+		'Ⴝ' => 'ⴝ',		'Ⴞ' => 'ⴞ',		'Ⴟ' => 'ⴟ',		'Ⴠ' => 'ⴠ',
+		'Ⴡ' => 'ⴡ',		'Ⴢ' => 'ⴢ',		'Ⴣ' => 'ⴣ',		'Ⴤ' => 'ⴤ',
+		'Ⴥ' => 'ⴥ',		'Ḁ' => 'ḁ',		'Ḃ' => 'ḃ',		'Ḅ' => 'ḅ',
+		'Ḇ' => 'ḇ',		'Ḉ' => 'ḉ',		'Ḋ' => 'ḋ',		'Ḍ' => 'ḍ',
+		'Ḏ' => 'ḏ',		'Ḑ' => 'ḑ',		'Ḓ' => 'ḓ',		'Ḕ' => 'ḕ',
+		'Ḗ' => 'ḗ',		'Ḙ' => 'ḙ',		'Ḛ' => 'ḛ',		'Ḝ' => 'ḝ',
+		'Ḟ' => 'ḟ',		'Ḡ' => 'ḡ',		'Ḣ' => 'ḣ',		'Ḥ' => 'ḥ',
+		'Ḧ' => 'ḧ',		'Ḩ' => 'ḩ',		'Ḫ' => 'ḫ',		'Ḭ' => 'ḭ',
+		'Ḯ' => 'ḯ',		'Ḱ' => 'ḱ',		'Ḳ' => 'ḳ',		'Ḵ' => 'ḵ',
+		'Ḷ' => 'ḷ',		'Ḹ' => 'ḹ',		'Ḻ' => 'ḻ',		'Ḽ' => 'ḽ',
+		'Ḿ' => 'ḿ',		'Ṁ' => 'ṁ',		'Ṃ' => 'ṃ',		'Ṅ' => 'ṅ',
+		'Ṇ' => 'ṇ',		'Ṉ' => 'ṉ',		'Ṋ' => 'ṋ',		'Ṍ' => 'ṍ',
+		'Ṏ' => 'ṏ',		'Ṑ' => 'ṑ',		'Ṓ' => 'ṓ',		'Ṕ' => 'ṕ',
+		'Ṗ' => 'ṗ',		'Ṙ' => 'ṙ',		'Ṛ' => 'ṛ',		'Ṝ' => 'ṝ',
+		'Ṟ' => 'ṟ',		'Ṡ' => 'ṡ',		'Ṣ' => 'ṣ',		'Ṥ' => 'ṥ',
+		'Ṧ' => 'ṧ',		'Ṩ' => 'ṩ',		'Ṫ' => 'ṫ',		'Ṭ' => 'ṭ',
+		'Ṯ' => 'ṯ',		'Ṱ' => 'ṱ',		'Ṳ' => 'ṳ',		'Ṵ' => 'ṵ',
+		'Ṷ' => 'ṷ',		'Ṹ' => 'ṹ',		'Ṻ' => 'ṻ',		'Ṽ' => 'ṽ',
+		'Ṿ' => 'ṿ',		'Ẁ' => 'ẁ',		'Ẃ' => 'ẃ',		'Ẅ' => 'ẅ',
+		'Ẇ' => 'ẇ',		'Ẉ' => 'ẉ',		'Ẋ' => 'ẋ',		'Ẍ' => 'ẍ',
+		'Ẏ' => 'ẏ',		'Ẑ' => 'ẑ',		'Ẓ' => 'ẓ',		'Ẕ' => 'ẕ',
+		'ẖ' => 'ẖ',		'ẗ' => 'ẗ',		'ẘ' => 'ẘ',		'ẙ' => 'ẙ',
+		'ẚ' => 'aʾ',	'ẛ' => 'ṡ',		'Ạ' => 'ạ',		'Ả' => 'ả',
+		'Ấ' => 'ấ',		'Ầ' => 'ầ',		'Ẩ' => 'ẩ',		'Ẫ' => 'ẫ',
+		'Ậ' => 'ậ',		'Ắ' => 'ắ',		'Ằ' => 'ằ',		'Ẳ' => 'ẳ',
+		'Ẵ' => 'ẵ',		'Ặ' => 'ặ',		'Ẹ' => 'ẹ',		'Ẻ' => 'ẻ',
+		'Ẽ' => 'ẽ',		'Ế' => 'ế',		'Ề' => 'ề',		'Ể' => 'ể',
+		'Ễ' => 'ễ',		'Ệ' => 'ệ',		'Ỉ' => 'ỉ',		'Ị' => 'ị',
+		'Ọ' => 'ọ',		'Ỏ' => 'ỏ',		'Ố' => 'ố',		'Ồ' => 'ồ',
+		'Ổ' => 'ổ',		'Ỗ' => 'ỗ',		'Ộ' => 'ộ',		'Ớ' => 'ớ',
+		'Ờ' => 'ờ',		'Ở' => 'ở',		'Ỡ' => 'ỡ',		'Ợ' => 'ợ',
+		'Ụ' => 'ụ',		'Ủ' => 'ủ',		'Ứ' => 'ứ',		'Ừ' => 'ừ',
+		'Ử' => 'ử',		'Ữ' => 'ữ',		'Ự' => 'ự',		'Ỳ' => 'ỳ',
+		'Ỵ' => 'ỵ',		'Ỷ' => 'ỷ',		'Ỹ' => 'ỹ',		'Ἀ' => 'ἀ',
+		'Ἁ' => 'ἁ',		'Ἂ' => 'ἂ',		'Ἃ' => 'ἃ',		'Ἄ' => 'ἄ',
+		'Ἅ' => 'ἅ',		'Ἆ' => 'ἆ',		'Ἇ' => 'ἇ',		'Ἐ' => 'ἐ',
+		'Ἑ' => 'ἑ',		'Ἒ' => 'ἒ',		'Ἓ' => 'ἓ',		'Ἔ' => 'ἔ',
+		'Ἕ' => 'ἕ',		'Ἠ' => 'ἠ',		'Ἡ' => 'ἡ',		'Ἢ' => 'ἢ',
+		'Ἣ' => 'ἣ',		'Ἤ' => 'ἤ',		'Ἥ' => 'ἥ',		'Ἦ' => 'ἦ',
+		'Ἧ' => 'ἧ',		'Ἰ' => 'ἰ',		'Ἱ' => 'ἱ',		'Ἲ' => 'ἲ',
+		'Ἳ' => 'ἳ',		'Ἴ' => 'ἴ',		'Ἵ' => 'ἵ',		'Ἶ' => 'ἶ',
+		'Ἷ' => 'ἷ',		'Ὀ' => 'ὀ',		'Ὁ' => 'ὁ',		'Ὂ' => 'ὂ',
+		'Ὃ' => 'ὃ',		'Ὄ' => 'ὄ',		'Ὅ' => 'ὅ',		'ὐ' => 'ὐ',
+		'ὒ' => 'ὒ',	'ὔ' => 'ὔ',	'ὖ' => 'ὖ',		'Ὑ' => 'ὑ',
+		'Ὓ' => 'ὓ',		'Ὕ' => 'ὕ',		'Ὗ' => 'ὗ',		'Ὠ' => 'ὠ',
+		'Ὡ' => 'ὡ',		'Ὢ' => 'ὢ',		'Ὣ' => 'ὣ',		'Ὤ' => 'ὤ',
+		'Ὥ' => 'ὥ',		'Ὦ' => 'ὦ',		'Ὧ' => 'ὧ',		'ᾀ' => 'ἀι',
+		'ᾁ' => 'ἁι',	'ᾂ' => 'ἂι',	'ᾃ' => 'ἃι',	'ᾄ' => 'ἄι',
+		'ᾅ' => 'ἅι',	'ᾆ' => 'ἆι',	'ᾇ' => 'ἇι',	'ᾈ' => 'ᾀ',
+		'ᾉ' => 'ᾁ',		'ᾊ' => 'ᾂ',		'ᾋ' => 'ᾃ',		'ᾌ' => 'ᾄ',
+		'ᾍ' => 'ᾅ',		'ᾎ' => 'ᾆ',		'ᾏ' => 'ᾇ',		'ᾐ' => 'ἠι',
+		'ᾑ' => 'ἡι',	'ᾒ' => 'ἢι',	'ᾓ' => 'ἣι',	'ᾔ' => 'ἤι',
+		'ᾕ' => 'ἥι',	'ᾖ' => 'ἦι',	'ᾗ' => 'ἧι',	'ᾘ' => 'ᾐ',
+		'ᾙ' => 'ᾑ',		'ᾚ' => 'ᾒ',		'ᾛ' => 'ᾓ',		'ᾜ' => 'ᾔ',
+		'ᾝ' => 'ᾕ',		'ᾞ' => 'ᾖ',		'ᾟ' => 'ᾗ',		'ᾠ' => 'ὠι',
+		'ᾡ' => 'ὡι',	'ᾢ' => 'ὢι',	'ᾣ' => 'ὣι',	'ᾤ' => 'ὤι',
+		'ᾥ' => 'ὥι',	'ᾦ' => 'ὦι',	'ᾧ' => 'ὧι',	'ᾨ' => 'ᾠ',
+		'ᾩ' => 'ᾡ',		'ᾪ' => 'ᾢ',		'ᾫ' => 'ᾣ',		'ᾬ' => 'ᾤ',
+		'ᾭ' => 'ᾥ',		'ᾮ' => 'ᾦ',		'ᾯ' => 'ᾧ',		'ᾲ' => 'ὰι',
+		'ᾳ' => 'αι',	'ᾴ' => 'άι',	'ᾶ' => 'ᾶ',		'ᾷ' => 'ᾶι',
+		'Ᾰ' => 'ᾰ',		'Ᾱ' => 'ᾱ',		'Ὰ' => 'ὰ',		'Ά' => 'ά',
+		'ᾼ' => 'ᾳ',		'ι' => 'ι',		'ῂ' => 'ὴι',	'ῃ' => 'ηι',
+		'ῄ' => 'ήι',	'ῆ' => 'ῆ',		'ῇ' => 'ῆι',	'Ὲ' => 'ὲ',
+		'Έ' => 'έ',		'Ὴ' => 'ὴ',		'Ή' => 'ή',		'ῌ' => 'ῃ',
+		'ῒ' => 'ῒ',	'ΐ' => 'ΐ',	'ῖ' => 'ῖ',		'ῗ' => 'ῗ',
+		'Ῐ' => 'ῐ',		'Ῑ' => 'ῑ',		'Ὶ' => 'ὶ',		'Ί' => 'ί',
+		'ῢ' => 'ῢ',	'ΰ' => 'ΰ',	'ῤ' => 'ῤ',		'ῦ' => 'ῦ',
+		'ῧ' => 'ῧ',		'Ῠ' => 'ῠ',		'Ῡ' => 'ῡ',		'Ὺ' => 'ὺ',
+		'Ύ' => 'ύ',		'Ῥ' => 'ῥ',		'ῲ' => 'ὼι',	'ῳ' => 'ωι',
+		'ῴ' => 'ώι',	'ῶ' => 'ῶ',		'ῷ' => 'ῶι',	'Ὸ' => 'ὸ',
+		'Ό' => 'ό',		'Ὼ' => 'ὼ',		'Ώ' => 'ώ',		'ῼ' => 'ῳ',
+		'Ω' => 'ω',		'K' => 'k',		'Å' => 'å',		'Ⅎ' => 'ⅎ',
+		'Ⅰ' => 'ⅰ',		'Ⅱ' => 'ⅱ',		'Ⅲ' => 'ⅲ',		'Ⅳ' => 'ⅳ',
+		'Ⅴ' => 'ⅴ',		'Ⅵ' => 'ⅵ',		'Ⅶ' => 'ⅶ',		'Ⅷ' => 'ⅷ',
+		'Ⅸ' => 'ⅸ',		'Ⅹ' => 'ⅹ',		'Ⅺ' => 'ⅺ',		'Ⅻ' => 'ⅻ',
+		'Ⅼ' => 'ⅼ',		'Ⅽ' => 'ⅽ',		'Ⅾ' => 'ⅾ',		'Ⅿ' => 'ⅿ',
+		'Ↄ' => 'ↄ',		'Ⓐ' => 'ⓐ',		'Ⓑ' => 'ⓑ',		'Ⓒ' => 'ⓒ',
+		'Ⓓ' => 'ⓓ',		'Ⓔ' => 'ⓔ',		'Ⓕ' => 'ⓕ',		'Ⓖ' => 'ⓖ',
+		'Ⓗ' => 'ⓗ',		'Ⓘ' => 'ⓘ',		'Ⓙ' => 'ⓙ',		'Ⓚ' => 'ⓚ',
+		'Ⓛ' => 'ⓛ',		'Ⓜ' => 'ⓜ',		'Ⓝ' => 'ⓝ',		'Ⓞ' => 'ⓞ',
+		'Ⓟ' => 'ⓟ',		'Ⓠ' => 'ⓠ',		'Ⓡ' => 'ⓡ',		'Ⓢ' => 'ⓢ',
+		'Ⓣ' => 'ⓣ',		'Ⓤ' => 'ⓤ',		'Ⓥ' => 'ⓥ',		'Ⓦ' => 'ⓦ',
+		'Ⓧ' => 'ⓧ',		'Ⓨ' => 'ⓨ',		'Ⓩ' => 'ⓩ',		'Ⰰ' => 'ⰰ',
+		'Ⰱ' => 'ⰱ',		'Ⰲ' => 'ⰲ',		'Ⰳ' => 'ⰳ',		'Ⰴ' => 'ⰴ',
+		'Ⰵ' => 'ⰵ',		'Ⰶ' => 'ⰶ',		'Ⰷ' => 'ⰷ',		'Ⰸ' => 'ⰸ',
+		'Ⰹ' => 'ⰹ',		'Ⰺ' => 'ⰺ',		'Ⰻ' => 'ⰻ',		'Ⰼ' => 'ⰼ',
+		'Ⰽ' => 'ⰽ',		'Ⰾ' => 'ⰾ',		'Ⰿ' => 'ⰿ',		'Ⱀ' => 'ⱀ',
+		'Ⱁ' => 'ⱁ',		'Ⱂ' => 'ⱂ',		'Ⱃ' => 'ⱃ',		'Ⱄ' => 'ⱄ',
+		'Ⱅ' => 'ⱅ',		'Ⱆ' => 'ⱆ',		'Ⱇ' => 'ⱇ',		'Ⱈ' => 'ⱈ',
+		'Ⱉ' => 'ⱉ',		'Ⱊ' => 'ⱊ',		'Ⱋ' => 'ⱋ',		'Ⱌ' => 'ⱌ',
+		'Ⱍ' => 'ⱍ',		'Ⱎ' => 'ⱎ',		'Ⱏ' => 'ⱏ',		'Ⱐ' => 'ⱐ',
+		'Ⱑ' => 'ⱑ',		'Ⱒ' => 'ⱒ',		'Ⱓ' => 'ⱓ',		'Ⱔ' => 'ⱔ',
+		'Ⱕ' => 'ⱕ',		'Ⱖ' => 'ⱖ',		'Ⱗ' => 'ⱗ',		'Ⱘ' => 'ⱘ',
+		'Ⱙ' => 'ⱙ',		'Ⱚ' => 'ⱚ',		'Ⱛ' => 'ⱛ',		'Ⱜ' => 'ⱜ',
+		'Ⱝ' => 'ⱝ',		'Ⱞ' => 'ⱞ',		'Ⱡ' => 'ⱡ',		'Ɫ' => 'ɫ',
+		'Ᵽ' => 'ᵽ',		'Ɽ' => 'ɽ',		'Ⱨ' => 'ⱨ',		'Ⱪ' => 'ⱪ',
+		'Ⱬ' => 'ⱬ',		'Ⱶ' => 'ⱶ',		'Ⲁ' => 'ⲁ',		'Ⲃ' => 'ⲃ',
+		'Ⲅ' => 'ⲅ',		'Ⲇ' => 'ⲇ',		'Ⲉ' => 'ⲉ',		'Ⲋ' => 'ⲋ',
+		'Ⲍ' => 'ⲍ',		'Ⲏ' => 'ⲏ',		'Ⲑ' => 'ⲑ',		'Ⲓ' => 'ⲓ',
+		'Ⲕ' => 'ⲕ',		'Ⲗ' => 'ⲗ',		'Ⲙ' => 'ⲙ',		'Ⲛ' => 'ⲛ',
+		'Ⲝ' => 'ⲝ',		'Ⲟ' => 'ⲟ',		'Ⲡ' => 'ⲡ',		'Ⲣ' => 'ⲣ',
+		'Ⲥ' => 'ⲥ',		'Ⲧ' => 'ⲧ',		'Ⲩ' => 'ⲩ',		'Ⲫ' => 'ⲫ',
+		'Ⲭ' => 'ⲭ',		'Ⲯ' => 'ⲯ',		'Ⲱ' => 'ⲱ',		'Ⲳ' => 'ⲳ',
+		'Ⲵ' => 'ⲵ',		'Ⲷ' => 'ⲷ',		'Ⲹ' => 'ⲹ',		'Ⲻ' => 'ⲻ',
+		'Ⲽ' => 'ⲽ',		'Ⲿ' => 'ⲿ',		'Ⳁ' => 'ⳁ',		'Ⳃ' => 'ⳃ',
+		'Ⳅ' => 'ⳅ',		'Ⳇ' => 'ⳇ',		'Ⳉ' => 'ⳉ',		'Ⳋ' => 'ⳋ',
+		'Ⳍ' => 'ⳍ',		'Ⳏ' => 'ⳏ',		'Ⳑ' => 'ⳑ',		'Ⳓ' => 'ⳓ',
+		'Ⳕ' => 'ⳕ',		'Ⳗ' => 'ⳗ',		'Ⳙ' => 'ⳙ',		'Ⳛ' => 'ⳛ',
+		'Ⳝ' => 'ⳝ',		'Ⳟ' => 'ⳟ',		'Ⳡ' => 'ⳡ',		'Ⳣ' => 'ⳣ',
+		'ff' => 'ff',	'fi' => 'fi',	'fl' => 'fl',	'ffi' => 'ffi',
+		'ffl' => 'ffl',	'ſt' => 'st',	'st' => 'st',	'ﬓ' => 'մն',
+		'ﬔ' => 'մե',	'ﬕ' => 'մի',	'ﬖ' => 'վն',	'ﬗ' => 'մխ',
+		'A' => 'a',		'B' => 'b',		'C' => 'c',		'D' => 'd',
+		'E' => 'e',		'F' => 'f',		'G' => 'g',		'H' => 'h',
+		'I' => 'i',		'J' => 'j',		'K' => 'k',		'L' => 'l',
+		'M' => 'm',	'N' => 'n',		'O' => 'o',		'P' => 'p',
+		'Q' => 'q',		'R' => 'r',		'S' => 's',		'T' => 't',
+		'U' => 'u',		'V' => 'v',		'W' => 'w',	'X' => 'x',
+		'Y' => 'y',		'Z' => 'z',		'𐐀' => '𐐨',	'𐐁' => '𐐩',
+		'𐐂' => '𐐪',	'𐐃' => '𐐫',	'𐐄' => '𐐬',	'𐐅' => '𐐭',
+		'𐐆' => '𐐮',	'𐐇' => '𐐯',	'𐐈' => '𐐰',	'𐐉' => '𐐱',
+		'𐐊' => '𐐲',	'𐐋' => '𐐳',	'𐐌' => '𐐴',	'𐐍' => '𐐵',
+		'𐐎' => '𐐶',	'𐐏' => '𐐷',	'𐐐' => '𐐸',	'𐐑' => '𐐹',
+		'𐐒' => '𐐺',	'𐐓' => '𐐻',	'𐐔' => '𐐼',	'𐐕' => '𐐽',
+		'𐐖' => '𐐾',	'𐐗' => '𐐿',	'𐐘' => '𐑀',	'𐐙' => '𐑁',
+		'𐐚' => '𐑂',	'𐐛' => '𐑃',	'𐐜' => '𐑄',	'𐐝' => '𐑅',
+		'𐐞' => '𐑆',	'𐐟' => '𐑇',	'𐐠' => '𐑈',	'𐐡' => '𐑉',
+		'𐐢' => '𐑊',	'𐐣' => '𐑋',	'𐐤' => '𐑌',	'𐐥' => '𐑍',
+		'𐑎' => '𐐦',	'𐑏' => '𐐧',
+	);
+
+	return strtr($string, $case_folding);
+}
+
+// Convert the given UTF-8 string to uppercase.
+/**
+ * Convert the given UTF-8 string to uppercase.
+ * equivalent to mb_strtoupper($string, 'UTF-8')
+ *
+ * @param string $string
+ * @return string
+ */
+function utf8_strtoupper($string)
+{
+	static $case_folding = array(
+		'a' => 'A',		'b' => 'B',		'c' => 'C',		'd' => 'D',
+		'e' => 'E',		'f' => 'F',		'g' => 'G',		'h' => 'H',
+		'i' => 'I',		'j' => 'J',		'k' => 'K',		'l' => 'L',
+		'm' => 'M',		'n' => 'N',		'o' => 'O',		'p' => 'P',
+		'q' => 'Q',		'r' => 'R',		's' => 'S',		't' => 'T',
+		'u' => 'U',		'v' => 'V',		'w' => 'W',		'x' => 'X',
+		'y' => 'Y',		'z' => 'Z',		'μ' => 'µ',		'à' => 'À',
+		'á' => 'Á',		'â' => 'Â',		'ã' => 'Ã',		'ä' => 'Ä',
+		'å' => 'Å',		'æ' => 'Æ',		'ç' => 'Ç',		'è' => 'È',
+		'é' => 'É',		'ê' => 'Ê',		'ë' => 'Ë',		'ì' => 'Ì',
+		'í' => 'Í',		'î' => 'Î',		'ï' => 'Ï',		'ð' => 'Ð',
+		'ñ' => 'Ñ',		'ò' => 'Ò',		'ó' => 'Ó',		'ô' => 'Ô',
+		'õ' => 'Õ',		'ö' => 'Ö',		'ø' => 'Ø',		'ù' => 'Ù',
+		'ú' => 'Ú',		'û' => 'Û',		'ü' => 'Ü',		'ý' => 'Ý',
+		'þ' => 'Þ',		'ss' => 'ß',	'ā' => 'Ā',		'ă' => 'Ă',
+		'ą' => 'Ą',		'ć' => 'Ć',		'ĉ' => 'Ĉ',		'ċ' => 'Ċ',
+		'č' => 'Č',		'ď' => 'Ď',		'đ' => 'Đ',		'ē' => 'Ē',
+		'ĕ' => 'Ĕ',		'ė' => 'Ė',		'ę' => 'Ę',		'ě' => 'Ě',
+		'ĝ' => 'Ĝ',		'ğ' => 'Ğ',		'ġ' => 'Ġ',		'ģ' => 'Ģ',
+		'ĥ' => 'Ĥ',		'ħ' => 'Ħ',		'ĩ' => 'Ĩ',		'ī' => 'Ī',
+		'ĭ' => 'Ĭ',		'į' => 'Į',		'i̇' => 'İ',		'ij' => 'IJ',
+		'ĵ' => 'Ĵ',		'ķ' => 'Ķ',		'ĺ' => 'Ĺ',		'ļ' => 'Ļ',
+		'ľ' => 'Ľ',		'ŀ' => 'Ŀ',		'ł' => 'Ł',		'ń' => 'Ń',
+		'ņ' => 'Ņ',		'ň' => 'Ň',		'ʼn' => 'ʼn',	'ŋ' => 'Ŋ',
+		'ō' => 'Ō',		'ŏ' => 'Ŏ',		'ő' => 'Ő',		'œ' => 'Œ',
+		'ŕ' => 'Ŕ',		'ŗ' => 'Ŗ',		'ř' => 'Ř',		'ś' => 'Ś',
+		'ŝ' => 'Ŝ',		'ş' => 'Ş',		'š' => 'Š',		'ţ' => 'Ţ',
+		'ť' => 'Ť',		'ŧ' => 'Ŧ',		'ũ' => 'Ũ',		'ū' => 'Ū',
+		'ŭ' => 'Ŭ',		'ů' => 'Ů',		'ű' => 'Ű',		'ų' => 'Ų',
+		'ŵ' => 'Ŵ',		'ŷ' => 'Ŷ',		'ÿ' => 'Ÿ',		'ź' => 'Ź',
+		'ż' => 'Ż',		'ž' => 'Ž',		's' => 'ſ',		'ɓ' => 'Ɓ',
+		'ƃ' => 'Ƃ',		'ƅ' => 'Ƅ',		'ɔ' => 'Ɔ',		'ƈ' => 'Ƈ',
+		'ɖ' => 'Ɖ',		'ɗ' => 'Ɗ',		'ƌ' => 'Ƌ',		'ǝ' => 'Ǝ',
+		'ə' => 'Ə',		'ɛ' => 'Ɛ',		'ƒ' => 'Ƒ',		'ɠ' => 'Ɠ',
+		'ɣ' => 'Ɣ',		'ɩ' => 'Ɩ',		'ɨ' => 'Ɨ',		'ƙ' => 'Ƙ',
+		'ɯ' => 'Ɯ',		'ɲ' => 'Ɲ',		'ɵ' => 'Ɵ',		'ơ' => 'Ơ',
+		'ƣ' => 'Ƣ',		'ƥ' => 'Ƥ',		'ʀ' => 'Ʀ',		'ƨ' => 'Ƨ',
+		'ʃ' => 'Ʃ',		'ƭ' => 'Ƭ',		'ʈ' => 'Ʈ',		'ư' => 'Ư',
+		'ʊ' => 'Ʊ',		'ʋ' => 'Ʋ',		'ƴ' => 'Ƴ',		'ƶ' => 'Ƶ',
+		'ʒ' => 'Ʒ',		'ƹ' => 'Ƹ',		'ƽ' => 'Ƽ',		'dž' => 'DŽ',
+		'dž' => 'Dž',		'lj' => 'LJ',		'lj' => 'Lj',		'nj' => 'NJ',
+		'nj' => 'Nj',		'ǎ' => 'Ǎ',		'ǐ' => 'Ǐ',		'ǒ' => 'Ǒ',
+		'ǔ' => 'Ǔ',		'ǖ' => 'Ǖ',		'ǘ' => 'Ǘ',		'ǚ' => 'Ǚ',
+		'ǜ' => 'Ǜ',		'ǟ' => 'Ǟ',		'ǡ' => 'Ǡ',		'ǣ' => 'Ǣ',
+		'ǥ' => 'Ǥ',		'ǧ' => 'Ǧ',		'ǩ' => 'Ǩ',		'ǫ' => 'Ǫ',
+		'ǭ' => 'Ǭ',		'ǯ' => 'Ǯ',		'ǰ' => 'ǰ',		'dz' => 'DZ',
+		'dz' => 'Dz',		'ǵ' => 'Ǵ',		'ƕ' => 'Ƕ',		'ƿ' => 'Ƿ',
+		'ǹ' => 'Ǹ',		'ǻ' => 'Ǻ',		'ǽ' => 'Ǽ',		'ǿ' => 'Ǿ',
+		'ȁ' => 'Ȁ',		'ȃ' => 'Ȃ',		'ȅ' => 'Ȅ',		'ȇ' => 'Ȇ',
+		'ȉ' => 'Ȉ',		'ȋ' => 'Ȋ',		'ȍ' => 'Ȍ',		'ȏ' => 'Ȏ',
+		'ȑ' => 'Ȑ',		'ȓ' => 'Ȓ',		'ȕ' => 'Ȕ',		'ȗ' => 'Ȗ',
+		'ș' => 'Ș',		'ț' => 'Ț',		'ȝ' => 'Ȝ',		'ȟ' => 'Ȟ',
+		'ƞ' => 'Ƞ',		'ȣ' => 'Ȣ',		'ȥ' => 'Ȥ',		'ȧ' => 'Ȧ',
+		'ȩ' => 'Ȩ',		'ȫ' => 'Ȫ',		'ȭ' => 'Ȭ',		'ȯ' => 'Ȯ',
+		'ȱ' => 'Ȱ',		'ȳ' => 'Ȳ',		'ⱥ' => 'Ⱥ',		'ȼ' => 'Ȼ',
+		'ƚ' => 'Ƚ',		'ⱦ' => 'Ⱦ',		'ɂ' => 'Ɂ',		'ƀ' => 'Ƀ',
+		'ʉ' => 'Ʉ',		'ʌ' => 'Ʌ',		'ɇ' => 'Ɇ',		'ɉ' => 'Ɉ',
+		'ɋ' => 'Ɋ',		'ɍ' => 'Ɍ',		'ɏ' => 'Ɏ',		'ι' => 'ͅ',
+		'ά' => 'Ά',		'έ' => 'Έ',		'ή' => 'Ή',		'ί' => 'Ί',
+		'ό' => 'Ό',		'ύ' => 'Ύ',		'ώ' => 'Ώ',		'ΐ' => 'ΐ',
+		'α' => 'Α',		'β' => 'Β',		'γ' => 'Γ',		'δ' => 'Δ',
+		'ε' => 'Ε',		'ζ' => 'Ζ',		'η' => 'Η',		'θ' => 'Θ',
+		'ι' => 'Ι',		'κ' => 'Κ',		'λ' => 'Λ',		'μ' => 'Μ',
+		'ν' => 'Ν',		'ξ' => 'Ξ',		'ο' => 'Ο',		'π' => 'Π',
+		'ρ' => 'Ρ',		'σ' => 'Σ',		'τ' => 'Τ',		'υ' => 'Υ',
+		'φ' => 'Φ',		'χ' => 'Χ',		'ψ' => 'Ψ',		'ω' => 'Ω',
+		'ϊ' => 'Ϊ',		'ϋ' => 'Ϋ',		'ΰ' => 'ΰ',	'σ' => 'ς',
+		'β' => 'ϐ',		'θ' => 'ϑ',		'φ' => 'ϕ',		'π' => 'ϖ',
+		'ϙ' => 'Ϙ',		'ϛ' => 'Ϛ',		'ϝ' => 'Ϝ',		'ϟ' => 'Ϟ',
+		'ϡ' => 'Ϡ',		'ϣ' => 'Ϣ',		'ϥ' => 'Ϥ',		'ϧ' => 'Ϧ',
+		'ϩ' => 'Ϩ',		'ϫ' => 'Ϫ',		'ϭ' => 'Ϭ',		'ϯ' => 'Ϯ',
+		'κ' => 'ϰ',		'ρ' => 'ϱ',		'θ' => 'ϴ',		'ε' => 'ϵ',
+		'ϸ' => 'Ϸ',		'ϲ' => 'Ϲ',		'ϻ' => 'Ϻ',		'ͻ' => 'Ͻ',
+		'ͼ' => 'Ͼ',		'ͽ' => 'Ͽ',		'ѐ' => 'Ѐ',		'ё' => 'Ё',
+		'ђ' => 'Ђ',		'ѓ' => 'Ѓ',		'є' => 'Є',		'ѕ' => 'Ѕ',
+		'і' => 'І',		'ї' => 'Ї',		'ј' => 'Ј',		'љ' => 'Љ',
+		'њ' => 'Њ',		'ћ' => 'Ћ',		'ќ' => 'Ќ',		'ѝ' => 'Ѝ',
+		'ў' => 'Ў',		'џ' => 'Џ',		'а' => 'А',		'б' => 'Б',
+		'в' => 'В',		'г' => 'Г',		'д' => 'Д',		'е' => 'Е',
+		'ж' => 'Ж',		'з' => 'З',		'и' => 'И',		'й' => 'Й',
+		'к' => 'К',		'л' => 'Л',		'м' => 'М',		'н' => 'Н',
+		'о' => 'О',		'п' => 'П',		'р' => 'Р',		'с' => 'С',
+		'т' => 'Т',		'у' => 'У',		'ф' => 'Ф',		'х' => 'Х',
+		'ц' => 'Ц',		'ч' => 'Ч',		'ш' => 'Ш',		'щ' => 'Щ',
+		'ъ' => 'Ъ',		'ы' => 'Ы',		'ь' => 'Ь',		'э' => 'Э',
+		'ю' => 'Ю',		'я' => 'Я',		'ѡ' => 'Ѡ',		'ѣ' => 'Ѣ',
+		'ѥ' => 'Ѥ',		'ѧ' => 'Ѧ',		'ѩ' => 'Ѩ',		'ѫ' => 'Ѫ',
+		'ѭ' => 'Ѭ',		'ѯ' => 'Ѯ',		'ѱ' => 'Ѱ',		'ѳ' => 'Ѳ',
+		'ѵ' => 'Ѵ',		'ѷ' => 'Ѷ',		'ѹ' => 'Ѹ',		'ѻ' => 'Ѻ',
+		'ѽ' => 'Ѽ',		'ѿ' => 'Ѿ',		'ҁ' => 'Ҁ',		'ҋ' => 'Ҋ',
+		'ҍ' => 'Ҍ',		'ҏ' => 'Ҏ',		'ґ' => 'Ґ',		'ғ' => 'Ғ',
+		'ҕ' => 'Ҕ',		'җ' => 'Җ',		'ҙ' => 'Ҙ',		'қ' => 'Қ',
+		'ҝ' => 'Ҝ',		'ҟ' => 'Ҟ',		'ҡ' => 'Ҡ',		'ң' => 'Ң',
+		'ҥ' => 'Ҥ',		'ҧ' => 'Ҧ',		'ҩ' => 'Ҩ',		'ҫ' => 'Ҫ',
+		'ҭ' => 'Ҭ',		'ү' => 'Ү',		'ұ' => 'Ұ',		'ҳ' => 'Ҳ',
+		'ҵ' => 'Ҵ',		'ҷ' => 'Ҷ',		'ҹ' => 'Ҹ',		'һ' => 'Һ',
+		'ҽ' => 'Ҽ',		'ҿ' => 'Ҿ',		'ӏ' => 'Ӏ',		'ӂ' => 'Ӂ',
+		'ӄ' => 'Ӄ',		'ӆ' => 'Ӆ',		'ӈ' => 'Ӈ',		'ӊ' => 'Ӊ',
+		'ӌ' => 'Ӌ',		'ӎ' => 'Ӎ',		'ӑ' => 'Ӑ',		'ӓ' => 'Ӓ',
+		'ӕ' => 'Ӕ',		'ӗ' => 'Ӗ',		'ә' => 'Ә',		'ӛ' => 'Ӛ',
+		'ӝ' => 'Ӝ',		'ӟ' => 'Ӟ',		'ӡ' => 'Ӡ',		'ӣ' => 'Ӣ',
+		'ӥ' => 'Ӥ',		'ӧ' => 'Ӧ',		'ө' => 'Ө',		'ӫ' => 'Ӫ',
+		'ӭ' => 'Ӭ',		'ӯ' => 'Ӯ',		'ӱ' => 'Ӱ',		'ӳ' => 'Ӳ',
+		'ӵ' => 'Ӵ',		'ӷ' => 'Ӷ',		'ӹ' => 'Ӹ',		'ӻ' => 'Ӻ',
+		'ӽ' => 'Ӽ',		'ӿ' => 'Ӿ',		'ԁ' => 'Ԁ',		'ԃ' => 'Ԃ',
+		'ԅ' => 'Ԅ',		'ԇ' => 'Ԇ',		'ԉ' => 'Ԉ',		'ԋ' => 'Ԋ',
+		'ԍ' => 'Ԍ',		'ԏ' => 'Ԏ',		'ԑ' => 'Ԑ',		'ԓ' => 'Ԓ',
+		'ա' => 'Ա',		'բ' => 'Բ',		'գ' => 'Գ',		'դ' => 'Դ',
+		'ե' => 'Ե',		'զ' => 'Զ',		'է' => 'Է',		'ը' => 'Ը',
+		'թ' => 'Թ',		'ժ' => 'Ժ',		'ի' => 'Ի',		'լ' => 'Լ',
+		'խ' => 'Խ',		'ծ' => 'Ծ',		'կ' => 'Կ',		'հ' => 'Հ',
+		'ձ' => 'Ձ',		'ղ' => 'Ղ',		'ճ' => 'Ճ',		'մ' => 'Մ',
+		'յ' => 'Յ',		'ն' => 'Ն',		'շ' => 'Շ',		'ո' => 'Ո',
+		'չ' => 'Չ',		'պ' => 'Պ',		'ջ' => 'Ջ',		'ռ' => 'Ռ',
+		'ս' => 'Ս',		'վ' => 'Վ',		'տ' => 'Տ',		'ր' => 'Ր',
+		'ց' => 'Ց',		'ւ' => 'Ւ',		'փ' => 'Փ',		'ք' => 'Ք',
+		'օ' => 'Օ',		'ֆ' => 'Ֆ',		'եւ' => 'և',		'ⴀ' => 'Ⴀ',
+		'ⴁ' => 'Ⴁ',		'ⴂ' => 'Ⴂ',		'ⴃ' => 'Ⴃ',		'ⴄ' => 'Ⴄ',
+		'ⴅ' => 'Ⴅ',		'ⴆ' => 'Ⴆ',		'ⴇ' => 'Ⴇ',		'ⴈ' => 'Ⴈ',
+		'ⴉ' => 'Ⴉ',		'ⴊ' => 'Ⴊ',		'ⴋ' => 'Ⴋ',		'ⴌ' => 'Ⴌ',
+		'ⴍ' => 'Ⴍ',		'ⴎ' => 'Ⴎ',		'ⴏ' => 'Ⴏ',		'ⴐ' => 'Ⴐ',
+		'ⴑ' => 'Ⴑ',		'ⴒ' => 'Ⴒ',		'ⴓ' => 'Ⴓ',		'ⴔ' => 'Ⴔ',
+		'ⴕ' => 'Ⴕ',		'ⴖ' => 'Ⴖ',		'ⴗ' => 'Ⴗ',		'ⴘ' => 'Ⴘ',
+		'ⴙ' => 'Ⴙ',		'ⴚ' => 'Ⴚ',		'ⴛ' => 'Ⴛ',		'ⴜ' => 'Ⴜ',
+		'ⴝ' => 'Ⴝ',		'ⴞ' => 'Ⴞ',		'ⴟ' => 'Ⴟ',		'ⴠ' => 'Ⴠ',
+		'ⴡ' => 'Ⴡ',		'ⴢ' => 'Ⴢ',		'ⴣ' => 'Ⴣ',		'ⴤ' => 'Ⴤ',
+		'ⴥ' => 'Ⴥ',		'ḁ' => 'Ḁ',		'ḃ' => 'Ḃ',		'ḅ' => 'Ḅ',
+		'ḇ' => 'Ḇ',		'ḉ' => 'Ḉ',		'ḋ' => 'Ḋ',		'ḍ' => 'Ḍ',
+		'ḏ' => 'Ḏ',		'ḑ' => 'Ḑ',		'ḓ' => 'Ḓ',		'ḕ' => 'Ḕ',
+		'ḗ' => 'Ḗ',		'ḙ' => 'Ḙ',		'ḛ' => 'Ḛ',		'ḝ' => 'Ḝ',
+		'ḟ' => 'Ḟ',		'ḡ' => 'Ḡ',		'ḣ' => 'Ḣ',		'ḥ' => 'Ḥ',
+		'ḧ' => 'Ḧ',		'ḩ' => 'Ḩ',		'ḫ' => 'Ḫ',		'ḭ' => 'Ḭ',
+		'ḯ' => 'Ḯ',		'ḱ' => 'Ḱ',		'ḳ' => 'Ḳ',		'ḵ' => 'Ḵ',
+		'ḷ' => 'Ḷ',		'ḹ' => 'Ḹ',		'ḻ' => 'Ḻ',		'ḽ' => 'Ḽ',
+		'ḿ' => 'Ḿ',		'ṁ' => 'Ṁ',		'ṃ' => 'Ṃ',		'ṅ' => 'Ṅ',
+		'ṇ' => 'Ṇ',		'ṉ' => 'Ṉ',		'ṋ' => 'Ṋ',		'ṍ' => 'Ṍ',
+		'ṏ' => 'Ṏ',		'ṑ' => 'Ṑ',		'ṓ' => 'Ṓ',		'ṕ' => 'Ṕ',
+		'ṗ' => 'Ṗ',		'ṙ' => 'Ṙ',		'ṛ' => 'Ṛ',		'ṝ' => 'Ṝ',
+		'ṟ' => 'Ṟ',		'ṡ' => 'Ṡ',		'ṣ' => 'Ṣ',		'ṥ' => 'Ṥ',
+		'ṧ' => 'Ṧ',		'ṩ' => 'Ṩ',		'ṫ' => 'Ṫ',		'ṭ' => 'Ṭ',
+		'ṯ' => 'Ṯ',		'ṱ' => 'Ṱ',		'ṳ' => 'Ṳ',		'ṵ' => 'Ṵ',
+		'ṷ' => 'Ṷ',		'ṹ' => 'Ṹ',		'ṻ' => 'Ṻ',		'ṽ' => 'Ṽ',
+		'ṿ' => 'Ṿ',		'ẁ' => 'Ẁ',		'ẃ' => 'Ẃ',		'ẅ' => 'Ẅ',
+		'ẇ' => 'Ẇ',		'ẉ' => 'Ẉ',		'ẋ' => 'Ẋ',		'ẍ' => 'Ẍ',
+		'ẏ' => 'Ẏ',		'ẑ' => 'Ẑ',		'ẓ' => 'Ẓ',		'ẕ' => 'Ẕ',
+		'ẖ' => 'ẖ',		'ẗ' => 'ẗ',		'ẘ' => 'ẘ',		'ẙ' => 'ẙ',
+		'aʾ' => 'ẚ',	'ṡ' => 'ẛ',		'ạ' => 'Ạ',		'ả' => 'Ả',
+		'ấ' => 'Ấ',		'ầ' => 'Ầ',		'ẩ' => 'Ẩ',		'ẫ' => 'Ẫ',
+		'ậ' => 'Ậ',		'ắ' => 'Ắ',		'ằ' => 'Ằ',		'ẳ' => 'Ẳ',
+		'ẵ' => 'Ẵ',		'ặ' => 'Ặ',		'ẹ' => 'Ẹ',		'ẻ' => 'Ẻ',
+		'ẽ' => 'Ẽ',		'ế' => 'Ế',		'ề' => 'Ề',		'ể' => 'Ể',
+		'ễ' => 'Ễ',		'ệ' => 'Ệ',		'ỉ' => 'Ỉ',		'ị' => 'Ị',
+		'ọ' => 'Ọ',		'ỏ' => 'Ỏ',		'ố' => 'Ố',		'ồ' => 'Ồ',
+		'ổ' => 'Ổ',		'ỗ' => 'Ỗ',		'ộ' => 'Ộ',		'ớ' => 'Ớ',
+		'ờ' => 'Ờ',		'ở' => 'Ở',		'ỡ' => 'Ỡ',		'ợ' => 'Ợ',
+		'ụ' => 'Ụ',		'ủ' => 'Ủ',		'ứ' => 'Ứ',		'ừ' => 'Ừ',
+		'ử' => 'Ử',		'ữ' => 'Ữ',		'ự' => 'Ự',		'ỳ' => 'Ỳ',
+		'ỵ' => 'Ỵ',		'ỷ' => 'Ỷ',		'ỹ' => 'Ỹ',		'ἀ' => 'Ἀ',
+		'ἁ' => 'Ἁ',		'ἂ' => 'Ἂ',		'ἃ' => 'Ἃ',		'ἄ' => 'Ἄ',
+		'ἅ' => 'Ἅ',		'ἆ' => 'Ἆ',		'ἇ' => 'Ἇ',		'ἐ' => 'Ἐ',
+		'ἑ' => 'Ἑ',		'ἒ' => 'Ἒ',		'ἓ' => 'Ἓ',		'ἔ' => 'Ἔ',
+		'ἕ' => 'Ἕ',		'ἠ' => 'Ἠ',		'ἡ' => 'Ἡ',		'ἢ' => 'Ἢ',
+		'ἣ' => 'Ἣ',		'ἤ' => 'Ἤ',		'ἥ' => 'Ἥ',		'ἦ' => 'Ἦ',
+		'ἧ' => 'Ἧ',		'ἰ' => 'Ἰ',		'ἱ' => 'Ἱ',		'ἲ' => 'Ἲ',
+		'ἳ' => 'Ἳ',		'ἴ' => 'Ἴ',		'ἵ' => 'Ἵ',		'ἶ' => 'Ἶ',
+		'ἷ' => 'Ἷ',		'ὀ' => 'Ὀ',		'ὁ' => 'Ὁ',		'ὂ' => 'Ὂ',
+		'ὃ' => 'Ὃ',		'ὄ' => 'Ὄ',		'ὅ' => 'Ὅ',		'ὐ' => 'ὐ',
+		'ὒ' => 'ὒ',	'ὔ' => 'ὔ',	'ὖ' => 'ὖ',		'ὑ' => 'Ὑ',
+		'ὓ' => 'Ὓ',		'ὕ' => 'Ὕ',		'ὗ' => 'Ὗ',		'ὠ' => 'Ὠ',
+		'ὡ' => 'Ὡ',		'ὢ' => 'Ὢ',		'ὣ' => 'Ὣ',		'ὤ' => 'Ὤ',
+		'ὥ' => 'Ὥ',		'ὦ' => 'Ὦ',		'ὧ' => 'Ὧ',		'ἀι' => 'ᾀ',
+		'ἁι' => 'ᾁ',	'ἂι' => 'ᾂ',	'ἃι' => 'ᾃ',	'ἄι' => 'ᾄ',
+		'ἅι' => 'ᾅ',	'ἆι' => 'ᾆ',	'ἇι' => 'ᾇ',	'ᾀ' => 'ᾈ',
+		'ᾁ' => 'ᾉ',		'ᾂ' => 'ᾊ',		'ᾃ' => 'ᾋ',		'ᾄ' => 'ᾌ',
+		'ᾅ' => 'ᾍ',		'ᾆ' => 'ᾎ',		'ᾇ' => 'ᾏ',		'ἠι' => 'ᾐ',
+		'ἡι' => 'ᾑ',	'ἢι' => 'ᾒ',	'ἣι' => 'ᾓ',	'ἤι' => 'ᾔ',
+		'ἥι' => 'ᾕ',	'ἦι' => 'ᾖ',	'ἧι' => 'ᾗ',	'ᾐ' => 'ᾘ',
+		'ᾑ' => 'ᾙ',		'ᾒ' => 'ᾚ',		'ᾓ' => 'ᾛ',		'ᾔ' => 'ᾜ',
+		'ᾕ' => 'ᾝ',		'ᾖ' => 'ᾞ',		'ᾗ' => 'ᾟ',		'ὠι' => 'ᾠ',
+		'ὡι' => 'ᾡ',	'ὢι' => 'ᾢ',	'ὣι' => 'ᾣ',	'ὤι' => 'ᾤ',
+		'ὥι' => 'ᾥ',	'ὦι' => 'ᾦ',	'ὧι' => 'ᾧ',	'ᾠ' => 'ᾨ',
+		'ᾡ' => 'ᾩ',		'ᾢ' => 'ᾪ',		'ᾣ' => 'ᾫ',		'ᾤ' => 'ᾬ',
+		'ᾥ' => 'ᾭ',		'ᾦ' => 'ᾮ',		'ᾧ' => 'ᾯ',		'ὰι' => 'ᾲ',
+		'αι' => 'ᾳ',	'άι' => 'ᾴ',	'ᾶ' => 'ᾶ',		'ᾶι' => 'ᾷ',
+		'ᾰ' => 'Ᾰ',		'ᾱ' => 'Ᾱ',		'ὰ' => 'Ὰ',		'ά' => 'Ά',
+		'ᾳ' => 'ᾼ',		'ι' => 'ι',		'ὴι' => 'ῂ',	'ηι' => 'ῃ',
+		'ήι' => 'ῄ',	'ῆ' => 'ῆ',		'ῆι' => 'ῇ',	'ὲ' => 'Ὲ',
+		'έ' => 'Έ',		'ὴ' => 'Ὴ',		'ή' => 'Ή',		'ῃ' => 'ῌ',
+		'ῒ' => 'ῒ',	'ΐ' => 'ΐ',	'ῖ' => 'ῖ',		'ῗ' => 'ῗ',
+		'ῐ' => 'Ῐ',		'ῑ' => 'Ῑ',		'ὶ' => 'Ὶ',		'ί' => 'Ί',
+		'ῢ' => 'ῢ',	'ΰ' => 'ΰ',	'ῤ' => 'ῤ',		'ῦ' => 'ῦ',
+		'ῧ' => 'ῧ',		'ῠ' => 'Ῠ',		'ῡ' => 'Ῡ',		'ὺ' => 'Ὺ',
+		'ύ' => 'Ύ',		'ῥ' => 'Ῥ',		'ὼι' => 'ῲ',	'ωι' => 'ῳ',
+		'ώι' => 'ῴ',	'ῶ' => 'ῶ',		'ῶι' => 'ῷ',	'ὸ' => 'Ὸ',
+		'ό' => 'Ό',		'ὼ' => 'Ὼ',		'ώ' => 'Ώ',		'ῳ' => 'ῼ',
+		'ω' => 'Ω',		'k' => 'K',		'å' => 'Å',		'ⅎ' => 'Ⅎ',
+		'ⅰ' => 'Ⅰ',		'ⅱ' => 'Ⅱ',		'ⅲ' => 'Ⅲ',		'ⅳ' => 'Ⅳ',
+		'ⅴ' => 'Ⅴ',		'ⅵ' => 'Ⅵ',		'ⅶ' => 'Ⅶ',		'ⅷ' => 'Ⅷ',
+		'ⅸ' => 'Ⅸ',		'ⅹ' => 'Ⅹ',		'ⅺ' => 'Ⅺ',		'ⅻ' => 'Ⅻ',
+		'ⅼ' => 'Ⅼ',		'ⅽ' => 'Ⅽ',		'ⅾ' => 'Ⅾ',		'ⅿ' => 'Ⅿ',
+		'ↄ' => 'Ↄ',		'ⓐ' => 'Ⓐ',		'ⓑ' => 'Ⓑ',		'ⓒ' => 'Ⓒ',
+		'ⓓ' => 'Ⓓ',		'ⓔ' => 'Ⓔ',		'ⓕ' => 'Ⓕ',		'ⓖ' => 'Ⓖ',
+		'ⓗ' => 'Ⓗ',		'ⓘ' => 'Ⓘ',		'ⓙ' => 'Ⓙ',		'ⓚ' => 'Ⓚ',
+		'ⓛ' => 'Ⓛ',		'ⓜ' => 'Ⓜ',		'ⓝ' => 'Ⓝ',		'ⓞ' => 'Ⓞ',
+		'ⓟ' => 'Ⓟ',		'ⓠ' => 'Ⓠ',		'ⓡ' => 'Ⓡ',		'ⓢ' => 'Ⓢ',
+		'ⓣ' => 'Ⓣ',		'ⓤ' => 'Ⓤ',		'ⓥ' => 'Ⓥ',		'ⓦ' => 'Ⓦ',
+		'ⓧ' => 'Ⓧ',		'ⓨ' => 'Ⓨ',		'ⓩ' => 'Ⓩ',		'ⰰ' => 'Ⰰ',
+		'ⰱ' => 'Ⰱ',		'ⰲ' => 'Ⰲ',		'ⰳ' => 'Ⰳ',		'ⰴ' => 'Ⰴ',
+		'ⰵ' => 'Ⰵ',		'ⰶ' => 'Ⰶ',		'ⰷ' => 'Ⰷ',		'ⰸ' => 'Ⰸ',
+		'ⰹ' => 'Ⰹ',		'ⰺ' => 'Ⰺ',		'ⰻ' => 'Ⰻ',		'ⰼ' => 'Ⰼ',
+		'ⰽ' => 'Ⰽ',		'ⰾ' => 'Ⰾ',		'ⰿ' => 'Ⰿ',		'ⱀ' => 'Ⱀ',
+		'ⱁ' => 'Ⱁ',		'ⱂ' => 'Ⱂ',		'ⱃ' => 'Ⱃ',		'ⱄ' => 'Ⱄ',
+		'ⱅ' => 'Ⱅ',		'ⱆ' => 'Ⱆ',		'ⱇ' => 'Ⱇ',		'ⱈ' => 'Ⱈ',
+		'ⱉ' => 'Ⱉ',		'ⱊ' => 'Ⱊ',		'ⱋ' => 'Ⱋ',		'ⱌ' => 'Ⱌ',
+		'ⱍ' => 'Ⱍ',		'ⱎ' => 'Ⱎ',		'ⱏ' => 'Ⱏ',		'ⱐ' => 'Ⱐ',
+		'ⱑ' => 'Ⱑ',		'ⱒ' => 'Ⱒ',		'ⱓ' => 'Ⱓ',		'ⱔ' => 'Ⱔ',
+		'ⱕ' => 'Ⱕ',		'ⱖ' => 'Ⱖ',		'ⱗ' => 'Ⱗ',		'ⱘ' => 'Ⱘ',
+		'ⱙ' => 'Ⱙ',		'ⱚ' => 'Ⱚ',		'ⱛ' => 'Ⱛ',		'ⱜ' => 'Ⱜ',
+		'ⱝ' => 'Ⱝ',		'ⱞ' => 'Ⱞ',		'ⱡ' => 'Ⱡ',		'ɫ' => 'Ɫ',
+		'ᵽ' => 'Ᵽ',		'ɽ' => 'Ɽ',		'ⱨ' => 'Ⱨ',		'ⱪ' => 'Ⱪ',
+		'ⱬ' => 'Ⱬ',		'ⱶ' => 'Ⱶ',		'ⲁ' => 'Ⲁ',		'ⲃ' => 'Ⲃ',
+		'ⲅ' => 'Ⲅ',		'ⲇ' => 'Ⲇ',		'ⲉ' => 'Ⲉ',		'ⲋ' => 'Ⲋ',
+		'ⲍ' => 'Ⲍ',		'ⲏ' => 'Ⲏ',		'ⲑ' => 'Ⲑ',		'ⲓ' => 'Ⲓ',
+		'ⲕ' => 'Ⲕ',		'ⲗ' => 'Ⲗ',		'ⲙ' => 'Ⲙ',		'ⲛ' => 'Ⲛ',
+		'ⲝ' => 'Ⲝ',		'ⲟ' => 'Ⲟ',		'ⲡ' => 'Ⲡ',		'ⲣ' => 'Ⲣ',
+		'ⲥ' => 'Ⲥ',		'ⲧ' => 'Ⲧ',		'ⲩ' => 'Ⲩ',		'ⲫ' => 'Ⲫ',
+		'ⲭ' => 'Ⲭ',		'ⲯ' => 'Ⲯ',		'ⲱ' => 'Ⲱ',		'ⲳ' => 'Ⲳ',
+		'ⲵ' => 'Ⲵ',		'ⲷ' => 'Ⲷ',		'ⲹ' => 'Ⲹ',		'ⲻ' => 'Ⲻ',
+		'ⲽ' => 'Ⲽ',		'ⲿ' => 'Ⲿ',		'ⳁ' => 'Ⳁ',		'ⳃ' => 'Ⳃ',
+		'ⳅ' => 'Ⳅ',		'ⳇ' => 'Ⳇ',		'ⳉ' => 'Ⳉ',		'ⳋ' => 'Ⳋ',
+		'ⳍ' => 'Ⳍ',		'ⳏ' => 'Ⳏ',		'ⳑ' => 'Ⳑ',		'ⳓ' => 'Ⳓ',
+		'ⳕ' => 'Ⳕ',		'ⳗ' => 'Ⳗ',		'ⳙ' => 'Ⳙ',		'ⳛ' => 'Ⳛ',
+		'ⳝ' => 'Ⳝ',		'ⳟ' => 'Ⳟ',		'ⳡ' => 'Ⳡ',		'ⳣ' => 'Ⳣ',
+		'ff' => 'ff',	'fi' => 'fi',	'fl' => 'fl',	'ffi' => 'ffi',
+		'ffl' => 'ffl',	'st' => 'ſt',	'st' => 'st',	'մն' => 'ﬓ',
+		'մե' => 'ﬔ',	'մի' => 'ﬕ',	'վն' => 'ﬖ',	'մխ' => 'ﬗ',
+		'a' => 'A',		'b' => 'B',		'c' => 'C',		'd' => 'D',
+		'e' => 'E',		'f' => 'F',		'g' => 'G',		'h' => 'H',
+		'i' => 'I',		'j' => 'J',		'k' => 'K',		'l' => 'L',
+		'm' => 'M',	'n' => 'N',		'o' => 'O',		'p' => 'P',
+		'q' => 'Q',		'r' => 'R',		's' => 'S',		't' => 'T',
+		'u' => 'U',		'v' => 'V',		'w' => 'W',	'x' => 'X',
+		'y' => 'Y',		'z' => 'Z',		'𐐨' => '𐐀',	'𐐩' => '𐐁',
+		'𐐪' => '𐐂',	'𐐫' => '𐐃',	'𐐬' => '𐐄',	'𐐭' => '𐐅',
+		'𐐮' => '𐐆',	'𐐯' => '𐐇',	'𐐰' => '𐐈',	'𐐱' => '𐐉',
+		'𐐲' => '𐐊',	'𐐳' => '𐐋',	'𐐴' => '𐐌',	'𐐵' => '𐐍',
+		'𐐶' => '𐐎',	'𐐷' => '𐐏',	'𐐸' => '𐐐',	'𐐹' => '𐐑',
+		'𐐺' => '𐐒',	'𐐻' => '𐐓',	'𐐼' => '𐐔',	'𐐽' => '𐐕',
+		'𐐾' => '𐐖',	'𐐿' => '𐐗',	'𐑀' => '𐐘',	'𐑁' => '𐐙',
+		'𐑂' => '𐐚',	'𐑃' => '𐐛',	'𐑄' => '𐐜',	'𐑅' => '𐐝',
+		'𐑆' => '𐐞',	'𐑇' => '𐐟',	'𐑈' => '𐐠',	'𐑉' => '𐐡',
+		'𐑊' => '𐐢',	'𐑋' => '𐐣',	'𐑌' => '𐐤',	'𐑍' => '𐐥',
+		'𐐦' => '𐑎',	'𐐧' => '𐑏',
+	);
+
+	return strtr($string, $case_folding);
+}
+
+/**
+ * Fixes corrupted serialized strings after a character set conversion.
+ */
+function fix_serialized_columns()
+{
+	global $smcFunc;
+
+	$request = $smcFunc['db_query']('', '
+		SELECT id_action, extra
+		FROM {db_prefix}log_actions
+		WHERE action IN ({string:remove}, {string:delete})',
+		array(
+			'remove' => 'remove',
+			'delete' => 'delete',
+		)
+	);
+	while ($row = $smcFunc['db_fetch_assoc']($request))
+	{
+		if (@unserialize($row['extra']) === false && preg_match('~^(a:3:{s:5:"topic";i:\d+;s:7:"subject";s:)(\d+):"(.+)"(;s:6:"member";s:5:"\d+";})$~', $row['extra'], $matches) === 1)
+			$smcFunc['db_query']('', '
+				UPDATE {db_prefix}log_actions
+				SET extra = {string:extra}
+				WHERE id_action = {int:current_action}',
+				array(
+					'current_action' => $row['id_action'],
+					'extra' => $matches[1] . strlen($matches[3]) . ':"' . $matches[3] . '"' . $matches[4],
+				)
+			);
+	}
+	$smcFunc['db_free_result']($request);
+
+	// Refresh some cached data.
+	updateSettings(array(
+		'memberlist_updated' => time(),
+	));
+
+}
+
 ?>

+ 1 - 1
Sources/Subs-Compat.php

@@ -10,7 +10,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/Subs-Db-mysql.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 839 - 0
Sources/Subs-Db-mysqli.php

@@ -0,0 +1,839 @@
+<?php
+
+/**
+ * This file has all the main functions in it that relate to the database.
+ *
+ * Simple Machines Forum (SMF)
+ *
+ * @package SMF
+ * @author Simple Machines http://www.simplemachines.org
+ * @copyright 2012 Simple Machines
+ * @license http://www.simplemachines.org/about/smf/license.php BSD
+ *
+ * @version 2.1 Alpha 1
+ */
+
+if (!defined('SMF'))
+	die('No direct access...');
+
+/**
+ *  Maps the implementations in this file (smf_db_function_name)
+ *  to the $smcFunc['db_function_name'] variable.
+ *
+ * @param string $db_server
+ * @param string $db_name
+ * @param string $db_user
+ * @param string $db_passwd
+ * @param string $db_prefix
+ * @param array $db_options
+ * @return null
+ */
+function smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $db_options = array())
+{
+	global $smcFunc, $mysql_set_mode;
+
+	// Map some database specific functions, only do this once.
+	if (!isset($smcFunc['db_fetch_assoc']) || $smcFunc['db_fetch_assoc'] != 'mysqli_fetch_assoc')
+		$smcFunc += array(
+			'db_query'                  => 'smf_db_query',
+			'db_quote'                  => 'smf_db_quote',
+			'db_fetch_assoc'            => 'mysqli_fetch_assoc',
+			'db_fetch_row'              => 'mysqli_fetch_row',
+			'db_free_result'            => 'mysqli_free_result',
+			'db_insert'                 => 'smf_db_insert',
+			'db_insert_id'              => 'smf_db_insert_id',
+			'db_num_rows'               => 'mysqli_num_rows',
+			'db_data_seek'              => 'mysqli_data_seek',
+			'db_num_fields'             => 'mysqli_num_fields',
+			'db_escape_string'          => 'addslashes',
+			'db_unescape_string'        => 'stripslashes',
+			'db_server_info'            => 'smf_db_get_server_info',
+			'db_affected_rows'          => 'smf_db_affected_rows',
+			'db_transaction'            => 'smf_db_transaction',
+			'db_error'                  => 'mysqli_error',
+			'db_select_db'              => 'smf_db_select',
+			'db_title'                  => 'MySQLi',
+			'db_sybase'                 => false,
+			'db_case_sensitive'         => false,
+			'db_escape_wildcard_string' => 'smf_db_escape_wildcard_string',
+		);
+
+	if (!empty($db_options['persist']))
+		$connection = @mysqli_connect('p:' . $db_server, $db_user, $db_passwd);
+	else
+		$connection = @mysqli_connect($db_server, $db_user, $db_passwd);
+
+	// Something's wrong, show an error if its fatal (which we assume it is)
+	if (!$connection)
+	{
+		if (!empty($db_options['non_fatal']))
+			return null;
+		else
+			display_db_error();
+	}
+
+	// Select the database, unless told not to
+	if (empty($db_options['dont_select_db']) && !@mysqli_select_db($connection, $db_name) && empty($db_options['non_fatal']))
+		display_db_error();
+
+	// This makes it possible to have SMF automatically change the sql_mode and autocommit if needed.
+	if (isset($mysql_set_mode) && $mysql_set_mode === true)
+		$smcFunc['db_query']('', 'SET sql_mode = \'\', AUTOCOMMIT = 1',
+		array(),
+		false
+	);
+
+	return $connection;
+}
+
+/**
+ * Extend the database functionality. It calls the respective file's init
+ * to add the implementations in that file to $smcFunc array.
+ *
+ * @param string $type indicated which additional file to load. ('extra', 'packages')
+ */
+function db_extend($type = 'extra')
+{
+	global $sourcedir;
+
+	// we force the MySQL files as nothing syntactically changes with MySQLi
+	require_once($sourcedir . '/Db' . strtoupper($type[0]) . substr($type, 1) . '-mysql.php');
+	$initFunc = 'db_' . $type . '_init';
+	$initFunc();
+}
+
+/**
+ * Fix up the prefix so it doesn't require the database to be selected.
+ *
+ * @param string &db_prefix
+ * @param string $db_name
+ */
+function db_fix_prefix(&$db_prefix, $db_name)
+{
+	$db_prefix = is_numeric(substr($db_prefix, 0, 1)) ? $db_name . '.' . $db_prefix : '`' . $db_name . '`.' . $db_prefix;
+}
+
+/**
+ * Wrap mysqli_select_db so the connection does not need to be specified
+ *
+ * @param string &database
+ * @param object $connection
+ */
+function smf_db_select($database, $connection = null)
+{
+	global $db_connection;
+	return mysqli_select_db($connection === null ? $db_connection : $connection, $database);
+}
+
+/**
+ * Wrap mysqli_get_server_info so the connection does not need to be specified
+ *
+ * @param object $connection
+ */
+function smf_db_get_server_info($connection = null)
+{
+	global $db_connection;
+	return mysqli_get_server_info($connection === null ? $db_connection : $connection);
+}
+
+/**
+ * Callback for preg_replace_callback on the query.
+ * It allows to replace on the fly a few pre-defined strings, for convenience ('query_see_board', 'query_wanna_see_board'), with
+ * their current values from $user_info.
+ * In addition, it performs checks and sanitization on the values sent to the database.
+ *
+ * @param $matches
+ */
+function smf_db_replacement__callback($matches)
+{
+	global $db_callback, $user_info, $db_prefix;
+
+	list ($values, $connection) = $db_callback;
+	if (!is_object($connection))
+		display_db_error();
+
+	if ($matches[1] === 'db_prefix')
+		return $db_prefix;
+
+	if ($matches[1] === 'query_see_board')
+		return $user_info['query_see_board'];
+
+	if ($matches[1] === 'query_wanna_see_board')
+		return $user_info['query_wanna_see_board'];
+
+	if (!isset($matches[2]))
+		smf_db_error_backtrace('Invalid value inserted or no type specified.', '', E_USER_ERROR, __FILE__, __LINE__);
+
+	if (!isset($values[$matches[2]]))
+		smf_db_error_backtrace('The database value you\'re trying to insert does not exist: ' . htmlspecialchars($matches[2]), '', E_USER_ERROR, __FILE__, __LINE__);
+
+	$replacement = $values[$matches[2]];
+
+	switch ($matches[1])
+	{
+		case 'int':
+			if (!is_numeric($replacement) || (string) $replacement !== (string) (int) $replacement)
+				smf_db_error_backtrace('Wrong value type sent to the database. Integer expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
+			return (string) (int) $replacement;
+		break;
+
+		case 'string':
+		case 'text':
+			return sprintf('\'%1$s\'', mysqli_real_escape_string($connection, $replacement));
+		break;
+
+		case 'array_int':
+			if (is_array($replacement))
+			{
+				if (empty($replacement))
+					smf_db_error_backtrace('Database error, given array of integer values is empty. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
+
+				foreach ($replacement as $key => $value)
+				{
+					if (!is_numeric($value) || (string) $value !== (string) (int) $value)
+						smf_db_error_backtrace('Wrong value type sent to the database. Array of integers expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
+
+					$replacement[$key] = (string) (int) $value;
+				}
+
+				return implode(', ', $replacement);
+			}
+			else
+				smf_db_error_backtrace('Wrong value type sent to the database. Array of integers expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
+
+		break;
+
+		case 'array_string':
+			if (is_array($replacement))
+			{
+				if (empty($replacement))
+					smf_db_error_backtrace('Database error, given array of string values is empty. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
+
+				foreach ($replacement as $key => $value)
+					$replacement[$key] = sprintf('\'%1$s\'', mysqli_real_escape_string($connection, $value));
+
+				return implode(', ', $replacement);
+			}
+			else
+				smf_db_error_backtrace('Wrong value type sent to the database. Array of strings expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
+		break;
+
+		case 'date':
+			if (preg_match('~^(\d{4})-([0-1]?\d)-([0-3]?\d)$~', $replacement, $date_matches) === 1)
+				return sprintf('\'%04d-%02d-%02d\'', $date_matches[1], $date_matches[2], $date_matches[3]);
+			else
+				smf_db_error_backtrace('Wrong value type sent to the database. Date expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
+		break;
+
+		case 'float':
+			if (!is_numeric($replacement))
+				smf_db_error_backtrace('Wrong value type sent to the database. Floating point number expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__);
+			return (string) (float) $replacement;
+		break;
+
+		case 'identifier':
+			// Backticks inside identifiers are supported as of MySQL 4.1. We don't need them for SMF.
+			return '`' . strtr($replacement, array('`' => '', '.' => '')) . '`';
+		break;
+
+		case 'raw':
+			return $replacement;
+		break;
+
+		default:
+			smf_db_error_backtrace('Undefined type used in the database query. (' . $matches[1] . ':' . $matches[2] . ')', '', false, __FILE__, __LINE__);
+		break;
+	}
+}
+
+/**
+ * Just like the db_query, escape and quote a string, but not executing the query.
+ *
+ * @param string $db_string
+ * @param array $db_values
+ * @param object $connection = null
+ */
+function smf_db_quote($db_string, $db_values, $connection = null)
+{
+	global $db_callback, $db_connection;
+
+	// Only bother if there's something to replace.
+	if (strpos($db_string, '{') !== false)
+	{
+		// This is needed by the callback function.
+		$db_callback = array($db_values, $connection === null ? $db_connection : $connection);
+
+		// Do the quoting and escaping
+		$db_string = preg_replace_callback('~{([a-z_]+)(?::([a-zA-Z0-9_-]+))?}~', 'smf_db_replacement__callback', $db_string);
+
+		// Clear this global variable.
+		$db_callback = array();
+	}
+
+	return $db_string;
+}
+
+/**
+ * Do a query.  Takes care of errors too.
+ *
+ * @param string $identifier
+ * @param string $db_string
+ * @param array $db_values = array()
+ * @param object $connection = null
+ */
+function smf_db_query($identifier, $db_string, $db_values = array(), $connection = null)
+{
+	global $db_cache, $db_count, $db_connection, $db_show_debug, $time_start;
+	global $db_unbuffered, $db_callback, $modSettings;
+
+	// Comments that are allowed in a query are preg_removed.
+	static $allowed_comments_from = array(
+		'~\s+~s',
+		'~/\*!40001 SQL_NO_CACHE \*/~',
+		'~/\*!40000 USE INDEX \([A-Za-z\_]+?\) \*/~',
+		'~/\*!40100 ON DUPLICATE KEY UPDATE id_msg = \d+ \*/~',
+	);
+	static $allowed_comments_to = array(
+		' ',
+		'',
+		'',
+		'',
+	);
+
+	// Decide which connection to use.
+	$connection = $connection === null ? $db_connection : $connection;
+
+	// Get a connection if we are shutting down, sometimes the link is closed before sessions are written
+        if (!is_object($connection))
+	{
+		global $db_server, $db_user, $db_passwd, $db_name, $db_show_debug, $ssi_db_user, $ssi_db_passwd;
+
+		// Are we in SSI mode?  If so try that username and password first
+		if (SMF == 'SSI' && !empty($ssi_db_user) && !empty($ssi_db_passwd))
+		{
+			if (empty($db_persist))
+				$db_connection = @mysqli_connect($db_server, $ssi_db_user, $ssi_db_passwd);
+			else
+				$db_connection = @mysqli_connect('p:' . $db_server, $ssi_db_user, $ssi_db_passwd);
+		}
+		// Fall back to the regular username and password if need be
+		if (!$db_connection)
+		{
+			if (empty($db_persist))
+				$db_connection = @mysqli_connect($db_server, $db_user, $db_passwd);
+			else
+				$db_connection = @mysqli_connect('p:' . $db_server, $db_user, $db_passwd);
+		}
+
+		if (!$db_connection || !@mysqli_select_db($db_connection, $db_name))
+			$db_connection = false;
+
+		$connection = $db_connection;
+	}
+
+	// One more query....
+	$db_count = !isset($db_count) ? 1 : $db_count + 1;
+
+	if (empty($modSettings['disableQueryCheck']) && strpos($db_string, '\'') !== false && empty($db_values['security_override']))
+		smf_db_error_backtrace('Hacking attempt...', 'Illegal character (\') used in query...', true, __FILE__, __LINE__);
+
+	// Use "ORDER BY null" to prevent Mysql doing filesorts for Group By clauses without an Order By
+	if (strpos($db_string, 'GROUP BY') !== false && strpos($db_string, 'ORDER BY') === false && strpos($db_string, 'INSERT INTO') === false)
+	{
+		// Add before LIMIT
+		if ($pos = strpos($db_string, 'LIMIT '))
+			$db_string = substr($db_string, 0, $pos) . "\t\t\tORDER BY null\n" . substr($db_string, $pos, strlen($db_string));
+		else
+			// Append it.
+			$db_string .= "\n\t\t\tORDER BY null";
+	}
+
+	if (empty($db_values['security_override']) && (!empty($db_values) || strpos($db_string, '{db_prefix}') !== false))
+	{
+		// Pass some values to the global space for use in the callback function.
+		$db_callback = array($db_values, $connection);
+
+		// Inject the values passed to this function.
+		$db_string = preg_replace_callback('~{([a-z_]+)(?::([a-zA-Z0-9_-]+))?}~', 'smf_db_replacement__callback', $db_string);
+
+		// This shouldn't be residing in global space any longer.
+		$db_callback = array();
+	}
+
+	// Debugging.
+	if (isset($db_show_debug) && $db_show_debug === true)
+	{
+		// Get the file and line number this function was called.
+		list ($file, $line) = smf_db_error_backtrace('', '', 'return', __FILE__, __LINE__);
+
+		// Initialize $db_cache if not already initialized.
+		if (!isset($db_cache))
+			$db_cache = array();
+
+		if (!empty($_SESSION['debug_redirect']))
+		{
+			$db_cache = array_merge($_SESSION['debug_redirect'], $db_cache);
+			$db_count = count($db_cache) + 1;
+			$_SESSION['debug_redirect'] = array();
+		}
+
+		// Don't overload it.
+		$st = microtime();
+		$db_cache[$db_count]['q'] = $db_count < 50 ? $db_string : '...';
+		$db_cache[$db_count]['f'] = $file;
+		$db_cache[$db_count]['l'] = $line;
+		$db_cache[$db_count]['s'] = array_sum(explode(' ', $st)) - array_sum(explode(' ', $time_start));
+	}
+
+	// First, we clean strings out of the query, reduce whitespace, lowercase, and trim - so we can check it over.
+	if (empty($modSettings['disableQueryCheck']))
+	{
+		$clean = '';
+		$old_pos = 0;
+		$pos = -1;
+		while (true)
+		{
+			$pos = strpos($db_string, '\'', $pos + 1);
+			if ($pos === false)
+				break;
+			$clean .= substr($db_string, $old_pos, $pos - $old_pos);
+
+			while (true)
+			{
+				$pos1 = strpos($db_string, '\'', $pos + 1);
+				$pos2 = strpos($db_string, '\\', $pos + 1);
+				if ($pos1 === false)
+					break;
+				elseif ($pos2 == false || $pos2 > $pos1)
+				{
+					$pos = $pos1;
+					break;
+				}
+
+				$pos = $pos2 + 1;
+			}
+			$clean .= ' %s ';
+
+			$old_pos = $pos + 1;
+		}
+		$clean .= substr($db_string, $old_pos);
+		$clean = trim(strtolower(preg_replace($allowed_comments_from, $allowed_comments_to, $clean)));
+
+		// We don't use UNION in SMF, at least so far.  But it's useful for injections.
+		if (strpos($clean, 'union') !== false && preg_match('~(^|[^a-z])union($|[^[a-z])~s', $clean) != 0)
+			$fail = true;
+		// Comments?  We don't use comments in our queries, we leave 'em outside!
+		elseif (strpos($clean, '/*') > 2 || strpos($clean, '--') !== false || strpos($clean, ';') !== false)
+			$fail = true;
+		// Trying to change passwords, slow us down, or something?
+		elseif (strpos($clean, 'sleep') !== false && preg_match('~(^|[^a-z])sleep($|[^[_a-z])~s', $clean) != 0)
+			$fail = true;
+		elseif (strpos($clean, 'benchmark') !== false && preg_match('~(^|[^a-z])benchmark($|[^[a-z])~s', $clean) != 0)
+			$fail = true;
+		// Sub selects?  We don't use those either.
+		elseif (preg_match('~\([^)]*?select~s', $clean) != 0)
+			$fail = true;
+
+		if (!empty($fail) && function_exists('log_error'))
+			smf_db_error_backtrace('Hacking attempt...', 'Hacking attempt...' . "\n" . $db_string, E_USER_ERROR, __FILE__, __LINE__);
+	}
+
+	if (empty($db_unbuffered))
+		$ret = @mysqli_query($connection, $db_string);
+	else
+		$ret = @mysqli_query($connection, $db_string, MYSQLI_USE_RESULT);
+
+	if ($ret === false && empty($db_values['db_error_skip']))
+		$ret = smf_db_error($db_string, $connection);
+
+	// Debugging.
+	if (isset($db_show_debug) && $db_show_debug === true)
+		$db_cache[$db_count]['t'] = array_sum(explode(' ', microtime())) - array_sum(explode(' ', $st));
+
+	return $ret;
+}
+
+/**
+ * affected_rows
+ * @param object $connection
+ */
+function smf_db_affected_rows($connection = null)
+{
+	global $db_connection;
+
+	return mysqli_affected_rows($connection === null ? $db_connection : $connection);
+}
+
+/**
+ * insert_id
+ *
+ * @param string $table
+ * @param string $field = null
+ * @param object $connection = null
+ */
+function smf_db_insert_id($table, $field = null, $connection = null)
+{
+	global $db_connection, $db_prefix;
+
+	$table = str_replace('{db_prefix}', $db_prefix, $table);
+
+	// MySQL doesn't need the table or field information.
+	return mysqli_insert_id($connection === null ? $db_connection : $connection);
+}
+
+/**
+ * Do a transaction.
+ *
+ * @param string $type - the step to perform (i.e. 'begin', 'commit', 'rollback')
+ * @param object $connection = null
+ */
+function smf_db_transaction($type = 'commit', $connection = null)
+{
+	global $db_connection;
+
+	// Decide which connection to use
+	$connection = $connection === null ? $db_connection : $connection;
+
+	if ($type == 'begin')
+		return @mysqli_query($connection, 'BEGIN');
+	elseif ($type == 'rollback')
+		return @mysqli_query($connection, 'ROLLBACK');
+	elseif ($type == 'commit')
+		return @mysqli_query($connection, 'COMMIT');
+
+	return false;
+}
+
+/**
+ * Database error!
+ * Backtrace, log, try to fix.
+ *
+ * @param string $db_string
+ * @param object $connection = null
+ */
+function smf_db_error($db_string, $connection = null)
+{
+	global $txt, $context, $sourcedir, $webmaster_email, $modSettings;
+	global $forum_version, $db_connection, $db_last_error, $db_persist;
+	global $db_server, $db_user, $db_passwd, $db_name, $db_show_debug, $ssi_db_user, $ssi_db_passwd;
+	global $smcFunc;
+
+	// Get the file and line numbers.
+	list ($file, $line) = smf_db_error_backtrace('', '', 'return', __FILE__, __LINE__);
+
+	// Decide which connection to use
+	$connection = $connection === null ? $db_connection : $connection;
+
+	// This is the error message...
+	$query_error = mysqli_error($connection);
+	$query_errno = mysqli_errno($connection);
+
+	// Error numbers:
+	//    1016: Can't open file '....MYI'
+	//    1030: Got error ??? from table handler.
+	//    1034: Incorrect key file for table.
+	//    1035: Old key file for table.
+	//    1205: Lock wait timeout exceeded.
+	//    1213: Deadlock found.
+	//    2006: Server has gone away.
+	//    2013: Lost connection to server during query.
+
+	// Log the error.
+	if ($query_errno != 1213 && $query_errno != 1205 && function_exists('log_error'))
+		log_error($txt['database_error'] . ': ' . $query_error . (!empty($modSettings['enableErrorQueryLogging']) ? "\n\n$db_string" : ''), 'database', $file, $line);
+
+	// Database error auto fixing ;).
+	if (function_exists('cache_get_data') && (!isset($modSettings['autoFixDatabase']) || $modSettings['autoFixDatabase'] == '1'))
+	{
+		// Force caching on, just for the error checking.
+		$old_cache = @$modSettings['cache_enable'];
+		$modSettings['cache_enable'] = '1';
+
+		if (($temp = cache_get_data('db_last_error', 600)) !== null)
+			$db_last_error = max(@$db_last_error, $temp);
+
+		if (@$db_last_error < time() - 3600 * 24 * 3)
+		{
+			// We know there's a problem... but what?  Try to auto detect.
+			if ($query_errno == 1030 && strpos($query_error, ' 127 ') !== false)
+			{
+				preg_match_all('~(?:[\n\r]|^)[^\']+?(?:FROM|JOIN|UPDATE|TABLE) ((?:[^\n\r(]+?(?:, )?)*)~s', $db_string, $matches);
+
+				$fix_tables = array();
+				foreach ($matches[1] as $tables)
+				{
+					$tables = array_unique(explode(',', $tables));
+					foreach ($tables as $table)
+					{
+						// Now, it's still theoretically possible this could be an injection.  So backtick it!
+						if (trim($table) != '')
+							$fix_tables[] = '`' . strtr(trim($table), array('`' => '')) . '`';
+					}
+				}
+
+				$fix_tables = array_unique($fix_tables);
+			}
+			// Table crashed.  Let's try to fix it.
+			elseif ($query_errno == 1016)
+			{
+				if (preg_match('~\'([^\.\']+)~', $query_error, $match) != 0)
+					$fix_tables = array('`' . $match[1] . '`');
+			}
+			// Indexes crashed.  Should be easy to fix!
+			elseif ($query_errno == 1034 || $query_errno == 1035)
+			{
+				preg_match('~\'([^\']+?)\'~', $query_error, $match);
+				$fix_tables = array('`' . $match[1] . '`');
+			}
+		}
+
+		// Check for errors like 145... only fix it once every three days, and send an email. (can't use empty because it might not be set yet...)
+		if (!empty($fix_tables))
+		{
+			// Subs-Admin.php for updateSettingsFile(), Subs-Post.php for sendmail().
+			require_once($sourcedir . '/Subs-Admin.php');
+			require_once($sourcedir . '/Subs-Post.php');
+
+			// Make a note of the REPAIR...
+			cache_put_data('db_last_error', time(), 600);
+			if (($temp = cache_get_data('db_last_error', 600)) === null)
+				updateSettingsFile(array('db_last_error' => time()));
+
+			// Attempt to find and repair the broken table.
+			foreach ($fix_tables as $table)
+				$smcFunc['db_query']('', "
+					REPAIR TABLE $table", false, false);
+
+			// And send off an email!
+			sendmail($webmaster_email, $txt['database_error'], $txt['tried_to_repair']);
+
+			$modSettings['cache_enable'] = $old_cache;
+
+			// Try the query again...?
+			$ret = $smcFunc['db_query']('', $db_string, false, false);
+			if ($ret !== false)
+				return $ret;
+		}
+		else
+			$modSettings['cache_enable'] = $old_cache;
+
+		// Check for the "lost connection" or "deadlock found" errors - and try it just one more time.
+		if (in_array($query_errno, array(1205, 1213, 2006, 2013)))
+		{
+			if (in_array($query_errno, array(2006, 2013)) && $db_connection == $connection)
+			{
+				// Are we in SSI mode?  If so try that username and password first
+				if (SMF == 'SSI' && !empty($ssi_db_user) && !empty($ssi_db_passwd))
+				{
+					if (empty($db_persist))
+						$db_connection = @mysqli_connect($db_server, $ssi_db_user, $ssi_db_passwd);
+					else
+						$db_connection = @mysqli_connect('p:' . $db_server, $ssi_db_user, $ssi_db_passwd);
+				}
+				// Fall back to the regular username and password if need be
+				if (!$db_connection)
+				{
+					if (empty($db_persist))
+						$db_connection = @mysqli_connect($db_server, $db_user, $db_passwd);
+					else
+						$db_connection = @mysqli_connect('p:' . $db_server, $db_user, $db_passwd);
+				}
+
+				if (!$db_connection || !@mysqli_select_db($db_connection, $db_name))
+					$db_connection = false;
+			}
+
+			if ($db_connection)
+			{
+				// Try a deadlock more than once more.
+				for ($n = 0; $n < 4; $n++)
+				{
+					$ret = $smcFunc['db_query']('', $db_string, false, false);
+
+					$new_errno = mysqli_errno($db_connection);
+					if ($ret !== false || in_array($new_errno, array(1205, 1213)))
+						break;
+				}
+
+				// If it failed again, shucks to be you... we're not trying it over and over.
+				if ($ret !== false)
+					return $ret;
+			}
+		}
+		// Are they out of space, perhaps?
+		elseif ($query_errno == 1030 && (strpos($query_error, ' -1 ') !== false || strpos($query_error, ' 28 ') !== false || strpos($query_error, ' 12 ') !== false))
+		{
+			if (!isset($txt))
+				$query_error .= ' - check database storage space.';
+			else
+			{
+				if (!isset($txt['mysql_error_space']))
+					loadLanguage('Errors');
+
+				$query_error .= !isset($txt['mysql_error_space']) ? ' - check database storage space.' : $txt['mysql_error_space'];
+			}
+		}
+	}
+
+	// Nothing's defined yet... just die with it.
+	if (empty($context) || empty($txt))
+		die($query_error);
+
+	// Show an error message, if possible.
+	$context['error_title'] = $txt['database_error'];
+	if (allowedTo('admin_forum'))
+		$context['error_message'] = nl2br($query_error) . '<br />' . $txt['file'] . ': ' . $file . '<br />' . $txt['line'] . ': ' . $line;
+	else
+		$context['error_message'] = $txt['try_again'];
+
+	// A database error is often the sign of a database in need of upgrade.  Check forum versions, and if not identical suggest an upgrade... (not for Demo/CVS versions!)
+	if (allowedTo('admin_forum') && !empty($forum_version) && $forum_version != 'SMF ' . @$modSettings['smfVersion'] && strpos($forum_version, 'Demo') === false && strpos($forum_version, 'CVS') === false)
+		$context['error_message'] .= '<br /><br />' . sprintf($txt['database_error_versions'], $forum_version, $modSettings['smfVersion']);
+
+	if (allowedTo('admin_forum') && isset($db_show_debug) && $db_show_debug === true)
+	{
+		$context['error_message'] .= '<br /><br />' . nl2br($db_string);
+	}
+
+	// It's already been logged... don't log it again.
+	fatal_error($context['error_message'], false);
+}
+
+/**
+ * insert
+ *
+ * @param string $method - options 'replace', 'ignore', 'insert'
+ * @param $table
+ * @param $columns
+ * @param $data
+ * @param $keys
+ * @param bool $disable_trans = false
+ * @param object $connection = null
+ */
+function smf_db_insert($method = 'replace', $table, $columns, $data, $keys, $disable_trans = false, $connection = null)
+{
+	global $smcFunc, $db_connection, $db_prefix;
+
+	$connection = $connection === null ? $db_connection : $connection;
+
+	// With nothing to insert, simply return.
+	if (empty($data))
+		return;
+
+	// Replace the prefix holder with the actual prefix.
+	$table = str_replace('{db_prefix}', $db_prefix, $table);
+
+	// Inserting data as a single row can be done as a single array.
+	if (!is_array($data[array_rand($data)]))
+		$data = array($data);
+
+	// Create the mold for a single row insert.
+	$insertData = '(';
+	foreach ($columns as $columnName => $type)
+	{
+		// Are we restricting the length?
+		if (strpos($type, 'string-') !== false)
+			$insertData .= sprintf('SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ', $columnName);
+		else
+			$insertData .= sprintf('{%1$s:%2$s}, ', $type, $columnName);
+	}
+	$insertData = substr($insertData, 0, -2) . ')';
+
+	// Create an array consisting of only the columns.
+	$indexed_columns = array_keys($columns);
+
+	// Here's where the variables are injected to the query.
+	$insertRows = array();
+	foreach ($data as $dataRow)
+		$insertRows[] = smf_db_quote($insertData, array_combine($indexed_columns, $dataRow), $connection);
+
+	// Determine the method of insertion.
+	$queryTitle = $method == 'replace' ? 'REPLACE' : ($method == 'ignore' ? 'INSERT IGNORE' : 'INSERT');
+
+	// Do the insert.
+	$smcFunc['db_query']('', '
+		' . $queryTitle . ' INTO ' . $table . '(`' . implode('`, `', $indexed_columns) . '`)
+		VALUES
+			' . implode(',
+			', $insertRows),
+		array(
+			'security_override' => true,
+			'db_error_skip' => $table === $db_prefix . 'log_errors',
+		),
+		$connection
+	);
+}
+
+/**
+ * This function tries to work out additional error information from a back trace.
+ *
+ * @param $error_message
+ * @param $log_message
+ * @param $error_type
+ * @param $file
+ * @param $line
+ */
+function smf_db_error_backtrace($error_message, $log_message = '', $error_type = false, $file = null, $line = null)
+{
+	if (empty($log_message))
+		$log_message = $error_message;
+
+	foreach (debug_backtrace() as $step)
+	{
+		// Found it?
+		if (strpos($step['function'], 'query') === false && !in_array(substr($step['function'], 0, 7), array('smf_db_', 'preg_re', 'db_erro', 'call_us')) && strpos($step['function'], '__') !== 0)
+		{
+			$log_message .= '<br />Function: ' . $step['function'];
+			break;
+		}
+
+		if (isset($step['line']))
+		{
+			$file = $step['file'];
+			$line = $step['line'];
+		}
+	}
+
+	// A special case - we want the file and line numbers for debugging.
+	if ($error_type == 'return')
+		return array($file, $line);
+
+	// Is always a critical error.
+	if (function_exists('log_error'))
+		log_error($log_message, 'critical', $file, $line);
+
+	if (function_exists('fatal_error'))
+	{
+		fatal_error($error_message, false);
+
+		// Cannot continue...
+		exit;
+	}
+	elseif ($error_type)
+		trigger_error($error_message . ($line !== null ? '<em>(' . basename($file) . '-' . $line . ')</em>' : ''), $error_type);
+	else
+		trigger_error($error_message . ($line !== null ? '<em>(' . basename($file) . '-' . $line . ')</em>' : ''));
+}
+
+/**
+ * Escape the LIKE wildcards so that they match the character and not the wildcard.
+ *
+ * @param $string
+ * @param bool $translate_human_wildcards = false, if true, turns human readable wildcards into SQL wildcards.
+ */
+function smf_db_escape_wildcard_string($string, $translate_human_wildcards=false)
+{
+	$replacements = array(
+		'%' => '\%',
+		'_' => '\_',
+		'\\' => '\\\\',
+	);
+
+	if ($translate_human_wildcards)
+		$replacements += array(
+			'*' => '%',
+		);
+
+	return strtr($string, $replacements);
+}
+?>

+ 1 - 1
Sources/Subs-Db-postgresql.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 1 - 1
Sources/Subs-Db-sqlite.php

@@ -7,7 +7,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

+ 53 - 1
Sources/Subs-Editor.php

@@ -8,7 +8,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1
@@ -2164,6 +2164,7 @@ function AutoSuggestHandler($checkRegistered = null)
 	// These are all registered types.
 	$searchTypes = array(
 		'member' => 'Member',
+		'membergroups' => 'MemberGroups',
 		'versions' => 'SMFVersions',
 	);
 
@@ -2234,6 +2235,57 @@ function AutoSuggest_Search_Member()
 	return $xml_data;
 }
 
+/**
+ * Search for a membergroup by name
+ *
+ * @return string
+ */
+function AutoSuggest_Search_MemberGroups()
+{
+	global $txt, $smcFunc, $context;
+
+	$_REQUEST['search'] = trim($smcFunc['strtolower']($_REQUEST['search'])) . '*';
+	$_REQUEST['search'] = strtr($_REQUEST['search'], array('%' => '\%', '_' => '\_', '*' => '%', '?' => '_', '&#038;' => '&amp;'));
+
+	// Find the group.
+	// Only return groups which are not post-based and not "Hidden", but not the "Administrators" or "Moderators" groups.
+	$request = $smcFunc['db_query']('', '
+		SELECT id_group, group_name
+		FROM {db_prefix}membergroups
+		WHERE group_name LIKE {string:search}
+			AND min_posts = {int:min_posts}
+			AND id_group NOT IN ({array_int:invalid_groups})
+			AND hidden != {int:hidden}
+		',
+		array(
+			'min_posts' => -1,
+			'invalid_groups' => array(1,3),
+			'hidden' => 2,
+			'search' => $_REQUEST['search'],
+		)
+	);
+	$xml_data = array(
+		'items' => array(
+			'identifier' => 'item',
+			'children' => array(),
+		),
+	);
+	while ($row = $smcFunc['db_fetch_assoc']($request))
+	{
+		$row['group_name'] = strtr($row['group_name'], array('&amp;' => '&#038;', '&lt;' => '&#060;', '&gt;' => '&#062;', '&quot;' => '&#034;'));
+
+		$xml_data['items']['children'][] = array(
+			'attributes' => array(
+				'id' => $row['id_group'],
+			),
+			'value' => $row['group_name'],
+		);
+	}
+	$smcFunc['db_free_result']($request);
+
+	return $xml_data;
+}
+
 /**
  * Provides a list of possible SMF versions to use in emulation
  *

+ 8 - 7
Sources/Subs-Graphics.php

@@ -12,7 +12,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1
@@ -848,8 +848,8 @@ function showCodeImage($code)
 	{
 		$font_list = array($font_list[0]);
 		// Try use Screenge if we can - it looks good!
-		if (in_array('Screenge.ttf', $ttfont_list))
-			$ttfont_list = array('Screenge.ttf');
+		if (in_array('AnonymousPro.ttf', $ttfont_list))
+			$ttfont_list = array('AnonymousPro.ttf');
 		else
 			$ttfont_list = empty($ttfont_list) ? array() : array($ttfont_list[0]);
 
@@ -873,7 +873,7 @@ function showCodeImage($code)
 		$loaded_fonts[$font_index] = imageloadfont($settings['default_theme_dir'] . '/fonts/' . $font_list[$font_index]);
 
 	// Determine the dimensions of each character.
-	$total_width = $character_spacing * strlen($code) + 20;
+	$total_width = $character_spacing * strlen($code) + 40;
 	$max_height = 0;
 	foreach ($characters as $char_index => $character)
 	{
@@ -923,6 +923,7 @@ function showCodeImage($code)
 			// Can we use true type fonts?
 			$can_do_ttf = function_exists('imagettftext');
 
+
 			// How much rotation will we give?
 			if ($rotationType == 'none')
 				$angle = 0;
@@ -1112,12 +1113,12 @@ function showLetterImage($letter)
 	$random_font = $font_list[array_rand($font_list)];
 
 	// Check if the given letter exists.
-	if (!file_exists($settings['default_theme_dir'] . '/fonts/' . $random_font . '/' . $letter . '.gif'))
+	if (!file_exists($settings['default_theme_dir'] . '/fonts/' . $random_font . '/' . $letter . '.png'))
 		return false;
 
 	// Include it!
-	header('Content-type: image/gif');
-	include($settings['default_theme_dir'] . '/fonts/' . $random_font . '/' . $letter . '.gif');
+	header('Content-type: image/png');
+	include($settings['default_theme_dir'] . '/fonts/' . $random_font . '/' . $letter . '.png');
 
 	// Nothing more to come.
 	die();

+ 1 - 1
Sources/Subs-List.php

@@ -6,7 +6,7 @@
  *
  * @package SMF
  * @author Simple Machines http://www.simplemachines.org
- * @copyright 2012 Simple Machines
+ * @copyright 2013 Simple Machines and individual contributors
  * @license http://www.simplemachines.org/about/smf/license.php BSD
  *
  * @version 2.1 Alpha 1

Some files were not shown because too many files changed in this diff