diff options
-rw-r--r-- | app/class/database.class.php | 51 | ||||
-rw-r--r-- | app/class/form.class.php | 179 | ||||
-rw-r--r-- | app/class/globals.php | 22 | ||||
-rw-r--r-- | app/model/dbconfig.php | 42 | ||||
-rw-r--r-- | app/view/dbconfig.php | 90 | ||||
-rw-r--r-- | app/view/formctrl.php | 39 | ||||
-rw-r--r-- | app/view/stdpage.php | 7 |
7 files changed, 427 insertions, 3 deletions
diff --git a/app/class/database.class.php b/app/class/database.class.php index cdfdfce..3d94e16 100644 --- a/app/class/database.class.php +++ b/app/class/database.class.php @@ -115,6 +115,57 @@ abstract class database return true; } + + /* + * Test and set new database configuration parameters. + * If the given params fail, error's are set and this + * function returns false. On success, parameters are + * written to 'dbconfig.php' and true is returned. + */ + public static function setConfig(string $engine, string $host, + string $uname, string $passwd, string $name) : bool + { + global $_SCROTT; + + /* test configuration */ + $_SCROTT['conf'] = "conf"; + $_SCROTT['dbEngine'] = $engine; + $_SCROTT['dbHost'] = $host; + $_SCROTT['dbUname'] = $uname; + $_SCROTT['dbPasswd'] = $passwd; + $_SCROTT['dbName'] = $name; + + try + { + $db = self::getInstance(); + } + catch (Exception $e) + { + logError(ERROR, $e->getMessage()); + return false; + } + + /* write file */ + $f = fopen(DATABASE_CONFIG_FILE, "w"); + + if (!$f) + { + logError(ERROR, "Can not create configuration file"); + return false; + } + + fwrite($f, "<?php\n"); + fwrite($f, "\$_SCROTT['conf'] = 'conf';\n"); + fwrite($f, "\$_SCROTT['dbEngine'] = '" . $engine . "';\n"); + fwrite($f, "\$_SCROTT['dbHost'] = '" . $host . "';\n"); + fwrite($f, "\$_SCROTT['dbUname'] = '" . $uname . "';\n"); + fwrite($f, "\$_SCROTT['dbPasswd'] = '" . $passwd . "';\n"); + fwrite($f, "\$_SCROTT['dbName'] = '" . $name . "';\n"); + fwrite($f, "?>\n"); + + fclose($f); + return true; + } } ?> diff --git a/app/class/form.class.php b/app/class/form.class.php new file mode 100644 index 0000000..8f9d936 --- /dev/null +++ b/app/class/form.class.php @@ -0,0 +1,179 @@ +<?php + +/* + * SCROTT IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * For more information, please refer to UNLICENSE + */ + +/* + * Model web-forms and simplify the process of accepting, validating, and + * sanitizing user input. + */ +class form +{ + /* + * These arrays track the meta-data of all fields defined on this + * form. This mainly includes their type, range (if numeric), and + * whether or not the field is required. + */ + private $textFields = array(); + private $numbFields = array(); + private $enumFields = array(); + + /* + * Add new text field to this form + */ + public function text(string $name, bool $required = true, + string $deflt = "") : void + { + $this->textFields[] = array( + 'name' => $name, + 'required' => $required, + 'default' => $deflt, + ); + } + + /* + * Add new numeric field to this form + */ + public function numeric(string $name, ?int $min = NULL, ?int $max = NULL, + int $deflt = 0, bool $isInt = true, bool $required = true) : void + { + $this->numbFields[] = array( + 'name' => $name, + 'min' => $min, + 'max' => $max, + 'default' => $deflt, + 'isInt' => $isInt, + 'required' => $required, + ); + } + + /* + * Add new enumeration field to this form + */ + public function enum(string $name, array $values, $deflt = NULL, + bool $required = true) : void + { + $this->enumFields[] = array( + 'name' => $name, + 'values' => $values, + 'default' => $deflt, + 'required' => $required, + ); + } + + /* + * Add new boolean field to this form + */ + public function flag(string $name) : void + { + $this->enum($name, array("1", "0"), "0", false); + } + + /* + * Populate this form with input data from web page. If an error + * is encountered, or a required field is missing, false is returned. + */ + public function populate(array $input) : bool + { + /* detect duplicate names */ + $names = array(); + + foreach ($this->textFields as $field) + $names[] = $field['name']; + + foreach ($this->numbFields as $field) + $names[] = $field['name']; + + foreach ($this->enumFields as $field) + $names[] = $field['name']; + + if (count(array_unique($names)) != count($names)) + throw new Exception("Internal error: Duplicate field names defined in form"); + + /* init text fields */ + foreach ($this->textFields as $field) + { + if (isset($input[$field['name']]) && $input[$field['name']] != "") + $this->{$field['name']} = htmlentities($input[$field['name']], ENT_QUOTES); + + else if ($field['required']) + logError(ERROR, $field['name'] . " is required"); + + else + $this->{$field['name']} = $field['default']; + } + + /* init numeric fields */ + foreach ($this->numbFields as $field) + { + if (isset($input[$field['name']]) && $input[$field['name']] != "") + { + if (!is_numeric($input[$field['name']])) + { + logError(ERROR, $field['name'] . " must be numeric"); + continue; + } + + if ($field['isInt'] && (floor($input[$field['name']]) != $input[$field['name']])) + { + logError(ERROR, $field['name'] . " must be an integer"); + continue; + } + + if (!is_null($field['min']) && ($input[$field['name']] < $field['min'])) + { + logError(ERROR, $field['name'] . " must be no less than " . $field['min']); + continue; + } + + if (!is_null($field['max']) && ($input[$field['name']] > $field['max'])) + { + logError(ERROR, $field['name'] . " must be no more than " . $field['max']); + continue; + } + + $this->{$field['name']} = $input[$field['name']]; + } + + else if ($field['required']) + logError(ERROR, $field['name'] . " is required"); + + else + $this->{$field['name']} = $field['default']; + } + + /* init enum fields */ + foreach ($this->enumFields as $field) + { + if (isset($input[$field['name']]) && $input[$field['name']] != "") + { + if (array_search($input[$field['name']], $field['values']) === false) + { + logError(ERROR, $field['name'] . " is not an appropriate value"); + continue; + } + + $this->{$field['name']} = $input[$field['name']]; + } + + else if ($field['required']) + logError(ERROR, $field['name'] . " is required"); + + else + $this->{$field['name']} = $field['default']; + } + + return !isError(ERROR); + } +} + +?> diff --git a/app/class/globals.php b/app/class/globals.php index 615efa6..776fc35 100644 --- a/app/class/globals.php +++ b/app/class/globals.php @@ -77,6 +77,28 @@ function require_https() : void } /* + * Check whether an action string was submitted by the user agent + */ +function isAction(string $action) : bool +{ + if (!isset($_REQUEST['action'])) + return false; + + return $_REQUEST['action'] == $action; +} + +/* + * Get an array of submitted form input + */ +function input() : array +{ + if (!isset($_REQUEST['input'])) + throw new Exception("Requested form input, but no input data was supplied"); + + return $_REQUEST['input']; +} + +/* * Check for errors, warnings, or notices */ function isError(string $level) : bool diff --git a/app/model/dbconfig.php b/app/model/dbconfig.php new file mode 100644 index 0000000..c66b052 --- /dev/null +++ b/app/model/dbconfig.php @@ -0,0 +1,42 @@ +<?php + +/* + * SCROTT IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * For more information, please refer to UNLICENSE + */ + +require_once "class/database.class.php"; +require_once "class/form.class.php"; + +/* + * Action: save - Write given configuration parameters to the database + * configuration file. + */ +if (isAction("save")) +{ + $form = new form(); + $form->text("dbHost"); + $form->text("dbUname"); + $form->text("dbPasswd", false); + $form->text("dbName"); + + if (!$form->populate(input())) + return; + + $stat = database::setConfig("mysql", $form->dbHost, $form->dbUname, + $form->dbPasswd, $form->dbName); + + if (!$stat) + return; + + location("/"); +} + +?> diff --git a/app/view/dbconfig.php b/app/view/dbconfig.php new file mode 100644 index 0000000..006c28e --- /dev/null +++ b/app/view/dbconfig.php @@ -0,0 +1,90 @@ +<?php + +/* + * SCROTT IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * For more information, please refer to UNLICENSE + */ + +require_once "model/dbconfig.php"; +require_once "view/stdpage.php"; +require_once "view/formctrl.php"; + +?> + +<!DOCTYPE html> + +<html lang="en"> + <head> + <?=stdpage\head( "Database Configuration" )?> + + <style type="text/css"> + body { padding-top: 50px; } + </style> + </head> + + <body> + <?=stdpage\top()?> + + <div class="container"> + <div class="jumbotron"> + <h2 class="text-center"> + <span class="glyphicon glyphicon-user"></span> + <span class="glyphicon glyphicon-th"></span> + <span class="glyphicon glyphicon-edit"></span> + <span class="glyphicon glyphicon-tasks"></span> + <span class="glyphicon glyphicon-inbox"></span> + <span class="glyphicon glyphicon-envelope"></span> + </h2> + + <h1 class="text-center">Welcome to Scrott!</h1> + <hr /> + + <p class="text-center">You're seeing this page because the file "dbconfig.php" is missing.</p> + <p class="text-center">Please fill out the form below to configure Scrott to use your database.</p> + <hr /> + + <p class="text-center">This form will save your database parameters to the configuration file.<br /> + Scrott needs this information before it can begin working!</p> + + <h5 class="text-center"> + It is presumed that you are the administrator for this Scrott install.<br /> + There is a security risk involved with exposing this page to the public! + </h5> + + <div class="row"> + <div class="col-md-2"></div> + + <div class="col-md-8"> + <div class="panel panel-default"> + <div class="panel-body"> + <form method="post" action="<?=ap()?>"> + <?=formctrl\formname( "save" )?> + <?=formctrl\text( "Engine", "dbEngine", "MySQL", "", false, true )?> + <?=formctrl\text( "Hostname", "dbHost", "", "localhost" )?> + <?=formctrl\text( "Username", "dbUname", "", "root" )?> + <?=formctrl\password( "Password", "dbPasswd", false )?> + <?=formctrl\text( "Database Name", "dbName", "", "db_scrott" )?> + + <button type="submit" class="btn btn-primary pull-right"> + Use these settings + </button> + </form> + </div> + </div> + </div> + + <div class="col-md-2"></div> + </div> + </div> + </div> + + <?=stdpage\foot()?> + </body> +</html> diff --git a/app/view/formctrl.php b/app/view/formctrl.php new file mode 100644 index 0000000..3683eb4 --- /dev/null +++ b/app/view/formctrl.php @@ -0,0 +1,39 @@ +<?php + +/* + * SCROTT IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * For more information, please refer to UNLICENSE + */ + +namespace formctrl; + +?> +<?php function formname(string $name) : void { ?> + + <input type="hidden" name="action" value="<?=$name?>" /> + +<?php } ?> +<?php function text(string $label, string $name, string $value = "", string $placeholder = "", + bool $required = true, bool $disabled = false) : void { ?> + + <div class="form-group"> + <label><?=$label?></label> + <input type="text" name="input[<?=$name?>]" value="<?=$value?>" placeholder="<?=$placeholder?>" class="form-control" <?=($required ? "required" : "")?> <?=($disabled ? "disabled" : "")?> /> + </div> + +<?php } ?> +<?php function password(string $label, string $name, bool $required = true) : void {?> + + <div class="form-group"> + <label><?=$label?></label> + <input type="password" name="input[<?=$name?>]" class="form-control" <?=($required ? "required" : "")?> /> + </div> + +<?php } ?> diff --git a/app/view/stdpage.php b/app/view/stdpage.php index 157dc1b..4bdc0c5 100644 --- a/app/view/stdpage.php +++ b/app/view/stdpage.php @@ -17,7 +17,6 @@ namespace stdpage; require_once "class/globals.php"; ?> - <?php function head(?string $title = NULL) : void { ?> <!-- @@ -54,12 +53,14 @@ require_once "class/globals.php"; <title>Scrott<?=($title ? " - ".$title : "")?></title> <link rel="stylesheet" type="text/css" href="<?=ar()?>/static/css/bootstrap.min.css" /> -<?php } ?> -<?php function top() : void { ?> <?php } ?> +<?php function top() : void { ?> +<?php } ?> <?php function foot() : void { ?> + <script type="text/javascript" src="<?=ar()?>/static/js/jquery.min.js"></script> <script type="text/javascript" src="<?=ar()?>/static/js/bootstrap.min.js"></script> + <?php } ?> |