From fa5ecc6ff6ed0c9f639273871dc70cd7a2537825 Mon Sep 17 00:00:00 2001
From: Starbeamrainbowlabs <sbrl@starbeamrainbowlabs.com>
Date: Sat, 2 Apr 2016 15:07:18 +0100
Subject: [PATCH] Unlock the uploading of arbitrary file types.

---
 Changelog.md               |  5 +++++
 README.md                  | 11 ++++++++--
 build/index.php            | 44 +++++++++++++++++++++++++++-----------
 module_index.json          |  4 ++--
 modules/feature-upload.php | 33 +++++++++++++++++++---------
 settings.fragment.php      |  9 +++++++-
 6 files changed, 79 insertions(+), 27 deletions(-)

diff --git a/Changelog.md b/Changelog.md
index 019bb20..b60aa46 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,7 +1,12 @@
 # Changelog
 # v0.11-dev
+
+# Changed
  - Enhanced the dev help page some more
+
+# Fixed
  - Fixed the downloader
+ - Fixed an issue with the recent changes page and redirects causing a large number of warnings
 
 ## v0.10
 
diff --git a/README.md b/README.md
index fe89301..3e392c3 100644
--- a/README.md
+++ b/README.md
@@ -16,16 +16,22 @@ Above: A Main Page with the sidebar enabled.
 - Page creation
 - Sub pages
 - Markdown-powered syntax
+	- Templating support
+	- Additional syntax for resizing and floating images
 - Internal links
 - Printable page view
 - Customisable theme
 - ~~Basic 'search' bar~~ A full text search engine!
 - (Optional) Sidebar with a tree of all the current pages
-- List of all pages & details
+- List of all pages and details
+	- List of all tags and pages with any given tag
+	- List of recent changes
 - Inbuilt help page
 - Tags
 - Image upload
 - Page protection
+- Customisable module based system
+	- Allows you to add or remove features at will
 
 ## Demo
 A Live demo of the latest stable version can be found over at [my website](//starbeamrainbowlabs.com/labs/peppermint)
@@ -110,10 +116,11 @@ Key				| Value			| Explanation
 `footer_message`	| string( + HTML) | A message that will appear at the bottom of every page. May contain HTML.
 `editing_message`	| string( + HTML) | A message that will appear just before the submit button on the editing page. May contain HTML.
 `upload_enabled`	| boolean	| Whether to allow image uploads to the server.
-`upload_allowed_file_types` | array of strings | An array of mime types that are allowed to be uploaded. Currently only images are supported, but other types may be supported in the future.
+`upload_allowed_file_types` | array of strings | An array of mime types that are allowed to be uploaded. Note that Pepperminty Wiki only does minimal checking of the actual content that is being uploaded - so please don't add any dangerous file types here on a parmanant bases for your own safety!
 `preview_file_type`	| mime type	| The default file type for previews. Defaults to image/png. Also supports `image/jpeg` and `image/webp`. `image/webp` is a new image format that reduces image sizez by ~20%, but PHP still has some issues with invalid webp images.
 `default_preview_size` | number	| The default size of preview images.
 `mime_extension_mappings_location` | path | The location of a file that maps mime types onto file extensions and vice versa. Used to generate the file extension for an uploaded file. Set to the default location of the mime.types file on Linux. If you aren't using linux, download [this pastebin](http://pastebin.com/mjM3zKjz) and point this setting at it instead.
+`mime_mappings_overrides` | array of strings | An array of override mime mappings to translate mime types into the appropriate file extension. Use if the file pointed to by the above assigns weird file extensions to any file types.
 `min_preview_size`	| number	| The minimum allowed size for generated preview images in pixels.
 `max_preview_size`	| number	| The maximum allowed size for generated preview images in pixels.
 `search_characters_context` | number | The number of characters that should be displayed either side of a matching term in the context below each search result.
diff --git a/build/index.php b/build/index.php
index 738fcc6..b1ae0a2 100644
--- a/build/index.php
+++ b/build/index.php
@@ -205,7 +205,14 @@ $settings->default_preview_size = 640;
 // configuration guide for windows instructions.
 $settings->mime_extension_mappings_location = "/etc/mime.types";
 
-// The minimum and maximum sizes of generated preview images in pixels.
+// Override mappings to convert mime types into the appropriate file extension.
+// Used to override the above file if it assigns weird extensions
+// to any mime types.
+$settings->mime_mappings_overrides = [
+	"text/plain" => "txt"
+];
+
+// The minimum and maximum allowed sizes of generated preview images in pixels.
 $settings->min_preview_size = 1;
 $settings->max_preview_size = 2048;
 
@@ -1603,7 +1610,7 @@ register_module([
 					$title_display = human_filesize($rchange->newsize - $rchange->sizediff) . " -> " .  human_filesize($rchange->newsize);
 					
 					$pageDisplayName = $rchange->page;
-					if(isset($pageindex->$pageDisplayName) and $pageindex->$pageDisplayName->redirect)
+					if(isset($pageindex->$pageDisplayName) and !empty($pageindex->$pageDisplayName->redirect))
 						$pageDisplayName = "<em>$pageDisplayName</em>";
 					
 					$content .= "\t\t\t<li><a href='?page=" . rawurlencode($rchange->page) . "'>$pageDisplayName</a> <span class='editor'>&#9998; $rchange->user</span> <time class='cursor-query' title='" . date("l jS \of F Y \a\\t h:ia T", $rchange->timestamp) . "'>" . human_time_since($rchange->timestamp) . "</time> <span class='$size_display_class' title='$title_display'>($size_display)</span></li>\n";
@@ -2286,7 +2293,7 @@ register_module([
 						exit(page_renderer::render("Upload failed - $settings->sitename", "<p>Your upload couldn't be processed because you are not logged in.</p><p>Try <a href='?action=login&returnto=" . rawurlencode("?action=upload") . "'>logging in</a> first."));
 					}
 					
-					// Calculate the target ename, removing any characters we
+					// Calculate the target name, removing any characters we
 					// are unsure about.
 					$target_name = makepathsafe($_POST["name"]);
 					$temp_filename = $_FILES["file"]["tmp_name"];
@@ -2295,6 +2302,14 @@ register_module([
 					$mime_type = finfo_file($mimechecker, $temp_filename);
 					finfo_close($mimechecker);
 					
+					if(!in_array($mime_type, $settings->upload_allowed_file_types))
+					{
+						http_response_code(415);
+						exit(page_renderer::render("Unknown file type - Upload error - $settings->sitename", "<p>$settings->sitename recieved the file you tried to upload successfully, but detected that the type of file you uploaded is not in the allowed file types list. The file has been discarded.</p>
+						<p>The file you tried to upload appeared to be of type <code>$mime_type</code>, but $settings->sitename currently only allows the uploading of the following file types: <code>" . implode("</code>, <code>", $settings->upload_allowed_file_types) . "</code>.</p>
+						<p><a href='?action=$settings->defaultaction'>Go back</a> to the Main Page.</p>"));
+					}
+					
 					// Perform appropriate checks based on the *real* filetype
 					switch(substr($mime_type, 0, strpos($mime_type, "/")))
 					{
@@ -2305,22 +2320,27 @@ register_module([
 							if(!is_int($imagesize[0]) or !is_int($imagesize[1]))
 							{
 								http_response_code(415);
-								exit(page_renderer::render("Upload Error - $settings->sitename", "<p>The file that you uploaded doesn't appear to be an image. $settings->sitename currently only supports uploading images (videos coming soon). <a href='?action=upload'>Go back to try again</a>.</p>"));
+								exit(page_renderer::render("Upload Error - $settings->sitename", "<p>Although the file that you uploaded appears to be an image, $settings->sitename has been unable to determine it's dimensions. The uploaded file has been discarded. <a href='?action=upload'>Go back to try again</a>.</p>
+								<p>You may wish to consider <a href='https://github.com/sbrl/Pepperminty-Wiki'>opening an issue</a> against Pepperminty Wiki (the software that powers $settings->sitename) if this isn't the first time that you have seen this message.</p>"));
 							}
 							
 							break;
-						
-						case "video":
-							http_response_code(501);
-							exit(page_renderer::render("Upload Error - $settings->sitename", "<p>You uploaded a video, but $settings->sitename doesn't support them yet. Please try again later.</p>"));
-						
-						default:
-							http_response_code(415);
-							exit(page_renderer::render("Upload Error - $settings->sitename", "<p>You uploaded an unnknown file type which couldn't be processed. $settings->sitename thinks that the file you uploaded was a(n) '$mime_type', which isn't supported.</p>"));
 					}
 					
 					$file_extension = system_mime_type_extension($mime_type);
 					
+					// Override the detected file extension if a file extension
+					// is explicitly specified in the settings
+					if(isset($settings->mime_mappings_overrides))
+						$file_extension = $settings->mime_mappings_overrides[$mime_type];
+					
+					if(in_array($file_extension, [ "php", ".htaccess", "asp" ]))
+					{
+						http_response_code(415);
+						exit(page_renderer::render("Upload Error - $settings->sitename", "<p>The file you uploaded appears to be dangerous and has been discarded. Please contact $settings->sitename's administrator for assistance.</p>
+						<p>Additional information: The file uploaded appeared to be of type <code>$mime_type</code>, which mapped onto the extension <code>$file_extension</code>. This file extension has the potential to be executed accidentally by the web server.</p>"));
+					}
+					
 					$new_filename = "$paths->upload_file_prefix$target_name.$file_extension";
 					$new_description_filename = "$new_filename.md";
 					
diff --git a/module_index.json b/module_index.json
index 7721418..3996b94 100644
--- a/module_index.json
+++ b/module_index.json
@@ -41,7 +41,7 @@
         "author": "Starbeamrainbowlabs",
         "description": "Adds recent changes. Access through the 'recent-changes' action.",
         "id": "feature-recent-changes",
-        "lastupdate": 1458824847,
+        "lastupdate": 1459601264,
         "optional": false
     },
     {
@@ -68,7 +68,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": 1451133883,
+        "lastupdate": 1459605921,
         "optional": false
     },
     {
diff --git a/modules/feature-upload.php b/modules/feature-upload.php
index f0f7d54..bf276a8 100644
--- a/modules/feature-upload.php
+++ b/modules/feature-upload.php
@@ -65,7 +65,7 @@ register_module([
 						exit(page_renderer::render("Upload failed - $settings->sitename", "<p>Your upload couldn't be processed because you are not logged in.</p><p>Try <a href='?action=login&returnto=" . rawurlencode("?action=upload") . "'>logging in</a> first."));
 					}
 					
-					// Calculate the target ename, removing any characters we
+					// Calculate the target name, removing any characters we
 					// are unsure about.
 					$target_name = makepathsafe($_POST["name"]);
 					$temp_filename = $_FILES["file"]["tmp_name"];
@@ -74,6 +74,14 @@ register_module([
 					$mime_type = finfo_file($mimechecker, $temp_filename);
 					finfo_close($mimechecker);
 					
+					if(!in_array($mime_type, $settings->upload_allowed_file_types))
+					{
+						http_response_code(415);
+						exit(page_renderer::render("Unknown file type - Upload error - $settings->sitename", "<p>$settings->sitename recieved the file you tried to upload successfully, but detected that the type of file you uploaded is not in the allowed file types list. The file has been discarded.</p>
+						<p>The file you tried to upload appeared to be of type <code>$mime_type</code>, but $settings->sitename currently only allows the uploading of the following file types: <code>" . implode("</code>, <code>", $settings->upload_allowed_file_types) . "</code>.</p>
+						<p><a href='?action=$settings->defaultaction'>Go back</a> to the Main Page.</p>"));
+					}
+					
 					// Perform appropriate checks based on the *real* filetype
 					switch(substr($mime_type, 0, strpos($mime_type, "/")))
 					{
@@ -84,22 +92,27 @@ register_module([
 							if(!is_int($imagesize[0]) or !is_int($imagesize[1]))
 							{
 								http_response_code(415);
-								exit(page_renderer::render("Upload Error - $settings->sitename", "<p>The file that you uploaded doesn't appear to be an image. $settings->sitename currently only supports uploading images (videos coming soon). <a href='?action=upload'>Go back to try again</a>.</p>"));
+								exit(page_renderer::render("Upload Error - $settings->sitename", "<p>Although the file that you uploaded appears to be an image, $settings->sitename has been unable to determine it's dimensions. The uploaded file has been discarded. <a href='?action=upload'>Go back to try again</a>.</p>
+								<p>You may wish to consider <a href='https://github.com/sbrl/Pepperminty-Wiki'>opening an issue</a> against Pepperminty Wiki (the software that powers $settings->sitename) if this isn't the first time that you have seen this message.</p>"));
 							}
 							
 							break;
-						
-						case "video":
-							http_response_code(501);
-							exit(page_renderer::render("Upload Error - $settings->sitename", "<p>You uploaded a video, but $settings->sitename doesn't support them yet. Please try again later.</p>"));
-						
-						default:
-							http_response_code(415);
-							exit(page_renderer::render("Upload Error - $settings->sitename", "<p>You uploaded an unnknown file type which couldn't be processed. $settings->sitename thinks that the file you uploaded was a(n) '$mime_type', which isn't supported.</p>"));
 					}
 					
 					$file_extension = system_mime_type_extension($mime_type);
 					
+					// Override the detected file extension if a file extension
+					// is explicitly specified in the settings
+					if(isset($settings->mime_mappings_overrides))
+						$file_extension = $settings->mime_mappings_overrides[$mime_type];
+					
+					if(in_array($file_extension, [ "php", ".htaccess", "asp" ]))
+					{
+						http_response_code(415);
+						exit(page_renderer::render("Upload Error - $settings->sitename", "<p>The file you uploaded appears to be dangerous and has been discarded. Please contact $settings->sitename's administrator for assistance.</p>
+						<p>Additional information: The file uploaded appeared to be of type <code>$mime_type</code>, which mapped onto the extension <code>$file_extension</code>. This file extension has the potential to be executed accidentally by the web server.</p>"));
+					}
+					
 					$new_filename = "$paths->upload_file_prefix$target_name.$file_extension";
 					$new_description_filename = "$new_filename.md";
 					
diff --git a/settings.fragment.php b/settings.fragment.php
index ac37889..00e17f1 100644
--- a/settings.fragment.php
+++ b/settings.fragment.php
@@ -202,7 +202,14 @@ $settings->default_preview_size = 640;
 // configuration guide for windows instructions.
 $settings->mime_extension_mappings_location = "/etc/mime.types";
 
-// The minimum and maximum sizes of generated preview images in pixels.
+// Override mappings to convert mime types into the appropriate file extension.
+// Used to override the above file if it assigns weird extensions
+// to any mime types.
+$settings->mime_mappings_overrides = [
+	"text/plain" => "txt"
+];
+
+// The minimum and maximum allowed sizes of generated preview images in pixels.
 $settings->min_preview_size = 1;
 $settings->max_preview_size = 2048;
 
