Ivan - Add Database Backup, FileDownloader, FileUploader and LinearCongruentialGenerator classes [skip ci]

This commit is contained in:
ivan 2017-01-12 16:45:01 -03:00
parent be49990091
commit 134540fed9
14 changed files with 246 additions and 18 deletions

View File

@ -4,7 +4,8 @@
"respect/validation": "^1.1", "respect/validation": "^1.1",
"phpmailer/phpmailer": "^5.2", "phpmailer/phpmailer": "^5.2",
"google/recaptcha": "~1.1", "google/recaptcha": "~1.1",
"gabordemooij/redbean": "^4.3" "gabordemooij/redbean": "^4.3",
"ifsnop/mysqldump-php": "2.*"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "5.0.*" "phpunit/phpunit": "5.0.*"

View File

@ -9,6 +9,8 @@ require_once 'system/get-logs.php';
require_once 'system/get-mail-templates.php'; require_once 'system/get-mail-templates.php';
require_once 'system/edit-mail-template.php'; require_once 'system/edit-mail-template.php';
require_once 'system/recover-mail-template.php'; require_once 'system/recover-mail-template.php';
require_once 'system/backup-database.php';
require_once 'system/download.php';
$systemControllerGroup = new ControllerGroup(); $systemControllerGroup = new ControllerGroup();
$systemControllerGroup->setGroupPath('/system'); $systemControllerGroup->setGroupPath('/system');
@ -23,5 +25,7 @@ $systemControllerGroup->addController(new GetLogsController);
$systemControllerGroup->addController(new GetMailTemplatesController); $systemControllerGroup->addController(new GetMailTemplatesController);
$systemControllerGroup->addController(new EditMailTemplateController); $systemControllerGroup->addController(new EditMailTemplateController);
$systemControllerGroup->addController(new RecoverMailTemplateController); $systemControllerGroup->addController(new RecoverMailTemplateController);
$systemControllerGroup->addController(new BackupDatabaseController);
$systemControllerGroup->addController(new DownloadController);
$systemControllerGroup->finalize(); $systemControllerGroup->finalize();

View File

@ -0,0 +1,30 @@
<?php
use Ifsnop\Mysqldump as IMysqldump;
class BackupDatabaseController extends Controller {
const PATH = '/backup-database';
public function validations() {
return [
'permission' => 'staff_3',
'requestData' => []
];
}
public function handler() {
global $mysql_host;
global $mysql_database;
global $mysql_user;
global $mysql_password;
$fileDownloader = FileDownloader::getInstance();
$fileDownloader->setFileName('backup.sql');
$mysqlDump = new IMysqldump\Mysqldump('mysql:host='. $mysql_host .';dbname=' . $mysql_database, $mysql_user, $mysql_password);
$mysqlDump->start($fileDownloader->getFullFilePath());
if($fileDownloader->download()) {
$fileDownloader->eraseFile();
}
}
}

View File

@ -0,0 +1,24 @@
<?php
use Ifsnop\Mysqldump as IMysqldump;
use Respect\Validation\Validator as DataValidator;
class DownloadController extends Controller {
const PATH = '/download';
public function validations() {
return [
'permission' => 'staff_1',
'requestData' => [
'file' => [
'validation' => DataValidator::alnum('_.')->noWhitespace()
]
]
];
}
public function handler() {
$fileDownloader = FileDownloader::getInstance();
$fileDownloader->setFileName(Controller::request('file'));
$fileDownloader->download();
}
}

View File

@ -40,7 +40,10 @@ class InitSettingsController extends Controller {
'allow-attachments' => 0, 'allow-attachments' => 0,
'max-size' => 0, 'max-size' => 0,
'title' => 'Support Center', 'title' => 'Support Center',
'url' => 'http://www.opensupports.com/support' 'url' => 'http://www.opensupports.com/support',
'ticket-gap' => Hashing::generateRandomPrime(100000, 999999),
'file-gap' => Hashing::generateRandomPrime(100000, 999999),
'file-first-number' => Hashing::generateRandomNumber(100000, 999999),
]); ]);
} }

View File

@ -35,4 +35,5 @@ class ERRORS {
const INVALID_TEMPLATE = 'INVALID_TEMPLATE'; const INVALID_TEMPLATE = 'INVALID_TEMPLATE';
const INVALID_SUBJECT = 'INVALID_SUBJECT'; const INVALID_SUBJECT = 'INVALID_SUBJECT';
const INVALID_BODY = 'INVALID_BODY'; const INVALID_BODY = 'INVALID_BODY';
const INVALID_FILE = 'INVALID_FILE';
} }

View File

@ -18,6 +18,10 @@ include_once 'libs/Hashing.php';
include_once 'libs/MailSender.php'; include_once 'libs/MailSender.php';
include_once 'libs/Date.php'; include_once 'libs/Date.php';
include_once 'libs/DataStoreList.php'; include_once 'libs/DataStoreList.php';
include_once 'libs/LinearCongruentialGenerator.php';
include_once 'libs/FileManager.php';
include_once 'libs/FileDownloader.php';
include_once 'libs/FileUploader.php';
// LOAD DATA // LOAD DATA
spl_autoload_register(function ($class) { spl_autoload_register(function ($class) {

View File

@ -0,0 +1,40 @@
<?php
class FileDownloader extends FileManager {
private static $instance = null;
public static function getInstance() {
if (self::$instance === null) {
self::$instance = new FileDownloader();
}
return self::$instance;
}
private function __construct() {}
public function download() {
$fullFilePath = $this->getFullFilePath();
if(file_exists($fullFilePath) && is_file($fullFilePath)) {
header('Cache-control: private');
header('Content-Type: application/octet-stream');
header('Content-Length: '.filesize($fullFilePath));
header('Content-Disposition: filename='. $this->getFileName());
flush();
$file = fopen($fullFilePath, 'r');
print fread($file, filesize($fullFilePath));
fclose($file);
return true;
} else {
return false;
}
}
public function eraseFile() {
unlink($this->getLocalPath() . $this->getFileName());
}
}

View File

@ -0,0 +1,26 @@
<?php
abstract class FileManager {
private $fileName;
private $localPath = 'files/';
public function setLocalPath($localPath) {
$this->localPath = $localPath;
}
public function setFileName($fileName) {
$this->fileName = $fileName;
}
public function getLocalPath() {
return $this->localPath;
}
public function getFileName() {
return $this->fileName;
}
public function getFullFilePath() {
return $this->getLocalPath() . $this->getFileName();
}
}

View File

@ -1,5 +1,10 @@
<?php <?php
class FileUploader {
class FileUploader extends FileManager {
private $maxSize = 1024;
private $linearCongruentialGenerator;
private $linearCongruentialGeneratorOffset;
private static $instance = null; private static $instance = null;
public static function getInstance() { public static function getInstance() {
@ -12,7 +17,40 @@ class FileUploader {
private function __construct() {} private function __construct() {}
public function upload() { public function upload($file) {
// TODO: Implement file upload features $newFileName = $this->generateNewName($file['name']);
if($file['size'] > (1024 * $this->maxSize)) {
return false;
}
move_uploaded_file($file['tmp_name'], $this->getLocalPath() . $newFileName);
return true;
} }
private function generateNewName($fileName) {
$newName = $fileName;
$newName = strtolower($newName);
$newName = preg_replace('/\s+/', '_', $newName);
if ($this->linearCongruentialGenerator instanceof LinearCongruentialGenerator) {
$newName = $this->linearCongruentialGenerator->generate($this->linearCongruentialGeneratorOffset) . '_' . $newName;
}
return $newName;
}
public function setGeneratorValues($gap, $first, $offset) {
$this->linearCongruentialGenerator = new LinearCongruentialGenerator();
$this->linearCongruentialGeneratorOffset = $offset;
$this->linearCongruentialGenerator->setGap($gap);
$this->linearCongruentialGenerator->setFirst($first);
}
public function setMaxSize($maxSize) {
$this->maxSize = $maxSize;
}
} }

View File

@ -7,10 +7,36 @@ class Hashing {
public static function verifyPassword($password, $hash) { public static function verifyPassword($password, $hash) {
return password_verify($password, $hash); return password_verify($password, $hash);
} }
public static function generateRandomToken() { public static function generateRandomToken() {
return md5(uniqid(rand())); return md5(uniqid(rand()));
} }
public static function getRandomTicketNumber($min,$max) {
return rand($min,$max); public static function generateRandomNumber($min, $max) {
return rand($min, $max);
}
public static function generateRandomPrime($min, $max) {
$number = Hashing::generateRandomNumber($min, $max);
while(!Hashing::isPrime($number)) {
$number = Hashing::generateRandomNumber($min, $max);
}
return $number;
}
public static function isPrime($number) {
$sqrt = sqrt($number);
$prime = true;
for($i = 0; $i < $sqrt; $i++) {
if($sqrt % 2 === 0) {
$prime = false;
break;
}
}
return $prime;
} }
} }

View File

@ -0,0 +1,30 @@
<?php
class LinearCongruentialGenerator {
private $gap;
private $first;
private $min = 100000;
private $max = 999999;
public function setRange($min, $max) {
$this->min = $min;
$this->max = $max;
}
public function setGap($gap) {
if(!Hashing::isPrime($gap)) throw new Exception('LinearCongruentialGenerator: gap must be prime');
$this->gap = $gap;
}
public function setFirst($first) {
$this->first = $first;
}
public function generate($offset) {
return ($this->first - $this->min + $offset * $this->gap) % ($this->max - $this->min + 1) + $this->min;
}
public function generateFirst() {
return Hashing::generateRandomNumber($this->min, $this->max);
}
}

View File

@ -46,17 +46,16 @@ class Ticket extends DataStore {
} }
public function generateUniqueTicketNumber() { public function generateUniqueTicketNumber() {
$linearCongruentialGenerator = new LinearCongruentialGenerator();
$ticketQuantity = Ticket::count(); $ticketQuantity = Ticket::count();
$minValue = 100000;
$maxValue = 999999;
if ($ticketQuantity === 0) { if ($ticketQuantity === 0) {
$ticketNumber = Hashing::getRandomTicketNumber($minValue, $maxValue); $ticketNumber = $linearCongruentialGenerator->generateFirst();
} else { } else {
$firstTicketNumber = Ticket::getTicket(1)->ticketNumber; $linearCongruentialGenerator->setGap(Setting::getSetting('ticket-gap')->value);
$gap = 176611; //TODO: USE RANDOM PRIME INSTEAD $linearCongruentialGenerator->setFirst(Ticket::getTicket(1)->ticketNumber);
$ticketNumber = ($firstTicketNumber - $minValue + $ticketQuantity * $gap) % ($maxValue - $minValue + 1) + $minValue; $ticketNumber = $linearCongruentialGenerator->generate($ticketQuantity);
} }
return $ticketNumber; return $ticketNumber;

View File

@ -147,13 +147,15 @@ describe '/ticket/create' do
csrf_token: $csrf_token csrf_token: $csrf_token
}) })
ticket_number_gap = $database.getRow('setting', 'ticket-gap', 'name')['value'].to_i
ticket0 = $database.getRow('ticket','Winter is coming','title')['ticket_number'].to_i ticket0 = $database.getRow('ticket','Winter is coming','title')['ticket_number'].to_i
ticket1 = $database.getRow('ticket','Winter is coming1','title')['ticket_number'].to_i ticket1 = $database.getRow('ticket','Winter is coming1','title')['ticket_number'].to_i
ticket2 = $database.getRow('ticket','Winter is coming2','title')['ticket_number'].to_i ticket2 = $database.getRow('ticket','Winter is coming2','title')['ticket_number'].to_i
ticket3 = $database.getRow('ticket','Winter is coming3','title')['ticket_number'].to_i ticket3 = $database.getRow('ticket','Winter is coming3','title')['ticket_number'].to_i
(ticket1).should.equal((ticket0 - 100000 + 1 * 176611) % 900000 + 100000) (ticket1).should.equal((ticket0 - 100000 + 1 * ticket_number_gap) % 900000 + 100000)
(ticket2).should.equal((ticket0 - 100000 + 2 * 176611) % 900000 + 100000) (ticket2).should.equal((ticket0 - 100000 + 2 * ticket_number_gap) % 900000 + 100000)
(ticket3).should.equal((ticket0 - 100000 + 3 * 176611) % 900000 + 100000) (ticket3).should.equal((ticket0 - 100000 + 3 * ticket_number_gap) % 900000 + 100000)
end end
end end