1
0
Fork 0
mirror of https://github.com/sbrl/Pepperminty-Wiki.git synced 2024-12-22 13:45:02 +00:00

patching PHP 7.3.11 issue and double dot in file uploads

This commit is contained in:
Sean Feeney 2020-04-17 21:53:05 -07:00
parent 8d2179e290
commit b1ebaaab61
2 changed files with 133 additions and 133 deletions

View file

@ -13,19 +13,19 @@ register_module([
* @apiGroup Upload * @apiGroup Upload
* @apiPermission User * @apiPermission User
* *
* @apiParam {boolean} avatar Optional. If true then a special page to upload your avatar is displayed instead. * @apiParam {bool} avatar Optional. If true then a special page to upload your avatar is displayed instead.
*/ */
/** /**
* @api {post} ?action=upload Upload a file * @api {post} ?action=upload Upload a file
* @apiName UploadFile * @apiName UploadFile
* @apiGroup Upload * @apiGroup Upload
* @apiPermission User * @apiPermission User
* *
* @apiParam {string} name The name of the file to upload. * @apiParam {string} name The name of the file to upload.
* @apiParam {string} description A description of the file. * @apiParam {string} description A description of the file.
* @apiParam {file} file The file to upload. * @apiParam {file} file The file to upload.
* @apiParam {boolean} avatar Whether this upload should be uploaded as the current user's avatar. If specified, any filenames provided will be ignored. * @apiParam {bool} avatar Whether this upload should be uploaded as the current user's avatar. If specified, any filenames provided will be ignored.
* *
* @apiUse UserNotLoggedInError * @apiUse UserNotLoggedInError
* @apiError UploadsDisabledError Uploads are currently disabled in the wiki's settings. * @apiError UploadsDisabledError Uploads are currently disabled in the wiki's settings.
@ -35,30 +35,30 @@ register_module([
* @apiError DuplicateFileError The filename specified is a duplicate of a file that already exists. * @apiError DuplicateFileError The filename specified is a duplicate of a file that already exists.
* @apiError FileTamperedError Pepperminty Wiki couldn't verify that the file wasn't tampered with during theupload process. * @apiError FileTamperedError Pepperminty Wiki couldn't verify that the file wasn't tampered with during theupload process.
*/ */
/* /*
* ██ ██ ██████ ██ ██████ █████ ██████ * ██ ██ ██████ ██ ██████ █████ ██████
* ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ * ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
* ██ ██ ██████ ██ ██ ██ ███████ ██ ██ * ██ ██ ██████ ██ ██ ██ ███████ ██ ██
* ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ * ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
* ██████ ██ ███████ ██████ ██ ██ ██████ * ██████ ██ ███████ ██████ ██ ██ ██████
*/ */
add_action("upload", function() { add_action("upload", function() {
global $settings, $env, $pageindex, $paths; global $settings, $env, $pageindex, $paths;
$is_avatar = !empty($_POST["avatar"]) || !empty($_GET["avatar"]); $is_avatar = !empty($_POST["avatar"]) || !empty($_GET["avatar"]);
switch($_SERVER["REQUEST_METHOD"]) switch($_SERVER["REQUEST_METHOD"])
{ {
case "GET": case "GET":
// Send upload page // Send upload page
if(!$settings->upload_enabled) if(!$settings->upload_enabled)
exit(page_renderer::render("Upload Disabled - $setting->sitename", "<p>You can't upload anything at the moment because $settings->sitename has uploads disabled. Try contacting $settings->admindetails_name, your site Administrator. <a href='javascript:history.back();'>Go back</a>.</p>")); exit(page_renderer::render("Upload Disabled - $setting->sitename", "<p>You can't upload anything at the moment because $settings->sitename has uploads disabled. Try contacting $settings->admindetails_name, your site Administrator. <a href='javascript:history.back();'>Go back</a>.</p>"));
if(!$env->is_logged_in) if(!$env->is_logged_in)
exit(page_renderer::render("Upload Error - $settings->sitename", "<p>You are not currently logged in, so you can't upload anything.</p> exit(page_renderer::render("Upload Error - $settings->sitename", "<p>You are not currently logged in, so you can't upload anything.</p>
<p>Try <a href='?action=login&returnto=" . rawurlencode("?action=upload") . "'>logging in</a> first.</p>")); <p>Try <a href='?action=login&returnto=" . rawurlencode("?action=upload") . "'>logging in</a> first.</p>"));
if($is_avatar) { if($is_avatar) {
exit(page_renderer::render("Upload avatar - $settings->sitename", "<h1>Upload avatar</h1> exit(page_renderer::render("Upload avatar - $settings->sitename", "<h1>Upload avatar</h1>
<p>Select an image below, and then press upload. $settings->sitename currently supports the following file types (though not all of them may be suitable for an avatar): " . implode(", ", $settings->upload_allowed_file_types) . "</p> <p>Select an image below, and then press upload. $settings->sitename currently supports the following file types (though not all of them may be suitable for an avatar): " . implode(", ", $settings->upload_allowed_file_types) . "</p>
@ -66,13 +66,13 @@ register_module([
<label for='file'>Select a file to upload.</label> <label for='file'>Select a file to upload.</label>
<input type='file' name='file' id='file-upload-selector' tabindex='1' /> <input type='file' name='file' id='file-upload-selector' tabindex='1' />
<br /> <br />
<p class='editing_message'>$settings->editing_message</p> <p class='editing_message'>$settings->editing_message</p>
<input type='hidden' name='avatar' value='yes' /> <input type='hidden' name='avatar' value='yes' />
<input type='submit' value='Upload' tabindex='20' /> <input type='submit' value='Upload' tabindex='20' />
</form>")); </form>"));
} }
exit(page_renderer::render("Upload - $settings->sitename", "<h1>Upload file</h1> exit(page_renderer::render("Upload - $settings->sitename", "<h1>Upload file</h1>
<p>Select an image or file below, and then type a name for it in the box. This server currently supports uploads up to " . human_filesize(get_max_upload_size()) . " in size.</p> <p>Select an image or file below, and then type a name for it in the box. This server currently supports uploads up to " . human_filesize(get_max_upload_size()) . " in size.</p>
<p>$settings->sitename currently supports uploading of the following file types: " . implode(", ", $settings->upload_allowed_file_types) . ".</p> <p>$settings->sitename currently supports uploading of the following file types: " . implode(", ", $settings->upload_allowed_file_types) . ".</p>
@ -95,16 +95,16 @@ register_module([
document.getElementById('file-upload-name').value = newName; document.getElementById('file-upload-name').value = newName;
}); });
</script>")); </script>"));
break; break;
case "POST": case "POST":
// Recieve file // Receive file
if(!$settings->editing) { if(!$settings->editing) {
exit(page_renderer::render_main("Upload failed - $settings->sitename", "<p>Your upload couldn't be processed because editing is currently disabled on $settings->sitename. Please contact $settings->admindetails_name, $settings->sitename's administrator for more information - their contact details can be found at the bottom of this page. <a href='index.php'>Go back to the main page</a>.")); exit(page_renderer::render_main("Upload failed - $settings->sitename", "<p>Your upload couldn't be processed because editing is currently disabled on $settings->sitename. Please contact $settings->admindetails_name, $settings->sitename's administrator for more information - their contact details can be found at the bottom of this page. <a href='index.php'>Go back to the main page</a>."));
} }
// Make sure uploads are enabled // Make sure uploads are enabled
if(!$settings->upload_enabled) if(!$settings->upload_enabled)
{ {
@ -113,7 +113,7 @@ register_module([
http_response_code(412); http_response_code(412);
exit(page_renderer::render("Upload failed - $settings->sitename", "<p>Your upload couldn't be processed because uploads are currently disabled on $settings->sitename. <a href='index.php'>Go back to the main page</a>.</p>")); exit(page_renderer::render("Upload failed - $settings->sitename", "<p>Your upload couldn't be processed because uploads are currently disabled on $settings->sitename. <a href='index.php'>Go back to the main page</a>.</p>"));
} }
// Make sure that the user is logged in // Make sure that the user is logged in
if(!$env->is_logged_in) if(!$env->is_logged_in)
{ {
@ -135,16 +135,16 @@ register_module([
exit(page_renderer::render("Upload failed - $settings->sitename", "<p>Your upload couldn't be processed because " . (($_FILES["file"]["error"] == 1 || $_FILES["file"]["error"] == 2) ? "the file is too large" : "an error occurred") . ".</p><p>Please contact $settings->admindetails_name, $settings->sitename's administrator for help.</p>")); exit(page_renderer::render("Upload failed - $settings->sitename", "<p>Your upload couldn't be processed because " . (($_FILES["file"]["error"] == 1 || $_FILES["file"]["error"] == 2) ? "the file is too large" : "an error occurred") . ".</p><p>Please contact $settings->admindetails_name, $settings->sitename's administrator for help.</p>"));
} }
// Calculate the target name, removing any characters we // Calculate the target name, removing any characters we
// are unsure about. // are unsure about.
$target_name = makepathsafe($_POST["name"] ?? "Users/$env->user/Avatar"); $target_name = makepathsafe($_POST["name"] ?? "Users/$env->user/Avatar");
$temp_filename = $_FILES["file"]["tmp_name"]; $temp_filename = $_FILES["file"]["tmp_name"];
$mimechecker = finfo_open(FILEINFO_MIME_TYPE); $mimechecker = finfo_open(FILEINFO_MIME_TYPE);
$mime_type = finfo_file($mimechecker, $temp_filename); $mime_type = finfo_file($mimechecker, $temp_filename);
finfo_close($mimechecker); finfo_close($mimechecker);
if(!in_array($mime_type, $settings->upload_allowed_file_types)) if(!in_array($mime_type, $settings->upload_allowed_file_types))
{ {
http_response_code(415); http_response_code(415);
@ -152,20 +152,20 @@ register_module([
<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>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>")); <p><a href='?action=$settings->defaultaction'>Go back</a> to the Main Page.</p>"));
} }
// Perform appropriate checks based on the *real* filetype // Perform appropriate checks based on the *real* filetype
if($is_avatar && substr($mime_type, 0, strpos($mime_type, "/")) !== "image") { if($is_avatar && substr($mime_type, 0, strpos($mime_type, "/")) !== "image") {
http_response_code(415); http_response_code(415);
exit(page_renderer::render_main("Error uploading avatar - $settings->sitename", "<p>That file appears to be unsuitable as an avatar, as $settings->sitename has detected it to be of type <code>$mime_type</code>, which doesn't appear to be an image. Please try <a href='?action=upload&avatar=yes'>uploading a different file</a> to use as your avatar.</p>")); exit(page_renderer::render_main("Error uploading avatar - $settings->sitename", "<p>That file appears to be unsuitable as an avatar, as $settings->sitename has detected it to be of type <code>$mime_type</code>, which doesn't appear to be an image. Please try <a href='?action=upload&avatar=yes'>uploading a different file</a> to use as your avatar.</p>"));
} }
switch(substr($mime_type, 0, strpos($mime_type, "/"))) switch(substr($mime_type, 0, strpos($mime_type, "/")))
{ {
case "image": case "image":
$extra_data = []; $extra_data = [];
// Check SVG uploads with a special function // Check SVG uploads with a special function
$imagesize = $mime_type !== "image/svg+xml" ? getimagesize($temp_filename, $extra_data) : upload_check_svg($temp_filename); $imagesize = $mime_type !== "image/svg+xml" ? getimagesize($temp_filename, $extra_data) : upload_check_svg($temp_filename);
// Make sure that the image size is defined // Make sure that the image size is defined
if(!is_int($imagesize[0]) or !is_int($imagesize[1])) if(!is_int($imagesize[0]) or !is_int($imagesize[1]))
{ {
@ -175,70 +175,70 @@ register_module([
} }
break; break;
} }
$file_extension = system_mime_type_extension($mime_type); $file_extension = system_mime_type_extension($mime_type);
// Override the detected file extension if a file extension // Override the detected file extension if a file extension
// is explicitly specified in the settings // is explicitly specified in the settings
if(isset($settings->mime_mappings_overrides->$mime_type)) if(isset($settings->mime_mappings_overrides->$mime_type))
$file_extension = $settings->mime_mappings_overrides->$mime_type; $file_extension = $settings->mime_mappings_overrides->$mime_type;
if(in_array($file_extension, [ "php", ".htaccess", "asp", "aspx" ])) if(in_array($file_extension, [ "php", ".htaccess", "asp", "aspx" ]))
{ {
http_response_code(415); 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> 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>")); <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>"));
} }
// Rewrite the name to include the _actual_ file extension we've cleverly calculated :D // Rewrite the name to include the _actual_ file extension we've cleverly calculated :D
// The path to the place (relative to the wiki data root) // The path to the place (relative to the wiki data root)
// that we're actually going to store the uploaded file itself // that we're actually going to store the uploaded file itself
$new_filename = "$paths->upload_file_prefix$target_name.$file_extension"; $new_filename = "$paths->upload_file_prefix$target_name$file_extension";
// The path (relative, as before) to the description file // The path (relative, as before) to the description file
$new_description_filename = "$new_filename.md"; $new_description_filename = "$new_filename.md";
// The page path that the new file will be stored under // The page path that the new file will be stored under
$new_pagepath = $new_filename; $new_pagepath = $new_filename;
// Rewrite the paths to store avatars in the right place // Rewrite the paths to store avatars in the right place
if($is_avatar) { if($is_avatar) {
$new_pagepath = $target_name; $new_pagepath = $target_name;
$new_filename = "$target_name.$file_extension"; $new_filename = "$target_name.$file_extension";
} }
if(isset($pageindex->$new_pagepath) && !$is_avatar) if(isset($pageindex->$new_pagepath) && !$is_avatar)
exit(page_renderer::render("Upload Error - $settings->sitename", "<p>A page or file has already been uploaded with the name '$new_filename'. Try deleting it first. If you do not have permission to delete things, try contacting one of the moderators.</p>")); exit(page_renderer::render("Upload Error - $settings->sitename", "<p>A page or file has already been uploaded with the name '$new_filename'. Try deleting it first. If you do not have permission to delete things, try contacting one of the moderators.</p>"));
// Delete the previously uploaded avatar, if it exists // Delete the previously uploaded avatar, if it exists
// In the future we _may_ not need this once we have // In the future we _may_ not need this once we have
// file history online. // file history online.
if($is_avatar && isset($pageindex->$new_pagepath) && $pageindex->$new_pagepath->uploadedfile) if($is_avatar && isset($pageindex->$new_pagepath) && $pageindex->$new_pagepath->uploadedfile)
unlink($pageindex->$new_pagepath->uploadedfilepath); unlink($pageindex->$new_pagepath->uploadedfilepath);
// Make sure that the palce we're uploading to exists // Make sure that the palce we're uploading to exists
if(!file_exists(dirname($env->storage_prefix . $new_filename))) if(!file_exists(dirname($env->storage_prefix . $new_filename)))
mkdir(dirname($env->storage_prefix . $new_filename), 0775, true); mkdir(dirname($env->storage_prefix . $new_filename), 0775, true);
if(!move_uploaded_file($temp_filename, $env->storage_prefix . $new_filename)) if(!move_uploaded_file($temp_filename, $env->storage_prefix . $new_filename))
{ {
http_response_code(409); http_response_code(409);
exit(page_renderer::render("Upload Error - $settings->sitename", "<p>The file you uploaded was valid, but $settings->sitename couldn't verify that it was tampered with during the upload process. This probably means that either is a configuration error, or that $settings->sitename has been attacked. Please contact " . $settings->admindetails_name . ", your $settings->sitename Administrator.</p>")); exit(page_renderer::render("Upload Error - $settings->sitename", "<p>The file you uploaded was valid, but $settings->sitename couldn't verify that it was tampered with during the upload process. This probably means that either is a configuration error, or that $settings->sitename has been attacked. Please contact " . $settings->admindetails_name . ", your $settings->sitename Administrator.</p>"));
} }
$description = $_POST["description"] ?? "_(No description provided)_\n"; $description = $_POST["description"] ?? "_(No description provided)_\n";
// Escape the raw html in the provided description if the setting is enabled // Escape the raw html in the provided description if the setting is enabled
if($settings->clean_raw_html) if($settings->clean_raw_html)
$description = htmlentities($description, ENT_QUOTES); $description = htmlentities($description, ENT_QUOTES);
file_put_contents($env->storage_prefix . $new_description_filename, $description); file_put_contents($env->storage_prefix . $new_description_filename, $description);
// Construct a new entry for the pageindex // Construct a new entry for the pageindex
$entry = new stdClass(); $entry = new stdClass();
// Point to the description's filepath since this property // Point to the description's filepath since this property
// should point to a markdown file // should point to a markdown file
$entry->filename = $new_description_filename; $entry->filename = $new_description_filename;
$entry->size = strlen($description ?? "(No description provided)"); $entry->size = strlen($description ?? "(No description provided)");
$entry->lastmodified = time(); $entry->lastmodified = time();
$entry->lasteditor = $env->user; $entry->lasteditor = $env->user;
@ -249,17 +249,17 @@ register_module([
// Assign the new entry to the image's filepath as that // Assign the new entry to the image's filepath as that
// should be the page name. // should be the page name.
$pageindex->$new_pagepath = $entry; $pageindex->$new_pagepath = $entry;
// Generate a revision to keep the page history up to date // Generate a revision to keep the page history up to date
if(module_exists("feature-history")) if(module_exists("feature-history"))
{ {
$oldsource = ""; // Only variables can be passed by reference, not literals $oldsource = ""; // Only variables can be passed by reference, not literals
history_add_revision($entry, $description, $oldsource, false); history_add_revision($entry, $description, $oldsource, false);
} }
// Save the pageindex // Save the pageindex
save_pageindex(); save_pageindex();
if(module_exists("feature-recent-changes")) if(module_exists("feature-recent-changes"))
{ {
add_recent_change([ add_recent_change([
@ -270,36 +270,36 @@ register_module([
"filesize" => filesize($env->storage_prefix . $entry->uploadedfilepath) "filesize" => filesize($env->storage_prefix . $entry->uploadedfilepath)
]); ]);
} }
header("location: ?action=view&page=$new_pagepath&upload=success"); header("location: ?action=view&page=$new_pagepath&upload=success");
break; break;
} }
}); });
/** /**
* @api {get} ?action=preview&page={pageName}[&size={someSize}] Get a preview of a file * @api {get} ?action=preview&page={pageName}[&size={someSize}] Get a preview of a file
* @apiName PreviewFile * @apiName PreviewFile
* @apiGroup Upload * @apiGroup Upload
* @apiPermission Anonymous * @apiPermission Anonymous
* *
* @apiParam {string} page The name of the file to preview. * @apiParam {string} page The name of the file to preview.
* @apiParam {number} size Optional. The size fo the resulting preview. Will be clamped to fit within the bounds specified in the wiki's settings. May also be set to the keyword 'original', which will cause the original file to be returned with it's appropriate mime type instead. * @apiParam {number} size Optional. The size fo the resulting preview. Will be clamped to fit within the bounds specified in the wiki's settings. May also be set to the keyword 'original', which will cause the original file to be returned with it's appropriate mime type instead.
* *
* @apiError PreviewNoFileError No file was found associated with the specified page. * @apiError PreviewNoFileError No file was found associated with the specified page.
* @apiError PreviewUnknownFileTypeError Pepperminty Wiki was unable to generate a preview for the requested file's type. * @apiError PreviewUnknownFileTypeError Pepperminty Wiki was unable to generate a preview for the requested file's type.
*/ */
/* /*
* ██████ ██████ ███████ ██ ██ ██ ███████ ██ ██ * ██████ ██████ ███████ ██ ██ ██ ███████ ██ ██
* ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ * ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
* ██████ ██████ █████ ██ ██ ██ █████ ██ ██ * ██████ ██████ █████ ██ ██ ██ █████ ██ ██
* ██ ██ ██ ██ ██ ██ ██ ██ ██ ███ ██ * ██ ██ ██ ██ ██ ██ ██ ██ ██ ███ ██
* ██ ██ ██ ███████ ████ ██ ███████ ███ ███ * ██ ██ ██ ███████ ████ ██ ███████ ███ ███
*/ */
add_action("preview", function() { add_action("preview", function() {
global $settings, $env, $pageindex, $start_time; global $settings, $env, $pageindex, $start_time;
if(empty($pageindex->{$env->page}->uploadedfilepath)) if(empty($pageindex->{$env->page}->uploadedfilepath))
{ {
$im = errorimage("The page '$env->page' doesn't have an associated file."); $im = errorimage("The page '$env->page' doesn't have an associated file.");
@ -307,14 +307,14 @@ register_module([
imagepng($im); imagepng($im);
exit(); exit();
} }
$filepath = realpath($env->storage_prefix . $pageindex->{$env->page}->uploadedfilepath); $filepath = realpath($env->storage_prefix . $pageindex->{$env->page}->uploadedfilepath);
$mime_type = $pageindex->{$env->page}->uploadedfilemime; $mime_type = $pageindex->{$env->page}->uploadedfilemime;
$shortFilename = substr($filepath, 1 + (strrpos($filepath, '/') !== false ? strrpos($filepath, '/') : -1)); $shortFilename = substr($filepath, 1 + (strrpos($filepath, '/') !== false ? strrpos($filepath, '/') : -1));
header("content-disposition: inline; filename=\"$shortFilename\""); header("content-disposition: inline; filename=\"$shortFilename\"");
header("last-modified: " . gmdate('D, d M Y H:i:s T', $pageindex->{$env->page}->lastmodified)); header("last-modified: " . gmdate('D, d M Y H:i:s T', $pageindex->{$env->page}->lastmodified));
// If the size is set to original, then send (or redirect to) the original image // If the size is set to original, then send (or redirect to) the original image
// Also do the same for SVGs if svg rendering is disabled. // Also do the same for SVGs if svg rendering is disabled.
if(isset($_GET["size"]) and $_GET["size"] == "original" or if(isset($_GET["size"]) and $_GET["size"] == "original" or
@ -322,18 +322,18 @@ register_module([
{ {
// Get the file size // Get the file size
$filesize = filesize($filepath); $filesize = filesize($filepath);
// Send some headers // Send some headers
header("content-length: $filesize"); header("content-length: $filesize");
header("content-type: $mime_type"); header("content-type: $mime_type");
// Open the file and send it to the user // Open the file and send it to the user
$handle = fopen($filepath, "rb"); $handle = fopen($filepath, "rb");
fpassthru($handle); fpassthru($handle);
fclose($handle); fclose($handle);
exit(); exit();
} }
// Determine the target size of the image // Determine the target size of the image
$target_size = 512; $target_size = 512;
if(isset($_GET["size"])) if(isset($_GET["size"]))
@ -342,12 +342,12 @@ register_module([
$target_size = $settings->min_preview_size; $target_size = $settings->min_preview_size;
if($target_size > $settings->max_preview_size) if($target_size > $settings->max_preview_size)
$target_size = $settings->max_preview_size; $target_size = $settings->max_preview_size;
// Determine the output file type // Determine the output file type
$output_mime = $settings->preview_file_type; $output_mime = $settings->preview_file_type;
if(isset($_GET["type"]) and in_array($_GET["type"], [ "image/png", "image/jpeg", "image/webp" ])) if(isset($_GET["type"]) and in_array($_GET["type"], [ "image/png", "image/jpeg", "image/webp" ]))
$output_mime = $_GET["type"]; $output_mime = $_GET["type"];
/// ETag handling /// /// ETag handling ///
// Generate the etag and send it to the client // Generate the etag and send it to the client
$preview_etag = sha1("$output_mime|$target_size|$filepath|$mime_type"); $preview_etag = sha1("$output_mime|$target_size|$filepath|$mime_type");
@ -367,24 +367,24 @@ register_module([
} }
} }
/// ETag handling end /// /// ETag handling end ///
/* Disabled until we work out what to do about caching previews * /* Disabled until we work out what to do about caching previews *
$previewFilename = "$filepath.preview.$outputFormat"; $previewFilename = "$filepath.preview.$outputFormat";
if($target_size === $settings->default_preview_size) if($target_size === $settings->default_preview_size)
{ {
// The request is for the default preview size // The request is for the default preview size
// Check to see if we have a preview pre-rendered // Check to see if we have a preview pre-rendered
} }
*/ */
$preview = new Imagick(); $preview = new Imagick();
switch(substr($mime_type, 0, strpos($mime_type, "/"))) switch(substr($mime_type, 0, strpos($mime_type, "/")))
{ {
case "image": case "image":
$preview->readImage($filepath); $preview->readImage($filepath);
break; break;
case "application": case "application":
if($mime_type == "application/pdf") if($mime_type == "application/pdf")
{ {
@ -394,7 +394,7 @@ register_module([
$preview->setImageColorspace(255); $preview->setImageColorspace(255);
break; break;
} }
case "video": case "video":
case "audio": case "audio":
if($settings->data_storage_dir == ".") if($settings->data_storage_dir == ".")
@ -408,18 +408,18 @@ register_module([
// TODO: Add support for ranges here. // TODO: Add support for ranges here.
// Get the file size // Get the file size
$filesize = filesize($filepath); $filesize = filesize($filepath);
// Send some headers // Send some headers
header("content-length: $filesize"); header("content-length: $filesize");
header("content-type: $mime_type"); header("content-type: $mime_type");
// Open the file and send it to the user // Open the file and send it to the user
$handle = fopen($filepath, "rb"); $handle = fopen($filepath, "rb");
fpassthru($handle); fpassthru($handle);
fclose($handle); fclose($handle);
exit(); exit();
break; break;
default: default:
http_response_code(501); http_response_code(501);
$preview = errorimage("Unrecognised file type '$mime_type'.", $target_size); $preview = errorimage("Unrecognised file type '$mime_type'.", $target_size);
@ -427,10 +427,10 @@ register_module([
imagepng($preview); imagepng($preview);
exit(); exit();
} }
// Scale the image down to the target size // Scale the image down to the target size
$preview->resizeImage($target_size, $target_size, imagick::FILTER_LANCZOS, 1, true); $preview->resizeImage($target_size, $target_size, imagick::FILTER_LANCZOS, 1, true);
// Send the completed preview image to the user // Send the completed preview image to the user
header("content-type: $output_mime"); header("content-type: $output_mime");
header("x-generation-time: " . (microtime(true) - $start_time) . "s"); header("x-generation-time: " . (microtime(true) - $start_time) . "s");
@ -443,14 +443,14 @@ register_module([
file_put_contents($previewFilename, $preview->getImageBlob()); file_put_contents($previewFilename, $preview->getImageBlob());
*/ */
}); });
/* /*
* ██████ ██████ ███████ ██ ██ ██ ███████ ██ ██ * ██████ ██████ ███████ ██ ██ ██ ███████ ██ ██
* ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ * ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
* ██████ ██████ █████ ██ ██ ██ █████ ██ ██ * ██████ ██████ █████ ██ ██ ██ █████ ██ ██
* ██ ██ ██ ██ ██ ██ ██ ██ ██ ███ ██ * ██ ██ ██ ██ ██ ██ ██ ██ ██ ███ ██
* ██ ██ ██ ███████ ████ ██ ███████ ███ ███ * ██ ██ ██ ███████ ████ ██ ███████ ███ ███
* *
* ██████ ██ ███████ ██████ ██ █████ ██ ██ ███████ ██████ * ██████ ██ ███████ ██████ ██ █████ ██ ██ ███████ ██████
* ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ * ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
* ██ ██ ██ ███████ ██████ ██ ███████ ████ █████ ██████ * ██ ██ ██ ███████ ██████ ██ ███████ ████ █████ ██████
@ -462,7 +462,7 @@ register_module([
// Don't do anything if the action isn't view // Don't do anything if the action isn't view
if($env->action !== "view") if($env->action !== "view")
return; return;
if(isset($pageindex->{$env->page}->uploadedfile) and $pageindex->{$env->page}->uploadedfile == true) if(isset($pageindex->{$env->page}->uploadedfile) and $pageindex->{$env->page}->uploadedfile == true)
{ {
// We are looking at a page that is paired with an uploaded file // We are looking at a page that is paired with an uploaded file
@ -474,7 +474,7 @@ register_module([
$originalUrl = $env->storage_prefix == "./" ? $filepath : "?action=preview&size=original&page=" . rawurlencode($env->page); $originalUrl = $env->storage_prefix == "./" ? $filepath : "?action=preview&size=original&page=" . rawurlencode($env->page);
if($mime_type == "application/pdf") if($mime_type == "application/pdf")
$fileTypeDisplay = "pdf"; $fileTypeDisplay = "pdf";
$preview_html = ""; $preview_html = "";
switch($fileTypeDisplay) switch($fileTypeDisplay)
{ {
@ -494,29 +494,29 @@ register_module([
} }
$preview_html .= "</ul></nav>\n\t\t\t</figure>"; $preview_html .= "</ul></nav>\n\t\t\t</figure>";
break; break;
case "video": case "video":
$preview_html .= "\t\t\t<figure class='preview'> $preview_html .= "\t\t\t<figure class='preview'>
<video src='$previewUrl' controls preload='metadata'>Your browser doesn't support HTML5 video, but you can still <a href='$previewUrl'>download it</a> if you'd like.</video> <video src='$previewUrl' controls preload='metadata'>Your browser doesn't support HTML5 video, but you can still <a href='$previewUrl'>download it</a> if you'd like.</video>
</figure>"; </figure>";
break; break;
case "audio": case "audio":
$preview_html .= "\t\t\t<figure class='preview'> $preview_html .= "\t\t\t<figure class='preview'>
<audio src='$previewUrl' controls preload='metadata'>Your browser doesn't support HTML5 audio, but you can still <a href='$previewUrl'>download it</a> if you'd like.</audio> <audio src='$previewUrl' controls preload='metadata'>Your browser doesn't support HTML5 audio, but you can still <a href='$previewUrl'>download it</a> if you'd like.</audio>
</figure>"; </figure>";
break; break;
case "pdf": case "pdf":
$preview_html .= "\t\t\t<object type='application/pdf' data='$originalUrl'></object>"; $preview_html .= "\t\t\t<object type='application/pdf' data='$originalUrl'></object>";
break; break;
default: default:
$preview_html .= "\t\t\t<p><em>No preview is available, but you can download it instead:</em></p> $preview_html .= "\t\t\t<p><em>No preview is available, but you can download it instead:</em></p>
<a class='button' href='$originalUrl'>Download</a>"; <a class='button' href='$originalUrl'>Download</a>";
break; break;
} }
$fileInfo = []; $fileInfo = [];
$fileInfo["Name"] = str_replace("Files/", "", $filepath); $fileInfo["Name"] = str_replace("Files/", "", $filepath);
$fileInfo["Type"] = $mime_type; $fileInfo["Type"] = $mime_type;
@ -530,7 +530,7 @@ register_module([
} }
$fileInfo["Uploaded by"] = $pageindex->{$env->page}->lasteditor; $fileInfo["Uploaded by"] = $pageindex->{$env->page}->lasteditor;
$fileInfo["Short markdown embed code"] = "<input type='text' class='short-embed-markdown-code' value='![" . htmlentities($fileInfo["Name"], ENT_QUOTES | ENT_HTML5) . "](" . htmlentities($filepath, ENT_QUOTES | ENT_HTML5) . " | right | 350x350)' readonly /> <button class='short-embed-markdown-button'>Copy</button>"; $fileInfo["Short markdown embed code"] = "<input type='text' class='short-embed-markdown-code' value='![" . htmlentities($fileInfo["Name"], ENT_QUOTES | ENT_HTML5) . "](" . htmlentities($filepath, ENT_QUOTES | ENT_HTML5) . " | right | 350x350)' readonly /> <button class='short-embed-markdown-button'>Copy</button>";
$preview_html .= "\t\t\t<h2>File Information</h2> $preview_html .= "\t\t\t<h2>File Information</h2>
<table>"; <table>";
foreach ($fileInfo as $displayName => $displayValue) foreach ($fileInfo as $displayName => $displayValue)
@ -538,11 +538,11 @@ register_module([
$preview_html .= "<tr><th>$displayName</th><td>$displayValue</td></tr>\n"; $preview_html .= "<tr><th>$displayName</th><td>$displayValue</td></tr>\n";
} }
$preview_html .= "</table>"; $preview_html .= "</table>";
$parts["{content}"] = str_replace("</h1>", "</h1>\n$preview_html", $parts["{content}"]); $parts["{content}"] = str_replace("</h1>", "</h1>\n$preview_html", $parts["{content}"]);
} }
}); });
// Add the snippet that copies the embed markdown code to the clipboard // Add the snippet that copies the embed markdown code to the clipboard
page_renderer::add_js_snippet('window.addEventListener("load", function(event) { page_renderer::add_js_snippet('window.addEventListener("load", function(event) {
let button = document.querySelector(".short-embed-markdown-button"); let button = document.querySelector(".short-embed-markdown-button");
@ -553,7 +553,7 @@ register_module([
button.innerHTML = document.execCommand("copy") ? "Copied!" : "Failed to copy :-("; button.innerHTML = document.execCommand("copy") ? "Copied!" : "Failed to copy :-(";
}); });
});'); });');
// Register a section on the help page on uploading files // Register a section on the help page on uploading files
add_help_section("28-uploading-files", "Uploading Files", "<p>$settings->sitename supports the uploading of files, though it is up to " . $settings->admindetails_name . ", $settings->sitename's administrator as to whether it is enabled or not (uploads are currently " . (($settings->upload_enabled) ? "enabled" : "disabled") . ").</p> add_help_section("28-uploading-files", "Uploading Files", "<p>$settings->sitename supports the uploading of files, though it is up to " . $settings->admindetails_name . ", $settings->sitename's administrator as to whether it is enabled or not (uploads are currently " . (($settings->upload_enabled) ? "enabled" : "disabled") . ").</p>
<p>Currently Pepperminty Wiki (the software that $settings->sitename uses) only supports the uploading of images, videos, and audio, although more file types should be supported in the future (<a href='//github.com/sbrl/Pepperminty-Wiki/issues'>open an issue on GitHub</a> if you are interested in support for more file types).</p> <p>Currently Pepperminty Wiki (the software that $settings->sitename uses) only supports the uploading of images, videos, and audio, although more file types should be supported in the future (<a href='//github.com/sbrl/Pepperminty-Wiki/issues'>open an issue on GitHub</a> if you are interested in support for more file types).</p>
@ -622,7 +622,7 @@ function upload_check_svg($temp_filename)
exit(page_renderer::render("Upload Error - $settings->sitename", "<p>$settings->sitename detected that you uploaded an SVG image and performed some extra security checks on your file. Whilst performing these checks it was discovered that the file you uploaded contains some Javascript, which could be dangerous. The uploaded file has been discarded. <a href='?action=upload'>Go back to try again</a>.</p> exit(page_renderer::render("Upload Error - $settings->sitename", "<p>$settings->sitename detected that you uploaded an SVG image and performed some extra security checks on your file. Whilst performing these checks it was discovered that the file you uploaded contains some Javascript, which could be dangerous. 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>")); <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>"));
} }
// Find and return the size of the SVG image // Find and return the size of the SVG image
return getsvgsize($temp_filename); return getsvgsize($temp_filename);
} }
@ -652,7 +652,7 @@ function getsvgsize($svgFilename)
$imageSize = [ intval($rootAttrs->width), intval($rootAttrs->height) ]; $imageSize = [ intval($rootAttrs->width), intval($rootAttrs->height) ];
else if(isset($rootAttrs->viewBox)) else if(isset($rootAttrs->viewBox))
$imageSize = array_map("intval", array_slice(explode(" ", $rootAttrs->viewBox), -2, 2)); $imageSize = array_map("intval", array_slice(explode(" ", $rootAttrs->viewBox), -2, 2));
return $imageSize; return $imageSize;
} }

View file

@ -7,17 +7,17 @@ register_module([
"id" => "page-login", "id" => "page-login",
"code" => function() { "code" => function() {
global $settings; global $settings;
/** /**
* @api {get} ?action=login[&failed=yes][&returnto={someUrl}] Get the login page * @api {get} ?action=login[&failed=yes][&returnto={someUrl}] Get the login page
* @apiName Login * @apiName Login
* @apiGroup Authorisation * @apiGroup Authorisation
* @apiPermission Anonymous * @apiPermission Anonymous
* *
* @apiParam {string} failed Setting to yes causes a login failure message to be displayed above the login form. * @apiParam {string} failed Setting to yes causes a login failure message to be displayed above the login form.
* @apiParam {string} returnto Set to the url to redirect to upon a successful login. * @apiParam {string} returnto Set to the url to redirect to upon a successful login.
*/ */
/* /*
* ██ ██████ ██████ ██ ███ ██ * ██ ██████ ██████ ██ ███ ██
* ██ ██ ██ ██ ██ ████ ██ * ██ ██ ██ ██ ██ ████ ██
@ -27,18 +27,18 @@ register_module([
*/ */
add_action("login", function() { add_action("login", function() {
global $settings, $env; global $settings, $env;
// Build the action url that will actually perform the login // Build the action url that will actually perform the login
$login_form_action_url = "index.php?action=checklogin"; $login_form_action_url = "index.php?action=checklogin";
if(isset($_GET["returnto"])) if(isset($_GET["returnto"]))
$login_form_action_url .= "&returnto=" . rawurlencode($_GET["returnto"]); $login_form_action_url .= "&returnto=" . rawurlencode($_GET["returnto"]);
if($env->is_logged_in && !empty($_GET["returnto"])) if($env->is_logged_in && !empty($_GET["returnto"]))
{ {
http_response_code(307); http_response_code(307);
header("location: " . $_GET["returnto"]); header("location: " . $_GET["returnto"]);
} }
$title = "Login to $settings->sitename"; $title = "Login to $settings->sitename";
$content = "<h1>Login to $settings->sitename</h1>\n"; $content = "<h1>Login to $settings->sitename</h1>\n";
if(isset($_GET["failed"])) if(isset($_GET["failed"]))
@ -56,27 +56,27 @@ register_module([
</form>\n"; </form>\n";
exit(page_renderer::render_main($title, $content)); exit(page_renderer::render_main($title, $content));
}); });
/** /**
* @api {post} ?action=checklogin Perform a login * @api {post} ?action=checklogin Perform a login
* @apiName CheckLogin * @apiName CheckLogin
* @apiGroup Authorisation * @apiGroup Authorisation
* @apiPermission Anonymous * @apiPermission Anonymous
* *
* @apiParam {string} user The user name to login with. * @apiParam {string} user The user name to login with.
* @apiParam {string} pass The password to login with. * @apiParam {string} pass The password to login with.
* @apiParam {string} returnto The URL to redirect to upon a successful login. * @apiParam {string} returnto The URL to redirect to upon a successful login.
* *
* @apiError InvalidCredentialsError The supplied credentials were invalid. Note that this error is actually a redirect to ?action=login&failed=yes (with the returnto parameter appended if you supplied one) * @apiError InvalidCredentialsError The supplied credentials were invalid. Note that this error is actually a redirect to ?action=login&failed=yes (with the returnto parameter appended if you supplied one)
*/ */
/* /*
* ██████ ██ ██ ███████ ██████ ██ ██ * ██████ ██ ██ ███████ ██████ ██ ██
* ██ ██ ██ ██ ██ ██ ██ * ██ ██ ██ ██ ██ ██ ██
* ██ ███████ █████ ██ █████ * ██ ███████ █████ ██ █████
* ██ ██ ██ ██ ██ ██ ██ * ██ ██ ██ ██ ██ ██ ██
* ██████ ██ ██ ███████ ██████ ██ ██ * ██████ ██ ██ ███████ ██████ ██ ██
* *
* ██ ██████ ██████ ██ ███ ██ * ██ ██████ ██████ ██ ███ ██
* ██ ██ ██ ██ ██ ████ ██ * ██ ██ ██ ██ ██ ████ ██
* ██ ██ ██ ██ ███ ██ ██ ██ ██ * ██ ██ ██ ██ ███ ██ ██ ██ ██
@ -85,7 +85,7 @@ register_module([
*/ */
add_action("checklogin", function() { add_action("checklogin", function() {
global $settings, $env; global $settings, $env;
if(!isset($_POST["user"]) or !isset($_POST["pass"])) { if(!isset($_POST["user"]) or !isset($_POST["pass"])) {
http_response_code(302); http_response_code(302);
$nextUrl = "index.php?action=login&failed=yes&badrequest=yes"; $nextUrl = "index.php?action=login&failed=yes&badrequest=yes";
@ -94,13 +94,13 @@ register_module([
header("location: $nextUrl"); header("location: $nextUrl");
exit(); exit();
} }
// Actually do the login // Actually do the login
// The user wants to log in // The user wants to log in
$user = $_POST["user"]; $user = $_POST["user"];
$pass = $_POST["pass"]; $pass = $_POST["pass"];
// Verify their password // Verify their password
if(empty($settings->users->$user) || !verify_password($pass, $settings->users->$user->password)) { if(empty($settings->users->$user) || !verify_password($pass, $settings->users->$user->password)) {
// Login failed :-( // Login failed :-(
@ -112,16 +112,16 @@ register_module([
header("location: $nextUrl"); header("location: $nextUrl");
exit(); exit();
} }
// Success! :D // Success! :D
// Update the environment // Update the environment
$env->is_logged_in = true; $env->is_logged_in = true;
$env->user = $user; $env->user = $user;
$env->user_data = $settings->users->{$env->user}; $env->user_data = $settings->users->{$env->user};
$new_password_hash = hash_password_update($pass, $settings->users->$user->password); $new_password_hash = hash_password_update($pass, $settings->users->$user->password);
// Update the password hash // Update the password hash
if($new_password_hash !== null) { if($new_password_hash !== null) {
$env->user_data->password = $new_password_hash; $env->user_data->password = $new_password_hash;
@ -132,18 +132,18 @@ register_module([
} }
error_log("[Pepperminty Wiki] Updated password hash for $user."); error_log("[Pepperminty Wiki] Updated password hash for $user.");
} }
// If the email address is still in the old field, migrate it // If the email address is still in the old field, migrate it
if(!empty($settings->users->{$user}->email)) { if(!empty($settings->users->{$user}->email)) {
$settings->users->{$user}->emailAddress = $settings->users->{$user}->email; $settings->users->{$user}->emailAddress = $settings->users->{$user}->email;
unset($settings->users->{$user}->email); unset($settings->users->{$user}->email);
save_settings(); save_settings();
} }
$_SESSION["$settings->sessionprefix-user"] = $user; $_SESSION["$settings->sessionprefix-user"] = $user;
$_SESSION["$settings->sessionprefix-pass"] = $new_password_hash ?? hash_password($pass); $_SESSION["$settings->sessionprefix-pass"] = $new_password_hash ?? hash_password($pass);
$_SESSION["$settings->sessionprefix-expiretime"] = time() + 60*60*24*30; // 30 days from now $_SESSION["$settings->sessionprefix-expiretime"] = time() + 60*60*24*30; // 30 days from now
// Redirect to wherever the user was going // Redirect to wherever the user was going
http_response_code(302); http_response_code(302);
header("x-login-success: yes"); header("x-login-success: yes");
@ -153,34 +153,34 @@ register_module([
header("location: index.php"); header("location: index.php");
exit(); exit();
}); });
add_action("hash-cost-test", function() { add_action("hash-cost-test", function() {
global $env; global $env;
header("content-type: text/plain"); header("content-type: text/plain");
if(!$env->is_logged_in || !$env->is_admin) { if(!$env->is_logged_in || !$env->is_admin) {
http_response_code(401); http_response_code(401);
exit("Error: Only moderators are allowed to use this action."); exit("Error: Only moderators are allowed to use this action.");
} }
$time_compute = microtime(true); $time_compute = microtime(true);
$cost = hash_password_compute_cost(true); $cost = hash_password_compute_cost(true);
$time_compute = (microtime(true) - $time_compute)*1000; $time_compute = (microtime(true) - $time_compute)*1000;
$time_cost = microtime(true); $time_cost = microtime(true);
password_hash("testing", PASSWORD_DEFAULT, [ "cost" => $cost ]); password_hash("testing", PASSWORD_DEFAULT, [ "cost" => $cost ]);
$time_cost = (microtime(true) - $time_cost)*1000; $time_cost = (microtime(true) - $time_cost)*1000;
echo("Calculated cost: $cost ({$time_cost}ms)\n"); echo("Calculated cost: $cost ({$time_cost}ms)\n");
echo("Time taken: {$time_compute}ms\n"); echo("Time taken: {$time_compute}ms\n");
exit(date("r")); exit(date("r"));
}); });
// Register a section on logging in on the help page. // Register a section on logging in on the help page.
add_help_section("30-login", "Logging in", "<p>In order to edit $settings->sitename and have your edit attributed to you, you need to be logged in. Depending on the settings, logging in may be a required step if you want to edit at all. Thankfully, loggging in is not hard. Simply click the &quot;Login&quot; link in the top left, type your username and password, and then click login.</p> add_help_section("30-login", "Logging in", "<p>In order to edit $settings->sitename and have your edit attributed to you, you need to be logged in. Depending on the settings, logging in may be a required step if you want to edit at all. Thankfully, loggging in is not hard. Simply click the &quot;Login&quot; link in the top left, type your username and password, and then click login.</p>
<p>If you do not have an account yet and would like one, try contacting <a href='mailto:" . hide_email($settings->admindetails_email) . "'>$settings->admindetails_name</a>, $settings->sitename's administrator and ask them nicely to see if they can create you an account.</p>"); <p>If you do not have an account yet and would like one, try contacting <a href='mailto:" . hide_email($settings->admindetails_email) . "'>$settings->admindetails_name</a>, $settings->sitename's administrator and ask them nicely to see if they can create you an account.</p>");
// Re-check the password hashing cost, if necessary // Re-check the password hashing cost, if necessary
do_password_hash_code_update(); do_password_hash_code_update();
} }
@ -191,11 +191,11 @@ register_module([
*/ */
function do_password_hash_code_update() { function do_password_hash_code_update() {
global $settings, $paths; global $settings, $paths;
// There's no point if we're using Argon2i, as it doesn't take a cost // There's no point if we're using Argon2i, as it doesn't take a cost
if(hash_password_properties()["algorithm"] == PASSWORD_ARGON2I) if(defined("PASSWORD_ARGON2I") && hash_password_properties()["algorithm"] == PASSWORD_ARGON2I)
return; return;
// Skip rechecking if the automatic check has been disabled // Skip rechecking if the automatic check has been disabled
if($settings->password_cost_time_interval == -1) if($settings->password_cost_time_interval == -1)
return; return;
@ -203,9 +203,9 @@ function do_password_hash_code_update() {
if(isset($settings->password_cost_time_lastcheck) && if(isset($settings->password_cost_time_lastcheck) &&
time() - $settings->password_cost_time_lastcheck < $settings->password_cost_time_interval) time() - $settings->password_cost_time_lastcheck < $settings->password_cost_time_interval)
return; return;
$new_cost = hash_password_compute_cost(); $new_cost = hash_password_compute_cost();
// Save the new cost, but only if it's higher than the old one // Save the new cost, but only if it's higher than the old one
if($new_cost > $settings->password_cost) if($new_cost > $settings->password_cost)
$settings->password_cost = $new_cost; $settings->password_cost = $new_cost;
@ -215,13 +215,13 @@ function do_password_hash_code_update() {
} }
/** /**
* Figures out the appropriate algorithm & options for hashing passwords based * Figures out the appropriate algorithm & options for hashing passwords based
* on the current settings. * on the current settings.
* @return array The appropriate password hashing algorithm and options. * @return array The appropriate password hashing algorithm and options.
*/ */
function hash_password_properties() { function hash_password_properties() {
global $settings; global $settings;
$result = [ $result = [
"algorithm" => constant($settings->password_algorithm), "algorithm" => constant($settings->password_algorithm),
"options" => [ "cost" => $settings->password_cost ] "options" => [ "cost" => $settings->password_cost ]
@ -235,7 +235,7 @@ function hash_password_properties() {
* in $settings. * in $settings.
* @package page-login * @package page-login
* @param string $pass The password to hash. * @param string $pass The password to hash.
* *
* @return string The hashed password. Uses password_hash() under-the-hood, but with some additional extras to avoid known issues. * @return string The hashed password. Uses password_hash() under-the-hood, but with some additional extras to avoid known issues.
*/ */
function hash_password($pass) { function hash_password($pass) {
@ -272,7 +272,7 @@ function hash_password_update($pass, $hash) {
/** /**
* Computes the appropriate cost value for password_hash based on the settings * Computes the appropriate cost value for password_hash based on the settings
* automatically. * automatically.
* Starts at 10 and works upwards in increments of 1. Goes on until a value is * Starts at 10 and works upwards in increments of 1. Goes on until a value is
* found that's greater than the target - or 10x the target time elapses. * found that's greater than the target - or 10x the target time elapses.
* @param bool $verbose Whether to output verbose progress information to the client or not. * @param bool $verbose Whether to output verbose progress information to the client or not.
* @return int The automatically calculated password hashing cost. * @return int The automatically calculated password hashing cost.
@ -283,9 +283,9 @@ function hash_password_compute_cost($verbose = false) {
if($props["algorithm"] == PASSWORD_ARGON2I) if($props["algorithm"] == PASSWORD_ARGON2I)
return null; return null;
$props["options"]["cost"] = 10; $props["options"]["cost"] = 10;
$target_cost_time = $settings->password_cost_time / 1000; // The setting is in ms $target_cost_time = $settings->password_cost_time / 1000; // The setting is in ms
do { do {
$props["options"]["cost"]++; $props["options"]["cost"]++;
$start_i = microtime(true); $start_i = microtime(true);
@ -297,6 +297,6 @@ function hash_password_compute_cost($verbose = false) {
// time in total (in case the specified algorithm doesn't take a // time in total (in case the specified algorithm doesn't take a
// cost parameter) // cost parameter)
} while($end_i - $start_i < $target_cost_time); } while($end_i - $start_i < $target_cost_time);
return $props["options"]["cost"]; return $props["options"]["cost"];
} }