Commit c1e6a6d3 authored by Malcolm Blaney's avatar Malcolm Blaney

Changes to conform to PSR, security improvements found from running

OWASP ZAP. Switched password generation and checking from crypt to
password_hash and password_verify.
parent 08b85023
<?php
// Dobrado Content Management System
// Copyright (C) 2013 Malcolm Blaney
// Copyright (C) 2016 Malcolm Blaney
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
......@@ -15,22 +15,20 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
include "php/functions/copy_page.php";
include "php/functions/db.php";
include "php/functions/new_user.php";
include "php/functions/permission.php";
include "php/functions/style.php";
include "php/functions/write_style.php";
include 'php/functions/copy_page.php';
include 'php/functions/db.php';
include 'php/functions/new_user.php';
include 'php/functions/permission.php';
include 'php/functions/style.php';
include 'php/functions/write_style.php';
include "php/config.php";
include "php/module.php";
include "php/page.php";
include "php/user.php";
include 'php/config.php';
include 'php/module.php';
include 'php/page.php';
include 'php/user.php';
session_start();
$user = new User();
// Need to create an object context here so that $this is defined.
$page = new Page($user, "admin");
?>
$page = new Page($user, 'admin');
<?php
// Dobrado Content Management System
// Copyright (C) 2015 Malcolm Blaney
// Copyright (C) 2016 Malcolm Blaney
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
......@@ -69,7 +69,7 @@ class Banking extends Base {
if ($result = $mysqli->query($query)) {
if ($banking = $result->fetch_assoc()) {
$bsb = $banking['bsb'];
$credit = $banking['credit'];
$credit = (int)$banking['credit'];
$name = $banking['name'];
$number = $banking['number'];
$reference = $banking['reference'];
......@@ -81,7 +81,7 @@ class Banking extends Base {
}
$mysqli->close();
$checked = $credit == '0' ? '' : ' checked';
$checked = $credit === 0 ? '' : ' checked';
return '<div class="reference">Please use: <b>'.$reference.
'</b> as the reference when paying your account.</div>'.
'<div class="edit"><a href="#">Edit your bank details</a></div>'.
......@@ -142,7 +142,7 @@ class Banking extends Base {
$query = 'SELECT reference FROM banking WHERE '.
'reference = "'.$reference.'"';
if ($result = $mysqli->query($query)) {
if ($result->num_rows == 0) {
if ($result->num_rows === 0) {
$result->close();
break;
}
......@@ -177,7 +177,7 @@ class Banking extends Base {
public function Factory($fn, $p = NULL) {
// This is used by functions/new_user.php
if ($fn == 'Settings') {
if ($fn === 'Settings') {
return $this->Settings($p);
}
}
......@@ -251,21 +251,7 @@ class Banking extends Base {
public function Update() {
// This is called when the version of the module is updated,
// to provide a way to update or modify tables etc..
$mysqli = connect_db();
$query = 'ALTER TABLE banking MODIFY COLUMN reference VARCHAR(20)';
if (!$mysqli->query($query)) {
$this->Log('Banking->Update 1: '.$mysqli->error);
}
$query = 'ALTER TABLE banking ADD COLUMN deposit TINYINT(1) '.
'AFTER surcharge';
if (!$mysqli->query($query)) {
$this->Log('Banking->Update 2: '.$mysqli->error);
}
$query = 'UPDATE banking SET deposit = 1';
if (!$mysqli->query($query)) {
$this->Log('Banking->Update 3: '.$mysqli->error);
}
$mysqli->close();
}
public function UpdateScript($path) {
......@@ -290,9 +276,7 @@ class Banking extends Base {
if ($invited && count($joined) !== 0) {
$group_query = '';
for ($i = 0; $i < count($joined); $i++) {
if ($group_query != '') {
$group_query .= ' OR ';
}
if ($group_query !== '') $group_query .= ' OR ';
$group_query .= 'users.system_group = "'.$joined[$i].'"';
}
// Add the host group too.
......@@ -319,7 +303,7 @@ class Banking extends Base {
$group = $banking['buyer_group'];
$user_list[] = $user;
// If the buyer_group for a user is empty, the default used is 'price'.
$buyer_groups[$user] = $group == '' ? 'price' : $group;
$buyer_groups[$user] = $group === '' ? 'price' : $group;
}
$result->close();
}
......@@ -384,7 +368,7 @@ class Banking extends Base {
$this->Log('Banking->BuyerGroup: '.$mysqli->error);
}
$mysqli->close();
return $buyer_group == '' ? 'price' : $buyer_group;
return $buyer_group === '' ? 'price' : $buyer_group;
}
public function UpdateUser($username, $reference, $name, $number, $bsb,
......@@ -403,7 +387,7 @@ class Banking extends Base {
else {
$this->Log('Banking->UpdateUser 1: '.$mysqli->error);
}
if ($count != 0) {
if ($count !== 0) {
$mysqli->close();
return array('error' => 'Bank reference already exists');
}
......@@ -434,12 +418,10 @@ class Banking extends Base {
$mysqli = connect_db();
$user_query = '';
for ($i = 0; $i < count($us_next_week); $i++) {
if ($user_query != '') {
$user_query .= ' OR ';
}
if ($user_query !== '') $user_query .= ' OR ';
$user_query .= 'user = "'.$mysqli->escape_string($us_next_week[$i]).'"';
}
if ($user_query != '') {
if ($user_query !== '') {
$query = 'UPDATE banking SET next_week='.$timestamp.' WHERE '.$user_query;
if (!$mysqli->query($query)) {
$this->Log('Banking->SaveNextWeek: '.$mysqli->error);
......@@ -493,8 +475,4 @@ class Banking extends Base {
return $banking;
}
// Private functions below here ////////////////////////////////////////////
}
?>
\ No newline at end of file
......@@ -23,15 +23,15 @@ class Browser extends Base {
public function Callback() {
if (!$this->user->loggedIn) {
return array("error" => "Permission denied.");
return array('error' => 'Permission denied.');
}
$us_action = $_POST['action'];
if ($us_action == "remove") {
if ($us_action === 'remove') {
return $this->RemoveFile();
}
if ($us_action == "upload") {
if ($us_action === 'upload') {
return $this->UploadFile();
}
}
......@@ -56,22 +56,22 @@ class Browser extends Base {
'<button class="upload">upload</button>'.
'</form>';
$html_path = $this->user->name == "admin" ? "/public/" :
"/".$this->user->name."/public/";
$html_path = $this->user->name === 'admin' ? '/public/' :
'/'.$this->user->name.'/public/';
// Display thumbnails for all files in this user's public folder.
if ($handle = opendir($this->PublicDirectory())) {
while (($file = readdir($handle)) !== false) {
// Ignore actual thumbnail files.
if (strpos($file, "_thumb.")) {
if (strpos($file, '_thumb.')) {
continue;
}
if (preg_match('/^(.+)\.(.+)$/', $file, $matches)) {
$name = $matches[1];
$type = $matches[2];
$src = "";
$src = '';
if (in_array($type, array("gif", "jpeg", "jpg", "png"))) {
$name .= "_thumb.".$type;
if (in_array($type, array('gif', 'jpeg', 'jpg', 'png'))) {
$name .= '_thumb.'.$type;
if (file_exists($this->PublicDirectory($name))) {
$src = '<img src="'.$html_path.$name.'">';
}
......@@ -110,7 +110,7 @@ class Browser extends Base {
}
public function Install($path) {
$this->AppendScript($path, "dobrado.browser.js", false);
$this->AppendScript($path, 'dobrado.browser.js', false);
$site_style = array('"",".browser","display","none"',
'"",".upload","float","right"',
'"","#upload-form","padding","20px 0px"',
......@@ -131,7 +131,7 @@ class Browser extends Base {
}
public function Placement() {
return "outside";
return 'outside';
}
public function Publish($id, $update) {
......@@ -152,73 +152,73 @@ class Browser extends Base {
}
public function UpdateScript($path) {
$this->AppendScript($path, "dobrado.browser.js", false);
$this->AppendScript($path, 'dobrado.browser.js', false);
}
// Private functions below here ////////////////////////////////////////////
private function RemoveFile() {
$regex = "/^([a-z0-9_-]{1,200})\.([a-z0-9]{1,10})$/i";
$us_file = basename($_POST["file"]);
$regex = '/^([a-z0-9_-]{1,200})\.([a-z0-9]{1,10})$/i';
$us_file = basename($_POST['file']);
if (preg_match($regex, $us_file, $matches)) {
$name = $matches[1];
$type = $matches[2];
unlink($this->PublicDirectory($us_file));
if (in_array($type, array("gif", "jpeg", "jpg", "png"))) {
unlink($this->PublicDirectory($name."_thumb.".$type));
if (in_array($type, array('gif', 'jpeg', 'jpg', 'png'))) {
unlink($this->PublicDirectory($name.'_thumb.'.$type));
}
return array("done" => true);
return array('done' => true);
}
return array("error" => "Filename does not have correct format.");
return array('error' => 'Filename does not have correct format.');
}
private function UploadFile() {
// First check if the user's upload directory is at capacity.
$handle = popen("/usr/bin/du -sm ".$this->PublicDirectory(), "r");
$handle = popen('/usr/bin/du -sm '.$this->PublicDirectory(), 'r');
$size = fgets($handle);
pclose($handle);
if (!preg_match('/^([0-9]+)/', $size, $matches)) {
return array("error" => "Could not check upload directory");
return array('error' => 'Could not check upload directory');
}
$size = $matches[1];
if ((int)$size > $this->user->config->MaxUpload()) {
return array("error" => "Upload directory full.");
return array('error' => 'Upload directory full.');
}
$max_file_size = $this->user->config->MaxFileSize();
// ["upload"]["size"] is given in bytes, MaxFileSize is in megabytes.
if ($_FILES["upload"]["size"] > $max_file_size * 1000000) {
return array("error" => "Upload file is too large. (max ".
$max_file_size."M)");
// ['upload']['size'] is given in bytes, MaxFileSize is in megabytes.
if ($_FILES['upload']['size'] > $max_file_size * 1000000) {
return array('error' => 'Upload file is too large. (max '.
$max_file_size.'M)');
}
// Replace spaces in the uploaded file name.
$file = preg_replace("/ /", "_", basename($_FILES["upload"]["name"]));
$regex = "/^([a-z0-9_-]{1,200})\.([a-z0-9]{1,10})$/i";
$file = preg_replace('/ /', '_', basename($_FILES['upload']['name']));
$regex = '/^([a-z0-9_-]{1,200})\.([a-z0-9]{1,10})$/i';
if (!preg_match($regex, $file, $matches)) {
return array("error" => "Filename does not have correct format.");
return array('error' => 'Filename does not have correct format.');
}
$path = $this->PublicDirectory($file);
if (file_exists($path)) {
return array("error" => "A file with that name already exists.");
return array('error' => 'A file with that name already exists.');
}
$name = $matches[1];
$type = $matches[2];
// Whitelist all file extensions.
$allowed = array("gif", "jpeg", "jpg", "png", "ogg", "mp3",
"ogv", "webm", "mp4", "pdf");
$allowed = array('gif', 'jpeg', 'jpg', 'png', 'ogg', 'mp3',
'ogv', 'webm', 'mp4', 'pdf');
if (!in_array($type, $allowed)) {
return array("error" => "File type not allowed.");
return array('error' => 'File type not allowed.');
}
$tmp = $_FILES["upload"]["tmp_name"];
$tmp = $_FILES['upload']['tmp_name'];
if (!move_uploaded_file($tmp, $path)) {
return array("error" => "File: ".$file." was not uploaded.");
return array('error' => 'File: '.$file.' was not uploaded.');
}
$name .= "_thumb.".$type;
$html_path = $this->user->name == "admin" ? "/public/" :
"/".$this->user->name."/public/";
$src = "";
$name .= '_thumb.'.$type;
$html_path = $this->user->name === 'admin' ? '/public/' :
'/'.$this->user->name.'/public/';
$src = '';
if (in_array($type, array("gif", "jpeg", "jpg", "png"))) {
if (in_array($type, array('gif', 'jpeg', 'jpg', 'png'))) {
list($old_width, $old_height) = getimagesize($path);
// Create a thumbnail, and resize if height greater than 150px or
// width greater than 300px.
......@@ -255,7 +255,7 @@ class Browser extends Base {
else {
$src = '<b>'.$type.'</b>';
}
return array("content" => $this->ImageContent($src, $html_path.$file));
return array('content' => $this->ImageContent($src, $html_path.$file));
}
private function ImageContent($src, $image) {
......@@ -268,36 +268,34 @@ class Browser extends Base {
private function ResizeImage($new_width, $new_height,
$old_width, $old_height,
$type, $new_path, $old_path="") {
$type, $new_path, $old_path = '') {
// If old path isn't given, saving the new image over the old one.
if ($old_path === "") {
if ($old_path === '') {
$old_path = $new_path;
}
$old_image = null;
if ($type == "gif") {
if ($type === 'gif') {
$old_image = imagecreatefromgif($old_path);
}
else if ($type == "jpg" || $type == "jpeg") {
else if ($type === 'jpg' || $type === 'jpeg') {
$old_image = imagecreatefromjpeg($old_path);
}
else if ($type == "png") {
else if ($type === 'png') {
$old_image = imagecreatefrompng($old_path);
}
$new_image = imagecreatetruecolor($new_width, $new_height);
imagecopyresampled($new_image, $old_image, 0, 0, 0, 0, $new_width,
$new_height, $old_width, $old_height);
if ($type == "gif") {
if ($type === 'gif') {
imagegif($new_image, $new_path);
}
else if ($type == "jpg" || $type == "jpeg") {
else if ($type === 'jpg' || $type === 'jpeg') {
imagejpeg($new_image, $new_path);
}
else if ($type == "png") {
else if ($type === 'png') {
imagepng($new_image, $new_path);
}
}
}
?>
\ No newline at end of file
This diff is collapsed.
......@@ -92,7 +92,7 @@ class Comment extends Base {
}
public function Factory($fn, $p = NULL) {
if ($fn == 'RemoveWebmention') {
if ($fn === 'RemoveWebmention') {
return $this->RemoveWebmention($p);
}
}
......@@ -213,7 +213,7 @@ class Comment extends Base {
$author = $mysqli->escape_string($author);
$author_photo = $mysqli->escape_string($user_detail['thumbnail']);
}
$author_url = $this->user->name == 'admin' ? '/' : '/'.$this->user->name;
$author_url = $this->user->name === 'admin' ? '/' : '/'.$this->user->name;
}
if (isset($us_content['description'])) {
$description = $mysqli->escape_string($us_content['description']);
......@@ -251,35 +251,20 @@ class Comment extends Base {
$mysqli->close();
// If page is published, let subscribers know this feed has been updated.
if ($published == 1) {
if ($published === 1) {
$reader = new Module($this->user, $this->owner, 'reader');
if ($reader->IsInstalled()) {
$page = 'rss/index.php?page='.$page.'&action=comment';
$xml_url = $this->user->config->Secure() ? 'https://' : 'http://';
$xml_url .= $this->user->config->ServerName().'/';
$xml_url .= $this->owner == 'admin' ? $page : $this->owner.'/'.$page;
$xml_url .= $this->owner === 'admin' ? $page : $this->owner.'/'.$page;
$reader->Factory('UpdateFeed', $xml_url);
}
}
}
public function Update() {
$mysqli = connect_db();
$query = 'ALTER TABLE comment ADD COLUMN author_photo VARCHAR(200) AFTER '.
'author';
if (!$mysqli->query($query)) {
$this->Log('Comment->Update 1: '.$mysqli->error);
}
$query = 'ALTER TABLE comment ADD COLUMN author_url VARCHAR(200) AFTER '.
'author_photo';
if (!$mysqli->query($query)) {
$this->Log('Comment->Update 2: '.$mysqli->error);
}
$mysqli->close();
$site_style = array('"",".comment-name .thumb","width","20px"',
'"",".comment-name .thumb","border-radius","2px"');
$this->AddSiteStyle($site_style);
}
public function UpdateScript($path) {
......@@ -305,5 +290,3 @@ class Comment extends Base {
}
}
?>
\ No newline at end of file
......@@ -28,10 +28,10 @@ class Commenteditor extends Base {
}
public function Callback() {
$id = (int)substr($_POST['id'], 9);
$us_mode = $_POST['mode'];
$id = isset($_POST['id']) ? (int)substr($_POST['id'], 9) : 0;
$us_mode = isset($_POST['mode']) ? $_POST['mode'] : '';
// box mode is used by the extended editor.
if ($us_mode == 'box') {
if ($us_mode === 'box') {
$description = '(Comments are open) ';
$checked = '';
if ($this->Locked($id)) {
......@@ -49,7 +49,7 @@ class Commenteditor extends Base {
'<button type="submit">Submit</button>'.
'</form>');
}
if ($us_mode == 'submit' &&
if ($us_mode === 'submit' &&
($this->user->loggedIn || !$this->Locked($id))) {
$comment = new Comment($this->user, $this->owner);
$id = new_module($this->user, $this->owner, 'comment', $this->user->page,
......@@ -84,7 +84,7 @@ class Commenteditor extends Base {
if ($this->user->loggedIn) {
$author = '';
$name = $this->user->name;
$url = $this->user->name == "admin" ? "/" : "/".$this->user->name;
$url = $this->user->name === 'admin' ? '/' : '/'.$this->user->name;
$detail = new Module($this->user, $this->owner, 'detail');
if ($detail->IsInstalled()) {
$user_detail = $detail->Factory('User');
......@@ -148,7 +148,7 @@ class Commenteditor extends Base {
}
public function Group() {
return "post-comment";
return 'post-comment';
}
public function IncludeScript() {
......@@ -156,7 +156,7 @@ class Commenteditor extends Base {
}
public function Install($path) {
$this->AppendScript($path, "dobrado.commenteditor.js", true);
$this->AppendScript($path, 'dobrado.commenteditor.js', true);
$mysqli = connect_db();
$query = 'CREATE TABLE IF NOT EXISTS comment_settings ('.
......@@ -166,7 +166,7 @@ class Commenteditor extends Base {
'PRIMARY KEY(user, box_id)'.
') ENGINE=MyISAM';
if (!$mysqli->query($query)) {
$this->Log("Commenteditor->Install: ".$mysqli->error);
$this->Log('Commenteditor->Install: '.$mysqli->error);
}
$mysqli->close();
......@@ -191,7 +191,7 @@ class Commenteditor extends Base {
}
public function Placement() {
return "middle";
return 'middle';
}
public function Publish($id, $update) {
......@@ -204,27 +204,27 @@ class Commenteditor extends Base {
$query = 'DELETE FROM comment_settings WHERE user = "'.$this->owner.'" '.
'AND box_id = '.$id;
if (!$mysqli->query($query)) {
$this->Log("Commenteditor->Remove 1: ".$mysqli->error);
$this->Log('Commenteditor->Remove 1: '.$mysqli->error);
}
}
else {
$query = 'DELETE FROM comment_settings WHERE user = "'.$this->owner.'"';
if (!$mysqli->query($query)) {
$this->Log("Commenteditor->Remove 2: ".$mysqli->error);
$this->Log('Commenteditor->Remove 2: '.$mysqli->error);
}
}
$mysqli->close();
}
public function SetContent($id, $us_content) {
if (!isset($us_content["comment-lock"])) return;
if (!isset($us_content['comment-lock'])) return;
$mysqli = connect_db();
$locked = $mysqli->escape_string($us_content["comment-lock"]);
$locked = $mysqli->escape_string($us_content['comment-lock']);
$query = 'UPDATE comment_settings SET locked = "'.$locked.'" '.
'WHERE user = "'.$this->owner.'" AND box_id = '.$id;
if (!$mysqli->query($query)) {
$this->Log("Commenteditor->SetContent: ".$mysqli->error);
$this->Log('Commenteditor->SetContent: '.$mysqli->error);
}
$mysqli->close();
}
......@@ -234,11 +234,9 @@ class Commenteditor extends Base {
}
public function UpdateScript($path) {
$this->AppendScript($path, "dobrado.commenteditor.js", true);
$this->AppendScript($path, 'dobrado.commenteditor.js', true);
}
// Public functions that aren't part of interface here /////////////////////
// Private functions below here ////////////////////////////////////////////
private function Locked($id) {
......@@ -248,17 +246,15 @@ class Commenteditor extends Base {
'user = "'.$this->owner.'" AND box_id = '.$id;
if ($result = $mysqli->query($query)) {
if ($comment_settings = $result->fetch_assoc()) {
$locked = $comment_settings["locked"] == 1;
$locked = (int)$comment_settings['locked'] === 1;
}
$result->close();
}
else {
$this->Log("Commenteditor->Locked: ".$mysqli->error);
$this->Log('Commenteditor->Locked: '.$mysqli->error);
}
$mysqli->close();
return $locked;
}
}
?>
\ No newline at end of file
......@@ -51,9 +51,9 @@ class Contact extends Base {
public function Callback() {
// mode is used by the Extended module, which calls this function.
$us_mode = $_POST['mode'];
$us_mode = isset($_POST['mode']) ? $_POST['mode'] : '';
if ($us_mode == 'box') {
$id = (int)substr($_POST['id'], 9);
$id = isset($_POST['id']) ? (int)substr($_POST['id'], 9) : 0;
return array('source' => $this->PlainContent($id), 'editor' => true);
}
if ($us_mode == 'contact') {
......@@ -76,11 +76,11 @@ class Contact extends Base {
'<body><p>A message was received at: <a href="'.$scheme.$server.'">'.
$server.'</a><br>For: <b>'.$name.'</b><br>Details:</p>';
foreach ($us_content as $us_key => $us_value) {
if ($us_key != 'contact-name' && $us_key != 'contact-organisation') {
if ($us_key !== 'contact-name' && $us_key !== 'contact-organisation') {
$us_message .= $us_key.': '.
nl2br(htmlspecialchars($us_value), false).'<br>';
// Look for an email field to use as the 'reply-to'.
if ($us_key == 'email') {
if ($us_key === 'email') {
$reply_to = $us_value;
}
}
......@@ -92,7 +92,7 @@ class Contact extends Base {
$email = $name.' <'.$user.'@'.$server.'>';
$sender = $this->Substitute('system-sender', '/!host/', $server);
$sender_name = $this->Substitute('system-sender-name');
if ($sender_name == '') {
if ($sender_name === '') {
$sender_name = $sender;
}
else {
......@@ -103,13 +103,13 @@ class Contact extends Base {
$headers = "MIME-Version: 1.0\r\n".
"Content-type: text/html; charset=iso-8859-1\r\n".
'From: '.$sender_name."\r\n";
if ($cc != '') {
if ($cc !== '') {
$headers .= 'Cc: '.$cc."\r\n";
}
if ($bcc != '') {
if ($bcc !== '') {
$headers .= 'Bcc: '.$bcc."\r\n";
}
if ($reply_to != '') {
if ($reply_to !== '') {
$headers .= 'Reply-To: '.$reply_to."\r\n";
}
if (mail($email, $subject, $us_message, $headers, '-f '.$sender)) {
......@@ -220,7 +220,7 @@ class Contact extends Base {
}
public function SetContent($id, $us_content) {
if (strcmp($us_content['data'], $this->PlainContent($id)) == 0) return;
if (strcmp($us_content['data'], $this->PlainContent($id)) === 0) return;
$time = time();
$mysqli = connect_db();
......@@ -249,8 +249,6 @@ class Contact extends Base {
$this->AppendScript($path, 'dobrado.contact.js', true);
}
// Public functions that aren't part of interface here /////////////////////
// Private functions below here ////////////////////////////////////////////
private function Insert($id, $content='') {
......@@ -293,5 +291,3 @@ class Contact extends Base {
}
}
?>
\ No newline at end of file
......@@ -18,7 +18,7 @@
class Detail extends Base {
public function Add($id) {
if ($this->user->page == 'index') {
if ($this->user->page === 'index') {
$mysqli = connect_db();
$query = 'INSERT INTO groups VALUES ("'.$this->owner.'", '.
'"comment-notifications-index", "'.$this->owner.'", 0)';
......@@ -124,7 +124,7 @@ class Detail extends Base {
$scheme = $this->user->config->Secure() ? 'https://' : 'http://';
$server_name = $this->user->config->ServerName();
$url = $scheme.$server_name;
if ($this->owner != 'admin') {
if ($this->owner !== 'admin') {
$url .= '/'.$this->owner;
}
$fancy = $this->user->config->FancyUrl();
......@@ -172,7 +172,7 @@ class Detail extends Base {
}
// Make sure only the owner can see other details.
if ($this->user->name == $this->owner) {
if ($this->user->name === $this->owner) {