diff --git a/README.md b/README.md index f72024d..30d39b5 100644 --- a/README.md +++ b/README.md @@ -1,125 +1,22 @@ # KISS Libre Hosting Panel Welcome in an actively developed keep-it-simple-and-stupid hosting panel designed for GNU/Linux operating systems. This software can be useful to serve everyday shared hosting services. This panel is designed to **respect the freedom** of its users. It works **without proprietary JavaScript**. To be honest, without **any line of JavaScript** in any form. This panel is designed to work without **any external dependency** not written by me. This allow fine-grained control over the software. It integrates with well-known and ultra-secure software packaged inside every GNU/Linux distribution in the world and used by millions of devices. -I would like to thank Giorgio Maone for his project NoScript, for the moral incentive, and Richard Stallman, for _The JavaScript Trap_ paper. +## More information -* https://www.gnu.org/philosophy/javascript-trap.html -* https://noscript.net/ - -## Disclaimer - -Do not try to become a system administrator if you do not like responsibilities, if you do not want to understand your infrastructure, if you do not want to have information security paranoia, etc. - -## Preamble - -An hosting panel is just the iceberg summit of a lot of technologies and protocols involved. Do not try to implement such project in production if you do not know what you are doing. You must gain confidence with the technologies involved. - -Papers: -* RFC 5321 - Simple Mail Transfer Protocol -* RFC 7208 - Sender Policy Framework (SPF) -* RFC 6376 - DomainKeys Identified Mail (DKIM) Signatures -* RFC 7489 - Domain-based Message Authentication, Reporting, and Conformance (DMARC) - -Software involved: -* Debian GNU/Linux stable (currently buster) -* MariaDB / MySQL -* Postfix -* Dovecot -* PureFTPd -* OpenDKIM -* Apache HTTP server / nginx -* PHP - -## Features - -Let me say that I love listening to the whishlist of my costumers. Here are the most important features/TODOs: - -- administration of own mailboxes (thanks to Postfix and Dovecot over MariaDB) - - [X] list - - [X] password reset - - [X] add - - [X] IMAP/SMTP documentation - - [ ] remove - - [ ] view quota -- administration of own mail aliases (thanks to Postfix and Dovecot over MariaDB) - - [X] list - - [X] change forward destination(s) - - [X] add - - [X] remove -- administration of own FTP accounts (thanks to Pure-FTPd over MariaDB) - - [X] list - - [X] add - - [X] remove - - [X] password reset -- [ ] administration of own MariaDB databases - - [ ] list own databases - - [ ] list own users - - [ ] change user password -- [ ] administration of User(s) - - [X] create - - [X] password reset - - [X] create a Domain for that User - - [ ] change login - - [ ] change e-mail -- plans - - [X] limit number of mailboxes per domain - - [X] limit number of mail forwardings -- action log - -## Installation - -Web interface: - -``` -git clone https://gitpull.it/source/boz-libre-hosting-panel.git -git clone https://gitpull.it/source/suckless-php.git -``` - -``` -# database -apt install mariadb-server - -# web server -apt install apache2 libjs-bootstrap certbot - -# FTP server -apt install pure-ftpd-mysql - -# mailserver -apt install postfix-mysql postfix-policyd-spf-python dovecot-mysql dovecot-imapd dovecot-pop3d spamassassin -``` - -## Why PHP7 - -This project is writted in PHP7 because: - -* Node.js is not an hypertext preprocessor -* Python is not an hypertext preprocessor -* Ruby is not an hypertext preprocessor -* Java is not an hypertext preprocessor. Well, Java JSP is an hypertext preprocessor but it's footprint is heavy as hell - -This PHP7 application is stateless. Does not have sessions. It's well-designed. It scales. It has a minimal memory footprint. If you do not like it, you are free to implement such project with your favorite programming language, with your 200MB of dependencies and crap. - -## Report a bug - -https://gitpull.it/tag/kiss_libre_hosting_panel/ - -## Report a feature - -https://gitpull.it/tag/kiss_libre_hosting_panel/ +https://gitpull.it/w/kiss_libre_hosting_panel/ ## License Copyright (C) 2018, 2019, 2020 [Valerio Bozzolan](https://boz.reyboz.it/) - KISS Libre Hosting Panel This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . diff --git a/cli/add-user b/cli/add-user new file mode 120000 index 0000000..1646e46 --- /dev/null +++ b/cli/add-user @@ -0,0 +1 @@ +../scripts/add-user.php \ No newline at end of file diff --git a/cli/database-upgrade b/cli/database-upgrade new file mode 120000 index 0000000..1dc3925 --- /dev/null +++ b/cli/database-upgrade @@ -0,0 +1 @@ +../scripts/database-upgrade.php \ No newline at end of file diff --git a/cli/update-mailbox-quotas b/cli/update-mailbox-quotas new file mode 120000 index 0000000..4682745 --- /dev/null +++ b/cli/update-mailbox-quotas @@ -0,0 +1 @@ +../scripts/update-mailbox-quotas.php \ No newline at end of file diff --git a/documentation/database/database-schema.sql b/documentation/database/database-schema.sql index d6fbeae..0625e85 100644 --- a/documentation/database/database-schema.sql +++ b/documentation/database/database-schema.sql @@ -1,404 +1,404 @@ -- MySQL dump 10.17 Distrib 10.3.22-MariaDB, for debian-linux-gnu (x86_64) -- -- Host: localhost Database: hostingclients -- ------------------------------------------------------ -- Server version 10.3.22-MariaDB-0+deb10u1 /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; /*!40101 SET NAMES utf8mb4 */; /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; /*!40103 SET TIME_ZONE='+00:00' */; /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -- --- Table structure for table `aw34w3_backup` +-- Table structure for table `{$prefix}backup` -- -DROP TABLE IF EXISTS `aw34w3_backup`; +DROP TABLE IF EXISTS `{$prefix}backup`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; -CREATE TABLE `aw34w3_backup` ( +CREATE TABLE `{$prefix}backup` ( `ID_backup` int(10) unsigned NOT NULL AUTO_INCREMENT, `backup_name` varchar(15) NOT NULL, `backup_description` varchar(64) NOT NULL, PRIMARY KEY (`ID_backup`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- --- Table structure for table `aw34w3_database` +-- Table structure for table `{$prefix}database` -- -DROP TABLE IF EXISTS `aw34w3_database`; +DROP TABLE IF EXISTS `{$prefix}database`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; -CREATE TABLE `aw34w3_database` ( +CREATE TABLE `{$prefix}database` ( `database_ID` int(10) unsigned NOT NULL AUTO_INCREMENT, `database_name` varchar(64) NOT NULL, `database_active` tinyint(1) NOT NULL DEFAULT 1, `database_creation_date` datetime NOT NULL DEFAULT current_timestamp(), PRIMARY KEY (`database_ID`), UNIQUE KEY `database_name` (`database_name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- --- Table structure for table `aw34w3_database_user` +-- Table structure for table `{$prefix}database_user` -- -DROP TABLE IF EXISTS `aw34w3_database_user`; +DROP TABLE IF EXISTS `{$prefix}database_user`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; -CREATE TABLE `aw34w3_database_user` ( +CREATE TABLE `{$prefix}database_user` ( `database_ID` int(10) unsigned NOT NULL, `user_ID` int(10) unsigned NOT NULL, PRIMARY KEY (`user_ID`,`database_ID`), KEY `ID_database` (`database_ID`), - CONSTRAINT `aw34w3_database_user_ibfk_1` FOREIGN KEY (`database_ID`) REFERENCES `aw34w3_database` (`database_ID`) ON DELETE CASCADE, - CONSTRAINT `aw34w3_database_user_ibfk_2` FOREIGN KEY (`user_ID`) REFERENCES `aw34w3_user` (`user_ID`) ON DELETE CASCADE + CONSTRAINT `{$prefix}database_user_ibfk_1` FOREIGN KEY (`database_ID`) REFERENCES `{$prefix}database` (`database_ID`) ON DELETE CASCADE, + CONSTRAINT `{$prefix}database_user_ibfk_2` FOREIGN KEY (`user_ID`) REFERENCES `{$prefix}user` (`user_ID`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- --- Table structure for table `aw34w3_domain` +-- Table structure for table `{$prefix}domain` -- -DROP TABLE IF EXISTS `aw34w3_domain`; +DROP TABLE IF EXISTS `{$prefix}domain`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; -CREATE TABLE `aw34w3_domain` ( +CREATE TABLE `{$prefix}domain` ( `domain_ID` int(10) unsigned NOT NULL AUTO_INCREMENT, `domain_name` varchar(64) NOT NULL, `domain_active` tinyint(1) NOT NULL DEFAULT 1, `domain_born` date DEFAULT NULL, `domain_expiration` date DEFAULT NULL, `domain_parent` int(10) unsigned DEFAULT NULL, `plan_ID` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`domain_ID`), UNIQUE KEY `domain_name` (`domain_name`), KEY `domain_parent` (`domain_parent`), KEY `plan_ID` (`plan_ID`), - CONSTRAINT `aw34w3_domain_ibfk_1` FOREIGN KEY (`domain_parent`) REFERENCES `aw34w3_domain` (`domain_ID`), - CONSTRAINT `aw34w3_domain_ibfk_2` FOREIGN KEY (`plan_ID`) REFERENCES `aw34w3_plan` (`plan_ID`) + CONSTRAINT `{$prefix}domain_ibfk_1` FOREIGN KEY (`domain_parent`) REFERENCES `{$prefix}domain` (`domain_ID`), + CONSTRAINT `{$prefix}domain_ibfk_2` FOREIGN KEY (`plan_ID`) REFERENCES `{$prefix}plan` (`plan_ID`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- --- Table structure for table `aw34w3_domain_user` +-- Table structure for table `{$prefix}domain_user` -- -DROP TABLE IF EXISTS `aw34w3_domain_user`; +DROP TABLE IF EXISTS `{$prefix}domain_user`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; -CREATE TABLE `aw34w3_domain_user` ( +CREATE TABLE `{$prefix}domain_user` ( `domain_ID` int(10) unsigned NOT NULL, `user_ID` int(10) unsigned NOT NULL, `domain_user_creation_date` datetime NOT NULL, PRIMARY KEY (`user_ID`,`domain_ID`), KEY `ID_domain` (`domain_ID`), - CONSTRAINT `aw34w3_domain_user_ibfk_1` FOREIGN KEY (`domain_ID`) REFERENCES `aw34w3_domain` (`domain_ID`) ON DELETE CASCADE, - CONSTRAINT `aw34w3_domain_user_ibfk_2` FOREIGN KEY (`user_ID`) REFERENCES `aw34w3_user` (`user_ID`) ON DELETE CASCADE + CONSTRAINT `{$prefix}domain_user_ibfk_1` FOREIGN KEY (`domain_ID`) REFERENCES `{$prefix}domain` (`domain_ID`) ON DELETE CASCADE, + CONSTRAINT `{$prefix}domain_user_ibfk_2` FOREIGN KEY (`user_ID`) REFERENCES `{$prefix}user` (`user_ID`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- --- Table structure for table `aw34w3_ftp` +-- Table structure for table `{$prefix}ftp` -- -DROP TABLE IF EXISTS `aw34w3_ftp`; +DROP TABLE IF EXISTS `{$prefix}ftp`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; -CREATE TABLE `aw34w3_ftp` ( +CREATE TABLE `{$prefix}ftp` ( `domain_ID` int(10) unsigned NOT NULL, `ftp_login` varchar(32) NOT NULL, `ftp_active` tinyint(1) NOT NULL DEFAULT 1, `ftp_password` varchar(64) NOT NULL, `ftp_directory` varchar(128) NOT NULL DEFAULT '/', `ftp_ulbandwidth` smallint(5) NOT NULL DEFAULT 600 COMMENT 'Upload Kilobytes per second', `ftp_dlbandwidth` smallint(5) NOT NULL DEFAULT 600 COMMENT 'Download kilobytes per second', `ftp_ipaccess` varchar(15) NOT NULL DEFAULT '*', `ftp_quotasize` smallint(5) NOT NULL DEFAULT 500 COMMENT 'Megabytes', `ftp_quotafiles` int(11) NOT NULL DEFAULT 0 COMMENT 'Number of files', `ftp_comment` tinytext DEFAULT NULL, PRIMARY KEY (`domain_ID`,`ftp_login`), UNIQUE KEY `FTP_login` (`ftp_login`), KEY `domain_name` (`domain_ID`), - CONSTRAINT `aw34w3_ftp_ibfk_1` FOREIGN KEY (`domain_ID`) REFERENCES `aw34w3_domain` (`domain_ID`) ON DELETE CASCADE + CONSTRAINT `{$prefix}ftp_ibfk_1` FOREIGN KEY (`domain_ID`) REFERENCES `{$prefix}domain` (`domain_ID`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- --- Table structure for table `aw34w3_mailbox` +-- Table structure for table `{$prefix}mailbox` -- -DROP TABLE IF EXISTS `aw34w3_mailbox`; +DROP TABLE IF EXISTS `{$prefix}mailbox`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; -CREATE TABLE `aw34w3_mailbox` ( +CREATE TABLE `{$prefix}mailbox` ( `mailbox_ID` int(10) unsigned NOT NULL AUTO_INCREMENT, `mailbox_active` tinyint(1) NOT NULL DEFAULT 1, `mailbox_username` varchar(64) NOT NULL DEFAULT '', `mailbox_password` text NOT NULL COMMENT 'doveadm pw -s SHA512-CRYPT', `mailbox_receive` tinyint(4) NOT NULL DEFAULT 1, `mailbox_reset_token` text DEFAULT NULL, `mailbox_description` text DEFAULT NULL, `domain_ID` int(10) unsigned NOT NULL, PRIMARY KEY (`mailbox_ID`), UNIQUE KEY `domain_ID` (`domain_ID`,`mailbox_username`) USING BTREE, KEY `ID_domain` (`domain_ID`), KEY `mailbox_active` (`mailbox_active`), - CONSTRAINT `aw34w3_mailbox_ibfk_1` FOREIGN KEY (`domain_ID`) REFERENCES `aw34w3_domain` (`domain_ID`) ON DELETE CASCADE + CONSTRAINT `{$prefix}mailbox_ibfk_1` FOREIGN KEY (`domain_ID`) REFERENCES `{$prefix}domain` (`domain_ID`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- --- Temporary table structure for view `aw34w3_mailbox_simple` +-- Temporary table structure for view `{$prefix}mailbox_simple` -- -DROP TABLE IF EXISTS `aw34w3_mailbox_simple`; -/*!50001 DROP VIEW IF EXISTS `aw34w3_mailbox_simple`*/; +DROP TABLE IF EXISTS `{$prefix}mailbox_simple`; +/*!50001 DROP VIEW IF EXISTS `{$prefix}mailbox_simple`*/; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; -/*!50001 CREATE TABLE `aw34w3_mailbox_simple` ( +/*!50001 CREATE TABLE `{$prefix}mailbox_simple` ( `email` tinyint NOT NULL, `domain` tinyint NOT NULL, `username` tinyint NOT NULL, `receive` tinyint NOT NULL, `password` tinyint NOT NULL, `path` tinyint NOT NULL ) ENGINE=MyISAM */; SET character_set_client = @saved_cs_client; -- --- Table structure for table `aw34w3_mailboxquota` +-- Table structure for table `{$prefix}mailboxquota` -- -DROP TABLE IF EXISTS `aw34w3_mailboxquota`; +DROP TABLE IF EXISTS `{$prefix}mailboxquota`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; -CREATE TABLE `aw34w3_mailboxquota` ( +CREATE TABLE `{$prefix}mailboxquota` ( `mailbox_ID` int(10) unsigned NOT NULL, `mailboxquota_date` datetime NOT NULL, `mailboxquota_bytes` int(10) unsigned NOT NULL, KEY `mailbox_ID` (`mailbox_ID`), KEY `mailboxquota_date` (`mailboxquota_date`), - CONSTRAINT `aw34w3_mailboxquota_ibfk_1` FOREIGN KEY (`mailbox_ID`) REFERENCES `aw34w3_mailbox` (`mailbox_ID`) ON DELETE CASCADE + CONSTRAINT `{$prefix}mailboxquota_ibfk_1` FOREIGN KEY (`mailbox_ID`) REFERENCES `{$prefix}mailbox` (`mailbox_ID`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- --- Temporary table structure for view `aw34w3_mailforward_mail2mail` +-- Temporary table structure for view `{$prefix}mailforward_mail2mail` -- -DROP TABLE IF EXISTS `aw34w3_mailforward_mail2mail`; -/*!50001 DROP VIEW IF EXISTS `aw34w3_mailforward_mail2mail`*/; +DROP TABLE IF EXISTS `{$prefix}mailforward_mail2mail`; +/*!50001 DROP VIEW IF EXISTS `{$prefix}mailforward_mail2mail`*/; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; -/*!50001 CREATE TABLE `aw34w3_mailforward_mail2mail` ( +/*!50001 CREATE TABLE `{$prefix}mailforward_mail2mail` ( `source` tinyint NOT NULL, `destination` tinyint NOT NULL ) ENGINE=MyISAM */; SET character_set_client = @saved_cs_client; -- --- Temporary table structure for view `aw34w3_mailforward_simple` +-- Temporary table structure for view `{$prefix}mailforward_simple` -- -DROP TABLE IF EXISTS `aw34w3_mailforward_simple`; -/*!50001 DROP VIEW IF EXISTS `aw34w3_mailforward_simple`*/; +DROP TABLE IF EXISTS `{$prefix}mailforward_simple`; +/*!50001 DROP VIEW IF EXISTS `{$prefix}mailforward_simple`*/; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; -/*!50001 CREATE TABLE `aw34w3_mailforward_simple` ( +/*!50001 CREATE TABLE `{$prefix}mailforward_simple` ( `source` tinyint NOT NULL, `destination` tinyint NOT NULL ) ENGINE=MyISAM */; SET character_set_client = @saved_cs_client; -- --- Table structure for table `aw34w3_mailforwardfrom` +-- Table structure for table `{$prefix}mailforwardfrom` -- -DROP TABLE IF EXISTS `aw34w3_mailforwardfrom`; +DROP TABLE IF EXISTS `{$prefix}mailforwardfrom`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; -CREATE TABLE `aw34w3_mailforwardfrom` ( +CREATE TABLE `{$prefix}mailforwardfrom` ( `mailforwardfrom_ID` int(10) unsigned NOT NULL AUTO_INCREMENT, `mailforwardfrom_username` varchar(32) NOT NULL DEFAULT '', `domain_ID` int(10) unsigned NOT NULL, PRIMARY KEY (`mailforwardfrom_ID`), UNIQUE KEY `mailforwardfrom_username` (`mailforwardfrom_username`,`domain_ID`), KEY `ID_domain` (`domain_ID`), - CONSTRAINT `aw34w3_mailforwardfrom_ibfk_1` FOREIGN KEY (`domain_ID`) REFERENCES `aw34w3_domain` (`domain_ID`) ON DELETE CASCADE + CONSTRAINT `{$prefix}mailforwardfrom_ibfk_1` FOREIGN KEY (`domain_ID`) REFERENCES `{$prefix}domain` (`domain_ID`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- --- Table structure for table `aw34w3_mailforwardto` +-- Table structure for table `{$prefix}mailforwardto` -- -DROP TABLE IF EXISTS `aw34w3_mailforwardto`; +DROP TABLE IF EXISTS `{$prefix}mailforwardto`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; -CREATE TABLE `aw34w3_mailforwardto` ( +CREATE TABLE `{$prefix}mailforwardto` ( `mailforwardfrom_ID` int(10) unsigned NOT NULL, `mailforwardto_address` varchar(128) NOT NULL, PRIMARY KEY (`mailforwardfrom_ID`,`mailforwardto_address`), KEY `mailforward_ID` (`mailforwardfrom_ID`,`mailforwardto_address`), - CONSTRAINT `aw34w3_mailforwardto_ibfk_1` FOREIGN KEY (`mailforwardfrom_ID`) REFERENCES `aw34w3_mailforwardfrom` (`mailforwardfrom_ID`) ON DELETE CASCADE + CONSTRAINT `{$prefix}mailforwardto_ibfk_1` FOREIGN KEY (`mailforwardfrom_ID`) REFERENCES `{$prefix}mailforwardfrom` (`mailforwardfrom_ID`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- --- Table structure for table `aw34w3_option` +-- Table structure for table `{$prefix}option` -- -DROP TABLE IF EXISTS `aw34w3_option`; +DROP TABLE IF EXISTS `{$prefix}option`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; -CREATE TABLE `aw34w3_option` ( +CREATE TABLE `{$prefix}option` ( `option_name` varchar(128) NOT NULL, `option_value` text NOT NULL, `option_autoload` tinyint(1) NOT NULL, PRIMARY KEY (`option_name`), KEY `option_autoload` (`option_autoload`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- --- Table structure for table `aw34w3_payment` +-- Table structure for table `{$prefix}payment` -- -DROP TABLE IF EXISTS `aw34w3_payment`; +DROP TABLE IF EXISTS `{$prefix}payment`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; -CREATE TABLE `aw34w3_payment` ( +CREATE TABLE `{$prefix}payment` ( `payment_ID` int(10) unsigned NOT NULL AUTO_INCREMENT, `payment_amount` float(10,2) NOT NULL, `payment_date` date NOT NULL, `payment_note` varchar(255) NOT NULL, `user_ID` int(10) unsigned NOT NULL, PRIMARY KEY (`payment_ID`), KEY `ID_client` (`user_ID`), - CONSTRAINT `aw34w3_payment_ibfk_1` FOREIGN KEY (`user_ID`) REFERENCES `aw34w3_user` (`user_ID`) ON DELETE CASCADE + CONSTRAINT `{$prefix}payment_ibfk_1` FOREIGN KEY (`user_ID`) REFERENCES `{$prefix}user` (`user_ID`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- --- Table structure for table `aw34w3_plan` +-- Table structure for table `{$prefix}plan` -- -DROP TABLE IF EXISTS `aw34w3_plan`; +DROP TABLE IF EXISTS `{$prefix}plan`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; -CREATE TABLE `aw34w3_plan` ( +CREATE TABLE `{$prefix}plan` ( `plan_ID` int(10) unsigned NOT NULL AUTO_INCREMENT, `plan_name` varchar(64) NOT NULL, `plan_uid` varchar(128) NOT NULL, `plan_yearlyprice` float(10,2) DEFAULT NULL, `plan_ftpusers` int(10) unsigned NOT NULL, `plan_databases` int(10) unsigned NOT NULL, `plan_mailboxes` int(10) unsigned NOT NULL, `plan_mailforwards` int(10) unsigned NOT NULL, PRIMARY KEY (`plan_ID`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- --- Table structure for table `aw34w3_user` +-- Table structure for table `{$prefix}user` -- -DROP TABLE IF EXISTS `aw34w3_user`; +DROP TABLE IF EXISTS `{$prefix}user`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; -CREATE TABLE `aw34w3_user` ( +CREATE TABLE `{$prefix}user` ( `user_ID` int(10) unsigned NOT NULL AUTO_INCREMENT, `user_uid` varchar(64) NOT NULL, `user_role` enum('user','admin') NOT NULL DEFAULT 'user', `user_email` varchar(128) NOT NULL, `user_active` tinyint(1) NOT NULL DEFAULT 1, `user_name` varchar(64) NOT NULL, `user_surname` varchar(64) NOT NULL, `user_password` varchar(40) NOT NULL, `user_birth` date DEFAULT NULL, `user_registration_date` datetime DEFAULT current_timestamp(), `user_last_login` datetime DEFAULT NULL, `user_last_online` datetime DEFAULT NULL, `user_reset` varchar(1023) DEFAULT NULL, PRIMARY KEY (`user_ID`), UNIQUE KEY `user_email` (`user_email`), UNIQUE KEY `user_login` (`user_uid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- --- Final view structure for view `aw34w3_mailbox_simple` +-- Final view structure for view `{$prefix}mailbox_simple` -- -/*!50001 DROP TABLE IF EXISTS `aw34w3_mailbox_simple`*/; -/*!50001 DROP VIEW IF EXISTS `aw34w3_mailbox_simple`*/; +/*!50001 DROP TABLE IF EXISTS `{$prefix}mailbox_simple`*/; +/*!50001 DROP VIEW IF EXISTS `{$prefix}mailbox_simple`*/; /*!50001 SET @saved_cs_client = @@character_set_client */; /*!50001 SET @saved_cs_results = @@character_set_results */; /*!50001 SET @saved_col_connection = @@collation_connection */; /*!50001 SET character_set_client = utf8mb4 */; /*!50001 SET character_set_results = utf8mb4 */; /*!50001 SET collation_connection = utf8mb4_general_ci */; /*!50001 CREATE ALGORITHM=UNDEFINED */ /*!50013 SQL SECURITY DEFINER */ -/*!50001 VIEW `aw34w3_mailbox_simple` AS select concat(`mailbox`.`mailbox_username`,'@',`domain`.`domain_name`) AS `email`,`domain`.`domain_name` AS `domain`,`mailbox`.`mailbox_username` AS `username`,`mailbox`.`mailbox_receive` AS `receive`,`mailbox`.`mailbox_password` AS `password`,concat(replace(`domain`.`domain_name`,'/',''),'/',replace(`mailbox`.`mailbox_username`,'/',''),'/') AS `path` from (`aw34w3_mailbox` `mailbox` join `aw34w3_domain` `domain`) where `mailbox`.`domain_ID` = `domain`.`domain_ID` and `domain`.`domain_active` = '1' */; +/*!50001 VIEW `{$prefix}mailbox_simple` AS select concat(`mailbox`.`mailbox_username`,'@',`domain`.`domain_name`) AS `email`,`domain`.`domain_name` AS `domain`,`mailbox`.`mailbox_username` AS `username`,`mailbox`.`mailbox_receive` AS `receive`,`mailbox`.`mailbox_password` AS `password`,concat(replace(`domain`.`domain_name`,'/',''),'/',replace(`mailbox`.`mailbox_username`,'/',''),'/') AS `path` from (`{$prefix}mailbox` `mailbox` join `{$prefix}domain` `domain`) where `mailbox`.`domain_ID` = `domain`.`domain_ID` and `domain`.`domain_active` = '1' */; /*!50001 SET character_set_client = @saved_cs_client */; /*!50001 SET character_set_results = @saved_cs_results */; /*!50001 SET collation_connection = @saved_col_connection */; -- --- Final view structure for view `aw34w3_mailforward_mail2mail` +-- Final view structure for view `{$prefix}mailforward_mail2mail` -- -/*!50001 DROP TABLE IF EXISTS `aw34w3_mailforward_mail2mail`*/; -/*!50001 DROP VIEW IF EXISTS `aw34w3_mailforward_mail2mail`*/; +/*!50001 DROP TABLE IF EXISTS `{$prefix}mailforward_mail2mail`*/; +/*!50001 DROP VIEW IF EXISTS `{$prefix}mailforward_mail2mail`*/; /*!50001 SET @saved_cs_client = @@character_set_client */; /*!50001 SET @saved_cs_results = @@character_set_results */; /*!50001 SET @saved_col_connection = @@collation_connection */; /*!50001 SET character_set_client = utf8mb4 */; /*!50001 SET character_set_results = utf8mb4 */; /*!50001 SET collation_connection = utf8mb4_general_ci */; /*!50001 CREATE ALGORITHM=UNDEFINED */ /*!50013 SQL SECURITY DEFINER */ -/*!50001 VIEW `aw34w3_mailforward_mail2mail` AS select concat(`mailforwardfrom`.`mailforwardfrom_username`,'@',`domain`.`domain_name`) AS `source`,`mailforwardto`.`mailforwardto_address` AS `destination` from ((`aw34w3_mailforwardfrom` `mailforwardfrom` join `aw34w3_domain` `domain`) join `aw34w3_mailforwardto` `mailforwardto`) where `mailforwardfrom`.`domain_ID` = `domain`.`domain_ID` and `domain`.`domain_active` = 1 and `mailforwardto`.`mailforwardfrom_ID` = `mailforwardfrom`.`mailforwardfrom_ID` */; +/*!50001 VIEW `{$prefix}mailforward_mail2mail` AS select concat(`mailforwardfrom`.`mailforwardfrom_username`,'@',`domain`.`domain_name`) AS `source`,`mailforwardto`.`mailforwardto_address` AS `destination` from ((`{$prefix}mailforwardfrom` `mailforwardfrom` join `{$prefix}domain` `domain`) join `{$prefix}mailforwardto` `mailforwardto`) where `mailforwardfrom`.`domain_ID` = `domain`.`domain_ID` and `domain`.`domain_active` = 1 and `mailforwardto`.`mailforwardfrom_ID` = `mailforwardfrom`.`mailforwardfrom_ID` */; /*!50001 SET character_set_client = @saved_cs_client */; /*!50001 SET character_set_results = @saved_cs_results */; /*!50001 SET collation_connection = @saved_col_connection */; -- --- Final view structure for view `aw34w3_mailforward_simple` +-- Final view structure for view `{$prefix}mailforward_simple` -- -/*!50001 DROP TABLE IF EXISTS `aw34w3_mailforward_simple`*/; -/*!50001 DROP VIEW IF EXISTS `aw34w3_mailforward_simple`*/; +/*!50001 DROP TABLE IF EXISTS `{$prefix}mailforward_simple`*/; +/*!50001 DROP VIEW IF EXISTS `{$prefix}mailforward_simple`*/; /*!50001 SET @saved_cs_client = @@character_set_client */; /*!50001 SET @saved_cs_results = @@character_set_results */; /*!50001 SET @saved_col_connection = @@collation_connection */; /*!50001 SET character_set_client = utf8mb4 */; /*!50001 SET character_set_results = utf8mb4 */; /*!50001 SET collation_connection = utf8mb4_general_ci */; /*!50001 CREATE ALGORITHM=UNDEFINED */ /*!50013 SQL SECURITY DEFINER */ -/*!50001 VIEW `aw34w3_mailforward_simple` AS select concat(`mailforwardfrom`.`mailforwardfrom_username`,'@',`domain`.`domain_name`) AS `source`,group_concat(`mailforwardto`.`mailforwardto_address` separator ',') AS `destination` from ((`aw34w3_mailforwardfrom` `mailforwardfrom` join `aw34w3_domain` `domain`) join `aw34w3_mailforwardto` `mailforwardto`) where `mailforwardfrom`.`domain_ID` = `domain`.`domain_ID` and `domain`.`domain_active` = 1 and `mailforwardto`.`mailforwardfrom_ID` = `mailforwardfrom`.`mailforwardfrom_ID` group by `mailforwardfrom`.`mailforwardfrom_ID` */; +/*!50001 VIEW `{$prefix}mailforward_simple` AS select concat(`mailforwardfrom`.`mailforwardfrom_username`,'@',`domain`.`domain_name`) AS `source`,group_concat(`mailforwardto`.`mailforwardto_address` separator ',') AS `destination` from ((`{$prefix}mailforwardfrom` `mailforwardfrom` join `{$prefix}domain` `domain`) join `{$prefix}mailforwardto` `mailforwardto`) where `mailforwardfrom`.`domain_ID` = `domain`.`domain_ID` and `domain`.`domain_active` = 1 and `mailforwardto`.`mailforwardfrom_ID` = `mailforwardfrom`.`mailforwardfrom_ID` group by `mailforwardfrom`.`mailforwardfrom_ID` */; /*!50001 SET character_set_client = @saved_cs_client */; /*!50001 SET character_set_results = @saved_cs_results */; /*!50001 SET collation_connection = @saved_col_connection */; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; -- Dump completed on 2020-04-05 10:44:44 diff --git a/documentation/database/patches/patch-0002-add-mailbox-lastsizebytes.sql b/documentation/database/patches/patch-0002-add-mailbox-lastsizebytes.sql new file mode 100644 index 0000000..70d902d --- /dev/null +++ b/documentation/database/patches/patch-0002-add-mailbox-lastsizebytes.sql @@ -0,0 +1 @@ +ALTER TABLE `{$prefix}mailbox` ADD COLUMN `mailbox_lastsizebytes` INT(10) UNSIGNED AFTER `mailbox_description`; diff --git a/documentation/database/patches/patch-0002-rename-denormalized-mailbox-lastsizebytes.sql b/documentation/database/patches/patch-0002-rename-denormalized-mailbox-lastsizebytes.sql deleted file mode 100644 index c4f2902..0000000 --- a/documentation/database/patches/patch-0002-rename-denormalized-mailbox-lastsizebytes.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE `{$prefix}mailbox` CHANGE `mailbox_lastquotabytes` `mailbox_lastsizebytes` INT(10) UNSIGNED; - diff --git a/documentation/etc/apache2/sites-available/kiss-libre-hosting-panel.conf b/documentation/etc/apache2/sites-available/kiss-libre-hosting-panel.conf new file mode 100644 index 0000000..cdec0c1 --- /dev/null +++ b/documentation/etc/apache2/sites-available/kiss-libre-hosting-panel.conf @@ -0,0 +1,12 @@ +# +# KISS Libre Hosting Panel minimal configuration +# +# See: +# https://gitpull.it/w/kiss_libre_hosting_panel/ +# + + ServerName panel.reyboz.it + + # path to the 'www' directory in the 'kiss-libre-hosting-panel' repository + DocumentRoot /var/www/kiss-libre-hosting-panel/www + diff --git a/include/class-Domain.php b/include/class-Domain.php index fbabbe2..12578ad 100644 --- a/include/class-Domain.php +++ b/include/class-Domain.php @@ -1,317 +1,317 @@ . // load Plan trait class_exists( 'Plan' ); /** * Methods for a Domain class */ trait DomainTrait { use PlanTrait; /** * Count of the Domain's Mailboxes * * This is a kind of cache * * @var int */ private $domainMailboxCount = null; /** * Count of the Domain's FTP accounts * * This is a kind of cache * * @var int */ private $domainFTPAccountCount = null; /** * Get domain ID * * @return int */ public function getDomainID() { return $this->get( 'domain_ID' ); } /* * Get domain name * * @return string */ public function getDomainName() { return $this->get( 'domain_name' ); } /** * Get a printable Domain firm * * It may be a link if you are allowed to edit this Domain. * * @return string */ public function getDomainFirm() { return HTML::a( $this->getDomainPermalink(), esc_html( $this->getDomainName() ) ); } /** * Get the sanitized relative directory name of this domain name * * Actually this should be valid for both the MTA and for the webserver. * * @return string */ public function getDomainDirname() { $dir = $this->getDomainName(); // it was validated during creation time, but validate also now // to prevent malicious actions over hacked databases require_safe_dirname( $dir ); return $dir; } /** * Get the domain edit URL * * @param boolean $absolute True for an absolute URL * @return string */ public function getDomainPermalink( $absolute = false ) { return Domain::permalink( $this->getDomainName(), $absolute ); } /** * Get the permalink to the edit plan page * * @param boolean $absolute True for an absolute URL * @return string */ public function getDomainPlanPermalink( $absolute = false ) { return Plan::domainPermalink( $this->getDomainName(), $absolute ); } /** * Check if you can create a new Mailbox for this Domain * * The Domain must have Plan informations. * * @return boolean */ public function canCreateMailboxInDomain() { return $this->getPlanMailboxes() > $this->getDomainMailboxCount() || has_permission( 'edit-email-all' ); } /** * Check if you can create a new FTP account for this Domain * * The Domain must have Plan informations. * * The Domain must have Plan informations. */ public function canCreateFTPAccountForDomain() { return $this->getPlanFTPUsers() > $this->getDomainFTPAccountCount() || has_permission( 'edit-ftp-all' ); } /** * Factory mailbox from this domain * * @return MailboxFullAPI */ public function factoryMailbox() { return ( new MailboxFullAPI() )->whereDomain( $this ); } /** * Factory e-mail forward from this domain * * @return MailforwardFullAPI */ public function factoryMailforwardfrom() { return ( new MailforwardfromQuery() )->whereDomain( $this ); } /** * Set a count of Domain's Mailboxes * * This method should not be used directly. * * @param $count int * @return self */ public function setDomainMailboxCount( $count ) { $this->domainMailboxCount = $count; } /** * Get the number of Mailboxes of this Domain * * This method has a layer of cache. * * @return int */ public function getDomainMailboxCount() { // check if we already know the count if( !isset( $this->domainMailboxCount ) ) { // count the number of mailboxes associated to this Domain $count = $this->factoryMailbox() ->select( 'COUNT(*) count' ) ->queryValue( 'count' ); // save in cache $this->domainMailboxCount = (int) $count; } return $this->domainMailboxCount; } /** * Get the number of FTP accounts of this Domain * * This method has a layer of cache. * * @return int */ public function getDomainFTPAccountCount() { // check if we already know the count if( !isset( $this->domainFTPAccountCount ) ) { // count the number of mailboxes associated to this Domain $count = $this->factoryFTP() ->select( 'COUNT(*) count' ) ->queryValue( 'count' ); // save in cache $this->domainFTPAccountCount = (int) $count; } return $this->domainFTPAccountCount; } /** * Get the expected MTA directory containing Domain's mailboxes * * This pathname should be considered true for the MTA host. * * TODO: actually all the mailbox are on the same host. * Then, we should support multiple hosts. * * @return string */ public function getDomainMailboxesPath() { // mailboxes are stored under a $BASE/domain/username filesystem structure return MAILBOX_BASE_PATH . __ . $this->getDomainDirname(); } /** * Get the expected and sanitized base domain directory containing its directories * * This pathname should be considered true both for the webserver serving * that domain and for the related FTP server. * * TODO: actually all the domains are on the same host. * Then, we should support multiple hosts. * * @return string */ public function getDomainBasePath() { // mailboxes are stored under a $BASE/domain/username filesystem structure - return VIRTUALHOSTS_DIR . __ . $this->getDomainDirname(); + return VIRTUALHOST_BASE_PATH . __ . $this->getDomainDirname(); } /** * Factory FTP users from this domain * * @return FTPAPI */ public function factoryFTP() { return ( new FTPAPI() )->whereDomain( $this ); } /** * Normalize a Domain object after being retrieved from database */ protected function normalizeDomain() { $this->integers( 'domain_ID' ); $this->booleans( 'domain_active' ); $this->dates( 'domain_born', 'domain_expiration' ); $this->normalizePlan(); } } /** * Describe the 'domain' table */ class Domain extends Queried { use DomainTrait; /** * Table name */ const T = 'domain'; const UID = 'domain_name'; /** * Constructor */ public function __construct() { $this->normalizeDomain(); } /** * Get the domain permalink * * @param string $domain_name Domain name * @param boolean $absolute True for an absolute URL */ public static function permalink( $domain_name = null, $absolute = false ) { $url = 'domain.php'; if( $domain_name ) { $url .= _ . $domain_name; } return site_page( $url, $absolute ); } /** * Force to get a Domain ID, whatever is passed * * @param mixed $domain Domain object or Domain ID * @return int */ public static function getID( $domain ) { return is_object( $domain ) ? $domain->getDomainID() : (int)$domain; } } diff --git a/load-example.php b/load-example.php index 7fac38f..eed1217 100644 --- a/load-example.php +++ b/load-example.php @@ -1,67 +1,74 @@ . /* * This is an example configuration file * * Please fill this file and save as 'load.php'! */ -// database credentials -$username = 'insert here database username'; -$password = 'insert here database password'; -$database = 'insert here database name'; +// change these MySQL/MariaDB database credentials +$username = 'libre_hosting_panel'; +$database = 'libre_hosting_panel'; +$password = 'insert here a password'; + $location = 'localhost'; // database prefix (if any) -$prefix = ''; - -// your contact e-mail -define( 'CONTACT_EMAIL', 'services@example.org' ); +$prefix = 'librehost_'; // your SMTP credentials define( 'MAIL_FROM', 'noreply@example.org' ); define( 'SMTP_USERNAME', 'noreply@example.org' ); define( 'SMTP_PASSWORD', 'insert here smtp password' ); define( 'SMTP_AUTH', 'PLAIN' ); define( 'SMTP_TLS', true ); define( 'SMTP_SERVER', 'mail.example.org' ); define( 'SMTP_PORT', 465 ); -// absolute path to the project directory without trailing slash -define( 'ABSPATH', __DIR__ ); +// your contact e-mail +define( 'CONTACT_EMAIL', 'services@example.org' ); // absolute web directory without trailing slash +// if your URL is http://asd.org/hosting/ then set '/hosting' +// if your URL is http://asd.org/ then set '' define( 'ROOT', '' ); +// absolute path to the project directory without trailing slash +// this is rarely changed +define( 'ABSPATH', __DIR__ ); + // other specific configuration about your hosting environments $HOSTING_CONFIG = new stdClass(); -// Mailbox password encryption custom mechanism (you can leave this commented for the default) +// Mailbox password encryption custom mechanism +// you can leave this commented for the default- this is just an example. # $HOSTING_CONFIG->MAILBOX_ENCRYPT_PWD = function ( $password ) { # $salt = bin2hex( openssl_random_pseudo_bytes( 3 ) ); # return '{SHA512-CRYPT}' . crypt( $password, "$6$$salt" ); # }; -// FTP password encryption custom mechanism (you can leave this commented for the default) +// FTP password encryption custom mechanism +// you can leave this commented for the default. this is just an example. # $HOSTING_CONFIG->FTP_ENCRYPT_PWD = function ( $password ) { # $salt = bin2hex( openssl_random_pseudo_bytes( 3 ) ); # return '{SHA512-CRYPT}' . crypt( $password, "$6$$salt" ); # }; -// path to the boz-php framework -require '/usr/share/php/suckless-php/load.php'; +// customize your path to the suckess-php framework +// https://gitpull.it/source/suckless-php/ +require __DIR__ . '/../suckless-php/load.php'; diff --git a/load-post.php b/load-post.php index aeb1796..16429ab 100644 --- a/load-post.php +++ b/load-post.php @@ -1,131 +1,136 @@ . /** * This is your versioned configuration file * * It does not contains secrets. * * This file is required after loading your * unversioned configuration file: * * load.php */ -// database version +// Database vesion // -// you can increase your database version if you added some patches in: +// Do not touch if not sure. +// +// the maintainer increases this database version +// each version is related to a patch here: // documentation/database/patches define( 'DATABASE_VERSION', 7 ); +/** + * VirtualHost(s) base path + * + * e.g. you may have /var/www/example.com/index.html + * do NOT end with a slash + */ +define_default( 'VIRTUALHOST_BASE_PATH', '/var/www' ); + +/** + * Mailbox base path + * + * Used by CLI scripts to calculate the current quotas. + * + * The mailboxes should have paths like: + * MAILBOX_BASE_PATH/domain_name/user_name/ + */ +define_default( 'MAILBOX_BASE_PATH', '/home/vmail' ); + // include path define_default( 'INCLUDE_PATH', ABSPATH . __ . 'include' ); // template path define_default( 'TEMPLATE_PATH', ABSPATH . __ . 'template' ); +// override default user class +define_default( 'SESSIONUSER_CLASS', 'User' ); + // autoload classes from the /include directory spl_autoload_register( function( $name ) { // TODO: autoload classes and create DomainTrait and use in Mailbox $path = INCLUDE_PATH . __ . "class-$name.php"; if( is_file( $path ) ) { require $path; } } ); -// override default user class -define( 'SESSIONUSER_CLASS', 'User' ); - // load common functions require INCLUDE_PATH . __ . 'functions.php'; // jquery URL // provided by the libjs-jquery package as default define_default( 'JQUERY_URL', '/javascript/jquery/jquery.min.js' ); // Bootstrap CSS/JavaScript files without trailing slash // provided by the libjs-bootstrap package as default define_default( 'BOOTSTRAP_DIR_URL', '/javascript/bootstrap' ); // path to the Net SMTP class // provided by the php-net-smtp package as default define_default( 'NET_SMTP', '/usr/share/php/Net/SMTP.php' ); -// base directory for your virtualhosts -// e.g. you may have /var/www/example.com/index.html -// do NOT end with a slash -// TODO: support multiple hosts -define_default( 'VIRTUALHOSTS_DIR', '/var/www' ); - // default currency simbol define_default( 'DEFAULT_CURRENCY_SYMBOL', '€' ); // register JavaScript/CSS files register_js( 'jquery', JQUERY_URL ); register_js( 'bootstrap', BOOTSTRAP_DIR_URL . '/js/bootstrap.min.js' ); register_css( 'bootstrap', BOOTSTRAP_DIR_URL . '/css/bootstrap.min.css' ); register_css( 'custom-css', ROOT . '/content/style.css' ); // GNU Gettext i18n define( 'GETTEXT_DOMAIN', 'reyboz-hosting-panel' ); define( 'GETTEXT_DIRECTORY', 'l10n' ); define( 'GETTEXT_DEFAULT_ENCODE', CHARSET ); // UTF-8 // common strings -define_default( 'SITE_NAME', "KISS Libre Hosting Panel" ); +define_default( 'SITE_NAME', "Libre Hosting Panel" ); define_default( 'CONTACT_EMAIL', 'support@' . DOMAIN ); -define_default( 'REPO_URL', 'https://gitpull.it/project/profile/15/' ); +define_default( 'REPO_URL', 'https://gitpull.it/source/kiss-libre-hosting-panel/' ); // limit session duration to 5 minutes (60s * 100m) define_default( 'SESSION_DURATION', 6000 ); -/** - * Mailbox base path - * - * Used by CLI scripts to calculate the current quotas. - * - * The mailboxes should have paths like: - * MAILBOX_BASE_PATH/domain_name/user_name/ - */ -define_default( 'MAILBOX_BASE_PATH', '/home/vmail' ); - // register web pages add_menu_entries( [ - new MenuEntry( 'index', '/', __( "Dashboard" ), null, 'backend' ), + new MenuEntry( 'index', '', __( "Dashboard" ), null, 'backend' ), new MenuEntry( 'login', 'login.php', __( "Login" ) ), - new MenuEntry( 'profile', 'profile.php', __( "Profile" ) ), + new MenuEntry( 'profile', 'profile.php', __( "Profile" ), null, 'read' ), new MenuEntry( 'logout', 'logout.php', __( "Logout" ), null, 'read' ), new MenuEntry( 'user-list', 'user-list.php', __( "Users" ), null, 'edit-user-all' ), new MenuEntry( 'activity', 'activity.php', __( "Last Activity" ), null, 'monitor' ), new MenuEntry( 'password-reset', 'password-reset.php', __( "Password reset" ) ), ] ); // permissions of a normal user register_permissions( 'user', [ 'read', 'backend', ] ); // permissions of an admin inherit_permissions( 'admin', 'user', [ 'edit-user-all', 'edit-email-all', 'edit-domain-all', 'edit-plan-all', 'edit-ftp-all', 'monitor', ] ); diff --git a/scripts/add-user.php b/scripts/add-user.php new file mode 100755 index 0000000..7933f11 --- /dev/null +++ b/scripts/add-user.php @@ -0,0 +1,126 @@ +#!/usr/bin/php +. + +// allowed only from command line interface +if( ! isset( $argv[ 0 ] ) ) { + exit( 1 ); +} + +// autoload the framework +require __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'load.php'; + +// command line arguments +$opts = getopt( 'h', [ + 'uid:', + 'role:', + 'email:', + 'name:', + 'surname:', + 'pwd:', + 'force::', + 'help', +] ); + +// show help +if( ! isset( $opts[ 'uid' ], $opts[ 'pwd' ], $opts[ 'role' ] ) || isset( $opts[ 'help' ] ) || isset( $opts[ 'h' ] ) ) { + + $roles = _roles(); + $roles_list = implode( '|', $roles ); + + printf( "Usage: %s [OPTIONS]\n", $argv[ 0 ] ); + echo "OPTIONS:\n"; + echo " --uid=UID username\n"; + echo " --email=EMAIL email\n"; + echo " --name=NAME first name\n"; + echo " --surname=SURNAME family name\n"; + echo " --role=ROLE role ($roles_list)\n"; + echo " --pwd=PASSWORD password\n"; + echo " --force update the user password if exists\n"; + echo " -h --help show this help and exit\n"; + exit( 0 ); +} + +// validate role +if( !Permissions::instance()->roleExists( $opts['role'] ) ) { + printf( "The role '%s' does not exist\n", $opts['role'] ); + exit( 1 ); +} + +// look for existing user +$user = User::factoryFromUID( $opts[ 'uid' ] ) + ->select( User::ID ) + ->queryRow(); + +// check if it exists +if( $user && ! isset( $opts[ 'force' ] ) ) { + printf( "User %s already exist\n", $opts[ 'uid' ] ); + exit( 1 ); +} + +// encrypt the password +$pwd = User::encryptPassword( $opts[ 'pwd' ] ); + +if( $user ) { + + // update the User + ( new UserAPI() ) + ->whereUser( $user ) + ->update( [ + 'user_password' => $pwd, + ] ); + + echo "Updated.\n"; + +} else { + + // insert a new user + ( new UserAPI() ) + ->insertRow( [ + 'user_uid' => $opts[ 'uid' ], + 'user_role' => $opts[ 'role' ], + 'user_name' => $opts[ 'name' ], + 'user_surname' => $opts[ 'surname' ], + 'user_email' => $opts[ 'email' ], + 'user_password' => $pwd, + 'user_active' => 1, + ] ); + + echo "Created\n"; +} + + +/** + * Get a list of available roles + * + * Well, it just remove the DEFAULT_USER_ROLE from the roles. + * + * @return array + */ +function _roles() { + + $good_roles = []; + + // get the existing roles + foreach( Permissions::instance()->getRoles() as $role ) { + if( $role !== DEFAULT_USER_ROLE ) { + $good_roles[] = $role; + } + } + + return $good_roles; +} diff --git a/cli/database-upgrade.php b/scripts/database-upgrade.php similarity index 91% rename from cli/database-upgrade.php rename to scripts/database-upgrade.php index 640dc7a..bfdfc12 100755 --- a/cli/database-upgrade.php +++ b/scripts/database-upgrade.php @@ -1,149 +1,147 @@ #!/usr/bin/php . /** * This is the script to upgrade the database * * It will execute the available database patches until the database * is to its latest version. * * To be honest, it also create the database schema if missing. */ require __DIR__ . '/../load.php'; echo <<limit( 1 ) ->queryRow(); } catch( Exception $e ) { $database_exists = false; } if( !$database_exists ) { // database schema installation echo "important tables are missing! assuming no database.\n"; echo "importing the schema for the first time\n"; - execute_queries_from_file( "$documentation_path/schema.sql" ); - - // if we have not imported any database version, just set the latest one - $version_exists = get_option( 'database_version', 0 ); - if( !$version_exists ) { - set_option( 'database_version', DATABASE_VERSION ); - } + execute_queries_from_file( "$documentation_path/database-schema.sql" ); } // get the current database version $current_database_version = get_option( 'database_version', 0 ); // notify about the current status printf( "current database version: %d\n", $current_database_version ); printf( "last database version: %d\n", DATABASE_VERSION ); // update to next database versions once at time while( $current_database_version < DATABASE_VERSION ) { $current_database_version++; // note that the patch name can have a name such as 0001-foo.sql $patch_name = sprintf( 'patch-%04d-*.sql', $current_database_version ); // path to the expected patch $patch_path = "$patch_directory/$patch_name"; // check if there is a database patch to be applied echo "looking for patch $patch_path\n"; $found = false; foreach( glob( $patch_path ) as $filename ) { execute_queries_from_file( $filename ); $found = true; } // actually the unexistence of a patch is good if( !$found ) { echo "\t skipped unexisting patch\n"; } // update the database version echo "\t increment database version to $current_database_version\n"; set_option( 'database_version', $current_database_version ); } echo "database upgrade end. good for you!\n"; /** * Execute some queries from a file * * @param string $file */ function execute_queries_from_file( $file ) { echo "\t executing queries from $file\n"; // get the patch content - $queries = file_get_contents( $file ); + $queries = @file_get_contents( $file ); + if( !$queries ) { + throw new Exception( "missing file $file" ); + } // replace the database prefix with the current one - $database_prefix = DB::instance()->getPrefix(); + //$database_prefix = DB::instance()->getPrefix(); // this cannot work in this phase + $database_prefix = $GLOBALS['prefix']; $queries = str_replace( '{$prefix}', $database_prefix, $queries ); // execute the patch queries (it will die in case of error) try { multiquery( $queries ); } catch( Exception $e ) { echo "\n"; printf( "ERROR:\n%s\n\n", $e->getMessage() ); printf( "DEBUG QUERIES:\n%s\n", $queries ); exit( 1 ); } } diff --git a/cli/update-mailbox-quotas.php b/scripts/update-mailbox-quotas.php similarity index 100% rename from cli/update-mailbox-quotas.php rename to scripts/update-mailbox-quotas.php diff --git a/www/login.php b/www/login.php index d17ba6a..978f3bc 100644 --- a/www/login.php +++ b/www/login.php @@ -1,77 +1,77 @@ . /* * This is the login page */ // load framework require '../load.php'; -// spawn header -Header::spawn(); - // go to the wanted page (or homepage) if( isset( $_POST['user_uid'] ) && login() ) { http_redirect( after_login_url(), 303 ); // Redirection "See Other" } + +// spawn header +Header::spawn(); ?>

" required="required" />

getURL(); }