diff --git a/build/index.php b/build/index.php
index 415d5f6..8abb4ac 100644
--- a/build/index.php
+++ b/build/index.php
@@ -337,6 +337,7 @@ $env->history->revision_data = false; // The revision data object from the page
 $env->user = $settings->anonymous_user_name; // The user's name
 $env->is_logged_in = false;  // Whether the user is logged in
 $env->is_admin = false; // Whether the user is an admin (moderator)
+$env->user_data = new stdClass(); // A logged in user's data
 $env->storage_prefix = $settings->data_storage_dir . DIRECTORY_SEPARATOR; // The data storage directory
 $env->perfdata = new stdClass(); // Performance data
 /// Paths ///
@@ -379,6 +380,7 @@ if(isset($_SESSION[$settings->sessionprefix . "-user"]) and
 	{
 		// The user is logged in
 		$env->is_logged_in = true;
+		$env->user_data = $settings->{$env->user};
 	}
 	else
 	{
@@ -386,7 +388,7 @@ if(isset($_SESSION[$settings->sessionprefix . "-user"]) and
 		// Unset the session variables, treat them as an anonymous user,
 		// and get out of here
 		$env->is_logged_in = false;
-		$env->user = "Anonymous";
+		$env->user = $settings->anonymous_user_name;
 		$env->pass = "";
 		// Clear the session data
 		$_SESSION = []; // Delete all the variables
@@ -879,6 +881,23 @@ function render_editor($editorName)
 	return "<span class='editor'>&#9998; $editorName</span>";
 }
 
+/**
+ * Saves the currently logged in uesr's data back to peppermint.json.
+ * @return bool Whethert he user's data was saved successfully. Returns false if the user isn't logged in.
+ */
+function save_userdata()
+{
+	global $env, $settings, $paths;
+	
+	if(!$env->is_logged_in)
+		return false;
+	
+	$settings->users->{$env->user} = $env->user_data;
+	file_put_contents($paths->settings_file, json_encode($settings, JSON_PRETTY_PRINT));
+	
+	return true;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -3773,7 +3792,7 @@ register_module([
 		/**
 		 * @api {get} ?action=user-preferences Get a user preferences configuration page.
 		 * @apiName UserPreferences
-		 * @apiGroup Utility
+		 * @apiGroup Settings
 		 * @apiPermission User
 		 */
 		
@@ -3803,19 +3822,52 @@ register_module([
 			$content .= "<input type='text' name='username' value='$env->user' readonly />\n";
 			$content .= "<h3>Change Password</h3\n>";
 			$content .= "<form method='post' action='?action=change-password'>\n";
-			$content .= "<label for='old-pass'>Old Password:</label>\n";
-			$content .= "<input type='password' name='old-pass'  />\n";
+			$content .= "<label for='old-pass'>Current Password:</label>\n";
+			$content .= "<input type='password' name='current-pass'  />\n";
 			$content .= "<br />\n";
 			$content .= "<label for='new-pass'>New Password:</label>\n";
 			$content .= "<input type='password' name='new-pass' />\n";
 			$content .= "<br />\n";
 			$content .= "<label for='new-pass-confirm'>Confirm New Password:</label>\n";
 			$content .= "<input type='password' name='new-pass-confirm' />\n";
+			$content .= "<br />\n";
+			$content .= "<input type='submit' value='Change Password' />\n";
 			$content .= "</form>\n";
 			
 			exit(page_renderer::render_main("User Preferences - $settings->sitename", $content));
 		});
 		
+		add_action("change-password", function() {
+		    global $env;
+			// Make sure the new password was typed correctly
+			// This comes before the current password check since that's more intensive
+			if($_POST["new-pass"] !== $_POST["new-pass-confirm"]) {
+				exit(page_renderer::render_main("Password mismatch - $settings->sitename", "<p>The new password you typed twice didn't match. <a href='javascript:history.back();'>Go back</a>.</p>"));
+			}
+			// Check the current password
+			if(hash_password($_POST["current-pass"]) !== $env->user_data->password) {
+				exit(page_renderer::render_main("Password mismatch - $settings->sitename", "<p>Error: You typed your current password incorrectly. <a href='javascript:history.back();'>Go back</a>.</p>"));
+			}
+			
+			// All's good! Go ahead and change the password.
+			$env->user_data->password = hash_password($_POST["current-pass"]);
+			// Save the userdata back to disk
+			save_userdata();
+		});
+		
+		/**
+		 * @api	{post}	?action=change-password	Change your password
+		 * @apiName			ChangePassword
+		 * @apiGroup		Settings
+		 * @apiPermission	User
+		 *
+		 * @apiParam	{string}	current-pass		Your current password.
+		 * @apiParam	{string}	new-pass			Your new password.
+		 * @apiParam	{string}	new-pass-confirm	Your new password again, to make sure you've typed it correctly.
+		 *
+		 * @apiError	PasswordMismatchError	The new password fields don't match.
+		 */
+		
 		add_help_section("910-user-preferences", "User Preferences", "<p>(help text coming soon)</p>");
 	}
 ]);
diff --git a/core.php b/core.php
index 9d507fd..4fd951d 100644
--- a/core.php
+++ b/core.php
@@ -20,6 +20,7 @@ $env->history->revision_data = false; // The revision data object from the page
 $env->user = $settings->anonymous_user_name; // The user's name
 $env->is_logged_in = false;  // Whether the user is logged in
 $env->is_admin = false; // Whether the user is an admin (moderator)
+$env->user_data = new stdClass(); // A logged in user's data
 $env->storage_prefix = $settings->data_storage_dir . DIRECTORY_SEPARATOR; // The data storage directory
 $env->perfdata = new stdClass(); // Performance data
 /// Paths ///
@@ -62,6 +63,7 @@ if(isset($_SESSION[$settings->sessionprefix . "-user"]) and
 	{
 		// The user is logged in
 		$env->is_logged_in = true;
+		$env->user_data = $settings->{$env->user};
 	}
 	else
 	{
@@ -69,7 +71,7 @@ if(isset($_SESSION[$settings->sessionprefix . "-user"]) and
 		// Unset the session variables, treat them as an anonymous user,
 		// and get out of here
 		$env->is_logged_in = false;
-		$env->user = "Anonymous";
+		$env->user = $settings->anonymous_user_name;
 		$env->pass = "";
 		// Clear the session data
 		$_SESSION = []; // Delete all the variables
@@ -562,6 +564,23 @@ function render_editor($editorName)
 	return "<span class='editor'>&#9998; $editorName</span>";
 }
 
+/**
+ * Saves the currently logged in uesr's data back to peppermint.json.
+ * @return bool Whethert he user's data was saved successfully. Returns false if the user isn't logged in.
+ */
+function save_userdata()
+{
+	global $env, $settings, $paths;
+	
+	if(!$env->is_logged_in)
+		return false;
+	
+	$settings->users->{$env->user} = $env->user_data;
+	file_put_contents($paths->settings_file, json_encode($settings, JSON_PRETTY_PRINT));
+	
+	return true;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/module_index.json b/module_index.json
index 7dd9012..82b0723 100755
--- a/module_index.json
+++ b/module_index.json
@@ -23,7 +23,7 @@
         "author": "Starbeamrainbowlabs",
         "description": "Adds an action called 'random' that redirects you to a random page.",
         "id": "action-random",
-        "lastupdate": 1481143095,
+        "lastupdate": 1482008539,
         "optional": false
     },
     {
@@ -50,7 +50,7 @@
         "author": "Starbeamrainbowlabs",
         "description": "The module everyone has been waiting for! Adds a web based gui that lets mods change the wiki settings.",
         "id": "feature-guiconfig",
-        "lastupdate": 1481488416,
+        "lastupdate": 1482008539,
         "optional": false
     },
     {
@@ -68,7 +68,7 @@
         "author": "Starbeamrainbowlabs",
         "description": "Adds recent changes. Access through the 'recent-changes' action.",
         "id": "feature-recent-changes",
-        "lastupdate": 1481374758,
+        "lastupdate": 1482008539,
         "optional": false
     },
     {
@@ -86,7 +86,7 @@
         "author": "Starbeamrainbowlabs",
         "description": "Adds proper search functionality to Pepperminty Wiki using an inverted index to provide a full text search engine. If pages don't show up, then you might have hit a stop word. If not, try requesting the `invindex-rebuild` action to rebuild the inverted index from scratch.",
         "id": "feature-search",
-        "lastupdate": 1481051853,
+        "lastupdate": 1482008539,
         "optional": false
     },
     {
@@ -95,7 +95,7 @@
         "author": "Starbeamrainbowlabs",
         "description": "Adds the ability to upload files to Pepperminty Wiki. Uploaded files act as pages and have the special 'File\/' prefix.",
         "id": "feature-upload",
-        "lastupdate": 1479634619,
+        "lastupdate": 1482008539,
         "optional": false
     },
     {
@@ -104,7 +104,7 @@
         "author": "Starbeamrainbowlabs",
         "description": "Adds a user preferences page, letting pople do things like change their email address and password.",
         "id": "feature-user-preferences",
-        "lastupdate": 1481923709,
+        "lastupdate": 1482357790,
         "optional": false
     },
     {
@@ -140,7 +140,7 @@
         "author": "Starbeamrainbowlabs",
         "description": "Allows you to edit pages by adding the edit and save actions. You should probably include this one.",
         "id": "page-edit",
-        "lastupdate": 1481374823,
+        "lastupdate": 1482008539,
         "optional": false
     },
     {
@@ -176,7 +176,7 @@
         "author": "Starbeamrainbowlabs",
         "description": "Adds a pair of actions (login and checklogin) that allow users to login. You need this one if you want your users to be able to login.",
         "id": "page-login",
-        "lastupdate": 1481567842,
+        "lastupdate": 1482008539,
         "optional": false
     },
     {
@@ -194,7 +194,7 @@
         "author": "Starbeamrainbowlabs",
         "description": "Adds an action to allow administrators to move pages.",
         "id": "page-move",
-        "lastupdate": 1480102277,
+        "lastupdate": 1482008539,
         "optional": false
     },
     {
@@ -212,7 +212,7 @@
         "author": "Starbeamrainbowlabs",
         "description": "Allows you to view pages. You really should include this one.",
         "id": "page-view",
-        "lastupdate": 1482008209,
+        "lastupdate": 1482008539,
         "optional": false
     },
     {
@@ -230,7 +230,7 @@
         "author": "Emanuil Rusev & Starbeamrainbowlabs",
         "description": "An upgraded (now default!) parser based on Emanuil Rusev's Parsedown Extra PHP library (https:\/\/github.com\/erusev\/parsedown-extra), which is licensed MIT. Please be careful, as this module adds some weight to your installation, and also *requires* write access to the disk on first load.",
         "id": "parser-parsedown",
-        "lastupdate": 1478204933,
+        "lastupdate": 1482008539,
         "optional": false
     }
 ]
\ No newline at end of file
diff --git a/modules/feature-user-preferences.php b/modules/feature-user-preferences.php
index 4886957..3186e2c 100644
--- a/modules/feature-user-preferences.php
+++ b/modules/feature-user-preferences.php
@@ -10,7 +10,7 @@ register_module([
 		/**
 		 * @api {get} ?action=user-preferences Get a user preferences configuration page.
 		 * @apiName UserPreferences
-		 * @apiGroup Utility
+		 * @apiGroup Settings
 		 * @apiPermission User
 		 */
 		
@@ -40,19 +40,52 @@ register_module([
 			$content .= "<input type='text' name='username' value='$env->user' readonly />\n";
 			$content .= "<h3>Change Password</h3\n>";
 			$content .= "<form method='post' action='?action=change-password'>\n";
-			$content .= "<label for='old-pass'>Old Password:</label>\n";
-			$content .= "<input type='password' name='old-pass'  />\n";
+			$content .= "<label for='old-pass'>Current Password:</label>\n";
+			$content .= "<input type='password' name='current-pass'  />\n";
 			$content .= "<br />\n";
 			$content .= "<label for='new-pass'>New Password:</label>\n";
 			$content .= "<input type='password' name='new-pass' />\n";
 			$content .= "<br />\n";
 			$content .= "<label for='new-pass-confirm'>Confirm New Password:</label>\n";
 			$content .= "<input type='password' name='new-pass-confirm' />\n";
+			$content .= "<br />\n";
+			$content .= "<input type='submit' value='Change Password' />\n";
 			$content .= "</form>\n";
 			
 			exit(page_renderer::render_main("User Preferences - $settings->sitename", $content));
 		});
 		
+		add_action("change-password", function() {
+		    global $env;
+			// Make sure the new password was typed correctly
+			// This comes before the current password check since that's more intensive
+			if($_POST["new-pass"] !== $_POST["new-pass-confirm"]) {
+				exit(page_renderer::render_main("Password mismatch - $settings->sitename", "<p>The new password you typed twice didn't match. <a href='javascript:history.back();'>Go back</a>.</p>"));
+			}
+			// Check the current password
+			if(hash_password($_POST["current-pass"]) !== $env->user_data->password) {
+				exit(page_renderer::render_main("Password mismatch - $settings->sitename", "<p>Error: You typed your current password incorrectly. <a href='javascript:history.back();'>Go back</a>.</p>"));
+			}
+			
+			// All's good! Go ahead and change the password.
+			$env->user_data->password = hash_password($_POST["current-pass"]);
+			// Save the userdata back to disk
+			save_userdata();
+		});
+		
+		/**
+		 * @api	{post}	?action=change-password	Change your password
+		 * @apiName			ChangePassword
+		 * @apiGroup		Settings
+		 * @apiPermission	User
+		 *
+		 * @apiParam	{string}	current-pass		Your current password.
+		 * @apiParam	{string}	new-pass			Your new password.
+		 * @apiParam	{string}	new-pass-confirm	Your new password again, to make sure you've typed it correctly.
+		 *
+		 * @apiError	PasswordMismatchError	The new password fields don't match.
+		 */
+		
 		add_help_section("910-user-preferences", "User Preferences", "<p>(help text coming soon)</p>");
 	}
 ]);
