Compare commits
97 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f4c9f4c98a | ||
![]() |
1426b5840c | ||
![]() |
3594c6e236 | ||
![]() |
a7c3a4f271 | ||
![]() |
dbb66ee3aa | ||
![]() |
2e2ac12073 | ||
![]() |
2926d3fab2 | ||
![]() |
29dc137f0e | ||
![]() |
99925c690e | ||
![]() |
171f2c7f11 | ||
![]() |
5ba6def517 | ||
![]() |
f50b8e1360 | ||
![]() |
ecd556cdba | ||
![]() |
38715854b1 | ||
![]() |
09c0a7c248 | ||
![]() |
23f1cd5b06 | ||
![]() |
410fe43330 | ||
![]() |
1f44769030 | ||
![]() |
4d8143eb0d | ||
![]() |
6b9605db73 | ||
![]() |
5109879ab6 | ||
![]() |
25ff5bf759 | ||
![]() |
a5ec3e879e | ||
![]() |
e3d634a8a3 | ||
![]() |
03ca96edaf | ||
![]() |
6642705d55 | ||
![]() |
8f2661e199 | ||
![]() |
d47ae1b9ae | ||
![]() |
800777a4e4 | ||
![]() |
a0c88ff757 | ||
![]() |
79d1ba2d6a | ||
![]() |
283b4b9d25 | ||
![]() |
c6d5191e76 | ||
![]() |
0368a99c80 | ||
![]() |
c59789f425 | ||
![]() |
9f985600c0 | ||
![]() |
ffbf29eb4c | ||
![]() |
c3171b972f | ||
![]() |
e60420ce79 | ||
![]() |
bf1c6b7972 | ||
![]() |
8cd5baeee2 | ||
![]() |
fadbd4a6b4 | ||
![]() |
25f08f5e76 | ||
![]() |
3be0a51c75 | ||
![]() |
d3003715dc | ||
![]() |
2d3ae751f7 | ||
![]() |
7ef1e6d53a | ||
![]() |
34f1b7d5f1 | ||
![]() |
7336611dd2 | ||
![]() |
cc998c9dfb | ||
![]() |
300dadbab6 | ||
![]() |
5e72d0e715 | ||
![]() |
b8486843a8 | ||
![]() |
93f023d81a | ||
![]() |
4dc50750c9 | ||
![]() |
dda397074c | ||
![]() |
9c175037fe | ||
![]() |
7090bef419 | ||
![]() |
e9f8d850f0 | ||
![]() |
1f736a9746 | ||
![]() |
39a38be904 | ||
![]() |
f68e75f801 | ||
![]() |
40d5b4141f | ||
![]() |
19ec250851 | ||
![]() |
ce380e587b | ||
![]() |
3c9b0c6415 | ||
![]() |
342e0d510f | ||
![]() |
0b1c3788ab | ||
![]() |
d3c56b815c | ||
![]() |
7dfaa0c22e | ||
![]() |
70b6463501 | ||
![]() |
75598698d1 | ||
![]() |
c4fa44cff5 | ||
![]() |
82de30f636 | ||
![]() |
084c2abd0e | ||
![]() |
2cf74eafc7 | ||
![]() |
2a62fcb377 | ||
![]() |
8954a01b69 | ||
![]() |
a5a6931236 | ||
![]() |
88e701b23d | ||
![]() |
c182551e8f | ||
![]() |
3964e5331b | ||
![]() |
32d0216a83 | ||
![]() |
297cebd57b | ||
![]() |
a1e13a9919 | ||
![]() |
3b8466ab7f | ||
![]() |
da0fb4738f | ||
![]() |
6bd36d6623 | ||
![]() |
d0b38d17b1 | ||
![]() |
838188174e | ||
![]() |
f6952b44fd | ||
![]() |
db0520368d | ||
![]() |
35a2b9cec2 | ||
![]() |
4b833acddf | ||
![]() |
9dad71e647 | ||
![]() |
ac7b4172a9 | ||
![]() |
fa68acc1e4 |
74 changed files with 7435 additions and 1479 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1 +1 @@
|
|||
config/config_override.inc.php
|
||||
config/config.php
|
26
.travis.yml
Normal file
26
.travis.yml
Normal file
|
@ -0,0 +1,26 @@
|
|||
language: php
|
||||
php:
|
||||
- '5.4'
|
||||
- '5.5'
|
||||
- '5.6'
|
||||
- '7.0'
|
||||
- hhvm
|
||||
- nightly
|
||||
matrix:
|
||||
allow_failures:
|
||||
- php: hhvm
|
||||
- php: nightly
|
||||
services:
|
||||
- mysql
|
||||
before_install:
|
||||
# Create example schema
|
||||
- mysql -u root -e "CREATE USER 'vmail'@'localhost' IDENTIFIED BY 'vmail';"
|
||||
- mysql -u root -e "CREATE DATABASE IF NOT EXISTS vmail;"
|
||||
- mysql -u root -e "GRANT ALL PRIVILEGES ON vmail.* TO 'vmail'@'localhost'"
|
||||
- mysql -u root -e "CREATE TABLE vmail.domains (id int(10) unsigned NOT NULL AUTO_INCREMENT, domain varchar(128) NOT NULL, PRIMARY KEY (domain), UNIQUE KEY id (id)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;"
|
||||
- mysql -u root -e "CREATE TABLE vmail.users (id int(10) unsigned NOT NULL AUTO_INCREMENT, username varchar(128) NOT NULL DEFAULT '', domain varchar(128) NOT NULL DEFAULT '', password varchar(128) NOT NULL DEFAULT '', mailbox_limit int(10) NOT NULL DEFAULT '128', max_user_redirects int(10) NOT NULL DEFAULT '0', PRIMARY KEY (username,domain), UNIQUE KEY id (id)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;"
|
||||
- mysql -u root -e "CREATE TABLE vmail.aliases (id int(10) unsigned NOT NULL AUTO_INCREMENT, source varchar(128) NOT NULL, destination text NOT NULL, multi_source varchar(32) DEFAULT NULL, is_created_by_user int(1) NOT NULL DEFAULT '0', PRIMARY KEY (source), UNIQUE KEY id (id)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;"
|
||||
# Copy the example config
|
||||
- cp config/config.php.example config/config.php
|
||||
notifications:
|
||||
email: false
|
296
README.md
296
README.md
|
@ -1,5 +1,9 @@
|
|||
:exclamation: This project **isn't maintained** anymore. New maintainers welcome, just contact [@ohartl](https://github.com/ohartl) . Please consider using alternatives like https://github.com/Andreas-Bresch/vmailManage/ :exclamation:
|
||||
|
||||
# WebMUM - Web Mailserver User Manager
|
||||
|
||||
[](https://travis-ci.org/ohartl/webmum)
|
||||
|
||||
***WebMUM is not compatible with the [new Mailserver-HowTo](https://thomas-leister.de/allgemein/sicherer-mailserver-dovecot-postfix-virtuellen-benutzern-mysql-ubuntu-server-xenial/)!,*** but we will try to implement the changes for the release of version 1.0.0.
|
||||
|
||||
WebMUM is a web frontend based on PHP which helps you to manage e-mail server via MySQL. This software is licensed under the MIT license.
|
||||
|
@ -9,19 +13,25 @@ Founder of this project is [ThomasLeister](https://github.com/ThomasLeister), a
|
|||
|
||||
Feel free to send in issues and pull requests, your support for this project is much appreciated!
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
Clone the WebMUM Repository to your webserver's virtual host root directory:
|
||||
|
||||
```php
|
||||
```bash
|
||||
git clone https://github.com/ohartl/webmum
|
||||
```
|
||||
|
||||
|
||||
A update / upgrade guide can be found [here](#update--upgrade-webmum).
|
||||
|
||||
|
||||
### Webserver
|
||||
|
||||
Now configure your webserver. URL rewriting to index.php is required.
|
||||
|
||||
### Nginx
|
||||
#### Nginx
|
||||
|
||||
Nginx config examples following, but you still need to change domain and path in config as explained in WebMum Config -> Paths.
|
||||
Nginx config examples following, but you still need to change domain and path in config as explained [here](#paths).
|
||||
|
||||
With subdirectory `webmum/` in URL (e.g. `http://mydomain.tld/webmum/`):
|
||||
|
||||
|
@ -41,7 +51,17 @@ server {
|
|||
}
|
||||
|
||||
location /webmum {
|
||||
try_files $uri $uri/ /webmum/index.php?$args;
|
||||
try_files $uri $uri/ /webmum/index.php?$args;
|
||||
}
|
||||
|
||||
# protect the codebase by denying direct access
|
||||
location ^~ /webmum/include/php {
|
||||
deny all;
|
||||
return 403;
|
||||
}
|
||||
location ^~ /webmum/config {
|
||||
deny all;
|
||||
return 403;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -64,14 +84,24 @@ server {
|
|||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php?$args;
|
||||
try_files $uri $uri/ /index.php?$args;
|
||||
}
|
||||
|
||||
# protect the codebase by denying direct access
|
||||
location ^~ /include/php {
|
||||
deny all;
|
||||
return 403;
|
||||
}
|
||||
location ^~ /config {
|
||||
deny all;
|
||||
return 403;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Apache
|
||||
#### Apache
|
||||
|
||||
Apache config examples following, but you still need to change domain and path in config as explained in WebMum Config -> Paths.
|
||||
Apache config examples following, but you still need to change domain and path in config as explained [here](#paths).
|
||||
|
||||
Please note: mod_rewrite must be enabled for URL rewriting:
|
||||
|
||||
|
@ -86,11 +116,11 @@ With subdirectory `webmum/` in URL (e.g. `http://mydomain.tld/webmum/`):
|
|||
ServerName domain.tld
|
||||
DocumentRoot /var/www/domain.tld
|
||||
|
||||
RewriteEngine on
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^\/webmum/(.*)\.css$ /webmum/$1.css [L]
|
||||
RewriteRule ^\/webmum/(.*)$ /webmum/index.php [L,QSA]
|
||||
RewriteEngine on
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^\/webmum/(.*)\.css$ /webmum/$1.css [L]
|
||||
RewriteRule ^\/webmum/(.*)$ /webmum/index.php [L,QSA]
|
||||
</VirtualHost>
|
||||
```
|
||||
|
||||
|
@ -101,92 +131,61 @@ Without subdirectory in URL (e.g. `http://webmum.mydomain.tld/`):
|
|||
ServerName webmum.domain.tld
|
||||
DocumentRoot /var/www/domain.tld/webmum
|
||||
|
||||
RewriteEngine on
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule (.*)\.css$ $1.css [L]
|
||||
RewriteRule ^(.*)$ /index.php [L,QSA]
|
||||
RewriteEngine on
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule (.*)\.css$ $1.css [L]
|
||||
RewriteRule ^(.*)$ /index.php [L,QSA]
|
||||
</VirtualHost>
|
||||
```
|
||||
|
||||
Access to the codebase is denied with a `.htaccess` file, that can be found in `/include/php`.
|
||||
|
||||
|
||||
|
||||
## WebMUM Configuration
|
||||
|
||||
Configure WebMUM via the configuration file at `config/config.inc.php`.
|
||||
Configure WebMUM via the configuration file at `config/config.inc.php`.
|
||||
|
||||
### MySQL
|
||||
|
||||
At first the database access has to be configured.
|
||||
At first the database access has to be configured under the config key `mysql`.
|
||||
|
||||
Check if you've got the same database schema as configured in the config key `schema`.
|
||||
|
||||
### Mailbox limit (Optional)
|
||||
|
||||
If you want to use your "mailbox_limit" column to limit the size of your users' mailboxes, just enable mailbox limit in the options.
|
||||
|
||||
```php
|
||||
/*
|
||||
* MySQL server and database settings
|
||||
*/
|
||||
|
||||
define("MYSQL_HOST", "localhost");
|
||||
define("MYSQL_USER", "vmail");
|
||||
define("MYSQL_PASSWORD", "vmail");
|
||||
define("MYSQL_DATABASE", "vmail");
|
||||
'options' => array(
|
||||
...
|
||||
'enable_mailbox_limits' => true,
|
||||
...
|
||||
),
|
||||
```
|
||||
|
||||
... then define the table names according to your own setup:
|
||||
|
||||
```php
|
||||
/*
|
||||
* Database table names
|
||||
*/
|
||||
|
||||
// Table names
|
||||
define("DBT_USERS", "users");
|
||||
define("DBT_DOMAINS", "domains");
|
||||
define("DBT_ALIASES", "aliases");
|
||||
```
|
||||
|
||||
... and finally the table column names:
|
||||
|
||||
```php
|
||||
// Users table columns
|
||||
define("DBC_USERS_ID", "id");
|
||||
define("DBC_USERS_USERNAME", "username");
|
||||
define("DBC_USERS_DOMAIN", "domain");
|
||||
define("DBC_USERS_PASSWORD", "password");
|
||||
//define("DBC_USERS_MAILBOXLIMIT", "mailbox_limit");
|
||||
WebMUM will then show a new field "Mailbox limit" in the frontend.
|
||||
|
||||
|
||||
// Domains table columns
|
||||
define("DBC_DOMAINS_ID", "id");
|
||||
define("DBC_DOMAINS_DOMAIN", "domain");
|
||||
|
||||
// Aliases table columns
|
||||
define("DBC_ALIASES_ID", "id");
|
||||
define("DBC_ALIASES_SOURCE", "source");
|
||||
define("DBC_ALIASES_DESTINATION", "destination");
|
||||
//define("DBC_ALIASES_MULTI_SOURCE", "multi_source");
|
||||
```
|
||||
|
||||
### Mailbox limit
|
||||
If you have a "mailbox_limit" column to limit the size of your users' mailboxes, just comment in the line
|
||||
|
||||
```php
|
||||
define("DBC_USERS_MAILBOXLIMIT", "mailbox_limit");
|
||||
```
|
||||
|
||||
in your configuration. WebMUM will then show a new field "Mailbox limit" in the frontend.
|
||||
|
||||
|
||||
### Multiple source redirect support
|
||||
### Multiple source redirect support (Optional)
|
||||
|
||||
As mailservers can only process a single source address for redirects the database table for aliases / redirects can only hold a single source address in a row.
|
||||
WebMum will, if you enabled the multiple source redirect support, do some magic so there is only a single address in a row even though multiple adresses where entered.
|
||||
To make this work another column in the database table is required, which holds an identifier for the list of source adresses, so they can be edited like normal redirects.
|
||||
WebMum will, if you enabled the multiple source redirect support, do some magic so there is only a single address in a row even though multiple addresses where entered.
|
||||
To make this work another column in the database table is required, which holds an identifier for the list of source addresses, so they can be edited like normal redirects.
|
||||
|
||||
By default you can only redirect a single address to a single or multiple destinations.
|
||||
If you want to enable support for redirecting multiple source adresses to a destionation, just comment in the line
|
||||
If you want to enable support for redirecting multiple source addresses to a destination, just enable it in the options:
|
||||
|
||||
```php
|
||||
define("DBC_ALIASES_MULTI_SOURCE", "multi_source");
|
||||
'options' => array(
|
||||
...
|
||||
'enable_multi_source_redirects' => true,
|
||||
...
|
||||
),
|
||||
```
|
||||
|
||||
in your configuration. And add the following column to your database table for aliases / redirects:
|
||||
And add the following column to your database table for aliases / redirects:
|
||||
|
||||
```sql
|
||||
ALTER TABLE `aliases` ADD COLUMN `multi_source` VARCHAR(32) NULL DEFAULT NULL;
|
||||
|
@ -194,38 +193,81 @@ ALTER TABLE `aliases` ADD COLUMN `multi_source` VARCHAR(32) NULL DEFAULT NULL;
|
|||
|
||||
WebMUM will then show a larger field for source addresses in the frontend and you can not list emails in source field.
|
||||
|
||||
|
||||
### Admin domain limits (Optional)
|
||||
|
||||
If you share your mailserver with others, host their domains and they should be able to manage their domains, but not all domains on that mailserver then this is the right option for you.
|
||||
You have to add that user to the `admins` array in your configuration and enable admin domain limits in the options:
|
||||
|
||||
```php
|
||||
'options' => array(
|
||||
...
|
||||
'enable_admin_domain_limits' => true,
|
||||
...
|
||||
),
|
||||
```
|
||||
|
||||
also you have to make an entry in the `admin_domain_limits` array, for example `peter@his.tld` should be able to manage his domains `his.tld` and `his-company.tld` then configure the following:
|
||||
|
||||
```php
|
||||
'admin_domain_limits' => array(
|
||||
'peter@his.tld' => array('his.tld', 'his-company.tld'),
|
||||
);
|
||||
```
|
||||
|
||||
Admins that have been listed in `admin_domain_limits` don't have access to the "Manage domains" pages, otherwise they could delete domains they are managing, but maybe someone else owns.
|
||||
|
||||
|
||||
|
||||
### Users redirects (Optional)
|
||||
|
||||
If you want to enable some users to create redirects on their own, either limited by a maximum number of redirects or unlimited, this is the right option for you.
|
||||
You have to enable this feature in the options:
|
||||
|
||||
```php
|
||||
'options' => array(
|
||||
...
|
||||
'enable_user_redirects' => true,
|
||||
...
|
||||
),
|
||||
```
|
||||
|
||||
And add the following columns to your database tables for aliases / redirects and users:
|
||||
|
||||
```sql
|
||||
ALTER TABLE `aliases` ADD COLUMN `is_created_by_user` INT(1) NOT NULL DEFAULT '0';
|
||||
```
|
||||
|
||||
Note: By choosing a default value for the `max_user_redirects` column on the users table, you can set the default state of user redirects for new users. `0` = unlimited, `-1` = disabled and a number larger than 0 will limit user redirects.
|
||||
|
||||
```sql
|
||||
ALTER TABLE `users` ADD COLUMN `max_user_redirects` INT(10) NOT NULL DEFAULT '-1';
|
||||
```
|
||||
|
||||
|
||||
### Paths
|
||||
(#path-config)
|
||||
|
||||
Define the URL of the web application, and it's subfolder:
|
||||
The `base_url` is the URL your WebMUM installation is accessible from outside, this also includes subdirectories if you installed it in a subdirectory for that specific domain.
|
||||
|
||||
```php
|
||||
/*
|
||||
* Frontend paths
|
||||
*/
|
||||
|
||||
define("FRONTEND_BASE_PATH", "http://mydomain.tld/webmum/");
|
||||
define("SUBDIR", "webmum/");
|
||||
'base_url' => 'http://localhost/webmum',
|
||||
```
|
||||
|
||||
In the example above, WebMUM is located in a subfolder named "webmum/". If you don't want to use a subfolder, but install WebMUM directly into the domain root,
|
||||
set the settings like this:
|
||||
In the example above, WebMUM is located in a subdirectory named "webmum/". If your WebMUM installation is directly accessible from a domain (has its own domain), then set the `base_url` to something like this:
|
||||
|
||||
```php
|
||||
define("FRONTEND_BASE_PATH", "http://webmum.mydomain.tld/");
|
||||
define("SUBDIR", "");
|
||||
'base_url' => 'http://webmum.mydomain.tld',
|
||||
```
|
||||
|
||||
|
||||
### Admin e-mail address
|
||||
|
||||
Only users with one of the specified email addresses will have access to the administrator's dashboard and will be able to create, edit and delete users, domains and redirects.
|
||||
|
||||
```php
|
||||
/*
|
||||
* Admin e-mail address
|
||||
*/
|
||||
|
||||
$admins = array("admin@domain.tld");
|
||||
'admins' = array(
|
||||
'admin@domain.tld',
|
||||
);
|
||||
```
|
||||
|
||||
Admin email accounts must exist in the virtual user database on your own server. (=> an e-mail account on a foreign server won't give you access!). You can then login into the admin dashboard with that e-mail address and the corresponding password.
|
||||
|
@ -233,25 +275,32 @@ Admin email accounts must exist in the virtual user database on your own server.
|
|||
### Minimal required password length
|
||||
|
||||
```php
|
||||
/*
|
||||
* Minimal password length
|
||||
*/
|
||||
|
||||
define("MIN_PASS_LENGTH", 8);
|
||||
'password' => array(
|
||||
...
|
||||
'min_length' => 8,
|
||||
...
|
||||
),
|
||||
```
|
||||
|
||||
### Logfile
|
||||
|
||||
When logging is enabled, WebMUM will write messages into a file "webmum.log" in a specified directory (e.g. when a login attempt fails).
|
||||
|
||||
To enable logging, comment in the lines
|
||||
Enable logging by setting it to enabled in the options:
|
||||
|
||||
```php
|
||||
# define("WRITE_LOG", true);
|
||||
# define("WRITE_LOG_PATH","/var/www/webmum/log/");
|
||||
'options' => array(
|
||||
...
|
||||
'enable_logging' => true,
|
||||
...
|
||||
),
|
||||
```
|
||||
|
||||
... and make sure that PHP has permissions to write the log file to the directory defined in WRITE_LOG_PATH.
|
||||
... and set a log path where the PHP user has permission to write the log file:
|
||||
|
||||
```php
|
||||
'log_path' => '/var/www/webmum/log/',
|
||||
```
|
||||
|
||||
"Login-failed-messages" have the following scheme:
|
||||
|
||||
|
@ -268,27 +317,32 @@ If you want to use **Fail2Ban** with WebMUM, the filter has to be:
|
|||
failregex = ^(.*)\: WebMUM login failed for IP <HOST>$
|
||||
```
|
||||
|
||||
### General options
|
||||
|
||||
To **restrict source adresses to managed domains only**, which is totally optional but recommended, just uncomment the following line
|
||||
### Validate that source addresses of redirects must be from the managed domains only
|
||||
|
||||
```php
|
||||
define("VALIDATE_ALIASES_SOURCE_DOMAIN_ENABLED", true);
|
||||
'options' => array(
|
||||
...
|
||||
'enable_validate_aliases_source_domain' => true,
|
||||
...
|
||||
),
|
||||
```
|
||||
|
||||
|
||||
### Frontend options
|
||||
|
||||
Choose delimiter beteween multiple email adresses: comma, semicolon or new line separated.
|
||||
Choose delimiter between multiple email addresses: comma, semicolon or new line separated.
|
||||
|
||||
**Tip:** new line is helpfull for long lists of addresses.
|
||||
**Tip:** new line is helpful for long lists of addresses.
|
||||
|
||||
```php
|
||||
define("FRONTEND_EMAIL_SEPARATOR_TEXT", ', '); // possible values: ', ' (default), '; ', PHP_EOL (newline)
|
||||
define("FRONTEND_EMAIL_SEPARATOR_FORM", ','); // possible values: ',' (default), ';', PHP_EOL (newline)
|
||||
'frontend_options' => array(
|
||||
// Separator for email lists
|
||||
'email_separator_text' => ', ', // possible values: ', ' (default), '; ', PHP_EOL (newline)
|
||||
'email_separator_form' => ',', // possible values: ',' (default), ';', PHP_EOL (newline)
|
||||
),
|
||||
```
|
||||
|
||||
The input for addresses can be separated by `,`, `;`, `:`, `|`, `newline` and combinations since all of them will result in a valid list of adresses in database, magic.
|
||||
The input for addresses can be separated by `,`, `;`, `:`, `|`, `newline` and combinations since all of them will result in a valid list of addresses in database, magic.
|
||||
|
||||
|
||||
## Update / Upgrade WebMUM
|
||||
|
@ -300,26 +354,26 @@ git stash
|
|||
git pull origin master
|
||||
git stash pop
|
||||
```
|
||||
|
||||
|
||||
... and you are ready to go. Git might complain about conflicting files - you will have to resolve the merge conflict manually then.
|
||||
|
||||
If you downloaded WebMUM as a ZIP package, you have to update WebMUM manually.
|
||||
|
||||
**After every update:**
|
||||
**After every update:**
|
||||
Please check if your config.inc.php fits the current requirements by comparing your version of the file with the config.inc.php in the repository.
|
||||
|
||||
## FAQ
|
||||
|
||||
### Which password scheme does WebMUM use?
|
||||
### Which password hash algorithm does WebMUM use?
|
||||
|
||||
By default WebMUM uses SHA512-CRYPT password scheme. It cloud be change in the config file to SHA256-CRYPT or BLOWFISH-CRYPT.
|
||||
By default WebMUM uses the `SHA-512` hash algorithm for passwords. You can also choose between the alternatives `SHA-256` or `BLOWFISH` in the config.
|
||||
|
||||
```php
|
||||
/*
|
||||
* Select one of the following algorithms
|
||||
* SHA-512, SHA-256, BLOWFISH
|
||||
*/
|
||||
define("PASS_HASH_SCHEMA", "SHA-512");
|
||||
'password' => array(
|
||||
...
|
||||
'hash_algorithm' => 'SHA-512', // Supported algorithms: SHA-512, SHA-256, BLOWFISH
|
||||
...
|
||||
),
|
||||
```
|
||||
|
||||
### "login/ cannot be found"
|
||||
|
|
1
config/.htaccess
Normal file
1
config/.htaccess
Normal file
|
@ -0,0 +1 @@
|
|||
Deny from all
|
|
@ -1,99 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* MySQL server and database settings
|
||||
*/
|
||||
|
||||
define("MYSQL_HOST", "localhost");
|
||||
define("MYSQL_USER", "vmail");
|
||||
define("MYSQL_PASSWORD", "vmail");
|
||||
define("MYSQL_DATABASE", "vmail");
|
||||
|
||||
|
||||
/*
|
||||
* Database table names
|
||||
*/
|
||||
|
||||
// Table names
|
||||
define("DBT_USERS", "users");
|
||||
define("DBT_DOMAINS", "domains");
|
||||
define("DBT_ALIASES", "aliases");
|
||||
|
||||
// Users table columns
|
||||
define("DBC_USERS_ID", "id");
|
||||
define("DBC_USERS_USERNAME", "username");
|
||||
define("DBC_USERS_DOMAIN", "domain");
|
||||
define("DBC_USERS_PASSWORD", "password");
|
||||
// define("DBC_USERS_MAILBOXLIMIT", "mailbox_limit"); // (Optional)
|
||||
|
||||
// Domains table columns
|
||||
define("DBC_DOMAINS_ID", "id");
|
||||
define("DBC_DOMAINS_DOMAIN", "domain");
|
||||
|
||||
// Aliases table columns
|
||||
define("DBC_ALIASES_ID", "id");
|
||||
define("DBC_ALIASES_SOURCE", "source");
|
||||
define("DBC_ALIASES_DESTINATION", "destination");
|
||||
// Enable multi source redirects, needs a new column in aliase table with VARCHAR(32)
|
||||
//define("DBC_ALIASES_MULTI_SOURCE", "multi_source"); // (Optional, Recommended)
|
||||
|
||||
|
||||
/*
|
||||
* General options
|
||||
*/
|
||||
// Enable validating that the source addresses are ending with domain from domains
|
||||
//define("VALIDATE_ALIASES_SOURCE_DOMAIN_ENABLED", true); // (Optional, Recommended)
|
||||
|
||||
|
||||
/*
|
||||
* Frontend paths
|
||||
*/
|
||||
|
||||
define("FRONTEND_BASE_PATH", "http://localhost/webmum/");
|
||||
define("SUBDIR", "webmum/");
|
||||
|
||||
|
||||
/*
|
||||
* Admin e-mail address
|
||||
*/
|
||||
|
||||
// outdated: define("ADMIN_EMAIL", "admin@domain.tld");
|
||||
// new: $admins = array("admin1@server.tld", "admin2@server.tld");
|
||||
$admins = array("admin@domain.tld");
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Password
|
||||
*/
|
||||
/*
|
||||
* Select on of the following schemas (only these are supported)
|
||||
* SHA-512, SHA-256, BLOWFISH
|
||||
*/
|
||||
define("PASS_HASH_SCHEMA", "SHA-512");
|
||||
|
||||
//minimum password length
|
||||
define("MIN_PASS_LENGTH", 8);
|
||||
|
||||
/*
|
||||
* Write log file? Failed login attempts will be written to the logfile.
|
||||
* You can mointor the logfile with fail2ban and ban attackers' IP-addresses.
|
||||
* Make sure that PHP has permission to create the log directory and webmum.log (write permissions for php user)
|
||||
*
|
||||
* Default: Do not write logfile
|
||||
*/
|
||||
|
||||
// define("WRITE_LOG", true);
|
||||
// define("WRITE_LOG_PATH","/var/www/webmum/log/");
|
||||
|
||||
|
||||
/*
|
||||
* Frontend options
|
||||
*/
|
||||
|
||||
// Separator for email lists
|
||||
define("FRONTEND_EMAIL_SEPARATOR_TEXT", ', '); // possible values: ', ' (default), '; ', PHP_EOL (newline)
|
||||
define("FRONTEND_EMAIL_SEPARATOR_FORM", ','); // possible values: ',' (default), ';', PHP_EOL (newline)
|
||||
|
||||
|
||||
?>
|
211
config/config.php.example
Normal file
211
config/config.php.example
Normal file
|
@ -0,0 +1,211 @@
|
|||
<?php
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// DO NOT EDIT THIS FILE! //
|
||||
// //
|
||||
// Instead, copy this config file to config.php and make your changes //
|
||||
// in the copied version. This is just a template! //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
return array(
|
||||
|
||||
/******************************************************
|
||||
* URL to your WebMUM installation.
|
||||
*/
|
||||
|
||||
'base_url' => 'http://localhost/webmum',
|
||||
|
||||
|
||||
/******************************************************
|
||||
* MySQL database connection settings
|
||||
*/
|
||||
|
||||
'mysql' => array(
|
||||
'host' => 'localhost',
|
||||
'user' => 'vmail',
|
||||
'password' => 'vmail',
|
||||
'database' => 'vmail',
|
||||
),
|
||||
|
||||
|
||||
/******************************************************
|
||||
* Database schema mapping
|
||||
*/
|
||||
|
||||
'schema' => array(
|
||||
// Table names
|
||||
'tables' => array(
|
||||
// Example:
|
||||
// 'table-keyword' => 'actual-table-name',
|
||||
|
||||
'users' => 'users',
|
||||
'domains' => 'domains',
|
||||
'aliases' => 'aliases',
|
||||
),
|
||||
|
||||
'attributes' => array(
|
||||
// Example:
|
||||
// 'table-keyword' => array(
|
||||
// 'attribute-keyword' => 'actual-attribute-name',
|
||||
// ...
|
||||
// ),
|
||||
|
||||
// Users table columns
|
||||
'users' => array(
|
||||
'id' => 'id',
|
||||
'username' => 'username',
|
||||
'domain' => 'domain',
|
||||
'password' => 'password',
|
||||
'mailbox_limit' => 'mailbox_limit', // (Optional see 'options.enable_mailbox_limits')
|
||||
'max_user_redirects' => 'max_user_redirects', // (Optional see 'options.enable_user_redirects')
|
||||
),
|
||||
|
||||
// Domains table columns
|
||||
'domains' => array(
|
||||
'id' => 'id',
|
||||
'domain' => 'domain',
|
||||
),
|
||||
|
||||
// Aliases table columns
|
||||
'aliases' => array(
|
||||
'id' => 'id',
|
||||
'source' => 'source',
|
||||
'destination' => 'destination',
|
||||
'multi_source' => 'multi_source', // (Optional see 'options.enable_multi_source_redirects')
|
||||
'is_created_by_user' => 'is_created_by_user', // (Optional see 'options.enable_user_redirects')
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
|
||||
/******************************************************
|
||||
* General options
|
||||
*/
|
||||
|
||||
'options' => array(
|
||||
|
||||
/**
|
||||
* Enable mailbox limits. (Default false == off)
|
||||
*
|
||||
* Needs a new db attribute in users table with INT(10).
|
||||
* (see 'schema.attributes.users.mailbox_limit')
|
||||
*/
|
||||
|
||||
'enable_mailbox_limits' => false,
|
||||
|
||||
|
||||
/**
|
||||
* Enable validating that the source addresses are ending with domain from domains. (Default true == on)
|
||||
*/
|
||||
|
||||
'enable_validate_aliases_source_domain' => true,
|
||||
|
||||
|
||||
/**
|
||||
* Enable multi source redirects. (Default false == off)
|
||||
*
|
||||
* Needs a new db attribute in aliases table with VARCHAR(32).
|
||||
* (see 'schema.attributes.aliases.multi_source')
|
||||
*/
|
||||
|
||||
'enable_multi_source_redirects' => false,
|
||||
|
||||
|
||||
/**
|
||||
* Enable limited admin domain access. (Default false == off)
|
||||
*
|
||||
* Limitations can be configured under 'admin_domain_limits'.
|
||||
*/
|
||||
|
||||
'enable_admin_domain_limits' => false,
|
||||
|
||||
|
||||
/**
|
||||
* Enable users can create own redirects. (Default false == off)
|
||||
*
|
||||
* Needs two new db attributes in users table with INT(10) and aliases table with INT(1) + DEFAULT 0
|
||||
* (see 'schema.attributes.users.max_user_redirects' and 'schema.attributes.aliases.is_created_by_user')
|
||||
*
|
||||
* A maximum number of redirects per user can be configured.
|
||||
*/
|
||||
|
||||
'enable_user_redirects' => false,
|
||||
|
||||
|
||||
/**
|
||||
* Enable logging for failed login attempts. (Default false == off)
|
||||
*
|
||||
* You can monitor the logfile with fail2ban and ban attackers' IP-addresses.
|
||||
* Path to logfile can be configured under 'log_path'.
|
||||
*/
|
||||
|
||||
'enable_logging' => false,
|
||||
|
||||
),
|
||||
|
||||
|
||||
/******************************************************
|
||||
* Admin e-mail addresses
|
||||
*
|
||||
* Users with these e-mail addresses will have admin access,
|
||||
* you can limit their access with the 'options.enable_admin_domain_limits' feature
|
||||
*/
|
||||
|
||||
'admins' => array(
|
||||
'admin@domain.tld',
|
||||
),
|
||||
|
||||
|
||||
/******************************************************
|
||||
* Limited admin domain access (only used if 'options.enable_admin_domain_limits' is true)
|
||||
*
|
||||
* Unlisted admins have access to every domain, the admin is limited to listed domains only!
|
||||
* Unlisted domains are not accessible by that admin.
|
||||
* Note that listed admins cannot create new domains!
|
||||
*/
|
||||
|
||||
'admin_domain_limits' => array(
|
||||
// Example:
|
||||
// 'low_rank_admin@domain.tld' => array('his-domain.tld', 'shared-domain.tld'),
|
||||
),
|
||||
|
||||
|
||||
/******************************************************
|
||||
* Password
|
||||
*/
|
||||
|
||||
'password' => array(
|
||||
|
||||
// Algorithm used for password encryption
|
||||
'hash_algorithm' => 'SHA-512', // Supported algorithms: SHA-512, SHA-256, BLOWFISH
|
||||
|
||||
// Minimum length for passwords
|
||||
'min_length' => 8,
|
||||
|
||||
),
|
||||
|
||||
|
||||
/******************************************************
|
||||
* Log file path (only used if 'options.enable_logging' is true)
|
||||
*
|
||||
* Make sure that PHP has permission to create the log directory and webmum.log (write permissions for php user)
|
||||
*/
|
||||
|
||||
'log_path' => '/var/www/webmum/log/',
|
||||
|
||||
|
||||
/******************************************************
|
||||
* Frontend options
|
||||
*/
|
||||
|
||||
'frontend_options' => array(
|
||||
|
||||
// Separator for email lists
|
||||
'email_separator_text' => ', ', // possible values: ', ' (default), '; ', PHP_EOL (newline)
|
||||
'email_separator_form' => ',', // possible values: ',' (default), ';', PHP_EOL (newline)
|
||||
|
||||
),
|
||||
|
||||
);
|
|
@ -5,16 +5,34 @@ body {
|
|||
background-color: white;
|
||||
}
|
||||
|
||||
/*
|
||||
* Main Layout
|
||||
*/
|
||||
hr {
|
||||
border: none;
|
||||
border-bottom: 1px solid #ccc;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
hr.invisible {
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.text-fail {
|
||||
color: #d90000;
|
||||
}
|
||||
|
||||
.text-warning {
|
||||
color: #ADA900;
|
||||
}
|
||||
|
||||
.text-success {
|
||||
color: #39AD00;
|
||||
}
|
||||
|
||||
|
||||
#header {
|
||||
position: relative;
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
background-color: rgba(15, 15, 15, 1);
|
||||
background: linear-gradient(rgba(63, 63, 63, 1), rgba(15, 15, 15, 1));
|
||||
background: rgba(15, 15, 15, 1) linear-gradient(rgba(63, 63, 63, 1), rgba(15, 15, 15, 1));
|
||||
color: white;
|
||||
line-height: 50px;
|
||||
box-sizing: border-box;
|
||||
|
@ -70,6 +88,13 @@ body {
|
|||
color: rgba(62, 59, 59, 1);
|
||||
}
|
||||
|
||||
#content .sub-header {
|
||||
font-weight: normal;
|
||||
font-size: .9em;
|
||||
color: #999;
|
||||
padding: 5px 0 0 5px;
|
||||
}
|
||||
|
||||
#content a {
|
||||
color: blue;
|
||||
text-decoration: none;
|
||||
|
@ -84,6 +109,10 @@ body {
|
|||
margin: 25px 0;
|
||||
}
|
||||
|
||||
#content .form hr {
|
||||
margin: 5px 0 15px;
|
||||
}
|
||||
|
||||
#content .form .input-group, #content .form .buttons {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
@ -99,6 +128,18 @@ body {
|
|||
color: #999;
|
||||
}
|
||||
|
||||
#content .form .input-group > .input-group {
|
||||
padding-left: 25px;
|
||||
}
|
||||
|
||||
#content .form .input-group > .input-group:first-of-type {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
#content .form .input-group > .input-group > label {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#content .form .input {
|
||||
}
|
||||
|
||||
|
@ -120,8 +161,31 @@ body {
|
|||
}
|
||||
|
||||
#content .form .input input[type="number"] {
|
||||
padding-left: 15px;
|
||||
padding-right: 0;
|
||||
min-width: 190px;
|
||||
min-width: 70px;
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
#content .form .input input[type="checkbox"],
|
||||
#content .form .input input[type="radio"] {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
min-width: inherit;
|
||||
vertical-align: middle;
|
||||
margin: 6px 0 8px 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#content .form .input input[type="checkbox"]+label,
|
||||
#content .form .input input[type="radio"]+label {
|
||||
padding-left: 3px;
|
||||
margin-right: 15px;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
#content .form .input textarea {
|
||||
|
@ -205,6 +269,11 @@ body {
|
|||
text-align: center;
|
||||
color: rgba(57, 57, 57, 1);
|
||||
text-decoration: none;
|
||||
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
#content a.button {
|
||||
|
@ -244,6 +313,14 @@ body {
|
|||
color: #fff;
|
||||
}
|
||||
|
||||
#content .button.button-disabled {
|
||||
background: #f1f1f1;
|
||||
border-color: #f1f1f1;
|
||||
box-shadow: none;
|
||||
cursor: not-allowed;
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
|
||||
#content .table {
|
||||
margin: 25px 0;
|
||||
|
@ -251,6 +328,10 @@ body {
|
|||
border: none;
|
||||
}
|
||||
|
||||
#content .table-compact {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
#content .table thead th {
|
||||
line-height: 38px;
|
||||
padding: 2px 15px 0;
|
||||
|
@ -269,8 +350,8 @@ body {
|
|||
}
|
||||
|
||||
#content .table tbody td {
|
||||
line-height: 33px;
|
||||
padding: 0 10px;
|
||||
line-height: 21px;
|
||||
padding: 9px 10px;
|
||||
border: 1px solid rgba(179, 176, 176, 1);
|
||||
}
|
||||
|
||||
|
@ -278,6 +359,10 @@ body {
|
|||
background-color: rgba(234, 234, 234, 1);
|
||||
}
|
||||
|
||||
#content .table tbody > tr.warning {
|
||||
background-color: #fcf897;
|
||||
}
|
||||
|
||||
#content .table a {
|
||||
color: rgb(148, 148, 255);
|
||||
}
|
||||
|
@ -285,47 +370,64 @@ body {
|
|||
color: blue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Footer
|
||||
*/
|
||||
|
||||
#content .notifications {
|
||||
}
|
||||
|
||||
#content .notification {
|
||||
height: auto;
|
||||
width: 100%;
|
||||
margin: 15px 0;
|
||||
text-align: center;
|
||||
border: 1px solid;
|
||||
border-radius: 3px;
|
||||
padding: 15px 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#content .notification.notification-fail {
|
||||
background-color: #fcacac;
|
||||
border-color: red;
|
||||
}
|
||||
|
||||
#content .notification.notification-warning {
|
||||
background-color: #fcf897;
|
||||
border-color: #ffe600;
|
||||
}
|
||||
|
||||
#content .notification.notification-success {
|
||||
background-color: rgba(182, 255, 183, 1);
|
||||
border-color: green;
|
||||
}
|
||||
|
||||
|
||||
#footer {
|
||||
position: relative;
|
||||
height: 20px;
|
||||
width: 100%;
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
padding: 15px 20px;
|
||||
box-sizing: border-box;
|
||||
color: grey;
|
||||
}
|
||||
|
||||
#footer ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
#footer li {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#footer li:not(:last-child):after {
|
||||
content: '|';
|
||||
color: #444444;
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
#footer a {
|
||||
text-decoration: none;
|
||||
color: grey;
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
/*
|
||||
* Notifications
|
||||
*/
|
||||
|
||||
#content .notification {
|
||||
height: auto;
|
||||
width: 100%;
|
||||
margin: 15px 0;
|
||||
text-align: center;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-radius: 3px;
|
||||
padding: 15px 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#content .notification.notification-fail {
|
||||
background-color: #fcacac;
|
||||
border-color: red;
|
||||
}
|
||||
|
||||
#content .notification.notification-success {
|
||||
background-color: rgba(182, 255, 183, 1);
|
||||
border-color: green;
|
||||
#footer a:hover {
|
||||
color: #666666;
|
||||
}
|
1
include/php/.htaccess
Normal file
1
include/php/.htaccess
Normal file
|
@ -0,0 +1 @@
|
|||
Deny from all
|
|
@ -1,38 +0,0 @@
|
|||
<?php
|
||||
/*
|
||||
* Checks, if the current user has the permission, which is required for an action.
|
||||
* $role_req = required role [string]
|
||||
*
|
||||
* Returns:
|
||||
* true: User has role $role_req
|
||||
* false: User doesn't have role $role_req
|
||||
*
|
||||
* Possible roles: user, admin
|
||||
*/
|
||||
|
||||
function user_has_permission($role_req){
|
||||
global $user;
|
||||
if($user->isLoggedIn() === true){
|
||||
// User is logged in. Check permissions
|
||||
// To be done. Load user role from database or better: save in SESSION
|
||||
if($role_req === "user"){
|
||||
if($user->getRole() == "user" || $user->getRole() == "admin"){
|
||||
return true;
|
||||
}
|
||||
else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if($role_req === "admin"){
|
||||
if($user->getRole() == "admin"){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
// User is not logged in => public user => no permissions
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
263
include/php/classes/Auth.php
Normal file
263
include/php/classes/Auth.php
Normal file
|
@ -0,0 +1,263 @@
|
|||
<?php
|
||||
|
||||
class Auth
|
||||
{
|
||||
const SESSION_IDENTIFIER = 'uid';
|
||||
|
||||
/**
|
||||
* @var User|null
|
||||
*/
|
||||
private static $loggedInUser = null;
|
||||
|
||||
|
||||
/**
|
||||
* Init Authentication
|
||||
*/
|
||||
public static function init()
|
||||
{
|
||||
static::loginUserViaSession();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check whether the user is logged in or not.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isLoggedIn()
|
||||
{
|
||||
return !is_null(static::$loggedInUser);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the currently logged in user.
|
||||
*
|
||||
* @return null|User
|
||||
*/
|
||||
public static function getUser()
|
||||
{
|
||||
return static::$loggedInUser;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param AbstractModel $user
|
||||
*/
|
||||
private static function loginUserByModel($user)
|
||||
{
|
||||
static::$loggedInUser = $user;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks session for logged in user, validates the login and finally logs him in.
|
||||
*/
|
||||
private static function loginUserViaSession()
|
||||
{
|
||||
global $_SESSION;
|
||||
|
||||
if(isset($_SESSION[static::SESSION_IDENTIFIER])
|
||||
&& !empty($_SESSION[static::SESSION_IDENTIFIER])
|
||||
){
|
||||
$userId = $_SESSION[static::SESSION_IDENTIFIER];
|
||||
|
||||
/** @var User $user */
|
||||
$user = User::find($userId);
|
||||
|
||||
// check if user still exists in database
|
||||
if(!is_null($user)){
|
||||
static::loginUserByModel($user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Login user with provided credentials and save login in session
|
||||
*
|
||||
* @param string $email
|
||||
* @param string $password
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function login($email, $password)
|
||||
{
|
||||
$email = strtolower($email);
|
||||
|
||||
$emailInParts = explode("@", $email);
|
||||
if(count($emailInParts) !== 2){
|
||||
return false;
|
||||
}
|
||||
$username = $emailInParts[0];
|
||||
$domain = $emailInParts[1];
|
||||
|
||||
/** @var User $user */
|
||||
$user = User::findWhereFirst(
|
||||
array(
|
||||
array(User::attr('username'), $username),
|
||||
array(User::attr('domain'), $domain),
|
||||
)
|
||||
);
|
||||
|
||||
// Check if user exists
|
||||
if(!is_null($user)){
|
||||
if(static::checkPasswordByHash($password, $user->getPasswordHash())){
|
||||
|
||||
static::loginUserByModel($user);
|
||||
|
||||
$_SESSION[static::SESSION_IDENTIFIER] = $user->getId();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function logout()
|
||||
{
|
||||
unset($_SESSION[static::SESSION_IDENTIFIER]);
|
||||
|
||||
static::$loggedInUser = null;
|
||||
|
||||
if(session_status() === PHP_SESSION_ACTIVE){
|
||||
session_destroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if current user has a certain role, but User::ROLE_ADMIN will have access to all
|
||||
*
|
||||
* @param string $requiredRole
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function hasPermission($requiredRole)
|
||||
{
|
||||
if(static::isLoggedIn()){
|
||||
$user = static::getUser();
|
||||
|
||||
return $user->getRole() === $requiredRole
|
||||
|| $user->getRole() === User::ROLE_ADMIN;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks the new password entered by user on certain criteria, and throws an exception if its invalid.
|
||||
*
|
||||
* @param string $password
|
||||
* @param string $passwordRepeated
|
||||
*
|
||||
* @throws AuthException Codes explained below
|
||||
* 2: One password field is empty
|
||||
* 3: Passwords aren't equal
|
||||
* 4: Passwort is too snort
|
||||
*/
|
||||
public static function validateNewPassword($password, $passwordRepeated)
|
||||
{
|
||||
// Check if one passwort input is empty
|
||||
if(empty($password)){
|
||||
throw new AuthException("First password field was'nt filled out.", 2);
|
||||
}
|
||||
if(empty($passwordRepeated)){
|
||||
throw new AuthException("Repeat password field was'nt filled out.", 2);
|
||||
}
|
||||
|
||||
// Check if password are equal
|
||||
if($password !== $passwordRepeated){
|
||||
throw new AuthException("The repeated password must be equal to the first one.", 3);
|
||||
}
|
||||
|
||||
// Check if password length is okay
|
||||
if(Config::has('password.min_length')
|
||||
&& strlen($password) < Config::get('password.min_length')
|
||||
){
|
||||
throw new AuthException("Passwords must be at least ".Config::get('password.min_length')." characters long.", 4);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $password
|
||||
* @param string $hash
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function checkPasswordByHash($password, $hash)
|
||||
{
|
||||
return crypt($password, $hash) === $hash;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private static function getPasswordSchemaPrefix()
|
||||
{
|
||||
$map = array(
|
||||
'SHA-256' => '$5$rounds=5000$',
|
||||
'BLOWFISH' => '$2a$09$',
|
||||
'SHA-512' => '$6$rounds=5000$',
|
||||
);
|
||||
|
||||
$key = Config::get('password.hash_algorithm', 'SHA-512');
|
||||
|
||||
if(!isset($map[$key])){
|
||||
$key = 'SHA-512';
|
||||
}
|
||||
|
||||
return $map[$key];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $password
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function generatePasswordHash($password)
|
||||
{
|
||||
if(function_exists('mt_rand')){
|
||||
mt_srand(time());
|
||||
$num = mt_rand(1, 100000);
|
||||
}
|
||||
else{
|
||||
srand(time());
|
||||
$num = rand(1, 100000);
|
||||
}
|
||||
|
||||
$salt = base64_encode($num);
|
||||
$schemaPrefix = static::getPasswordSchemaPrefix();
|
||||
|
||||
$hash = crypt($password, $schemaPrefix.$salt.'$');
|
||||
|
||||
return $hash;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $userId
|
||||
* @param $password
|
||||
*/
|
||||
public static function changeUserPassword($userId, $password)
|
||||
{
|
||||
$passwordHash = static::generatePasswordHash($password);
|
||||
|
||||
/** @var User $user */
|
||||
$user = User::find($userId);
|
||||
|
||||
if(!is_null($user)){
|
||||
$user->setPasswordHash($passwordHash);
|
||||
$user->save();
|
||||
}
|
||||
}
|
||||
}
|
6
include/php/classes/AuthException.php
Normal file
6
include/php/classes/AuthException.php
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?php
|
||||
|
||||
class AuthException extends Exception
|
||||
{
|
||||
|
||||
}
|
117
include/php/classes/Config.php
Normal file
117
include/php/classes/Config.php
Normal file
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
|
||||
class Config
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $config = array();
|
||||
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function __clone()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param array $configArray
|
||||
*/
|
||||
public static function init($configArray)
|
||||
{
|
||||
static::set(null, $configArray);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set a config value using "dot" notation.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return array
|
||||
*/
|
||||
public static function set($key, $value)
|
||||
{
|
||||
if(is_null($key)) return static::$config = $value;
|
||||
|
||||
$keys = explode('.', $key);
|
||||
|
||||
$array =& static::$config;
|
||||
while(count($keys) > 1){
|
||||
$key = array_shift($keys);
|
||||
|
||||
if(!isset($array[$key]) || !is_array($array[$key])){
|
||||
$array[$key] = array();
|
||||
}
|
||||
|
||||
$array =& $array[$key];
|
||||
}
|
||||
|
||||
$array[array_shift($keys)] = $value;
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a config value using "dot" notation.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $default
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get($key, $default = null)
|
||||
{
|
||||
if(is_null($key)) return static::$config;
|
||||
|
||||
if(isset(static::$config[$key])) return static::$config[$key];
|
||||
|
||||
$pointer = static::$config;
|
||||
foreach(explode('.', $key) as $segment){
|
||||
if(!is_array($pointer) || !array_key_exists($segment, $pointer)){
|
||||
return $default;
|
||||
}
|
||||
|
||||
$pointer = $pointer[$segment];
|
||||
}
|
||||
|
||||
return $pointer;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if a config value exists using "dot" notation.
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public static function has($key)
|
||||
{
|
||||
if(empty(static::$config) || is_null($key)) return false;
|
||||
|
||||
if(array_key_exists($key, static::$config)) return true;
|
||||
|
||||
$pointer = static::$config;
|
||||
foreach(explode('.', $key) as $segment){
|
||||
if(!is_array($pointer) || !array_key_exists($segment, $pointer)){
|
||||
return false;
|
||||
}
|
||||
|
||||
$pointer = $pointer[$segment];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
524
include/php/classes/Database.php
Normal file
524
include/php/classes/Database.php
Normal file
|
@ -0,0 +1,524 @@
|
|||
<?php
|
||||
|
||||
class Database
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Database
|
||||
*/
|
||||
protected static $instance = null;
|
||||
|
||||
|
||||
/**
|
||||
* @var mysqli
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $lastQuery;
|
||||
|
||||
|
||||
/**
|
||||
* @param string $host
|
||||
* @param string $user
|
||||
* @param string $password
|
||||
* @param string $database
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function __construct($host, $user, $password, $database)
|
||||
{
|
||||
if(!static::isInitialized()){
|
||||
$this->config = $database;
|
||||
|
||||
try{
|
||||
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
|
||||
|
||||
$this->db = new mysqli($host, $user, $password, $database);
|
||||
}
|
||||
catch(mysqli_sql_exception $e){
|
||||
throw new Exception('Unable to connect to the database.', 0, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function __clone()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Database
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if(!static::isInitialized()){
|
||||
throw new Exception('Database must be initialized before using it (see Database::init).');
|
||||
}
|
||||
|
||||
return static::$instance;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Database $instance
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
protected static function setInstance($instance)
|
||||
{
|
||||
static::$instance = $instance;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $host
|
||||
* @param string $user
|
||||
* @param string $password
|
||||
* @param string $database
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws Exception
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public static function init($host, $user = null, $password = null, $database = null)
|
||||
{
|
||||
if(!static::isInitialized()){
|
||||
if(is_array($host)){
|
||||
$database = isset($host['database']) ? $host['database'] : $database;
|
||||
$password = isset($host['password']) ? $host['password'] : $password;
|
||||
$user = isset($host['user']) ? $host['user'] : $user;
|
||||
$host = isset($host['host']) ? $host['host'] : $host;
|
||||
}
|
||||
|
||||
if(is_null($host) || is_null($user) || is_null($password) || is_null($database)){
|
||||
throw new InvalidArgumentException('Missing parameters for database initialization.');
|
||||
}
|
||||
|
||||
static::setInstance(
|
||||
new static($host, $user, $password, $database)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function isInitialized()
|
||||
{
|
||||
return !is_null(static::$instance);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Execute query
|
||||
*
|
||||
* @param string $query
|
||||
*
|
||||
* @return bool|mysqli_result
|
||||
*
|
||||
* @throws DatabaseException
|
||||
*/
|
||||
public function query($query)
|
||||
{
|
||||
$this->lastQuery = $query;
|
||||
|
||||
$result = $this->db->query($query);
|
||||
|
||||
if($this->db->errno !== 0){
|
||||
$ex = new DatabaseException('There was an error running the query ['.$this->db->error.']');
|
||||
|
||||
if(!is_null($this->lastQuery)){
|
||||
$ex->setQuery($this->lastQuery);
|
||||
}
|
||||
|
||||
throw $ex;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getInsertId()
|
||||
{
|
||||
return $this->db->insert_id;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Escape string
|
||||
*
|
||||
* @param string $input
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function escape($input)
|
||||
{
|
||||
return $this->db->real_escape_string($input);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $table
|
||||
* @param array $conditions
|
||||
* @param string $conditionConnector
|
||||
* @param null $orderBy
|
||||
* @param int $limit
|
||||
*
|
||||
* @return bool|mysqli_result
|
||||
*
|
||||
* @throws DatabaseException
|
||||
*/
|
||||
public function select($table, $conditions = array(), $conditionConnector = 'AND', $orderBy = null, $limit = 0)
|
||||
{
|
||||
return $this->query(
|
||||
sprintf(
|
||||
"SELECT * FROM `%s` %s%s%s",
|
||||
$table,
|
||||
static::helperWhere($conditions, $conditionConnector),
|
||||
static::helperOrderBy($orderBy),
|
||||
static::helperLimit($limit)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Insert into table
|
||||
*
|
||||
* @param string $table
|
||||
* @param array $values
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws DatabaseException
|
||||
*/
|
||||
public function insert($table, $values)
|
||||
{
|
||||
if(count($values) === 0){
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->query(
|
||||
sprintf(
|
||||
"INSERT INTO `%s` (%s) VALUES %s",
|
||||
$table,
|
||||
static::helperAttributeList(array_keys($values)),
|
||||
static::helperValueList(array_values($values))
|
||||
)
|
||||
);
|
||||
|
||||
return $this->getInsertId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update table
|
||||
*
|
||||
* @param string $table
|
||||
* @param array $values
|
||||
* @param array $conditions
|
||||
* @param string $conditionConnector
|
||||
*
|
||||
* @throws DatabaseException
|
||||
*/
|
||||
public function update($table, $values, $conditions = array(), $conditionConnector = 'AND')
|
||||
{
|
||||
if(count($values) === 0){
|
||||
return;
|
||||
}
|
||||
|
||||
$sqlValues = array();
|
||||
foreach($values as $attribute => $value){
|
||||
$sqlValues[] = array($attribute, '=', $value);
|
||||
}
|
||||
|
||||
$this->query(
|
||||
sprintf(
|
||||
"UPDATE `%s` SET %s %s",
|
||||
$table,
|
||||
static::helperConditionList($sqlValues, ','),
|
||||
static::helperWhere($conditions, $conditionConnector)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Count in table
|
||||
*
|
||||
* @param string $table
|
||||
* @param string $byAttribute
|
||||
* @param array $conditions
|
||||
* @param string $conditionConnector
|
||||
*
|
||||
* @return int
|
||||
*
|
||||
* @throws DatabaseException
|
||||
*/
|
||||
public function count($table, $byAttribute, $conditions = array(), $conditionConnector = 'AND')
|
||||
{
|
||||
$result = $this->query(
|
||||
sprintf(
|
||||
"SELECT COUNT(`%s`) FROM `%s` %s",
|
||||
$byAttribute,
|
||||
$table,
|
||||
static::helperWhere($conditions, $conditionConnector)
|
||||
)
|
||||
);
|
||||
|
||||
return intval($result->fetch_array(MYSQLI_NUM)[0]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $table
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
*
|
||||
* @throws DatabaseException
|
||||
*/
|
||||
public function delete($table, $attribute, $value)
|
||||
{
|
||||
$sql = sprintf(
|
||||
"DELETE FROM `%s` %s",
|
||||
$table,
|
||||
static::helperWhere(array($attribute, $value))
|
||||
);
|
||||
|
||||
$this->query($sql);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $potentialKeyword
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected static function isKeyword($potentialKeyword)
|
||||
{
|
||||
return in_array(
|
||||
strtoupper($potentialKeyword),
|
||||
array('AS', 'ASC', 'DESC')
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param array $attributes
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function helperAttributeList($attributes)
|
||||
{
|
||||
$sqlAttributes = array();
|
||||
foreach($attributes as $attribute){
|
||||
if(is_string($attribute)){ // raw
|
||||
$sqlAttributes[] = $attribute;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!is_array($attribute)){
|
||||
$attribute = array($attribute);
|
||||
}
|
||||
|
||||
$sqlPieces = array();
|
||||
for($i = 0; $i < count($attribute); ++$i){
|
||||
if(static::isKeyword($attribute[$i])){
|
||||
$sqlPieces[] = sprintf("%s", $attribute[$i]);
|
||||
}
|
||||
elseif(isset($attribute[$i + 1]) && !static::isKeyword($attribute[$i + 1])){
|
||||
$sqlPieces[] = sprintf("`%s`.`%s`", $attribute[$i], $attribute[++$i]);
|
||||
}
|
||||
else{
|
||||
$sqlPieces[] = sprintf("`%s`", $attribute[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
$sqlAttributes[] = implode(" ", $sqlPieces);
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
"%s",
|
||||
implode(', ', $sqlAttributes)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function helperValue($value)
|
||||
{
|
||||
if(is_null($value) || (is_string($value) && strtoupper($value) === 'NULL')){
|
||||
return "NULL";
|
||||
}
|
||||
elseif(is_array($value)){
|
||||
return static::helperValueList($value);
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
"'%s'",
|
||||
static::getInstance()->escape($value)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $values
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function helperValueList($values)
|
||||
{
|
||||
$sqlValues = array();
|
||||
|
||||
foreach($values as $val){
|
||||
$sqlValues[] = static::helperValue($val);
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
"(%s)",
|
||||
implode(', ', $sqlValues)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $conditions
|
||||
* array('attr', '=', '3') => "`attr` = '3'"
|
||||
* array(
|
||||
* array('`attr` = '3') (raw SQL) => `attr` = '3'
|
||||
* array('attr', 3) => `attr` = '3'
|
||||
* array('attr', '=', '3') => `attr` = '3'
|
||||
* array('attr', '<=', 3) => `attr` <= '3'
|
||||
* array('attr', 'LIKE', '%asd') => `attr` LIKE '%asd'
|
||||
* array('attr', 'IS', null) => `attr` IS NULL
|
||||
* array('attr', 'IS NOT', null) => `attr` IS NOT NULL
|
||||
* )
|
||||
* @param string $conditionConnector AND, OR
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function helperConditionList($conditions, $conditionConnector = 'AND')
|
||||
{
|
||||
// detect non nested array
|
||||
if(count($conditions) > 0 && !is_array($conditions[0])){
|
||||
$conditions = array($conditions);
|
||||
}
|
||||
|
||||
$conditionConnector = strtoupper($conditionConnector);
|
||||
if(in_array($conditionConnector, array('AND', 'OR'))){
|
||||
$conditionConnector = " ".$conditionConnector;
|
||||
}
|
||||
|
||||
$values = array();
|
||||
foreach($conditions as $val){
|
||||
switch(count($val)){
|
||||
case 1:
|
||||
// raw
|
||||
$values[] = $val;
|
||||
break;
|
||||
case 2:
|
||||
$v = static::helperValue($val[1]);
|
||||
$values[] = sprintf("`%s` = %s", $val[0], $v);
|
||||
break;
|
||||
case 3:
|
||||
$v = static::helperValue($val[2]);
|
||||
$values[] = sprintf("`%s` %s %s", $val[0], strtoupper($val[1]), $v);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return implode($conditionConnector." ", $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $conditions
|
||||
* @param string $conditionConnector AND, OR
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function helperWhere($conditions, $conditionConnector = 'AND')
|
||||
{
|
||||
if(count($conditions) > 0){
|
||||
return sprintf(
|
||||
" WHERE %s",
|
||||
static::helperConditionList($conditions, $conditionConnector)
|
||||
);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|null $orderBy Examples below:
|
||||
* null => ""
|
||||
* array() => ""
|
||||
* array('attr1' => 'asc', 'attr2' => 'desc') => " ORDER BY `attr1` ASC, `attr2` DESC"
|
||||
* array('attr1') => " ORDER BY `attr1` ASC"
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function helperOrderBy($orderBy = null)
|
||||
{
|
||||
if(is_null($orderBy) || count($orderBy) === 0){
|
||||
return "";
|
||||
}
|
||||
|
||||
$values = array();
|
||||
foreach($orderBy as $key => $val){
|
||||
if(is_int($key)){
|
||||
$values[] = array($val);
|
||||
}
|
||||
else{
|
||||
$values[] = array($key, strtoupper($val));
|
||||
}
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
" ORDER BY %s",
|
||||
static::helperAttributeList($values)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|array $limit
|
||||
* 0 => ""
|
||||
* 3 => " LIMIT 3"
|
||||
* array(3, 4) => " LIMIT 3,4"
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function helperLimit($limit = 0)
|
||||
{
|
||||
if(is_array($limit) && count($limit) == 2){
|
||||
$limit = $limit[0].",".$limit[1];
|
||||
}
|
||||
|
||||
if(is_string($limit) || (is_int($limit) && $limit > 0)){
|
||||
return sprintf(
|
||||
" LIMIT %s",
|
||||
$limit
|
||||
);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
31
include/php/classes/DatabaseException.php
Normal file
31
include/php/classes/DatabaseException.php
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
class DatabaseException extends Exception
|
||||
{
|
||||
/** @var string */
|
||||
protected $query;
|
||||
|
||||
/**
|
||||
* Set the executed SQL query
|
||||
*
|
||||
* @param string $query
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setQuery($query)
|
||||
{
|
||||
$this->query = $query;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the executed SQL query
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getQuery()
|
||||
{
|
||||
return $this->query;
|
||||
}
|
||||
}
|
146
include/php/classes/Message.php
Normal file
146
include/php/classes/Message.php
Normal file
|
@ -0,0 +1,146 @@
|
|||
<?php
|
||||
|
||||
class Message
|
||||
{
|
||||
|
||||
const TYPE_FAIL = 'fail';
|
||||
const TYPE_ERROR = 'fail';
|
||||
const TYPE_WARNING = 'warning';
|
||||
const TYPE_SUCCESS = 'success';
|
||||
|
||||
|
||||
/**
|
||||
* @var Message
|
||||
*/
|
||||
protected static $instance;
|
||||
|
||||
|
||||
/**
|
||||
* Holds all messages
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $messages = array();
|
||||
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function __clone()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Message
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if(is_null(static::$instance)){
|
||||
static::$instance = new static();
|
||||
}
|
||||
|
||||
return static::$instance;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a new message
|
||||
*
|
||||
* @param string $type Supported types: success, fail, info
|
||||
* @param string $text
|
||||
*/
|
||||
public function add($type, $text)
|
||||
{
|
||||
if(!in_array($type, array(static::TYPE_FAIL, static::TYPE_ERROR, static::TYPE_WARNING, static::TYPE_SUCCESS))){
|
||||
throw new InvalidArgumentException;
|
||||
}
|
||||
|
||||
$this->messages[] = array(
|
||||
'type' => $type,
|
||||
'message' => $text,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a new success message
|
||||
*
|
||||
* @param string $text
|
||||
*/
|
||||
public function fail($text)
|
||||
{
|
||||
$this->add(static::TYPE_FAIL, $text);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a new success message
|
||||
*
|
||||
* @param string $text
|
||||
*/
|
||||
public function error($text)
|
||||
{
|
||||
$this->add(static::TYPE_ERROR, $text);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a new success message
|
||||
*
|
||||
* @param string $text
|
||||
*/
|
||||
public function warning($text)
|
||||
{
|
||||
$this->add(static::TYPE_WARNING, $text);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a new success message
|
||||
*
|
||||
* @param string $text
|
||||
*/
|
||||
public function success($text)
|
||||
{
|
||||
$this->add(static::TYPE_SUCCESS, $text);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render all messages
|
||||
*
|
||||
* @param null|string $type null = render all
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render($type = null)
|
||||
{
|
||||
$out = '';
|
||||
|
||||
if(count($this->messages) > 0){
|
||||
$out .= '<div class="notifications">';
|
||||
|
||||
foreach($this->messages as $message){
|
||||
if(is_null($type) || $type == $message['type']){
|
||||
$out .= '<div class="notification notification-'.$message['type'].'">'.$message['message'].'</div>';
|
||||
}
|
||||
}
|
||||
|
||||
$out .= '</div>';
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
}
|
299
include/php/classes/Router.php
Normal file
299
include/php/classes/Router.php
Normal file
|
@ -0,0 +1,299 @@
|
|||
<?php
|
||||
|
||||
class Router
|
||||
{
|
||||
const METHOD_GET = 'GET';
|
||||
const METHOD_POST = 'POST';
|
||||
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $routes = array();
|
||||
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $errorPages = array(
|
||||
404 => 'include/php/template/error/not-found.php',
|
||||
403 => 'include/php/template/error/not-allowed.php'
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function __clone()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param array $routes
|
||||
*/
|
||||
public static function init($routes)
|
||||
{
|
||||
static::$routes = $routes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $method
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected static function isValidMethod($method)
|
||||
{
|
||||
return in_array(
|
||||
$method,
|
||||
array(
|
||||
static::METHOD_GET,
|
||||
static::METHOD_POST
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string|array $methods
|
||||
* @param string $pattern
|
||||
* @param callable|array|string $routeConfig
|
||||
* @param array $permission
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function addRoute($methods, $pattern, $routeConfig, $permission = null)
|
||||
{
|
||||
if(!is_array($methods)){
|
||||
$methods = array($methods);
|
||||
}
|
||||
|
||||
$config = array(
|
||||
'pattern' => $pattern,
|
||||
'config' => $routeConfig,
|
||||
'permission' => $permission,
|
||||
);
|
||||
|
||||
foreach($methods as $method){
|
||||
$method = strtoupper($method);
|
||||
|
||||
if(!static::isValidMethod($method)){
|
||||
throw new Exception('Unsupported HTTP method "'.$method.'".');
|
||||
}
|
||||
|
||||
if(!isset(static::$routes[$method])){
|
||||
static::$routes[$method] = array();
|
||||
}
|
||||
|
||||
static::$routes[$method][] = $config;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $pattern
|
||||
* @param callable|string $routeConfig
|
||||
* @param array $permission
|
||||
*/
|
||||
public static function addGet($pattern, $routeConfig, $permission = null)
|
||||
{
|
||||
static::addRoute(static::METHOD_GET, $pattern, $routeConfig, $permission);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $pattern
|
||||
* @param callable|string $routeConfig
|
||||
* @param array $permission
|
||||
*/
|
||||
public static function addPost($pattern, $routeConfig, $permission = null)
|
||||
{
|
||||
static::addRoute(static::METHOD_POST, $pattern, $routeConfig, $permission);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $pattern
|
||||
* @param callable|string $routeConfig
|
||||
* @param array $permission
|
||||
*/
|
||||
public static function addMixed($pattern, $routeConfig, $permission = null)
|
||||
{
|
||||
static::addRoute(array(static::METHOD_GET, static::METHOD_POST), $pattern, $routeConfig, $permission);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param string $method
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function execute($url, $method = self::METHOD_GET)
|
||||
{
|
||||
$method = strtoupper($method);
|
||||
|
||||
if(!static::isValidMethod($method) && !isset(self::$routes[$method])){
|
||||
throw new Exception('Unsupported HTTP method "'.$method.'".');
|
||||
}
|
||||
|
||||
if(isset(self::$routes[$method])){
|
||||
foreach(self::$routes[$method] as $route){
|
||||
if(rtrim($route['pattern'], '/') === rtrim($url, '/')){
|
||||
if(!is_null($route['permission'])){
|
||||
if(!Auth::isLoggedIn() || !Auth::hasPermission($route['permission'])){
|
||||
return static::loadAndBufferOutput(static::$errorPages[403]);
|
||||
}
|
||||
}
|
||||
|
||||
return static::resolveRouteConfig($route['config']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return static::loadAndBufferOutput(static::$errorPages[404]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public static function executeCurrentRequest()
|
||||
{
|
||||
return static::execute(
|
||||
static::getCurrentUrlPath(),
|
||||
isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : static::METHOD_GET
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param int $errorNumber
|
||||
*
|
||||
* @return string|null
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public static function displayError($errorNumber)
|
||||
{
|
||||
$errorPage = isset(static::$errorPages[$errorNumber])
|
||||
? static::loadAndBufferOutput(static::$errorPages[$errorNumber])
|
||||
: '';
|
||||
|
||||
echo Router::loadAndBufferOutput(
|
||||
'include/php/template/layout.php',
|
||||
array(
|
||||
'content' => $errorPage,
|
||||
)
|
||||
);
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param bool $removeGetParameters
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getCurrentUrlPath($removeGetParameters = true)
|
||||
{
|
||||
$baseUrl = parse_url(Config::get('base_url'));
|
||||
$basePath = isset($baseUrl['path']) ? rtrim($baseUrl['path'], '/') : '';
|
||||
|
||||
$url = $_SERVER['REQUEST_URI'];
|
||||
|
||||
if($removeGetParameters){
|
||||
$url = preg_replace('/\?.*/', '', $url); // Trim GET Parameters
|
||||
}
|
||||
|
||||
// Trim all leading slashes
|
||||
$url = rtrim($url, '/');
|
||||
|
||||
if(!empty($basePath) && ($basePathPos = strpos($url, $basePath)) === 0){
|
||||
$url = substr($url, strlen($basePath));
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param array $config
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function resolveRouteConfig($config)
|
||||
{
|
||||
if(is_string($config)){
|
||||
if(file_exists($config)){
|
||||
return static::loadAndBufferOutput($config);
|
||||
}
|
||||
}
|
||||
elseif(is_callable($config) && $config instanceof Closure){
|
||||
return $config();
|
||||
}
|
||||
|
||||
return static::loadAndBufferOutput(static::$errorPages[404]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
* @param array $variables
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function loadAndBufferOutput($file, $variables = array())
|
||||
{
|
||||
ob_start();
|
||||
|
||||
extract($variables);
|
||||
|
||||
require $file;
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate full url
|
||||
*
|
||||
* @param string $url
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function url($url = '')
|
||||
{
|
||||
return rtrim(
|
||||
sprintf(
|
||||
'%s/%s',
|
||||
rtrim(Config::get('base_url'), '/'),
|
||||
trim($url, '/')
|
||||
),
|
||||
'/'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Redirect user to an url
|
||||
*
|
||||
* @param string $url
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public static function redirect($url)
|
||||
{
|
||||
header("Location: ".static::url($url));
|
||||
exit;
|
||||
}
|
||||
}
|
|
@ -1,129 +0,0 @@
|
|||
<?php
|
||||
class USER {
|
||||
|
||||
/*
|
||||
* Class attributes
|
||||
*/
|
||||
|
||||
private $uid;
|
||||
private $email;
|
||||
private $role;
|
||||
private $loggedin = false;
|
||||
|
||||
/*
|
||||
* Constructor
|
||||
*
|
||||
* Fills the user object up with anonymous data
|
||||
*/
|
||||
|
||||
function __construct(){
|
||||
global $admins;
|
||||
|
||||
// Start session
|
||||
session_start();
|
||||
session_regenerate_id();
|
||||
|
||||
if(isset($_SESSION['email']) && in_array($_SESSION['email'], $admins)){
|
||||
$this->role = "admin";
|
||||
}
|
||||
else{
|
||||
$this->role = "user";
|
||||
}
|
||||
|
||||
if(isset($_SESSION['uid']) && $_SESSION['uid'] != ""){
|
||||
// revive session ...
|
||||
$this->uid = $_SESSION['uid'];
|
||||
$this->loggedin = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Getter functions
|
||||
*/
|
||||
|
||||
function getUID(){
|
||||
return $this->uid;
|
||||
}
|
||||
|
||||
function getRole(){
|
||||
return $this->role;
|
||||
}
|
||||
|
||||
function isLoggedIn(){
|
||||
return $this->loggedin;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Login function. Checks login data and writes information to SESSION
|
||||
*
|
||||
* Returns:
|
||||
* true: Login was successful
|
||||
* false: Login was not successful
|
||||
*/
|
||||
|
||||
function login($email, $password){
|
||||
global $db;
|
||||
// Prepare e-mail address
|
||||
$email = $db->escape_string($email);
|
||||
$email = strtolower($email);
|
||||
$password = $db->escape_string($password);
|
||||
$email_part = explode("@", $email);
|
||||
$username = $email_part[0];
|
||||
$domain = $email_part[1];
|
||||
|
||||
|
||||
// Check e-mail address
|
||||
$sql = "SELECT `".DBC_USERS_ID."`, `".DBC_USERS_PASSWORD."` FROM `".DBT_USERS."` WHERE `".DBC_USERS_USERNAME."` = '$username' AND `".DBC_USERS_DOMAIN."` = '$domain' LIMIT 1;";
|
||||
|
||||
if(!$result = $db->query($sql)){
|
||||
dbError($db->error);
|
||||
}
|
||||
|
||||
if($result->num_rows === 1){
|
||||
$userdata = $result->fetch_array(MYSQLI_ASSOC);
|
||||
$uid = $userdata[DBC_USERS_ID];
|
||||
$password_hash = $userdata[DBC_USERS_PASSWORD];
|
||||
|
||||
// Check password
|
||||
if (crypt($password, $password_hash) === $password_hash) {
|
||||
// Password is valid, start a logged-in user session
|
||||
$this->loggedin = true;
|
||||
$_SESSION['uid'] = $uid;
|
||||
$_SESSION['email'] = $email;
|
||||
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
// Password is invalid
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else{
|
||||
// User could not be found
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Changes user password.
|
||||
* Returns:
|
||||
* true: Change success
|
||||
* false: Error
|
||||
*/
|
||||
|
||||
function change_password($newpass, $newpass_rep){
|
||||
$pass_ok = check_new_pass($newpass, $newpass_rep);
|
||||
if($pass_ok === true){
|
||||
$pass_hash = gen_pass_hash($newpass);
|
||||
write_pass_hash_to_db($pass_hash, $this->uid);
|
||||
return true;
|
||||
}
|
||||
else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
|
@ -1,7 +0,0 @@
|
|||
<?php
|
||||
/*
|
||||
* Free resources
|
||||
*/
|
||||
|
||||
$db->close();
|
||||
?>
|
|
@ -1,35 +1,69 @@
|
|||
<?php
|
||||
|
||||
// Include config
|
||||
if(file_exists('config/config_override.inc.php')){
|
||||
require_once 'config/config_override.inc.php';
|
||||
}
|
||||
else{
|
||||
require_once 'config/config.inc.php';
|
||||
}
|
||||
/**
|
||||
* Register automatic loading for dependency injection
|
||||
*/
|
||||
spl_autoload_register(function($class){
|
||||
if(file_exists('include/php/models/'.$class.'.php')){
|
||||
include_once 'include/php/models/'.$class.'.php';
|
||||
}
|
||||
elseif(file_exists('include/php/classes/'.$class.'.php')){
|
||||
include_once 'include/php/classes/'.$class.'.php';
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* @param string $errorMessage
|
||||
* Start session as the very first thing
|
||||
*/
|
||||
function dbError($errorMessage){
|
||||
die('There was an error running the query ['.$errorMessage.']');
|
||||
}
|
||||
session_start();
|
||||
session_regenerate_id();
|
||||
|
||||
// Establish database connection
|
||||
|
||||
$db = new mysqli(MYSQL_HOST, MYSQL_USER, MYSQL_PASSWORD, MYSQL_DATABASE);
|
||||
|
||||
if($db->connect_errno > 0){
|
||||
die('Unable to connect to database [' . $db->connect_error . ']');
|
||||
}
|
||||
|
||||
/* Import classes */
|
||||
require_once 'include/php/classes/user.class.php';
|
||||
|
||||
$user = new USER();
|
||||
|
||||
/**
|
||||
* Load some global accessible functions
|
||||
*/
|
||||
require_once 'include/php/global.inc.php';
|
||||
require_once 'include/php/checkpermissions.inc.php';
|
||||
|
||||
?>
|
||||
|
||||
/**
|
||||
* Setting up
|
||||
*/
|
||||
if(file_exists('config/config.php') && !isset($_SESSION['installer'])){
|
||||
|
||||
/**
|
||||
* Loading config
|
||||
*/
|
||||
$configValues = require_once 'config/config.php';
|
||||
|
||||
if(!is_array($configValues)){
|
||||
throw new Exception('Config must return an array of config values.');
|
||||
}
|
||||
|
||||
Config::init($configValues);
|
||||
|
||||
/**
|
||||
* Establish database connection
|
||||
*/
|
||||
Database::init(Config::get('mysql'));
|
||||
|
||||
|
||||
/**
|
||||
* Initialize Authentication (Login User if in session)
|
||||
*/
|
||||
Auth::init();
|
||||
|
||||
|
||||
/**
|
||||
* Setup routes
|
||||
*/
|
||||
require_once 'include/php/routes.inc.php';
|
||||
}
|
||||
else{
|
||||
|
||||
/**
|
||||
* Switching to install mode
|
||||
*/
|
||||
define('INSTALLER_ENABLED', true);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,165 +1,35 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Message manager
|
||||
* Types of notifications:
|
||||
* success
|
||||
* fail
|
||||
* info
|
||||
*/
|
||||
|
||||
$MESSAGES = array();
|
||||
|
||||
function add_message($type, $message)
|
||||
{
|
||||
global $MESSAGES;
|
||||
$newmessage = array();
|
||||
$newmessage['type'] = $type;
|
||||
$newmessage['message'] = $message;
|
||||
|
||||
$MESSAGES[] = $newmessage;
|
||||
}
|
||||
|
||||
function output_messages()
|
||||
{
|
||||
global $MESSAGES;
|
||||
if(count($MESSAGES) > 0) {
|
||||
echo '<div class="messages">';
|
||||
foreach($MESSAGES as $message){
|
||||
echo '<div class="notification notification-'.$message['type'].'">'.$message['message'].'</div>';
|
||||
}
|
||||
echo '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Function checks password input for new password
|
||||
* Return codes:
|
||||
* true: password is okay
|
||||
* 2: One password field is empty
|
||||
* 3: Passwords are not equal
|
||||
* 4: Passwort is too snort
|
||||
*/
|
||||
|
||||
function check_new_pass($pass1, $pass2)
|
||||
{
|
||||
global $PASS_ERR;
|
||||
global $PASS_ERR_MSG;
|
||||
// Check if one passwort input is empty
|
||||
if($pass1 !== "" && $pass2 !== ""){
|
||||
// Check if password are equal
|
||||
if($pass1 === $pass2){
|
||||
// Check if password length is okay
|
||||
if(strlen($pass1) >= MIN_PASS_LENGTH){
|
||||
// Password is okay.
|
||||
return true;
|
||||
}
|
||||
else{
|
||||
// Password is not long enough
|
||||
$PASS_ERR = 4;
|
||||
$PASS_ERR_MSG = "Password is not long enough. Please enter a password which has ".MIN_PASS_LENGTH." characters or more.";
|
||||
|
||||
return $PASS_ERR;
|
||||
}
|
||||
}
|
||||
else{
|
||||
// Passwords are not equal
|
||||
$PASS_ERR = 3;
|
||||
$PASS_ERR_MSG = "Passwords are not equal.";
|
||||
|
||||
return $PASS_ERR;
|
||||
}
|
||||
}
|
||||
else{
|
||||
// One password is empty.
|
||||
$PASS_ERR = 2;
|
||||
$PASS_ERR_MSG = "Not all password fields were filled out";
|
||||
|
||||
return $PASS_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
function get_hash()
|
||||
{
|
||||
switch(PASS_HASH_SCHEMA){
|
||||
case "SHA-512":
|
||||
return '$6$rounds=5000$';
|
||||
break;
|
||||
|
||||
case "SHA-256":
|
||||
return '$5$rounds=5000$';
|
||||
break;
|
||||
|
||||
case "BLOWFISH":
|
||||
return '$2a$09$';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function gen_pass_hash($pass)
|
||||
{
|
||||
$salt = base64_encode(rand(1, 1000000) + microtime());
|
||||
$hash_schema = get_hash();
|
||||
$pass_hash = crypt($pass, $hash_schema.$salt.'$');
|
||||
|
||||
return $pass_hash;
|
||||
}
|
||||
|
||||
function write_pass_hash_to_db($pass_hash, $uid)
|
||||
{
|
||||
global $db;
|
||||
$uid = $db->escape_string($uid);
|
||||
$pass_hash = $db->escape_string($pass_hash);
|
||||
$db->query("UPDATE `".DBT_USERS."` SET `".DBC_USERS_PASSWORD."` = '$pass_hash' WHERE `".DBC_USERS_ID."` = '$uid';");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
* Add message to logfile
|
||||
*
|
||||
* @param string $text
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
function writeLog($text)
|
||||
{
|
||||
if(defined('WRITE_LOG') && defined('WRITE_LOG_PATH')){
|
||||
$logdestination = realpath(WRITE_LOG_PATH).DIRECTORY_SEPARATOR."webmum.log";
|
||||
if(is_writable(WRITE_LOG_PATH)){
|
||||
$logfile = fopen($logdestination, "a") or die("Unable to create or open logfile \"".$logdestination."\" in root directory!");
|
||||
fwrite($logfile, date('M d H:i:s').": ".$text."\n");
|
||||
fclose($logfile);
|
||||
if(Config::get('options.enable_logging', false) && Config::has('log_path')){
|
||||
|
||||
$logDestination = realpath(Config::get('log_path')).DIRECTORY_SEPARATOR."webmum.log";
|
||||
|
||||
if(is_writable(Config::get('log_path'))){
|
||||
if($logfile = fopen($logDestination, "a")){
|
||||
fwrite($logfile, date('M d H:i:s').": ".$text."\n");
|
||||
fclose($logfile);
|
||||
}
|
||||
else{
|
||||
throw new Exception('Unable to create or open logfile "'.$logDestination.'" in root directory!');
|
||||
}
|
||||
}
|
||||
else{
|
||||
die("Directory \"".WRITE_LOG_PATH."\" is not writable");
|
||||
throw new Exception('Directory "'.Config::get('log_path').'" isn\'t writable');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @return string
|
||||
*/
|
||||
function url($url)
|
||||
{
|
||||
$base = FRONTEND_BASE_PATH;
|
||||
if (substr($base, -1) === '/') {
|
||||
$base = substr($base, 0, -1);
|
||||
}
|
||||
if (strlen($url) > 0 && $url[0] === '/') {
|
||||
$url = substr($url, 1);
|
||||
}
|
||||
return $base.'/'.$url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
*/
|
||||
function redirect($url)
|
||||
{
|
||||
header("Location: ".url($url));
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Split comma, semicolon or newline separated list of emails to string
|
||||
*
|
||||
|
@ -169,26 +39,33 @@ function redirect($url)
|
|||
*/
|
||||
function stringToEmails($input)
|
||||
{
|
||||
$separators = array(',', ';', "\r\n", "\r", "\n", '|', ':');
|
||||
$list = explode(
|
||||
'|',
|
||||
str_replace(
|
||||
array(' ', ',', ';', "\r\n", "\r", "\n", '|', ':'),
|
||||
'|',
|
||||
$input
|
||||
)
|
||||
);
|
||||
|
||||
$list = explode('|', str_replace($separators, '|', $input));
|
||||
foreach($list as $i => &$email){
|
||||
if(empty($email)){
|
||||
unset($list[$i]);
|
||||
}
|
||||
else{
|
||||
$email = trim($email);
|
||||
}
|
||||
}
|
||||
|
||||
return array_values(
|
||||
array_map(
|
||||
'strtolower',
|
||||
array_unique(
|
||||
$emails = array_values(
|
||||
array_unique(
|
||||
array_map(
|
||||
'formatEmail',
|
||||
$list
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
asort($emails);
|
||||
|
||||
return $emails;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -201,6 +78,10 @@ function stringToEmails($input)
|
|||
*/
|
||||
function emailsToString($list, $glue = ',')
|
||||
{
|
||||
if(is_string($list)){
|
||||
return $list;
|
||||
}
|
||||
|
||||
return implode($glue, $list);
|
||||
}
|
||||
|
||||
|
@ -208,6 +89,7 @@ function emailsToString($list, $glue = ',')
|
|||
* Format single email address
|
||||
*
|
||||
* @param string $input
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function formatEmail($input)
|
||||
|
@ -220,6 +102,7 @@ function formatEmail($input)
|
|||
*
|
||||
* @param string|array $input
|
||||
* @param string $glue
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function formatEmails($input, $glue)
|
||||
|
@ -231,5 +114,53 @@ function formatEmails($input, $glue)
|
|||
return emailsToString($input, $glue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format email addresses for text output (not in an input field)
|
||||
*
|
||||
* @param string|array $input
|
||||
* @return string
|
||||
*/
|
||||
function formatEmailsText($input)
|
||||
{
|
||||
return formatEmails(
|
||||
$input,
|
||||
str_replace(PHP_EOL, '<br>', Config::get('frontend_options.email_separator_text', ', '))
|
||||
);
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
/**
|
||||
* Format email addresses for form output (in an input field)
|
||||
*
|
||||
* @param string|array $input
|
||||
* @return string
|
||||
*/
|
||||
function formatEmailsForm($input)
|
||||
{
|
||||
return strip_tags(
|
||||
formatEmails(
|
||||
$input,
|
||||
Config::get('frontend_options.email_separator_form', ',')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $textPattern
|
||||
* @param int|mixed $value
|
||||
* @return string
|
||||
*/
|
||||
function textValue($textPattern, $value)
|
||||
{
|
||||
$text = str_replace(
|
||||
array('_', ':val:', ':value:'),
|
||||
$value,
|
||||
$textPattern
|
||||
);
|
||||
|
||||
if(is_numeric($value) && $value > 1){
|
||||
$text .= 's';
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
457
include/php/models/AbstractModel.php
Normal file
457
include/php/models/AbstractModel.php
Normal file
|
@ -0,0 +1,457 @@
|
|||
<?php
|
||||
|
||||
abstract class AbstractModel
|
||||
{
|
||||
|
||||
/**
|
||||
* Db table for find methods
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $table;
|
||||
|
||||
|
||||
/**
|
||||
* Db id attribute for find methods
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $idAttribute;
|
||||
|
||||
|
||||
/**
|
||||
* Mapping model attributes and database attributes for saving
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $attributeDbAttributeMapping = null;
|
||||
|
||||
|
||||
/**
|
||||
* Initialize Model
|
||||
*/
|
||||
protected static function initModel()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get mapped db attribute
|
||||
*
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
public static function attr($name)
|
||||
{
|
||||
static::initModel();
|
||||
|
||||
if(isset(static::$attributeDbAttributeMapping[$name])){
|
||||
return static::$attributeDbAttributeMapping[$name];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Format or do other things before saving
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function preSave($data)
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Hold all data from a model
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $data = array();
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $data
|
||||
*/
|
||||
protected function __construct($data)
|
||||
{
|
||||
static::initModel();
|
||||
|
||||
if(isset($data[static::$idAttribute])){
|
||||
$id = is_numeric($data[static::$idAttribute]) && strpos($data[static::$idAttribute], ',') === false
|
||||
? intval($data[static::$idAttribute])
|
||||
: $data[static::$idAttribute];
|
||||
|
||||
$this->setId($id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a model from data
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return static|null The Model
|
||||
*/
|
||||
public static function create($data)
|
||||
{
|
||||
if(count($data) > 0){
|
||||
return new static($data);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a model collection from data
|
||||
*
|
||||
* @param array $multiData
|
||||
*
|
||||
* @return ModelCollection|static[]
|
||||
*/
|
||||
public static function createMultiple($multiData = array())
|
||||
{
|
||||
$collection = new ModelCollection();
|
||||
|
||||
foreach($multiData as $data){
|
||||
$model = static::create($data);
|
||||
|
||||
if(!is_null($model)){
|
||||
if(is_null($model->getId())){
|
||||
$collection->add($model);
|
||||
}
|
||||
else{
|
||||
$collection->add($model, $model->getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see create
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return AbstractModel|null
|
||||
*/
|
||||
public static function createAndSave($data)
|
||||
{
|
||||
$model = static::create($data);
|
||||
|
||||
if(!is_null($model)){
|
||||
$model->save();
|
||||
|
||||
return $model;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see createMultiple
|
||||
*
|
||||
* @param array $multiData
|
||||
*
|
||||
* @return ModelCollection|static[]
|
||||
*/
|
||||
public static function createMultipleAndSave($multiData = array())
|
||||
{
|
||||
$collection = new ModelCollection();
|
||||
|
||||
foreach($multiData as $data){
|
||||
$model = static::createAndSave($data);
|
||||
|
||||
if(!is_null($model)){
|
||||
$collection->add($model);
|
||||
}
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a model from mysqli result
|
||||
*
|
||||
* @param mysqli_result $result
|
||||
*
|
||||
* @return static|null
|
||||
*/
|
||||
public static function createFromDbResult($result)
|
||||
{
|
||||
if($result->num_rows === 0){
|
||||
return null;
|
||||
}
|
||||
|
||||
return static::create($result->fetch_assoc());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a model collection from mysqli result
|
||||
*
|
||||
* @param mysqli_result $result
|
||||
*
|
||||
* @return ModelCollection|static[]
|
||||
*/
|
||||
public static function createMultipleFromDbResult($result)
|
||||
{
|
||||
$rows = array();
|
||||
|
||||
while($row = $result->fetch_assoc()){
|
||||
$rows[] = $row;
|
||||
}
|
||||
|
||||
return static::createMultiple($rows);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function setAttribute($attribute, $value)
|
||||
{
|
||||
$this->data[$attribute] = $value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $attribute
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getAttribute($attribute)
|
||||
{
|
||||
if(isset($this->data[$attribute])){
|
||||
$value = $this->data[$attribute];
|
||||
|
||||
if(is_array($value)){
|
||||
return array_map('strip_tags', $value);
|
||||
}
|
||||
elseif(is_string($value)){
|
||||
return strip_tags($value);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->getAttribute('id');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
protected function setId($value)
|
||||
{
|
||||
$this->setAttribute('id', $value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find all models by raw sql
|
||||
*
|
||||
* @param $sql
|
||||
* @param null|string $useSpecificModel
|
||||
*
|
||||
* @return ModelCollection|static[]
|
||||
*/
|
||||
public static function findAllRaw($sql, $useSpecificModel = null)
|
||||
{
|
||||
$result = Database::getInstance()->query($sql);
|
||||
|
||||
if(is_null($useSpecificModel)){
|
||||
return static::createMultipleFromDbResult($result);
|
||||
}
|
||||
elseif(class_exists($useSpecificModel)){
|
||||
return call_user_func_array(array($useSpecificModel, 'createMultipleFromDbResult'), array($result));
|
||||
}
|
||||
|
||||
return new ModelCollection();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find a model by raw sql
|
||||
*
|
||||
* @param $sql
|
||||
* @param null|string $useSpecificModel
|
||||
*
|
||||
* @return AbstractModel
|
||||
*/
|
||||
public static function findRaw($sql, $useSpecificModel = null)
|
||||
{
|
||||
$result = Database::getInstance()->query($sql);
|
||||
|
||||
if(is_null($useSpecificModel)){
|
||||
return static::createFromDbResult($result);
|
||||
}
|
||||
elseif(class_exists($useSpecificModel)){
|
||||
return call_user_func_array(array($useSpecificModel, 'createFromDbResult'), array($result));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find models by a condition
|
||||
*
|
||||
* @param array $conditions see helperConditionArray
|
||||
* @param string $conditionConnector see helperConditionArray
|
||||
* @param array|null $orderBy
|
||||
* @param int $limit see helperLimit
|
||||
*
|
||||
* @return ModelCollection|static[]|AbstractModel|null
|
||||
*/
|
||||
public static function findWhere($conditions = array(), $conditionConnector = 'AND', $orderBy = null, $limit = 0)
|
||||
{
|
||||
static::initModel();
|
||||
|
||||
$result = Database::getInstance()->select(static::$table, $conditions, $conditionConnector, $orderBy, $limit);
|
||||
|
||||
if($limit === 1){
|
||||
return static::createFromDbResult($result);
|
||||
}
|
||||
|
||||
return static::createMultipleFromDbResult($result);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find all models
|
||||
*
|
||||
* @param array|null $orderBy see helperOrderBy
|
||||
*
|
||||
* @return ModelCollection|static[]
|
||||
*/
|
||||
public static function findAll($orderBy = null)
|
||||
{
|
||||
return static::findWhere(array(), 'AND', $orderBy);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find first model matching a condition
|
||||
*
|
||||
* @param array $conditions see helperConditionArray
|
||||
* @param string $conditionConnector see helperConditionArray
|
||||
* @param array|null $orderBy
|
||||
*
|
||||
* @return AbstractModel|null
|
||||
*/
|
||||
public static function findWhereFirst($conditions = array(), $conditionConnector = 'AND', $orderBy = null)
|
||||
{
|
||||
return static::findWhere($conditions, $conditionConnector, $orderBy, 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find a model by id
|
||||
*
|
||||
* @param mixed $id
|
||||
*
|
||||
* @return AbstractModel|null
|
||||
*/
|
||||
public static function find($id)
|
||||
{
|
||||
static::initModel();
|
||||
|
||||
return static::findWhereFirst(array(static::$idAttribute, $id));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Save model data to database
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$data = $this->preSave($this->data);
|
||||
|
||||
$values = array();
|
||||
foreach(static::$attributeDbAttributeMapping as $attribute => $sqlAttribute){
|
||||
if($sqlAttribute === static::$idAttribute){
|
||||
continue;
|
||||
}
|
||||
|
||||
$values[$sqlAttribute] = $data[$attribute];
|
||||
}
|
||||
|
||||
if(is_null($this->getId())){
|
||||
$insertId = Database::getInstance()->insert(static::$table, $values);
|
||||
|
||||
$this->setId(intval($insertId));
|
||||
}
|
||||
else{
|
||||
Database::getInstance()->update(static::$table, $values, array(static::$idAttribute, $this->getId()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete model from database
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
if(!is_null($this->getId())){
|
||||
|
||||
Database::getInstance()->delete(static::$table, static::$idAttribute, $this->getId());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Count models by a condition
|
||||
*
|
||||
* @param array $conditions see helperConditionArray
|
||||
* @param string $conditionConnector see helperConditionArray
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function countWhere($conditions = array(), $conditionConnector = 'AND')
|
||||
{
|
||||
static::initModel();
|
||||
|
||||
return Database::getInstance()->count(static::$table, static::$idAttribute, $conditions, $conditionConnector);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Count all models
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function count()
|
||||
{
|
||||
return static::countWhere();
|
||||
}
|
||||
|
||||
}
|
6
include/php/models/AbstractMultiRedirect.php
Normal file
6
include/php/models/AbstractMultiRedirect.php
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?php
|
||||
|
||||
abstract class AbstractMultiRedirect extends AbstractRedirect
|
||||
{
|
||||
|
||||
}
|
398
include/php/models/AbstractRedirect.php
Normal file
398
include/php/models/AbstractRedirect.php
Normal file
|
@ -0,0 +1,398 @@
|
|||
<?php
|
||||
|
||||
abstract class AbstractRedirect extends AbstractModel
|
||||
{
|
||||
use DomainLimitTrait;
|
||||
|
||||
|
||||
/**
|
||||
* Db table for find methods
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $table;
|
||||
|
||||
|
||||
/**
|
||||
* Db id attribute for find methods
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $idAttribute;
|
||||
|
||||
|
||||
/**
|
||||
* Mapping model attributes and database attributes for saving
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $attributeDbAttributeMapping = null;
|
||||
|
||||
|
||||
/**
|
||||
* @var ModelCollection
|
||||
*/
|
||||
protected $conflictingUsers = null;
|
||||
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected static function initModel()
|
||||
{
|
||||
if(is_null(static::$attributeDbAttributeMapping)){
|
||||
static::$table = Config::get('schema.tables.aliases', 'aliases');
|
||||
static::$idAttribute = Config::get('schema.attributes.aliases.id', 'id');
|
||||
|
||||
static::$attributeDbAttributeMapping = array(
|
||||
'id' => Config::get('schema.attributes.aliases.id', 'id'),
|
||||
'source' => Config::get('schema.attributes.aliases.source', 'source'),
|
||||
'destination' => Config::get('schema.attributes.aliases.destination', 'destination'),
|
||||
'multi_hash' => Config::get('schema.attributes.aliases.multi_source', 'multi_source'),
|
||||
);
|
||||
|
||||
if(Config::get('options.enable_user_redirects', false)){
|
||||
static::$attributeDbAttributeMapping['is_created_by_user'] = Config::get('schema.attributes.aliases.is_created_by_user', 'is_created_by_user');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function preSave($data)
|
||||
{
|
||||
$data = parent::preSave($data);
|
||||
|
||||
$data['source'] = emailsToString($data['source']);
|
||||
$data['destination'] = emailsToString($data['destination']);
|
||||
|
||||
if(Config::get('options.enable_user_redirects', false)){
|
||||
$data['is_created_by_user'] = $data['is_created_by_user'] ? 1 : 0;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function __construct($data)
|
||||
{
|
||||
parent::__construct($data);
|
||||
|
||||
$source = stringToEmails($data[static::attr('source')]);
|
||||
$destination = stringToEmails($data[static::attr('destination')]);
|
||||
|
||||
if(get_called_class() === 'Alias' || get_called_class() === 'Redirect'){
|
||||
$source = $source[0];
|
||||
}
|
||||
|
||||
if(get_called_class() === 'Alias' || get_called_class() === 'MultiAlias'){
|
||||
$destination = $destination[0];
|
||||
}
|
||||
|
||||
$this->setSource($source);
|
||||
$this->setDestination($destination);
|
||||
|
||||
if(Config::get('options.enable_multi_source_redirects', false)){
|
||||
$this->setMultiHash($data[static::attr('multi_hash')]);
|
||||
}
|
||||
|
||||
if(Config::get('options.enable_user_redirects', false)){
|
||||
$this->setIsCreatedByUser($data[static::attr('is_created_by_user')]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static function create($data)
|
||||
{
|
||||
if(get_called_class() !== 'AbstractRedirect'){
|
||||
return parent::create($data);
|
||||
}
|
||||
|
||||
$hasMultipleSources = array_key_exists(static::attr('source'), $data)
|
||||
&& strpos($data[static::attr('source')], ',') !== false;
|
||||
|
||||
$hasMultipleDestinations = array_key_exists(static::attr('destination'), $data)
|
||||
&& strpos($data[static::attr('destination')], ',') !== false;
|
||||
|
||||
if(Config::get('options.enable_multi_source_redirects', false) && $hasMultipleSources
|
||||
){
|
||||
if($hasMultipleDestinations){
|
||||
return MultiRedirect::create($data);
|
||||
}
|
||||
else{
|
||||
return MultiAlias::create($data);
|
||||
}
|
||||
}
|
||||
else{
|
||||
if($hasMultipleDestinations){
|
||||
return Redirect::create($data);
|
||||
}
|
||||
else{
|
||||
return Alias::create($data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array|string
|
||||
*/
|
||||
public function getSource()
|
||||
{
|
||||
return $this->getAttribute('source');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string|array $value
|
||||
*/
|
||||
public function setSource($value)
|
||||
{
|
||||
if(is_array($value)){
|
||||
$this->setAttribute('source', array_map('strtolower', $value));
|
||||
}
|
||||
else{
|
||||
$this->setAttribute('source', strtolower($value));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array|string
|
||||
*/
|
||||
public function getDestination()
|
||||
{
|
||||
return $this->getAttribute('destination');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string|array $value
|
||||
*/
|
||||
public function setDestination($value)
|
||||
{
|
||||
if(is_array($value)){
|
||||
$this->setAttribute('destination', array_map('strtolower', $value));
|
||||
}
|
||||
else{
|
||||
$this->setAttribute('destination', strtolower($value));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMultiHash()
|
||||
{
|
||||
return $this->getAttribute('multi_hash');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
*/
|
||||
public function setMultiHash($value)
|
||||
{
|
||||
$this->setAttribute('multi_hash', $value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isCreatedByUser()
|
||||
{
|
||||
return $this->getAttribute('is_created_by_user');
|
||||
}
|
||||
|
||||
|
||||
/**n
|
||||
* @param bool $value
|
||||
*/
|
||||
public function setIsCreatedByUser($value)
|
||||
{
|
||||
$this->setAttribute('is_created_by_user', $value ? true : false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getDomain()
|
||||
{
|
||||
$sources = $this->getSource();
|
||||
if(is_string($sources)){
|
||||
$sources = array($sources);
|
||||
}
|
||||
|
||||
$domains = array();
|
||||
foreach($sources as $source){
|
||||
$emailParts = explode('@', $source);
|
||||
if(count($emailParts) === 2){
|
||||
$domains[] = $emailParts[1];
|
||||
}
|
||||
}
|
||||
|
||||
return array_unique($domains);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return ModelCollection
|
||||
*/
|
||||
public function getConflictingUsers()
|
||||
{
|
||||
if(is_null($this->conflictingUsers)){
|
||||
$sources = $this->getSource();
|
||||
|
||||
if(is_string($sources)){
|
||||
$sources = array($sources);
|
||||
}
|
||||
|
||||
$this->conflictingUsers = new ModelCollection();
|
||||
foreach($sources as $source){
|
||||
$user = User::findByEmail($source);
|
||||
if(!is_null($user)){
|
||||
$this->conflictingUsers->add($user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->conflictingUsers;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $template
|
||||
*
|
||||
* @return array|string
|
||||
*/
|
||||
public function getConflictingMarkedSource($template = "<u>%email%</u>")
|
||||
{
|
||||
$conflictingUsers = $this->getConflictingUsers();
|
||||
|
||||
$sources = $this->getSource();
|
||||
|
||||
if(is_string($sources)){
|
||||
$sources = array($sources);
|
||||
}
|
||||
|
||||
foreach($conflictingUsers as $user){
|
||||
if(($key = array_search($user->getEmail(), $sources)) !== false){
|
||||
$sources[$key] = str_replace('%email%', $sources[$key], $template);
|
||||
}
|
||||
}
|
||||
|
||||
return $sources;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static function findAll($orderBy = null)
|
||||
{
|
||||
if(is_null($orderBy)){
|
||||
$orderBy = array(static::attr('source'));
|
||||
}
|
||||
|
||||
return parent::findAll($orderBy);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private static function generateRedirectBaseQuery()
|
||||
{
|
||||
if(Config::get('options.enable_multi_source_redirects', false)){
|
||||
return "SELECT r.* FROM (
|
||||
SELECT
|
||||
GROUP_CONCAT(g.`".static::$idAttribute."` ORDER BY g.`".static::$idAttribute."` SEPARATOR ',') AS `".static::$idAttribute."`,
|
||||
GROUP_CONCAT(g.`".static::attr('source')."` SEPARATOR ',') AS `".static::attr('source')."`,
|
||||
g.`".static::attr('destination')."`,
|
||||
g.`".static::attr('multi_hash')."`
|
||||
".(Config::get('options.enable_user_redirects', false) ? ", g.`".static::attr('is_created_by_user')."`" : "")."
|
||||
FROM `".static::$table."` AS g
|
||||
WHERE g.`".static::attr('multi_hash')."` IS NOT NULL
|
||||
GROUP BY g.`".static::attr('multi_hash')."`
|
||||
UNION
|
||||
SELECT
|
||||
s.`".static::$idAttribute."`,
|
||||
s.`".static::attr('source')."`,
|
||||
s.`".static::attr('destination')."`,
|
||||
s.`".static::attr('multi_hash')."`
|
||||
".(Config::get('options.enable_user_redirects', false) ? ", s.`".static::attr('is_created_by_user')."`" : "")."
|
||||
FROM `".static::$table."` AS s
|
||||
WHERE s.`".static::attr('multi_hash')."` IS NULL
|
||||
) AS r";
|
||||
}
|
||||
else{
|
||||
return "SELECT * FROM `".static::$table."`";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function findMultiAll($orderBy = null)
|
||||
{
|
||||
static::initModel();
|
||||
|
||||
if(is_null($orderBy)){
|
||||
$orderBy = array(static::attr('source'));
|
||||
}
|
||||
|
||||
$sql = static::generateRedirectBaseQuery()
|
||||
.Database::helperOrderBy($orderBy);
|
||||
|
||||
return static::findAllRaw($sql);
|
||||
}
|
||||
|
||||
|
||||
public static function findMultiWhere($conditions = array(), $conditionConnector = 'AND', $orderBy = null, $limit = 0)
|
||||
{
|
||||
$sql = static::generateRedirectBaseQuery()
|
||||
.Database::helperWhere($conditions, $conditionConnector)
|
||||
.Database::helperOrderBy($orderBy)
|
||||
.Database::helperLimit($limit);
|
||||
|
||||
if($limit === 1){
|
||||
return static::findRaw($sql);
|
||||
}
|
||||
|
||||
return static::findAllRaw($sql);
|
||||
}
|
||||
|
||||
|
||||
public static function findMultiWhereFirst($conditions = array(), $conditionConnector = 'AND', $orderBy = null)
|
||||
{
|
||||
return static::findMultiWhere($conditions, $conditionConnector, $orderBy, 1);
|
||||
}
|
||||
|
||||
|
||||
public static function findMulti($id)
|
||||
{
|
||||
static::initModel();
|
||||
|
||||
return static::findMultiWhereFirst(array(static::$idAttribute, $id));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param array|User|null $limitedBy
|
||||
*
|
||||
* @return ModelCollection|static[]
|
||||
*/
|
||||
public static function getMultiByLimitedDomains($limitedBy = null)
|
||||
{
|
||||
return static::filterModelCollectionByLimitedDomains(static::findMultiAll(), $limitedBy);
|
||||
}
|
||||
}
|
10
include/php/models/Alias.php
Normal file
10
include/php/models/Alias.php
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @method string getSource()
|
||||
* @method string getDestination()
|
||||
*/
|
||||
class Alias extends Redirect
|
||||
{
|
||||
|
||||
}
|
103
include/php/models/Domain.php
Normal file
103
include/php/models/Domain.php
Normal file
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
class Domain extends AbstractModel
|
||||
{
|
||||
use DomainLimitTrait;
|
||||
|
||||
|
||||
/**
|
||||
* Db table for find methods
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $table;
|
||||
|
||||
|
||||
/**
|
||||
* Db id attribute for find methods
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $idAttribute;
|
||||
|
||||
|
||||
/**
|
||||
* Mapping model attributes and database attributes for saving
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $attributeDbAttributeMapping = null;
|
||||
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected static function initModel()
|
||||
{
|
||||
if(is_null(static::$attributeDbAttributeMapping)){
|
||||
static::$table = Config::get('schema.tables.domains', 'domains');
|
||||
static::$idAttribute = Config::get('schema.attributes.domains.id', 'id');
|
||||
|
||||
static::$attributeDbAttributeMapping = array(
|
||||
'id' => Config::get('schema.attributes.domains.id', 'id'),
|
||||
'domain' => Config::get('schema.attributes.domains.domain', 'domain'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function __construct($data)
|
||||
{
|
||||
parent::__construct($data);
|
||||
|
||||
$this->setDomain($data[static::attr('domain')]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDomain()
|
||||
{
|
||||
return $this->getAttribute('domain');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
*/
|
||||
public function setDomain($value)
|
||||
{
|
||||
$this->setAttribute('domain', strtolower($value));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function countUsers()
|
||||
{
|
||||
return User::countWhere(
|
||||
array(User::attr('domain'), $this->getDomain())
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function countRedirects()
|
||||
{
|
||||
return AbstractRedirect::countWhere(
|
||||
array(
|
||||
array(AbstractRedirect::attr('source'), 'LIKE', "%@{$this->getDomain()}"),
|
||||
array(AbstractRedirect::attr('destination'), 'LIKE', "%@{$this->getDomain()}")
|
||||
),
|
||||
'OR'
|
||||
);
|
||||
}
|
||||
|
||||
}
|
69
include/php/models/DomainLimitTrait.php
Normal file
69
include/php/models/DomainLimitTrait.php
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
trait DomainLimitTrait
|
||||
{
|
||||
/**
|
||||
* @param array|User|null $limitedBy
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isInLimitedDomains($limitedBy = null)
|
||||
{
|
||||
if(!Config::get('options.enable_admin_domain_limits', false)) {
|
||||
return true;
|
||||
}
|
||||
if(is_null($limitedBy)){
|
||||
return static::isInLimitedDomains(Auth::getUser());
|
||||
}
|
||||
elseif($limitedBy instanceof User) {
|
||||
/** @var User $limitedBy */
|
||||
return !$limitedBy->isDomainLimited() || static::isInLimitedDomains($limitedBy->getDomainLimits());
|
||||
}
|
||||
|
||||
if(!is_array($limitedBy)){
|
||||
throw new InvalidArgumentException;
|
||||
}
|
||||
|
||||
/** @var string|array|string[] $domain */
|
||||
$domain = $this->getDomain();
|
||||
|
||||
if(is_string($domain)) {
|
||||
return in_array($domain, $limitedBy);
|
||||
}
|
||||
|
||||
foreach($domain as $d){
|
||||
if(!in_array($d, $limitedBy)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param ModelCollection|static[] $collection
|
||||
* @param array|User|null $limitedBy
|
||||
*
|
||||
* @return ModelCollection|static[]
|
||||
*/
|
||||
protected static function filterModelCollectionByLimitedDomains($collection, $limitedBy = null)
|
||||
{
|
||||
return $collection->searchAll(function($model) use ($limitedBy){
|
||||
/** @var static $model */
|
||||
//var_dump($model->isInLimitedDomains($limitedBy), $model->getDomain());
|
||||
return $model->isInLimitedDomains($limitedBy);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param array|User|null $limitedBy
|
||||
*
|
||||
* @return ModelCollection|static[]
|
||||
*/
|
||||
public static function getByLimitedDomains($limitedBy = null)
|
||||
{
|
||||
return static::filterModelCollectionByLimitedDomains(static::findAll(), $limitedBy);
|
||||
}
|
||||
}
|
278
include/php/models/ModelCollection.php
Normal file
278
include/php/models/ModelCollection.php
Normal file
|
@ -0,0 +1,278 @@
|
|||
<?php
|
||||
|
||||
class ModelCollection implements Iterator, ArrayAccess, Countable
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array|AbstractModel[]
|
||||
*/
|
||||
private $models = array();
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array|AbstractModel[] $array
|
||||
*/
|
||||
public function __construct($array = array())
|
||||
{
|
||||
if($this->isNumericArray($array)){
|
||||
foreach($array as $model){
|
||||
$this->add($model);
|
||||
}
|
||||
}
|
||||
else{
|
||||
foreach($array as $key => $model){
|
||||
$this->add($model, $key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isNumericArray($array)
|
||||
{
|
||||
return array_keys($array) === range(0, count($array) - 1)
|
||||
&& count(array_filter($array, 'is_string')) === 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a model to the collection,
|
||||
* but won't replace if it exists with that key
|
||||
*
|
||||
* @param AbstractModel $model
|
||||
* @param mixed|null $key
|
||||
*/
|
||||
public function add($model, $key = null)
|
||||
{
|
||||
if(is_null($model) || !($model instanceof AbstractModel)){
|
||||
return;
|
||||
}
|
||||
|
||||
if(is_null($key)){
|
||||
$this->models[] = $model;
|
||||
}
|
||||
elseif(!$this->has($key)){
|
||||
$this->models[$key] = $model;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replace a model with given key
|
||||
*
|
||||
* @param AbstractModel $model
|
||||
* @param mixed $key
|
||||
*/
|
||||
public function replace($model, $key)
|
||||
{
|
||||
if(is_null($model) || !($model instanceof AbstractModel)){
|
||||
return;
|
||||
}
|
||||
|
||||
$model[$key] = $model;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delete a model by key
|
||||
*
|
||||
* @param mixed $key
|
||||
*/
|
||||
public function delete($key)
|
||||
{
|
||||
if($this->has($key)){
|
||||
unset($this->models[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if collection has a model by key
|
||||
*
|
||||
* @param mixed $key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has($key)
|
||||
{
|
||||
return isset($this->models[$key]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a model from the collection by key
|
||||
*
|
||||
* @param mixed $key
|
||||
*
|
||||
* @return AbstractModel|null
|
||||
*/
|
||||
public function get($key)
|
||||
{
|
||||
if($this->has($key)){
|
||||
return $this->models[$key];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Search a model in collection with a condition
|
||||
*
|
||||
* @param callable $callable Gives back if the search matches
|
||||
*
|
||||
* @return AbstractModel|null
|
||||
*/
|
||||
public function search($callable)
|
||||
{
|
||||
if(is_callable($callable)){
|
||||
foreach($this->models as $model){
|
||||
if($callable($model)){
|
||||
return $model;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Search all models in collection with a condition
|
||||
*
|
||||
* @param callable $callable Gives back if the search matches
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function searchAll($callable)
|
||||
{
|
||||
$collection = new static;
|
||||
|
||||
if(is_callable($callable)){
|
||||
foreach($this->models as $model){
|
||||
if($callable($model)){
|
||||
$collection->add($model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert models to an array of strings
|
||||
*
|
||||
* @param callable $callable Gives back a string for a model
|
||||
*
|
||||
* @return array|string[]
|
||||
*/
|
||||
public function toStringArray($callable)
|
||||
{
|
||||
$strings = array();
|
||||
|
||||
if(is_callable($callable)){
|
||||
foreach($this->models as $model){
|
||||
$strings[] = $callable($model);
|
||||
}
|
||||
}
|
||||
|
||||
return $strings;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
return current($this->models);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
return next($this->models);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return key($this->models);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return $this->current() !== false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
reset($this->models);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return $this->has($offset);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->get($offset);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
$this->add($value, $offset);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
$this->delete($offset);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return count($this->models);
|
||||
}
|
||||
}
|
10
include/php/models/MultiAlias.php
Normal file
10
include/php/models/MultiAlias.php
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @method array getSource()
|
||||
* @method string getDestination()
|
||||
*/
|
||||
class MultiAlias extends AbstractMultiRedirect
|
||||
{
|
||||
|
||||
}
|
10
include/php/models/MultiRedirect.php
Normal file
10
include/php/models/MultiRedirect.php
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @method array getSource()
|
||||
* @method array getDestination()
|
||||
*/
|
||||
class MultiRedirect extends AbstractMultiRedirect
|
||||
{
|
||||
|
||||
}
|
10
include/php/models/Redirect.php
Normal file
10
include/php/models/Redirect.php
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @method string getSource()
|
||||
* @method array getDestination()
|
||||
*/
|
||||
class Redirect extends AbstractRedirect
|
||||
{
|
||||
|
||||
}
|
467
include/php/models/User.php
Normal file
467
include/php/models/User.php
Normal file
|
@ -0,0 +1,467 @@
|
|||
<?php
|
||||
|
||||
class User extends AbstractModel
|
||||
{
|
||||
use DomainLimitTrait;
|
||||
|
||||
|
||||
/**
|
||||
* Db table for find methods
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $table;
|
||||
|
||||
|
||||
/**
|
||||
* Db id attribute for find methods
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $idAttribute;
|
||||
|
||||
|
||||
/**
|
||||
* Mapping model attributes and database attributes for saving
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $attributeDbAttributeMapping = null;
|
||||
|
||||
|
||||
const ROLE_USER = 'user';
|
||||
const ROLE_ADMIN = 'admin';
|
||||
|
||||
|
||||
/**
|
||||
* @var AbstractRedirect
|
||||
*/
|
||||
protected $conflictingRedirect = null;
|
||||
|
||||
|
||||
/**
|
||||
* @var ModelCollection|AbstractRedirect[]
|
||||
*/
|
||||
protected $redirects = null;
|
||||
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected static function initModel()
|
||||
{
|
||||
if(is_null(static::$attributeDbAttributeMapping)){
|
||||
static::$table = Config::get('schema.tables.users', 'users');
|
||||
static::$idAttribute = Config::get('schema.attributes.users.id', 'id');
|
||||
|
||||
static::$attributeDbAttributeMapping = array(
|
||||
'id' => Config::get('schema.attributes.users.id', 'id'),
|
||||
'username' => Config::get('schema.attributes.users.username', 'username'),
|
||||
'domain' => Config::get('schema.attributes.users.domain', 'domain'),
|
||||
'password_hash' => Config::get('schema.attributes.users.password', 'password'),
|
||||
);
|
||||
|
||||
if(Config::get('options.enable_mailbox_limits', false)){
|
||||
static::$attributeDbAttributeMapping['mailbox_limit'] = Config::get('schema.attributes.users.mailbox_limit');
|
||||
}
|
||||
|
||||
if(Config::get('options.enable_user_redirects', false)){
|
||||
static::$attributeDbAttributeMapping['max_user_redirects'] = Config::get('schema.attributes.users.max_user_redirects');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function __construct($data)
|
||||
{
|
||||
parent::__construct($data);
|
||||
|
||||
$this->setUsername($data[static::attr('username')]);
|
||||
$this->setDomain($data[static::attr('domain')]);
|
||||
$this->setPasswordHash($data[static::attr('password_hash')]);
|
||||
|
||||
if(Config::get('options.enable_mailbox_limits', false)){
|
||||
$this->setMailboxLimit($data[static::attr('mailbox_limit')]);
|
||||
}
|
||||
|
||||
if(Config::get('options.enable_user_redirects', false)){
|
||||
$this->setMaxUserRedirects($data[static::attr('max_user_redirects')]);
|
||||
}
|
||||
|
||||
$this->setAttribute('role', static::getRoleByEmail($this->getEmail()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUsername()
|
||||
{
|
||||
return $this->getAttribute('username');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
*/
|
||||
public function setUsername($value)
|
||||
{
|
||||
$this->setAttribute('username', strtolower($value));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDomain()
|
||||
{
|
||||
return $this->getAttribute('domain');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
*/
|
||||
public function setDomain($value)
|
||||
{
|
||||
$this->setAttribute('domain', strtolower($value));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getEmail()
|
||||
{
|
||||
return $this->getUsername().'@'.$this->getDomain();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getPasswordHash()
|
||||
{
|
||||
return $this->getAttribute('password_hash');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
*/
|
||||
public function setPasswordHash($value)
|
||||
{
|
||||
$this->setAttribute('password_hash', $value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getMailboxLimit()
|
||||
{
|
||||
return $this->getAttribute('mailbox_limit');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param int $value
|
||||
*/
|
||||
public function setMailboxLimit($value)
|
||||
{
|
||||
$this->setAttribute('mailbox_limit', intval($value));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getMaxUserRedirects()
|
||||
{
|
||||
return $this->getAttribute('max_user_redirects');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param int $value
|
||||
*/
|
||||
public function setMaxUserRedirects($value)
|
||||
{
|
||||
$this->setAttribute('max_user_redirects', intval($value));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $attr
|
||||
* @param mixed $default
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
protected static function getAttributeDefaultValue($attr, $default)
|
||||
{
|
||||
static::initModel();
|
||||
|
||||
$sql = "SELECT DEFAULT(".static::attr($attr).") FROM `".static::$table."` LIMIT 1";
|
||||
|
||||
try {
|
||||
$result = Database::getInstance()->query($sql);
|
||||
|
||||
if($result->num_rows === 1){
|
||||
$row = $result->fetch_array();
|
||||
|
||||
return $row[0];
|
||||
}
|
||||
}
|
||||
catch(Exception $e) {
|
||||
if (strpos($e->getMessage(), 'doesn\'t have a default') !== false) {
|
||||
throw new Exception('Database table "'.static::$table.'" is missing a default value for attribute "'.static::attr($attr).'".');
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get mailbox limit default via database default value
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function getMailboxLimitDefault()
|
||||
{
|
||||
if(Config::get('options.enable_mailbox_limits', false)){
|
||||
return intval(static::getAttributeDefaultValue('mailbox_limit', 0));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get max user redirects default via database default value
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function getMaxUserRedirectsDefault()
|
||||
{
|
||||
if(Config::get('options.enable_user_redirects', false)){
|
||||
return intval(static::getAttributeDefaultValue('max_user_redirects', 0));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getRole()
|
||||
{
|
||||
return $this->getAttribute('role');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $email
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function getRoleByEmail($email)
|
||||
{
|
||||
if(in_array($email, Config::get('admins', array()))){
|
||||
return static::ROLE_ADMIN;
|
||||
}
|
||||
|
||||
return static::ROLE_USER;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is user limited by domain limits?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isDomainLimited()
|
||||
{
|
||||
$adminDomainLimits = Config::get('admin_domain_limits', array());
|
||||
|
||||
return Config::get('options.enable_admin_domain_limits', false)
|
||||
&& is_array($adminDomainLimits) && isset($adminDomainLimits[$this->getEmail()]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get domain limits, returns an empty array if user has no limits or ADMIN_DOMAIN_LIMITS_ENABLED is disabled
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDomainLimits()
|
||||
{
|
||||
if($this->isDomainLimited()){
|
||||
$adminDomainLimits = Config::get('admin_domain_limits', array());
|
||||
|
||||
if(!is_array($adminDomainLimits[$this->getEmail()])){
|
||||
throw new InvalidArgumentException('Config value of admin domain limits for email "'.$this->getEmail().'" needs to be of type array.');
|
||||
}
|
||||
|
||||
return $adminDomainLimits[$this->getEmail()];
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isAllowedToCreateUserRedirects()
|
||||
{
|
||||
return $this->getMaxUserRedirects() >= 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function canCreateUserRedirects()
|
||||
{
|
||||
if(!$this->isAllowedToCreateUserRedirects()
|
||||
|| (
|
||||
$this->getMaxUserRedirects() > 0
|
||||
&& $this->getSelfCreatedRedirects()->count() >= $this->getMaxUserRedirects()
|
||||
)
|
||||
){
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return AbstractRedirect
|
||||
*/
|
||||
public function getConflictingRedirect()
|
||||
{
|
||||
if(is_null($this->conflictingRedirect)){
|
||||
$this->conflictingRedirect = AbstractRedirect::findWhereFirst(
|
||||
array(AbstractRedirect::attr('source'), $this->getEmail())
|
||||
);
|
||||
}
|
||||
|
||||
return $this->conflictingRedirect;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return ModelCollection|AbstractRedirect[]
|
||||
*/
|
||||
public function getRedirects()
|
||||
{
|
||||
if(is_null($this->redirects)){
|
||||
$this->redirects = AbstractRedirect::findMultiWhere(
|
||||
array(AbstractRedirect::attr('destination'), 'LIKE', '%'.$this->getEmail().'%')
|
||||
);
|
||||
}
|
||||
|
||||
return $this->redirects;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return ModelCollection|AbstractRedirect[]
|
||||
*/
|
||||
public function getAnonymizedRedirects()
|
||||
{
|
||||
$redirects = $this->getRedirects();
|
||||
|
||||
foreach($redirects as $redirect){
|
||||
$emails = $redirect->getDestination();
|
||||
|
||||
if(is_array($emails) && count($emails) > 1){
|
||||
$redirect->setDestination(array($this->getEmail(), '…'));
|
||||
}
|
||||
}
|
||||
|
||||
return $redirects;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return ModelCollection|AbstractRedirect[]
|
||||
*/
|
||||
public function getSelfCreatedRedirects()
|
||||
{
|
||||
$redirects = $this->getRedirects();
|
||||
|
||||
return $redirects->searchAll(
|
||||
function($redirect) {
|
||||
/** @var AbstractRedirect $redirect */
|
||||
return $redirect->isCreatedByUser();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Change this users password, throws Exception if password is invalid.
|
||||
*
|
||||
* @param string $password
|
||||
* @param string $passwordRepeated
|
||||
*
|
||||
* @throws AuthException
|
||||
*/
|
||||
public function changePassword($password, $passwordRepeated)
|
||||
{
|
||||
Auth::validateNewPassword($password, $passwordRepeated);
|
||||
|
||||
$passwordHash = Auth::generatePasswordHash($password);
|
||||
|
||||
$this->setPasswordHash($passwordHash);
|
||||
$this->save();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static function findAll($orderBy = null)
|
||||
{
|
||||
if(is_null($orderBy)){
|
||||
$orderBy = array(static::attr('domain'), static::attr('username'));
|
||||
}
|
||||
|
||||
return parent::findAll($orderBy);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $email
|
||||
*
|
||||
* @return User|null
|
||||
*/
|
||||
public static function findByEmail($email)
|
||||
{
|
||||
$emailInParts = explode("@", $email);
|
||||
if(count($emailInParts) !== 2){
|
||||
return null;
|
||||
}
|
||||
$username = $emailInParts[0];
|
||||
$domain = $emailInParts[1];
|
||||
|
||||
return static::findWhereFirst(
|
||||
array(
|
||||
array(static::attr('username'), $username),
|
||||
array(static::attr('domain'), $domain)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
<h1>This page does not exist.</h1>
|
||||
|
||||
<p>
|
||||
Sorry, the page you requested could not be found.
|
||||
</p>
|
|
@ -1,29 +1,33 @@
|
|||
<?php
|
||||
<?php
|
||||
|
||||
if(Auth::getUser()->isDomainLimited()){
|
||||
Router::displayError(403);
|
||||
}
|
||||
|
||||
if(isset($_POST['domain'])){
|
||||
$domain = $db->escape_string($_POST['domain']);
|
||||
$domain = strtolower($domain);
|
||||
|
||||
if($domain !== ""){
|
||||
// Check if domain exists in database
|
||||
$domain_exists = $db->query("SELECT `".DBC_DOMAINS_DOMAIN."` FROM `".DBT_DOMAINS."` WHERE `".DBC_DOMAINS_DOMAIN."` = '$domain';");
|
||||
if($domain_exists->num_rows == 0){
|
||||
$sql = "INSERT INTO `".DBT_DOMAINS."` (`".DBC_DOMAINS_DOMAIN."`) VALUES ('$domain');";
|
||||
|
||||
if(!$result = $db->query($sql)){
|
||||
dbError($db->error);
|
||||
}
|
||||
else{
|
||||
// Created domain successfull, redirect to overview
|
||||
redirect("admin/listdomains/?created=1");
|
||||
}
|
||||
$inputDomain = $_POST['domain'];
|
||||
|
||||
if(!empty($inputDomain)){
|
||||
|
||||
$existingDomain = Domain::findWhere(array(Domain::attr('domain'), $inputDomain));
|
||||
|
||||
if(!is_null($existingDomain)){
|
||||
|
||||
Domain::createAndSave(
|
||||
array(
|
||||
Domain::attr('domain') => $inputDomain,
|
||||
)
|
||||
);
|
||||
|
||||
// Created domain successfull, redirect to overview
|
||||
Router::redirect("admin/listdomains/?created=1");
|
||||
}
|
||||
else{
|
||||
add_message("fail", "Domain already exists in database.");
|
||||
Message::getInstance()->fail("Domain already exists in database.");
|
||||
}
|
||||
}
|
||||
else{
|
||||
add_message("fail", "Empty domain could not be created.");
|
||||
Message::getInstance()->fail("Empty domain couldn't be created.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,10 +35,10 @@ if(isset($_POST['domain'])){
|
|||
|
||||
<h1>Create new domain</h1>
|
||||
|
||||
<?php output_messages(); ?>
|
||||
<?php echo Message::getInstance()->render(); ?>
|
||||
|
||||
<div class="buttons">
|
||||
<a class="button" href="<?php echo url('admin/listdomains'); ?>">❬ Back to domain list</a>
|
||||
<a class="button" href="<?php echo Router::url('admin/listdomains'); ?>">❬ Back to domain list</a>
|
||||
</div>
|
||||
|
||||
<form class="form" action="" method="post" autocomplete="off">
|
||||
|
|
|
@ -1,85 +1,85 @@
|
|||
<?php
|
||||
|
||||
if(Auth::getUser()->isDomainLimited()){
|
||||
Router::displayError(403);
|
||||
}
|
||||
|
||||
if(!isset($_GET['id'])){
|
||||
// Domain id not set, redirect to overview
|
||||
redirect("admin/listdomains");
|
||||
Router::redirect("admin/listdomains");
|
||||
}
|
||||
|
||||
$id = $db->escape_string($_GET['id']);
|
||||
$id = $_GET['id'];
|
||||
|
||||
//Load user data from DB
|
||||
$sql = "SELECT `".DBC_DOMAINS_DOMAIN."` FROM `".DBT_DOMAINS."` WHERE `".DBC_DOMAINS_ID."` = '$id' LIMIT 1;";
|
||||
/** @var Domain $domain */
|
||||
$domain = Domain::find($id);
|
||||
|
||||
if(!$result = $db->query($sql)){
|
||||
dbError($db->error);
|
||||
if(is_null($domain)){
|
||||
// Domain doesn't exist, redirect to overview
|
||||
Router::redirect("admin/listdomains");
|
||||
}
|
||||
|
||||
if($result->num_rows !== 1){
|
||||
// Domain does not exist, redirect to overview
|
||||
redirect("admin/listdomains");
|
||||
if(!$domain->isInLimitedDomains()){
|
||||
Router::redirect("admin/listdomains/?missing-permission=1");
|
||||
}
|
||||
|
||||
$row = $result->fetch_assoc();
|
||||
$domain = $row[DBC_DOMAINS_DOMAIN];
|
||||
|
||||
// Delete domain
|
||||
if(isset($_POST['confirm'])){
|
||||
$confirm = $_POST['confirm'];
|
||||
|
||||
|
||||
if($confirm === "yes"){
|
||||
|
||||
$admin_domains = array();
|
||||
foreach($admins as $admin) {
|
||||
// Check if admin domain is affected
|
||||
$isAdminDomain = false;
|
||||
foreach(Config::get('admins', array()) as $admin){
|
||||
$parts = explode("@", $admin);
|
||||
$admin_domains[] = $parts[1];
|
||||
if(count($parts) === 2 && $parts[2] === $domain->getDomain()){
|
||||
$isAdminDomain = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if admin domain is affected
|
||||
if(!in_array($domain, $admin_domains)){
|
||||
$sql = "DELETE FROM `".DBT_DOMAINS."` WHERE `".DBC_DOMAINS_ID."` = '$id'";
|
||||
if(!$isAdminDomain){
|
||||
|
||||
if(!$result = $db->query($sql)){
|
||||
dbError($db->error);
|
||||
}
|
||||
else{
|
||||
$sql = "DELETE FROM `".DBT_USERS."` WHERE `".DBC_USERS_DOMAIN."` = '$domain'";
|
||||
$users = User::findWhere(array(User::attr('domain'), $domain->getDomain()));
|
||||
|
||||
if(!$result = $db->query($sql)){
|
||||
dbError($db->error);
|
||||
}
|
||||
else{
|
||||
// Delete domain successfull, redirect to overview
|
||||
redirect("admin/listdomains/?deleted=1");
|
||||
}
|
||||
/** @var User $user */
|
||||
foreach($users as $user){
|
||||
$user->delete();
|
||||
}
|
||||
|
||||
$domain->delete();
|
||||
|
||||
// Delete domain successfull, redirect to overview
|
||||
Router::redirect("admin/listdomains/?deleted=1");
|
||||
}
|
||||
else{
|
||||
// Cannot delete domain with admin emails, redirect to overview
|
||||
redirect("admin/listdomains/?adm_del=1");
|
||||
Router::redirect("admin/listdomains/?adm_del=1");
|
||||
}
|
||||
}
|
||||
|
||||
else{
|
||||
// Choose to not delete domain, redirect to overview
|
||||
redirect("admin/listdomains");
|
||||
Router::redirect("admin/listdomains");
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<h1>Delete domain "<?php echo $domain ?>"?</h1>
|
||||
<h1>Delete domain "<?php echo $domain->getDomain() ?>"?</h1>
|
||||
|
||||
<div class="buttons">
|
||||
<a class="button" href="<?php echo url('admin/listdomains'); ?>">❬ Back to domain list</a>
|
||||
<a class="button" href="<?php echo Router::url('admin/listdomains'); ?>">❬ Back to domain list</a>
|
||||
</div>
|
||||
|
||||
<form class="form" action="" method="post">
|
||||
<form class="form" action="" method="post" autocomplete="off">
|
||||
<div class="input-group">
|
||||
<label>All mailboxes matching the domain will be deleted from the user database!</label>
|
||||
<div class="input-info">Mailbox directories in the filesystem won't be affected.</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label>Do you realy want to delete this domain?</label>
|
||||
<label for="confirm">Do you realy want to delete this domain?</label>
|
||||
<div class="input">
|
||||
<select name="confirm" autofocus required>
|
||||
<option value="no">No!</option>
|
||||
|
|
|
@ -2,98 +2,74 @@
|
|||
|
||||
if(!isset($_GET['id'])){
|
||||
// Redirect id not set, redirect to overview
|
||||
redirect("admin/listredirects");
|
||||
Router::redirect("admin/listredirects");
|
||||
}
|
||||
|
||||
$id = $db->escape_string($_GET['id']);
|
||||
$id = $_GET['id'];
|
||||
|
||||
if(defined('DBC_ALIASES_MULTI_SOURCE')){
|
||||
$sql = "SELECT r.* FROM (
|
||||
SELECT
|
||||
group_concat(g.`".DBC_ALIASES_ID."` ORDER BY g.`".DBC_ALIASES_ID."` SEPARATOR ',') AS `".DBC_ALIASES_ID."`,
|
||||
group_concat(g.`".DBC_ALIASES_SOURCE."` SEPARATOR ',') AS `".DBC_ALIASES_SOURCE."`,
|
||||
g.`".DBC_ALIASES_DESTINATION."`,
|
||||
g.`".DBC_ALIASES_MULTI_SOURCE."`
|
||||
FROM `".DBT_ALIASES."` AS g
|
||||
WHERE g.`".DBC_ALIASES_MULTI_SOURCE."` IS NOT NULL
|
||||
GROUP BY g.`".DBC_ALIASES_MULTI_SOURCE."`
|
||||
UNION
|
||||
SELECT
|
||||
s.`".DBC_ALIASES_ID."`,
|
||||
s.`".DBC_ALIASES_SOURCE."`,
|
||||
s.`".DBC_ALIASES_DESTINATION."`,
|
||||
s.`".DBC_ALIASES_MULTI_SOURCE."`
|
||||
FROM `".DBT_ALIASES."` AS s
|
||||
WHERE s.`".DBC_ALIASES_MULTI_SOURCE."` IS NULL
|
||||
) AS r
|
||||
WHERE `".DBC_ALIASES_ID."` = '$id' LIMIT 1;";
|
||||
}
|
||||
else{
|
||||
$sql = "SELECT `".DBC_ALIASES_ID."`, `".DBC_ALIASES_SOURCE."`, `".DBC_ALIASES_DESTINATION."` FROM `".DBT_ALIASES."` WHERE `".DBC_ALIASES_ID."` = '$id' LIMIT 1;";
|
||||
/** @var AbstractRedirect $redirect */
|
||||
$redirect = AbstractRedirect::findMulti($id);
|
||||
|
||||
if(is_null($redirect)){
|
||||
// Redirect doesn't exist, redirect to overview
|
||||
Router::redirect("admin/listredirects");
|
||||
}
|
||||
|
||||
if(!$result = $db->query($sql)){
|
||||
dbError($db->error);
|
||||
if(!$redirect->isInLimitedDomains()){
|
||||
Router::redirect("admin/listredirects/?missing-permission=1");
|
||||
}
|
||||
|
||||
if($result->num_rows !== 1){
|
||||
// Redirect does not exist, redirect to overview
|
||||
redirect("admin/listredirects");
|
||||
}
|
||||
|
||||
$redirect = $result->fetch_assoc();
|
||||
|
||||
if(isset($_POST['confirm'])){
|
||||
$confirm = $_POST['confirm'];
|
||||
|
||||
if($confirm === "yes"){
|
||||
|
||||
$key = DBC_ALIASES_ID;
|
||||
if(defined('DBC_ALIASES_MULTI_SOURCE') && !empty($redirect[DBC_ALIASES_MULTI_SOURCE])){
|
||||
$key = DBC_ALIASES_MULTI_SOURCE;
|
||||
}
|
||||
$value = $redirect[$key];
|
||||
if ($redirect instanceof AbstractMultiRedirect){
|
||||
|
||||
$sql = "DELETE FROM `".DBT_ALIASES."` WHERE `$key` = '$value'";
|
||||
// Get single source rows of multi source redirect/alias instead
|
||||
$hash = $redirect->getMultiHash();
|
||||
$singleRedirects = AbstractRedirect::findWhere(array(AbstractRedirect::attr('multi_hash'), $hash));
|
||||
|
||||
if(!$result = $db->query($sql)){
|
||||
dbError($db->error);
|
||||
/** @var AbstractRedirect $redirectToDelete */
|
||||
foreach($singleRedirects as $redirectToDelete){
|
||||
$redirectToDelete->delete();
|
||||
}
|
||||
}
|
||||
else{
|
||||
// Delete redirect successfull, redirect to overview
|
||||
redirect("admin/listredirects/?deleted=1");
|
||||
else {
|
||||
$redirect->delete();
|
||||
}
|
||||
|
||||
// Delete redirect successfull, redirect to overview
|
||||
Router::redirect("admin/listredirects/?deleted=1");
|
||||
}
|
||||
else{
|
||||
// Choose to not delete redirect, redirect to overview
|
||||
redirect("admin/listredirects");
|
||||
Router::redirect("admin/listredirects");
|
||||
}
|
||||
}
|
||||
|
||||
else{
|
||||
$source = $redirect[DBC_ALIASES_SOURCE];
|
||||
$destination = $redirect[DBC_ALIASES_DESTINATION];
|
||||
?>
|
||||
|
||||
<h1>Delete redirection?</h1>
|
||||
|
||||
<div class="buttons">
|
||||
<a class="button" href="<?php echo url('admin/listredirects'); ?>">❬ Back to redirect list</a>
|
||||
<a class="button" href="<?php echo Router::url('admin/listredirects'); ?>">❬ Back to redirect list</a>
|
||||
</div>
|
||||
|
||||
<form class="form" action="" method="post">
|
||||
<form class="form" action="" method="post" autocomplete="off">
|
||||
<div class="input-group">
|
||||
<label>Source</label>
|
||||
<div class="input-info"><?php echo strip_tags(formatEmails($source, FRONTEND_EMAIL_SEPARATOR_TEXT)); ?></div>
|
||||
<div class="input-info"><?php echo formatEmailsText($redirect->getSource()); ?></div>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label>Destination</label>
|
||||
<div class="input-info"><?php echo strip_tags(formatEmails($destination, FRONTEND_EMAIL_SEPARATOR_TEXT)); ?></div>
|
||||
<div class="input-info"><?php echo formatEmailsText($redirect->getDestination()); ?></div>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label>Do you realy want to delete this redirect?</label>
|
||||
<label for="confirm">Do you realy want to delete this redirect?</label>
|
||||
<div class="input">
|
||||
<select name="confirm" autofocus required>
|
||||
<option value="no">No!</option>
|
||||
|
|
|
@ -1,70 +1,146 @@
|
|||
<?php
|
||||
<?php
|
||||
|
||||
$id = $db->escape_string($_GET['id']);
|
||||
|
||||
//Load user data from DB
|
||||
$sql = "SELECT `".DBC_USERS_USERNAME."`, `".DBC_USERS_DOMAIN."` FROM `".DBT_USERS."` WHERE `".DBC_USERS_ID."` = '$id' LIMIT 1;";
|
||||
|
||||
if(!$result = $db->query($sql)){
|
||||
dbError($db->error);
|
||||
if(!isset($_GET['id'])){
|
||||
// Redirect id not set, redirect to overview
|
||||
Router::redirect('admin/listredirects');
|
||||
}
|
||||
|
||||
$row = $result->fetch_assoc();
|
||||
$id = $_GET['id'];
|
||||
|
||||
$username = $row[DBC_USERS_USERNAME];
|
||||
$domain = $row[DBC_USERS_DOMAIN];
|
||||
/** @var User $user */
|
||||
$user = User::find($id);
|
||||
|
||||
$mailAddress = $username."@".$domain;
|
||||
if(is_null($user)){
|
||||
// User doesn't exist, redirect to overview
|
||||
Router::redirect('admin/listusers');
|
||||
}
|
||||
|
||||
if(!$user->isInLimitedDomains()){
|
||||
Router::redirect('admin/listusers/?missing-permission=1');
|
||||
}
|
||||
|
||||
// Delete user
|
||||
if(isset($_POST['confirm'])){
|
||||
$confirm = $_POST['confirm'];
|
||||
|
||||
if($confirm === "yes"){
|
||||
|
||||
if($confirm === 'yes'){
|
||||
// Check if admin is affected
|
||||
if (!in_array($mailAddress, $admins)) {
|
||||
$sql = "DELETE FROM `".DBT_USERS."` WHERE `".DBC_USERS_ID."` = '$id'";
|
||||
|
||||
if(!$result = $db->query($sql)){
|
||||
dbError($db->error);
|
||||
}
|
||||
else{
|
||||
// Delete user successfull, redirect to overview
|
||||
redirect("admin/listusers/?deleted=1");
|
||||
if(!in_array($user->getEmail(), Config::get('admins', array()))){
|
||||
|
||||
// Delete redirects of this user
|
||||
if(isset($_POST['delete_redirects']) && $_POST['delete_redirects'] === 'yes'
|
||||
&& isset($_POST['selected_redirects']) && is_array($_POST['selected_redirects'])
|
||||
){
|
||||
$redirectMultiIds = $_POST['selected_redirects'];
|
||||
|
||||
foreach($redirectMultiIds as $redirectMultiId){
|
||||
$redirectIds = explode(',', $redirectMultiId);
|
||||
|
||||
foreach($redirectIds as $redirectId){
|
||||
|
||||
// Note: No Multi* selected, so there is only Alias & Redirect
|
||||
$redirects = AbstractRedirect::findWhere(
|
||||
array(
|
||||
array(AbstractRedirect::attr('id'), $redirectId),
|
||||
array(AbstractRedirect::attr('destination'), 'LIKE', '%'.$user->getEmail().'%')
|
||||
)
|
||||
);
|
||||
|
||||
/** @var AbstractRedirect $redirect */
|
||||
foreach($redirects as $redirect){
|
||||
if($redirect instanceof Alias) {
|
||||
$redirect->delete();
|
||||
}
|
||||
elseif($redirect instanceof Redirect) {
|
||||
$redirect->setDestination(
|
||||
array_diff(
|
||||
$redirect->getDestination(),
|
||||
array($user->getEmail())
|
||||
)
|
||||
);
|
||||
$redirect->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$user->delete();
|
||||
|
||||
// Delete user successful, redirect to overview
|
||||
Router::redirect('admin/listusers/?deleted=1');
|
||||
}
|
||||
else{
|
||||
// Admin tried to delete himself, redirect to overview
|
||||
redirect("admin/listusers/?adm_del=1");
|
||||
Router::redirect('admin/listusers/?adm_del=1');
|
||||
}
|
||||
}
|
||||
else{
|
||||
// Choose to not delete user, redirect to overview
|
||||
redirect("admin/listusers");
|
||||
Router::redirect('admin/listusers');
|
||||
}
|
||||
}
|
||||
|
||||
$redirects = $user->getAnonymizedRedirects();
|
||||
|
||||
?>
|
||||
|
||||
<h1>Delete user "<?php echo strip_tags($mailAddress) ?>"?</h1>
|
||||
<h1>Delete user "<?php echo $user->getEmail() ?>"?</h1>
|
||||
|
||||
<div class="buttons">
|
||||
<a class="button" href="<?php echo url('admin/listusers'); ?>">❬ Back to user list</a>
|
||||
<a class="button" href="<?php echo Router::url('admin/listusers'); ?>">❬ Back to user list</a>
|
||||
</div>
|
||||
|
||||
<form class="form" action="" method="post">
|
||||
<form class="form" action="" method="post" autocomplete="off">
|
||||
<div class="input-group">
|
||||
<label>The user's mailbox will be deleted from the database only!</label>
|
||||
<div class="input-info">The mailbox in the filesystem won't be affected.</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label>Do you realy want to delete this user?</label>
|
||||
<label>Redirects to this user:</label>
|
||||
<?php if($redirects->count() > 0): ?>
|
||||
<div class="input-info">Do you also want to delete the following redirects to this user?</div>
|
||||
<table class="table table-compact">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Source</th>
|
||||
<th>Destination</th>
|
||||
<tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach($redirects as $redirect): /** @var AbstractRedirect $redirect */ ?>
|
||||
<tr>
|
||||
<td><input type="checkbox" name="selected_redirects[]" value="<?php echo $redirect->getId(); ?>" checked></td>
|
||||
<td><?php echo formatEmailsText($redirect->getSource()); ?></td>
|
||||
<td><?php echo formatEmailsText($redirect->getDestination()); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="input">
|
||||
<select name="confirm" autofocus required>
|
||||
<option value="no">No!</option>
|
||||
<option value="yes">Yes!</option>
|
||||
</select>
|
||||
<label>
|
||||
<select name="delete_redirects" required>
|
||||
<option value="no">Don't delete the redirects.</option>
|
||||
<option value="yes">Yes, delete the selected redirects!</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="input-info">There are currently no redirects to this user.</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="confirm">Do you realy want to delete this user?</label>
|
||||
<div class="input">
|
||||
<label>
|
||||
<select name="confirm" autofocus required>
|
||||
<option value="no">No!</option>
|
||||
<option value="yes">Yes!</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -4,213 +4,210 @@ $id = null;
|
|||
$redirect = null;
|
||||
|
||||
if(isset($_GET['id'])){
|
||||
$id = $db->escape_string($_GET['id']);
|
||||
$id = $_GET['id'];
|
||||
|
||||
if(defined('DBC_ALIASES_MULTI_SOURCE')){
|
||||
$sql = "SELECT r.* FROM (
|
||||
SELECT
|
||||
group_concat(g.`".DBC_ALIASES_ID."` ORDER BY g.`".DBC_ALIASES_ID."` SEPARATOR ',') AS `".DBC_ALIASES_ID."`,
|
||||
group_concat(g.`".DBC_ALIASES_SOURCE."` SEPARATOR ',') AS `".DBC_ALIASES_SOURCE."`,
|
||||
g.`".DBC_ALIASES_DESTINATION."`,
|
||||
g.`".DBC_ALIASES_MULTI_SOURCE."`
|
||||
FROM `".DBT_ALIASES."` AS g
|
||||
WHERE g.`".DBC_ALIASES_MULTI_SOURCE."` IS NOT NULL
|
||||
GROUP BY g.`".DBC_ALIASES_MULTI_SOURCE."`
|
||||
UNION
|
||||
SELECT
|
||||
s.`".DBC_ALIASES_ID."`,
|
||||
s.`".DBC_ALIASES_SOURCE."`,
|
||||
s.`".DBC_ALIASES_DESTINATION."`,
|
||||
s.`".DBC_ALIASES_MULTI_SOURCE."`
|
||||
FROM `".DBT_ALIASES."` AS s
|
||||
WHERE s.`".DBC_ALIASES_MULTI_SOURCE."` IS NULL
|
||||
) AS r
|
||||
WHERE `".DBC_ALIASES_ID."` = '$id' LIMIT 1;";
|
||||
}
|
||||
else{
|
||||
$sql = "SELECT `".DBC_ALIASES_ID."`, `".DBC_ALIASES_SOURCE."`, `".DBC_ALIASES_DESTINATION."` FROM `".DBT_ALIASES."` WHERE `".DBC_ALIASES_ID."` = '$id' LIMIT 1;";
|
||||
/** @var AbstractRedirect $redirect */
|
||||
$redirect = AbstractRedirect::findMulti($id);
|
||||
|
||||
if(is_null($redirect)){
|
||||
// Redirect doesn't exist, redirect to overview
|
||||
Router::redirect("admin/listredirects");
|
||||
}
|
||||
|
||||
if(!$result = $db->query($sql)){
|
||||
dbError($db->error);
|
||||
if(!$redirect->isInLimitedDomains()){
|
||||
Router::redirect("admin/listredirects/?missing-permission=1");
|
||||
}
|
||||
|
||||
if($result->num_rows !== 1){
|
||||
// Redirect does not exist, redirect to overview
|
||||
redirect("admin/listredirects");
|
||||
}
|
||||
|
||||
$redirect = $result->fetch_assoc();
|
||||
|
||||
$sources = stringToEmails($redirect[DBC_ALIASES_SOURCE]);
|
||||
$destinations = stringToEmails($redirect[DBC_ALIASES_DESTINATION]);
|
||||
}
|
||||
|
||||
if(isset($_POST['savemode'])){
|
||||
$savemode = $_POST['savemode'];
|
||||
|
||||
$sources = stringToEmails($_POST['source']);
|
||||
$destinations = stringToEmails($_POST['destination']);
|
||||
$inputSources = stringToEmails($_POST['source']);
|
||||
$inputDestinations = stringToEmails($_POST['destination']);
|
||||
|
||||
// validate emails
|
||||
$emailErrors = array();
|
||||
|
||||
// basic email validation is not working 100% correct though
|
||||
foreach(array_merge($sources, $destinations) as $email){
|
||||
if(!filter_var($email, FILTER_VALIDATE_EMAIL)){
|
||||
$emailErrors[$email] = "Address \"$email\" is not a valid email address.";
|
||||
// basic email validation isn't working 100% correct though
|
||||
foreach(array_merge($inputSources, $inputDestinations) as $email){
|
||||
if(strpos($email, '@') === false){
|
||||
$emailErrors[$email] = "Address \"{$email}\" isn't a valid email address.";
|
||||
}
|
||||
}
|
||||
|
||||
if(defined('VALIDATE_ALIASES_SOURCE_ENABLED')){
|
||||
$sql = "SELECT GROUP_CONCAT(`".DBC_DOMAINS_DOMAIN."` SEPARATOR ',') as `".DBC_DOMAINS_DOMAIN."` FROM `".DBT_DOMAINS."`";
|
||||
if(!$resultDomains = $db->query($sql)){
|
||||
dbError($db->error);
|
||||
}
|
||||
$domainRow = $resultDomains->fetch_assoc();
|
||||
$domains = explode(',', $domainRow[DBC_DOMAINS_DOMAIN]);
|
||||
// validate source emails are on domains
|
||||
if(Config::get('options.enable_validate_aliases_source_domain', true)){
|
||||
$domains = Domain::getByLimitedDomains();
|
||||
|
||||
// validate source emails are on domains
|
||||
foreach($sources as $email){
|
||||
foreach($inputSources as $email){
|
||||
if(isset($emailErrors[$email])){
|
||||
continue;
|
||||
}
|
||||
$splited = explode('@', $email);
|
||||
if(count($splited) !== 2 || !in_array($splited[1], $domains)){
|
||||
$emailErrors[$email] = "Domain of source address \"$email\" not in domains.";
|
||||
|
||||
$emailParts = explode('@', $email);
|
||||
$searchResult = $domains->search(
|
||||
function($domain) use ($emailParts){
|
||||
/** @var Domain $domain */
|
||||
return $domain->getDomain() === $emailParts[1];
|
||||
}
|
||||
);
|
||||
|
||||
if(is_null($searchResult)){
|
||||
$emailErrors[$email] = "Domain of source address \"{$email}\" not in your domains.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// validate no redirect loops
|
||||
foreach(array_intersect($inputSources, $inputDestinations) as $email){
|
||||
$emailErrors[$email] = "Address \"{$email}\" cannot be in source and destination in same redirect.";
|
||||
}
|
||||
|
||||
|
||||
if(count($emailErrors) > 0){
|
||||
add_message("fail", implode("<br>", $emailErrors));
|
||||
Message::getInstance()->fail(implode("<br>", $emailErrors));
|
||||
}
|
||||
else{
|
||||
if(count($emailErrors) === 0 && $savemode === "edit" && !is_null($redirect)){
|
||||
|
||||
if(count($sources) > 0 && count($destinations) > 0){
|
||||
$destination = $db->escape_string(emailsToString($destinations));
|
||||
$source = $db->escape_string(emailsToString($sources));
|
||||
if(count($inputSources) > 0 && count($inputDestinations) > 0){
|
||||
|
||||
$key = DBC_ALIASES_ID;
|
||||
if(defined('DBC_ALIASES_MULTI_SOURCE') && !empty($redirect[DBC_ALIASES_MULTI_SOURCE])){
|
||||
$key = DBC_ALIASES_MULTI_SOURCE;
|
||||
if(Config::get('options.enable_multi_source_redirects', false) && $redirect instanceof AbstractMultiRedirect){
|
||||
$existingRedirectsToEdit = AbstractRedirect::findWhere(
|
||||
array(AbstractRedirect::attr('multi_hash'), $redirect->getMultiHash())
|
||||
);
|
||||
}
|
||||
$value = $redirect[$key];
|
||||
|
||||
$sql = "SELECT `".DBC_ALIASES_ID."`, `".DBC_ALIASES_SOURCE."` FROM `".DBT_ALIASES."` WHERE `$key` = '$value'";
|
||||
if(!$resultExisting = $db->query($sql)){
|
||||
dbError($db->error);
|
||||
else{
|
||||
$existingRedirectsToEdit = AbstractRedirect::findWhere(
|
||||
array(AbstractRedirect::attr('id'), $redirect->getId())
|
||||
);
|
||||
}
|
||||
|
||||
$sourceIdMap = array();
|
||||
while($existingRedirect = $resultExisting->fetch_assoc()){
|
||||
$sourceIdMap[$existingRedirect[DBC_ALIASES_SOURCE]] = $existingRedirect[DBC_ALIASES_ID];
|
||||
}
|
||||
|
||||
// multi source handling
|
||||
$hash = (count($sources) === 1) ? "NULL" : "'".md5($source)."'";
|
||||
|
||||
foreach($sources as $sourceAddress){
|
||||
$sourceAddress = $db->escape_string(formatEmail($sourceAddress));
|
||||
|
||||
if(isset($sourceIdMap[$sourceAddress])){
|
||||
// edit existing source
|
||||
$id = $sourceIdMap[$sourceAddress];
|
||||
|
||||
$additionalSql = defined('DBC_ALIASES_MULTI_SOURCE') ? ", `".DBC_ALIASES_MULTI_SOURCE."` = $hash " : "";
|
||||
$sql = "UPDATE `".DBT_ALIASES."` SET `".DBC_ALIASES_SOURCE."` = '$sourceAddress', `".DBC_ALIASES_DESTINATION."` = '$destination' $additionalSql WHERE `".DBC_ALIASES_ID."` = '$id';";
|
||||
|
||||
if(!$result = $db->query($sql)){
|
||||
dbError($db->error);
|
||||
}
|
||||
|
||||
unset($sourceIdMap[$sourceAddress]); // mark updated
|
||||
$emailsToCheck = $inputSources;
|
||||
foreach($existingRedirectsToEdit as $r){
|
||||
$key = array_search($r->getSource(), $emailsToCheck);
|
||||
if($key !== false){
|
||||
unset($emailsToCheck[$key]);
|
||||
}
|
||||
else{
|
||||
// add new source
|
||||
$additionalSql = defined('DBC_ALIASES_MULTI_SOURCE') ? ", `".DBC_ALIASES_MULTI_SOURCE."`" : "";
|
||||
$additionalSqlValue = defined('DBC_ALIASES_MULTI_SOURCE') ? ", $hash" : "";
|
||||
$sql = "INSERT INTO `".DBT_ALIASES."` (`".DBC_ALIASES_SOURCE."`, `".DBC_ALIASES_DESTINATION."` $additionalSql) VALUES ('$sourceAddress', '$destination' $additionalSqlValue);";
|
||||
}
|
||||
|
||||
if(!$result = $db->query($sql)){
|
||||
dbError($db->error);
|
||||
if(count($emailsToCheck) > 0){
|
||||
$existingRedirectsOther = AbstractRedirect::findWhere(
|
||||
array(
|
||||
array(AbstractRedirect::attr('source'), 'IN', $emailsToCheck)
|
||||
)
|
||||
);
|
||||
}
|
||||
else{
|
||||
$existingRedirectsOther = null;
|
||||
}
|
||||
|
||||
if(!is_null($existingRedirectsOther) && $existingRedirectsOther->count() > 0){
|
||||
$errorMessages = array();
|
||||
/** @var AbstractRedirect $existingRedirect */
|
||||
foreach($existingRedirectsOther as $id => $existingRedirect){
|
||||
if(!$existingRedirectsToEdit->has($id)){
|
||||
$errorMessages[] = "Source address \"{$existingRedirect->getSource()}\" is already redirected to some destination.";
|
||||
}
|
||||
}
|
||||
|
||||
Message::getInstance()->fail(implode("<br>", $errorMessages));
|
||||
}
|
||||
else{
|
||||
// multi source handling
|
||||
$hash = (count($inputSources) === 1) ? null : md5(emailsToString($inputSources));
|
||||
|
||||
// delete none updated redirect
|
||||
foreach($sourceIdMap as $source => $id){
|
||||
$sql = "DELETE FROM `".DBT_ALIASES."` WHERE `".DBC_ALIASES_ID."` = '$id';";
|
||||
foreach($inputSources as $sourceAddress){
|
||||
$sourceAddress = formatEmail($sourceAddress);
|
||||
|
||||
if(!$result = $db->query($sql)){
|
||||
dbError($db->error);
|
||||
/** @var AbstractRedirect $thisRedirect */
|
||||
$thisRedirect = $existingRedirectsToEdit->search(
|
||||
function($model) use ($sourceAddress){
|
||||
/** @var AbstractRedirect $model */
|
||||
return $model->getSource() === $sourceAddress;
|
||||
}
|
||||
);
|
||||
|
||||
if(!is_null($thisRedirect)){
|
||||
// edit existing source
|
||||
|
||||
$thisRedirect->setSource($sourceAddress);
|
||||
$thisRedirect->setDestination($inputDestinations);
|
||||
$thisRedirect->setMultiHash($hash);
|
||||
// Don't set 'isCreatedByUser' here, it will overwrite redirects created by user
|
||||
$thisRedirect->save();
|
||||
|
||||
$existingRedirectsToEdit->delete($thisRedirect->getId()); // mark updated
|
||||
}
|
||||
else{
|
||||
$data = array(
|
||||
AbstractRedirect::attr('source') => $sourceAddress,
|
||||
AbstractRedirect::attr('destination') => emailsToString($inputDestinations),
|
||||
AbstractRedirect::attr('multi_hash') => $hash,
|
||||
);
|
||||
|
||||
if(Config::get('options.enable_user_redirects', false)){
|
||||
$data[AbstractRedirect::attr('is_created_by_user')] = false;
|
||||
}
|
||||
|
||||
AbstractRedirect::createAndSave($data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Edit successfull, redirect to overview
|
||||
redirect("admin/listredirects/?edited=1");
|
||||
// Delete none updated redirect
|
||||
foreach($existingRedirectsToEdit as $redirect){
|
||||
$redirect->delete();
|
||||
}
|
||||
|
||||
// Edit successfull, redirect to overview
|
||||
Router::redirect("admin/listredirects/?edited=1");
|
||||
}
|
||||
}
|
||||
else{
|
||||
add_message("fail", "Redirect could not be edited. Fill out all fields.");
|
||||
Message::getInstance()->fail("Redirect couldn't be edited. Fill out all fields.");
|
||||
}
|
||||
}
|
||||
|
||||
else if(count($emailErrors) === 0 && $savemode === "create"){
|
||||
if(count($sources) > 0 && count($destinations) > 0){
|
||||
if(count($inputSources) > 0 && count($inputDestinations) > 0){
|
||||
|
||||
$values = array();
|
||||
foreach($sources as $source){
|
||||
$values[] = "'$source'";
|
||||
}
|
||||
$sql = "SELECT `".DBC_ALIASES_SOURCE."` FROM `".DBT_ALIASES."` WHERE `".DBC_ALIASES_SOURCE."` IN (".implode(',', $values).");";
|
||||
if(!$resultExisting = $db->query($sql)){
|
||||
dbError($db->error);
|
||||
}
|
||||
$existingRedirects = AbstractRedirect::findWhere(
|
||||
array(AbstractRedirect::attr('source'), 'IN', $inputSources)
|
||||
);
|
||||
|
||||
$errorExisting = array();
|
||||
while($existingRedirect = $resultExisting->fetch_assoc()){
|
||||
$email = $existingRedirect[DBC_ALIASES_SOURCE];
|
||||
$errorExisting[] = "Source address \"$email\" is already redirected to some destination.";
|
||||
}
|
||||
if($existingRedirects->count() > 0){
|
||||
$errorMessages = array();
|
||||
/** @var AbstractRedirect $existingRedirect */
|
||||
foreach($existingRedirects as $existingRedirect){
|
||||
$errorMessages[] = "Source address \"{$existingRedirect->getSource()}\" is already redirected to some destination.";
|
||||
}
|
||||
|
||||
if(count($errorExisting) > 0){
|
||||
add_message("fail", implode("<br>", $errorExisting));
|
||||
Message::getInstance()->fail(implode("<br>", $errorMessages));
|
||||
}
|
||||
else{
|
||||
$destination = $db->escape_string(emailsToString($destinations));
|
||||
$source = $db->escape_string(emailsToString($sources));
|
||||
$inputDestination = emailsToString($inputDestinations);
|
||||
$hash = (count($inputSources) === 1) ? null : md5(emailsToString($inputSources));
|
||||
|
||||
$values = array();
|
||||
if(count($sources) === 1){
|
||||
$additionalSqlValue = defined('DBC_ALIASES_MULTI_SOURCE') ? ", NULL" : "";
|
||||
$values[] = "('$source', '$destination' $additionalSqlValue)";
|
||||
}
|
||||
else{
|
||||
// multi source handling
|
||||
$hash = md5($source);
|
||||
foreach($inputSources as $inputSource){
|
||||
$data = array(
|
||||
AbstractRedirect::attr('source') => $inputSource,
|
||||
AbstractRedirect::attr('destination') => $inputDestination,
|
||||
AbstractRedirect::attr('multi_hash') => $hash,
|
||||
);
|
||||
|
||||
foreach($sources as $sourceAddress){
|
||||
$sourceAddress = $db->escape_string(formatEmail($sourceAddress));
|
||||
$additionalSqlValue = defined('DBC_ALIASES_MULTI_SOURCE') ? ", '$hash'" : "";
|
||||
$values[] = "('$sourceAddress', '$destination' $additionalSqlValue)";
|
||||
if(Config::get('options.enable_user_redirects', false)){
|
||||
$data[AbstractRedirect::attr('is_created_by_user')] = false;
|
||||
}
|
||||
|
||||
$a = AbstractRedirect::createAndSave($data);
|
||||
}
|
||||
|
||||
$additionalSql = defined('DBC_ALIASES_MULTI_SOURCE') ? ", `".DBC_ALIASES_MULTI_SOURCE."`" : "";
|
||||
$sql = "INSERT INTO `".DBT_ALIASES."` (`".DBC_ALIASES_SOURCE."`, `".DBC_ALIASES_DESTINATION."` $additionalSql) VALUES ".implode(',', $values).";";
|
||||
|
||||
if(!$result = $db->query($sql)){
|
||||
dbError($db->error);
|
||||
}
|
||||
else{
|
||||
// Redirect created, redirect to overview
|
||||
redirect("admin/listredirects/?created=1");
|
||||
}
|
||||
// Redirect created, redirect to overview
|
||||
Router::redirect("admin/listredirects/?created=1");
|
||||
}
|
||||
}
|
||||
else{
|
||||
add_message("fail", "Redirect could not be created. Fill out all fields.");
|
||||
Message::getInstance()->fail("Redirect couldn't be created. Fill out all fields.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -222,42 +219,72 @@ $mode = "create";
|
|||
if(isset($_GET['id'])){
|
||||
$mode = "edit";
|
||||
}
|
||||
|
||||
$domains = Domain::getByLimitedDomains();
|
||||
?>
|
||||
|
||||
<h1><?php echo ($mode === "create") ? 'Create' : 'Edit'; ?> Redirect</h1>
|
||||
|
||||
<div class="buttons">
|
||||
<a class="button" href="<?php echo url('admin/listredirects'); ?>">❬ Back to redirects list</a>
|
||||
</div>
|
||||
|
||||
<?php output_messages(); ?>
|
||||
|
||||
<form class="form" action="" method="post" autocomplete="off">
|
||||
<input name="savemode" type="hidden" value="<?php echo isset($mode) ? $mode : ''; ?>"/>
|
||||
|
||||
<div class="input-group">
|
||||
<div class="input-info">Enter single or multiple addresses separated by comma, semicolon or newline.</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="source">Source</label>
|
||||
<div class="input">
|
||||
<?php if(defined('DBC_ALIASES_MULTI_SOURCE')): ?>
|
||||
<textarea name="source" placeholder="Source" required autofocus><?php echo isset($sources) ? strip_tags(emailsToString($sources, FRONTEND_EMAIL_SEPARATOR_FORM)) : ''; ?></textarea>
|
||||
<?php else: ?>
|
||||
<input type="text" name="source" placeholder="Source (single address)" required autofocus value="<?php echo isset($sources) ? strip_tags(emailsToString($sources, FRONTEND_EMAIL_SEPARATOR_FORM)) : ''; ?>"/>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="destination">Destination</label>
|
||||
<div class="input">
|
||||
<textarea name="destination" placeholder="Destination" required><?php echo isset($destinations) ? strip_tags(emailsToString($destinations, FRONTEND_EMAIL_SEPARATOR_FORM)) : ''; ?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<h1><?php echo ($mode === "create") ? 'Create' : 'Edit'; ?> Redirect</h1>
|
||||
|
||||
<div class="buttons">
|
||||
<button type="submit" class="button button-primary">Save settings</button>
|
||||
<a class="button" href="<?php echo Router::url('admin/listredirects'); ?>">❬ Back to redirects list</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="notification">
|
||||
Please note that mailservers will prefer to deliver mails to redirects over mailboxes.<br>
|
||||
So make sure you don't accidentally override a mailbox with a redirect.
|
||||
</div>
|
||||
|
||||
<?php echo Message::getInstance()->render(); ?>
|
||||
|
||||
<?php if(Config::get('options.enable_validate_aliases_source_domain', true) && Auth::getUser()->isDomainLimited() && $domains->count() === 0): ?>
|
||||
<div class="notification notification-fail">
|
||||
You are listed for limited access to domains, but it seems there are no domains listed you can access.
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<form class="form" action="" method="post" autocomplete="off">
|
||||
<input name="savemode" type="hidden" value="<?php echo $mode; ?>"/>
|
||||
|
||||
<div class="input-group">
|
||||
<div class="input-info">Enter single or multiple addresses separated by comma, semicolon or newline.</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="source">Source</label>
|
||||
<div class="input-info">
|
||||
<?php if($domains->count() > 0): ?>
|
||||
<?php if(Auth::getUser()->isDomainLimited()): ?>
|
||||
You can create redirects for source addresses from these domains only:
|
||||
<?php else: ?>
|
||||
You can create redirects for every domain you want,<br>
|
||||
but here's a list of domains managed by WebMUM:
|
||||
<?php endif; ?>
|
||||
<ul>
|
||||
<?php foreach($domains as $domain): /** @var Domain $domain */ ?>
|
||||
<li><?php echo $domain->getDomain(); ?></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php else: ?>
|
||||
There are no domains managed by WebMUM yet.
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="input">
|
||||
<?php if(Config::get('options.enable_multi_source_redirects', false)): ?>
|
||||
<textarea name="source" placeholder="Source" required autofocus><?php echo formatEmailsForm(isset($_POST['source']) ? $_POST['source'] : (is_null($redirect) ? '' : $redirect->getSource())); ?></textarea>
|
||||
<?php else: ?>
|
||||
<input type="text" name="source" placeholder="Source (single address)" required autofocus value="<?php echo formatEmailsForm(isset($_POST['source']) ? $_POST['source'] : (is_null($redirect) ? '' : $redirect->getSource())); ?>"/>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="destination">Destination</label>
|
||||
<div class="input">
|
||||
<textarea name="destination" placeholder="Destination" required><?php echo formatEmailsForm(isset($_POST['destination']) ? $_POST['destination'] : (is_null($redirect) ? '' : $redirect->getDestination())); ?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="buttons">
|
||||
<button type="submit" class="button button-primary">Save settings</button>
|
||||
</div>
|
||||
</form>
|
||||
<?php endif; ?>
|
|
@ -1,133 +1,162 @@
|
|||
<?php
|
||||
// If mailbox_limit is supported in the MySQL database
|
||||
$mailbox_limit_default = 0;
|
||||
if(defined('DBC_USERS_MAILBOXLIMIT')){
|
||||
// Get mailbox_limit default value from DB
|
||||
$sql = "SELECT DEFAULT(".DBC_USERS_MAILBOXLIMIT.") AS `".DBC_USERS_MAILBOXLIMIT."` FROM `".DBT_USERS."` LIMIT 1;";
|
||||
<?php
|
||||
|
||||
if(!$result = $db->query($sql)){
|
||||
dbError($db->error);
|
||||
}
|
||||
else{
|
||||
while($row = $result->fetch_assoc()){
|
||||
$mailbox_limit_default = $row[DBC_USERS_MAILBOXLIMIT];
|
||||
$mailboxLimitDefault = User::getMailboxLimitDefault();
|
||||
|
||||
$canCreateUserRedirectsDefault = false;
|
||||
$maxUserRedirectsDefault = User::getMaxUserRedirectsDefault();
|
||||
|
||||
$saveMode = (isset($_POST['savemode']) && in_array($_POST['savemode'], array('edit', 'create')))
|
||||
? $_POST['savemode']
|
||||
: null;
|
||||
|
||||
if(!is_null($saveMode)){
|
||||
|
||||
$inputPassword = isset($_POST['password']) ? $_POST['password'] : null;
|
||||
$inputPasswordRepeated = isset($_POST['password_repeat']) ? $_POST['password_repeat'] : null;
|
||||
|
||||
$inputMailboxLimit = null;
|
||||
if(Config::get('options.enable_mailbox_limits', false)){
|
||||
$inputMailboxLimit = isset($_POST['mailbox_limit']) ? intval($_POST['mailbox_limit']) : $mailboxLimitDefault;
|
||||
if(!$inputMailboxLimit === 0 && empty($inputMailboxLimit)){
|
||||
$inputMailboxLimit = $mailboxLimitDefault;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$username = isset($_POST['username']) ? $db->escape_string(strtolower($_POST['username'])) : '';
|
||||
$domain = isset($_POST['domain']) ? $db->escape_string(strtolower($_POST['domain'])) : '';
|
||||
$inputMaxUserRedirects = null;
|
||||
if(Config::get('options.enable_user_redirects', false)){
|
||||
$inputMaxUserRedirects = isset($_POST['max_user_redirects']) ? intval($_POST['max_user_redirects']) : $maxUserRedirectsDefault;
|
||||
if(!$inputMaxUserRedirects === 0 && empty($inputMaxUserRedirects)){
|
||||
$inputMaxUserRedirects = $maxUserRedirectsDefault;
|
||||
}
|
||||
|
||||
if(isset($_POST['savemode'])){
|
||||
$savemode = $_POST['savemode'];
|
||||
if(isset($_POST['user_redirects']) && $_POST['user_redirects'] === 'no'){
|
||||
$inputMaxUserRedirects = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if($savemode === "edit"){
|
||||
if($saveMode === 'edit'){
|
||||
// Edit mode entered
|
||||
|
||||
if(!isset($_POST['id'])){
|
||||
// User id not set, redirect to overview
|
||||
redirect("admin/listusers");
|
||||
Router::redirect("admin/listusers");
|
||||
}
|
||||
|
||||
$id = $db->escape_string($_POST['id']);
|
||||
$inputId = $_POST['id'];
|
||||
|
||||
$sql = "SELECT `".DBC_USERS_ID."` FROM `".DBT_USERS."` WHERE `".DBC_USERS_ID."` = '$id' LIMIT 1;";
|
||||
if(!$resultExists = $db->query($sql)){
|
||||
dbError($db->error);
|
||||
/** @var User $userToEdit */
|
||||
$userToEdit = User::find($inputId);
|
||||
|
||||
if(is_null($userToEdit)){
|
||||
// User doesn't exist, redirect to overview
|
||||
Router::redirect("admin/listusers");
|
||||
}
|
||||
|
||||
if($resultExists->num_rows !== 1){
|
||||
// User does not exist, redirect to overview
|
||||
redirect("admin/listusers");
|
||||
if(!$userToEdit->isInLimitedDomains()){
|
||||
Router::redirect("admin/listusers/?missing-permission=1");
|
||||
}
|
||||
|
||||
if(defined('DBC_USERS_MAILBOXLIMIT')){
|
||||
$mailbox_limit = $db->escape_string($_POST['mailbox_limit']);
|
||||
if($mailbox_limit == ""){
|
||||
$mailbox_limit = $mailbox_limit_default;
|
||||
}
|
||||
|
||||
$sql = "UPDATE `".DBT_USERS."` SET `".DBC_USERS_MAILBOXLIMIT."` = '$mailbox_limit' WHERE `".DBC_USERS_ID."` = '$id';";
|
||||
if(!$result = $db->query($sql)){
|
||||
dbError($db->error);
|
||||
}
|
||||
if(!is_null($inputMailboxLimit)){
|
||||
$userToEdit->setMailboxLimit($inputMailboxLimit);
|
||||
}
|
||||
|
||||
if(!is_null($inputMaxUserRedirects)){
|
||||
$userToEdit->setMaxUserRedirects($inputMaxUserRedirects);
|
||||
}
|
||||
|
||||
$passwordError = false;
|
||||
|
||||
// Is there a changed password?
|
||||
if($_POST['password'] !== ""){
|
||||
$pass_ok = check_new_pass($_POST['password'], $_POST['password_repeat']);
|
||||
if($pass_ok === true){
|
||||
// Password is okay and can be set
|
||||
$pass_hash = gen_pass_hash($_POST['password']);
|
||||
write_pass_hash_to_db($pass_hash, $id);
|
||||
|
||||
// Edit user password successfull, redirect to overview
|
||||
redirect("admin/listusers/?edited=1");
|
||||
if(!empty($inputPassword) || !empty($inputPasswordRepeated)){
|
||||
try{
|
||||
$userToEdit->changePassword($inputPassword, $inputPasswordRepeated);
|
||||
}
|
||||
else{
|
||||
// Password is not okay
|
||||
// $editsuccessful = 2;
|
||||
add_message("fail", $PASS_ERR_MSG);
|
||||
catch(AuthException $passwordInvalidException){
|
||||
Message::getInstance()->fail($passwordInvalidException->getMessage());
|
||||
$passwordError = true;
|
||||
}
|
||||
}
|
||||
else{
|
||||
|
||||
$userToEdit->save();
|
||||
|
||||
if(!$passwordError){
|
||||
// Edit user successfull, redirect to overview
|
||||
redirect("admin/listusers/?edited=1");
|
||||
Router::redirect("admin/listusers/?edited=1");
|
||||
}
|
||||
}
|
||||
|
||||
else if($savemode === "create"){
|
||||
else if($saveMode === 'create'){
|
||||
// Create mode entered
|
||||
|
||||
if(defined('DBC_USERS_MAILBOXLIMIT')){
|
||||
$mailbox_limit = $db->escape_string($_POST['mailbox_limit']);
|
||||
}
|
||||
else{
|
||||
// make mailbox_limit dummy for "if"
|
||||
$mailbox_limit = 0;
|
||||
}
|
||||
$inputUsername = isset($_POST['username']) ? $_POST['username'] : null;
|
||||
$inputDomain = isset($_POST['domain']) ? $_POST['domain'] : null;
|
||||
|
||||
$pass = $_POST['password'];
|
||||
$pass_rep = $_POST['password_repeat'];
|
||||
if(!empty($inputUsername)
|
||||
&& !empty($inputDomain)
|
||||
&& (!empty($inputPassword) || !empty($inputPasswordRepeated))
|
||||
){
|
||||
|
||||
if(!empty($username) && !empty($domain) && !empty($mailbox_limit)){
|
||||
// Check if user already exists
|
||||
$user_exists = $db->query("SELECT `".DBC_USERS_USERNAME."`, `".DBC_USERS_DOMAIN."` FROM `".DBT_USERS."` WHERE `".DBC_USERS_USERNAME."` = '$username' AND `".DBC_USERS_DOMAIN."` = '$domain';");
|
||||
if($user_exists->num_rows == 0){
|
||||
// All fields filled with content
|
||||
// Check passwords
|
||||
$pass_ok = check_new_pass($pass, $pass_rep);
|
||||
if($pass_ok === true){
|
||||
// Password is okay ... continue
|
||||
$pass_hash = gen_pass_hash($pass);
|
||||
/** @var Domain $selectedDomain */
|
||||
$selectedDomain = Domain::findWhereFirst(
|
||||
array(Domain::attr('domain'), $inputDomain)
|
||||
);
|
||||
|
||||
// Differ between version with mailbox_limit and version without
|
||||
if(defined('DBC_USERS_MAILBOXLIMIT')){
|
||||
$sql = "INSERT INTO `".DBT_USERS."` (`".DBC_USERS_USERNAME."`, `".DBC_USERS_DOMAIN."`, `".DBC_USERS_PASSWORD."`, `".DBC_USERS_MAILBOXLIMIT."`) VALUES ('$username', '$domain', '$pass_hash', '$mailbox_limit')";
|
||||
if(!is_null($selectedDomain)){
|
||||
|
||||
if(!$selectedDomain->isInLimitedDomains()){
|
||||
Router::redirect("admin/listusers/?missing-permission=1");
|
||||
}
|
||||
|
||||
/** @var User $user */
|
||||
$user = User::findWhereFirst(
|
||||
array(
|
||||
array(User::attr('username'), $inputUsername),
|
||||
array(User::attr('domain'), $selectedDomain->getDomain()),
|
||||
)
|
||||
);
|
||||
|
||||
// Check if user already exists
|
||||
if(is_null($user)){
|
||||
try{
|
||||
// Check password then go on an insert user first
|
||||
Auth::validateNewPassword($inputPassword, $inputPasswordRepeated);
|
||||
|
||||
$data = array(
|
||||
User::attr('username') => $inputUsername,
|
||||
User::attr('domain') => $selectedDomain->getDomain(),
|
||||
User::attr('password_hash') => Auth::generatePasswordHash($inputPassword)
|
||||
);
|
||||
|
||||
if(!is_null($inputMailboxLimit)){
|
||||
$data[User::attr('mailbox_limit')] = $inputMailboxLimit;
|
||||
}
|
||||
|
||||
if(!is_null($inputMaxUserRedirects)){
|
||||
$data[User::attr('max_user_redirects')] = $inputMaxUserRedirects;
|
||||
}
|
||||
|
||||
/** @var User $user */
|
||||
$user = User::createAndSave($data);
|
||||
|
||||
// Redirect user to user list
|
||||
Router::redirect("admin/listusers/?created=1");
|
||||
}
|
||||
else{
|
||||
$sql = "INSERT INTO `".DBT_USERS."` (`".DBC_USERS_USERNAME."`, `".DBC_USERS_DOMAIN."`, `".DBC_USERS_PASSWORD."`) VALUES ('$username', '$domain', '$pass_hash')";
|
||||
catch(AuthException $passwordInvalidException){
|
||||
Message::getInstance()->fail($passwordInvalidException->getMessage());
|
||||
}
|
||||
|
||||
if(!$result = $db->query($sql)){
|
||||
dbError($db->error);
|
||||
}
|
||||
|
||||
// Redirect user to user list
|
||||
redirect("admin/listusers/?created=1");
|
||||
}
|
||||
else{
|
||||
// Password not okay
|
||||
add_message("fail", $PASS_ERR_MSG);
|
||||
Message::getInstance()->fail("User already exists in database.");
|
||||
}
|
||||
}
|
||||
else{
|
||||
add_message("fail", "User already exists in database.");
|
||||
Message::getInstance()->fail("The selected domain doesn't exist.");
|
||||
}
|
||||
}
|
||||
else{
|
||||
var_dump($_POST);
|
||||
// Fields missing
|
||||
add_message("fail", "Not all fields were filled out.");
|
||||
Message::getInstance()->fail("Not all fields were filled out.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -136,102 +165,129 @@ if(isset($_POST['savemode'])){
|
|||
$mode = "create";
|
||||
if(isset($_GET['id'])){
|
||||
$mode = "edit";
|
||||
$id = $db->escape_string($_GET['id']);
|
||||
$id = $_GET['id'];
|
||||
|
||||
//Load user data from DB
|
||||
$sql = "SELECT * from `".DBT_USERS."` WHERE `".DBC_USERS_ID."` = '$id' LIMIT 1;";
|
||||
/** @var User $user */
|
||||
$user = User::find($id);
|
||||
|
||||
if(!$result = $db->query($sql)){
|
||||
dbError($db->error);
|
||||
if(is_null($user)){
|
||||
// User doesn't exist, redirect to overview
|
||||
Router::redirect("admin/listusers");
|
||||
}
|
||||
|
||||
if($result->num_rows !== 1){
|
||||
// User does not exist, redirect to overview
|
||||
redirect("admin/listusers");
|
||||
if(!$user->isInLimitedDomains()){
|
||||
Router::redirect("admin/listusers/?missing-permission=1");
|
||||
}
|
||||
|
||||
$row = $result->fetch_assoc();
|
||||
|
||||
$username = $row[DBC_USERS_USERNAME];
|
||||
$domain = $row[DBC_USERS_DOMAIN];
|
||||
if(defined('DBC_USERS_MAILBOXLIMIT')){
|
||||
$mailbox_limit = $row[DBC_USERS_MAILBOXLIMIT];
|
||||
}
|
||||
}
|
||||
|
||||
//Load user data from DB
|
||||
$sql = "SELECT `".DBC_DOMAINS_DOMAIN."` FROM `".DBT_DOMAINS."`;";
|
||||
|
||||
if(!$resultDomains = $db->query($sql)){
|
||||
dbError($db->error);
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<h1><?php echo ($mode === "create") ? 'Create User' : 'Edit user "'.$username.'@'.$domain.'"'; ?></h1>
|
||||
<h1><?php echo ($mode === "create") ? "Create User" : "Edit user \"{$user->getEmail()}\""; ?></h1>
|
||||
|
||||
<div class="buttons">
|
||||
<a class="button" href="<?php echo url('admin/listusers'); ?>">❬ Back to user list</a>
|
||||
<a class="button" href="<?php echo Router::url('admin/listusers'); ?>">❬ Back to user list</a>
|
||||
</div>
|
||||
|
||||
<form class="form" action="" method="post">
|
||||
<form class="form" action="" method="post" autocomplete="off">
|
||||
<input type="hidden" name="savemode" value="<?php echo $mode; ?>"/>
|
||||
<?php if($mode === "edit" && isset($id)): ?>
|
||||
<input type="hidden" name="id" value="<?php echo $id; ?>"/>
|
||||
<?php endif; ?>
|
||||
<?php if($mode === "edit"): ?>
|
||||
<input type="hidden" name="id" value="<?php echo $user->getId(); ?>"/>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php output_messages(); ?>
|
||||
<?php echo Message::getInstance()->render(); ?>
|
||||
|
||||
<?php if($mode === "edit"): ?>
|
||||
<div class="input-group">
|
||||
<label>Username and Group cannot be edited</label>
|
||||
<div class="input-info">To rename or move a mailbox, you have to move in the filesystem first and create a new user here after.</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="input-group">
|
||||
<label for="username">Username</label>
|
||||
<div class="input">
|
||||
<input type="text" name="username" placeholder="Username" value="<?php echo isset($username) ? strip_tags($username) : ''; ?>" autofocus required/>
|
||||
<?php if($mode === "edit"): ?>
|
||||
<div class="input-group">
|
||||
<label>Username and Group cannot be edited</label>
|
||||
<div class="input-info">To rename or move a mailbox, you have to move in the filesystem first and create a new user here after.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="domain">Domain</label>
|
||||
<div class="input">
|
||||
<select name="domain" required>
|
||||
<option value="">-- Select a domain --</option>
|
||||
<?php while($row = $resultDomains->fetch_assoc()): ?>
|
||||
<option value="<?php echo strip_tags($row[DBC_DOMAINS_DOMAIN]); ?>" <?php echo (isset($domain) && $row[DBC_DOMAINS_DOMAIN] == $domain) ? 'selected' : ''; ?>>
|
||||
<?php echo strip_tags($row[DBC_DOMAINS_DOMAIN]); ?>
|
||||
</option>
|
||||
<?php endwhile; ?>
|
||||
</select>
|
||||
<?php else:
|
||||
/** @var ModelCollection $domains */
|
||||
$domains = Domain::getByLimitedDomains();
|
||||
?>
|
||||
<div class="input-group">
|
||||
<label for="username">Username</label>
|
||||
<div class="input">
|
||||
<input type="text" name="username" placeholder="Username" value="<?php echo isset($_POST['username']) ? strip_tags($_POST['username']) : ''; ?>" autofocus required/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="domain">Domain</label>
|
||||
<div class="input">
|
||||
<select name="domain" required>
|
||||
<option value="">-- Select a domain --</option>
|
||||
<?php foreach($domains as $domain): /** @var Domain $domain */ ?>
|
||||
<option value="<?php echo $domain->getDomain(); ?>" <?php echo ((isset($_POST['domain']) && $_POST['domain'] === $domain->getDomain()) || ($mode === "create" && $domains->count() === 1)) ? 'selected' : ''; ?>>
|
||||
<?php echo $domain->getDomain(); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="password">Password</label>
|
||||
<div class="input-info">The new password must be at least <?php echo MIN_PASS_LENGTH; ?> characters long.</div>
|
||||
<?php if(Config::has('password.min_length')): ?>
|
||||
<div class="input-info">The new password must be at least <?php echo Config::get('password.min_length'); ?> characters long.</div>
|
||||
<?php endif; ?>
|
||||
<div class="input input-action">
|
||||
<input type="password" name="password" placeholder="New password" <?php echo ($mode === "create") ? 'required' : ''; ?> minlength="<?php echo MIN_PASS_LENGTH; ?>"/>
|
||||
<input type="password" name="password" placeholder="New password" <?php echo ($mode === "create") ? 'required' : ''; ?> minlength="<?php echo Config::get('password.min_length', 0); ?>"/>
|
||||
<button type="button" class="button" onclick="pass=generatePassword();this.form.password.value=pass;this.form.password_repeat.value=pass;this.form.password.type='text';this.form.password_repeat.type='text'">Generate password</button>
|
||||
</div>
|
||||
<div class="input">
|
||||
<input type="password" name="password_repeat" placeholder="Repeat password" <?php echo ($mode === "create") ? 'required' : ''; ?> minlength="<?php echo MIN_PASS_LENGTH; ?>"/>
|
||||
<input type="password" name="password_repeat" placeholder="Repeat password" <?php echo ($mode === "create") ? 'required' : ''; ?> minlength="<?php echo Config::get('password.min_length', 0); ?>"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if(defined('DBC_USERS_MAILBOXLIMIT')): ?>
|
||||
<div class="input-group">
|
||||
<label>Mailbox limit</label>
|
||||
<div class="input-info">The default limit is <?php echo $mailbox_limit_default; ?> MB. Limit set to 0 means no limit in size.</div>
|
||||
<div class="input input-labeled input-labeled-right">
|
||||
<input name="mailbox_limit" type="number" value="<?php echo strip_tags(isset($mailbox_limit) ? $mailbox_limit : $mailbox_limit_default); ?>" placeholder="Mailbox limit in MB" min="0" required/>
|
||||
<span class="input-label">MB</span>
|
||||
<?php if(Config::get('options.enable_mailbox_limits', false)): ?>
|
||||
<div class="input-group">
|
||||
<label>Mailbox limit</label>
|
||||
<div class="input-info">The default limit is <?php echo $mailboxLimitDefault; ?> MB. Limit set to 0 means no limit in size.</div>
|
||||
<div class="input input-labeled input-labeled-right">
|
||||
<input name="mailbox_limit" type="number" value="<?php echo isset($_POST['mailbox_limit']) ? strip_tags($_POST['mailbox_limit']) : ((isset($user) && Config::get('options.enable_mailbox_limits', false)) ? $user->getMailboxLimit() : $mailboxLimitDefault); ?>" placeholder="Mailbox limit in MB" min="0" required/>
|
||||
<span class="input-label">MB</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if(Config::get('options.enable_user_redirects', false)):
|
||||
$canCreateUserRedirects = isset($_POST['user_redirects'])
|
||||
? $_POST['user_redirects'] === 'yes'
|
||||
: (isset($user) ? $user->isAllowedToCreateUserRedirects() : $canCreateUserRedirectsDefault);
|
||||
$maxUserRedirects = !$canCreateUserRedirects
|
||||
? $maxUserRedirectsDefault
|
||||
: (
|
||||
isset($_POST['max_user_redirects'])
|
||||
? strip_tags($_POST['max_user_redirects'])
|
||||
: (
|
||||
isset($user)
|
||||
? $user->getMaxUserRedirects()
|
||||
: $maxUserRedirectsDefault
|
||||
)
|
||||
)
|
||||
?>
|
||||
<div class="input-group">
|
||||
<label>User can create redirects to himself?</label>
|
||||
<div class="input-info">The maximum number of redirects can be limited by the limit redirects setting.</div>
|
||||
<div class="input">
|
||||
<select name="user_redirects" autofocus required>
|
||||
<option value="no" <?php echo !$canCreateUserRedirects ? 'selected' : ''; ?>>Disabled</option>
|
||||
<option value="yes" <?php echo $canCreateUserRedirects ? 'selected' : ''; ?>>Yes, creating redirects is allowed</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label>Limit redirects</label>
|
||||
<div class="input-info">The default limit is "<?php echo $maxUserRedirectsDefault; ?>". Set to "0" means unlimited redirects.</div>
|
||||
<div class="input input-labeled input-labeled-right">
|
||||
<input name="max_user_redirects" type="number" value="<?php echo $maxUserRedirects; ?>" placeholder="Mailbox limit in MB" min="0" required/>
|
||||
<span class="input-label">Redirects</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="buttons">
|
||||
<button type="submit" class="button button-primary">Save settings</button>
|
||||
|
|
|
@ -1,61 +1,66 @@
|
|||
<?php
|
||||
|
||||
if(Auth::getUser()->isDomainLimited()){
|
||||
Router::displayError(403);
|
||||
}
|
||||
|
||||
if(isset($_GET['deleted']) && $_GET['deleted'] == "1"){
|
||||
add_message("success", "Domain deleted successfully.");
|
||||
Message::getInstance()->success("Domain deleted successfully.");
|
||||
}
|
||||
else if(isset($_GET['created']) && $_GET['created'] == "1"){
|
||||
add_message("success", "Domain created successfully.");
|
||||
Message::getInstance()->success("Domain created successfully.");
|
||||
}
|
||||
else if(isset($_GET['adm_del']) && $_GET['adm_del'] == "1"){
|
||||
add_message("fail", "Domain could not be deleted because admin account would be affected.");
|
||||
Message::getInstance()->fail("Domain couldn't be deleted because admin account would be affected.");
|
||||
}
|
||||
else if(isset($_GET['missing-permission']) && $_GET['missing-permission'] == "1"){
|
||||
Message::getInstance()->fail("You don't have the permission to delete that domain.");
|
||||
}
|
||||
|
||||
$sql = "SELECT d.*, COUNT(DISTINCT u.`".DBC_USERS_ID."`) AS `user_count`, COUNT(DISTINCT r.`".DBC_ALIASES_ID."`) AS `redirect_count`
|
||||
FROM `".DBT_DOMAINS."` AS d
|
||||
LEFT JOIN `".DBT_USERS."` AS u ON (u.`".DBC_USERS_DOMAIN."` = d.`".DBC_DOMAINS_DOMAIN."`)
|
||||
LEFT JOIN `".DBT_ALIASES."` AS r ON (r.`".DBC_ALIASES_SOURCE."` LIKE CONCAT('%@', d.`".DBC_DOMAINS_DOMAIN."`))
|
||||
GROUP BY d.`".DBC_DOMAINS_DOMAIN."`
|
||||
ORDER BY `".DBC_DOMAINS_DOMAIN."` ASC;";
|
||||
|
||||
if(!$result = $db->query($sql)){
|
||||
dbError($db->error);
|
||||
}
|
||||
$domains = Domain::findAll();
|
||||
|
||||
?>
|
||||
|
||||
<h1>Domains</h1>
|
||||
<h1>Domains</h1>
|
||||
|
||||
<div class="buttons">
|
||||
<a class="button" href="<?php echo url('admin/createdomain'); ?>">Create new domain</a>
|
||||
</div>
|
||||
<?php if(!Auth::getUser()->isDomainLimited()): ?>
|
||||
<div class="buttons">
|
||||
<a class="button" href="<?php echo Router::url('admin/createdomain'); ?>">Create new domain</a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php output_messages(); ?>
|
||||
<?php echo Message::getInstance()->render(); ?>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<?php if($domains->count() > 0): ?>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Domain</th>
|
||||
<th>User count</th>
|
||||
<th>Redirect count</th>
|
||||
<th></th>
|
||||
<tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php while($row = $result->fetch_assoc()): ?>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach($domains as $domain): /** @var Domain $domain */ ?>
|
||||
<tr>
|
||||
<td><?php echo $domain->getDomain(); ?></td>
|
||||
<td><?php echo $domain->countUsers(); ?></td>
|
||||
<td><?php echo $domain->countRedirects(); ?></td>
|
||||
<td>
|
||||
<a href="<?php echo Router::url('admin/deletedomain/?id='.$domain->getId()); ?>">[Delete]</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td><?php echo strip_tags($row[DBC_DOMAINS_DOMAIN]); ?></td>
|
||||
<td><?php echo strip_tags($row['user_count']); ?></td>
|
||||
<td><?php echo strip_tags($row['redirect_count']); ?></td>
|
||||
<td>
|
||||
<a href="<?php echo url('admin/deletedomain/?id='.$row[DBC_DOMAINS_ID]); ?>">[Delete]</a>
|
||||
</td>
|
||||
<th><?php echo textValue('_ domain', $domains->count()); ?></th>
|
||||
</tr>
|
||||
<?php endwhile; ?>
|
||||
</tbody>
|
||||
<?php if ($result->num_rows > 0): ?>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th><?php echo $result->num_rows;?> Domains</th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
<?php endif; ?>
|
||||
</table>
|
||||
</tfoot>
|
||||
</table>
|
||||
<?php else: ?>
|
||||
<div class="notification notification-warning">
|
||||
There are currently no domains created you can manage.
|
||||
</div>
|
||||
<?php endif; ?>
|
|
@ -1,82 +1,86 @@
|
|||
<?php
|
||||
|
||||
if(isset($_GET['deleted']) && $_GET['deleted'] == "1"){
|
||||
add_message("success", "Redirect deleted successfully.");
|
||||
Message::getInstance()->success("Redirect deleted successfully.");
|
||||
}
|
||||
else if(isset($_GET['created']) && $_GET['created'] == "1"){
|
||||
add_message("success", "Redirect created successfully.");
|
||||
Message::getInstance()->success("Redirect created successfully.");
|
||||
}
|
||||
else if(isset($_GET['edited']) && $_GET['edited'] == "1"){
|
||||
add_message("success", "Redirect edited successfully.");
|
||||
}
|
||||
|
||||
if(defined('DBC_ALIASES_MULTI_SOURCE')){
|
||||
$sql = "SELECT r.* FROM (
|
||||
SELECT
|
||||
group_concat(g.`".DBC_ALIASES_ID."` ORDER BY g.`".DBC_ALIASES_ID."` SEPARATOR ',') AS `".DBC_ALIASES_ID."`,
|
||||
group_concat(g.`".DBC_ALIASES_SOURCE."` SEPARATOR ',') AS `".DBC_ALIASES_SOURCE."`,
|
||||
g.`".DBC_ALIASES_DESTINATION."`,
|
||||
g.`".DBC_ALIASES_MULTI_SOURCE."`
|
||||
FROM `".DBT_ALIASES."` AS g
|
||||
WHERE g.`".DBC_ALIASES_MULTI_SOURCE."` IS NOT NULL
|
||||
GROUP BY g.`".DBC_ALIASES_MULTI_SOURCE."`
|
||||
UNION
|
||||
SELECT
|
||||
s.`".DBC_ALIASES_ID."`,
|
||||
s.`".DBC_ALIASES_SOURCE."`,
|
||||
s.`".DBC_ALIASES_DESTINATION."`,
|
||||
s.`".DBC_ALIASES_MULTI_SOURCE."`
|
||||
FROM `".DBT_ALIASES."` AS s
|
||||
WHERE s.`".DBC_ALIASES_MULTI_SOURCE."` IS NULL
|
||||
) AS r
|
||||
ORDER BY `".DBC_ALIASES_SOURCE."` ASC";
|
||||
}
|
||||
else{
|
||||
$sql = "SELECT `".DBC_ALIASES_ID."`, `".DBC_ALIASES_SOURCE."`, `".DBC_ALIASES_DESTINATION."` FROM `".DBT_ALIASES."` ORDER BY `".DBC_ALIASES_SOURCE."` ASC;";
|
||||
}
|
||||
|
||||
if(!$result = $db->query($sql)){
|
||||
dbError($db->error);
|
||||
Message::getInstance()->success("Redirect edited successfully.");
|
||||
}
|
||||
else if(isset($_GET['missing-permission']) && $_GET['missing-permission'] == "1"){
|
||||
Message::getInstance()->fail("You don't have the permission to edit/delete redirects of that domain.");
|
||||
}
|
||||
|
||||
$redirects = AbstractRedirect::getMultiByLimitedDomains();
|
||||
?>
|
||||
|
||||
<h1>Redirects</h1>
|
||||
<h1>Redirects</h1>
|
||||
|
||||
<?php output_messages(); ?>
|
||||
<?php if(!(Auth::getUser()->isDomainLimited() && count(Domain::getByLimitedDomains()) === 0)): ?>
|
||||
<div class="buttons">
|
||||
<a class="button" href="<?php echo Router::url('admin/editredirect'); ?>">Create new redirect</a>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="notification notification-warning">
|
||||
You are listed for limited access to domains, but it seems there are no domains listed you can access.
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="buttons">
|
||||
<a class="button" href="<?php echo url('admin/editredirect'); ?>">Create new redirect</a>
|
||||
</div>
|
||||
<?php echo Message::getInstance()->render(); ?>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<?php if($redirects->count() > 0): ?>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Source</th>
|
||||
<th>Destination</th>
|
||||
<?php if(Config::get('options.enable_user_redirects', false)): ?>
|
||||
<th>Created by user</th>
|
||||
<?php endif; ?>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php while($row = $result->fetch_assoc()): ?>
|
||||
<tr>
|
||||
<td><?php echo strip_tags(formatEmails($row[DBC_ALIASES_SOURCE], FRONTEND_EMAIL_SEPARATOR_TEXT)); ?></td>
|
||||
<td><?php echo strip_tags(formatEmails($row[DBC_ALIASES_DESTINATION], FRONTEND_EMAIL_SEPARATOR_TEXT)); ?></td>
|
||||
<td>
|
||||
<a href="<?php echo url('admin/editredirect/?id='.$row[DBC_ALIASES_ID]); ?>">[Edit]</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="<?php echo url('admin/deleteredirect/?id='.$row[DBC_ALIASES_ID]); ?>">[Delete]</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endwhile; ?>
|
||||
</tbody>
|
||||
<?php if ($result->num_rows > 0): ?>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach($redirects as $redirect): /** @var AbstractRedirect $redirect */ ?>
|
||||
<tr<?php echo $redirect->getConflictingUsers()->count() > 0 ? ' class="warning"' : ''; ?>>
|
||||
<td>
|
||||
<?php if($redirect->getConflictingUsers()->count() > 0): ?>
|
||||
<strong><?php echo $redirect->getConflictingUsers()->count() === 1 ? 'The marked redirect overrides a mailbox.' : 'The marked redirects override mailboxes.'; ?></strong><br>
|
||||
<?php endif; ?>
|
||||
<?php echo formatEmailsText($redirect->getConflictingMarkedSource()); ?>
|
||||
</td>
|
||||
<td><?php echo formatEmailsText($redirect->getDestination()); ?></td>
|
||||
<?php if(Config::get('options.enable_user_redirects', false)): ?>
|
||||
<td><?php echo $redirect->isCreatedByUser() ? 'Yes' : 'No'; ?></td>
|
||||
<?php endif; ?>
|
||||
<td>
|
||||
<a href="<?php echo Router::url('admin/editredirect/?id='.$redirect->getId()); ?>">[Edit]</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="<?php echo Router::url('admin/deleteredirect/?id='.$redirect->getId()); ?>">[Delete]</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th><?php echo $result->num_rows;?> Redirects</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?php echo textValue('_ redirect', $redirects->count()); ?></th>
|
||||
<?php if(Config::get('options.enable_user_redirects', false)):
|
||||
$userRedirectsCount = AbstractRedirect::countWhere(
|
||||
array(AbstractRedirect::attr('is_created_by_user'), 1)
|
||||
);
|
||||
?>
|
||||
<th></th>
|
||||
<th><?php echo textValue('_ user redirect', $userRedirectsCount); ?></th>
|
||||
<?php endif; ?>
|
||||
</tr>
|
||||
</tfoot>
|
||||
<?php endif; ?>
|
||||
</table>
|
||||
</table>
|
||||
<?php elseif(!(Auth::getUser()->isDomainLimited() && count(Domain::getByLimitedDomains()) === 0)): ?>
|
||||
<div class="notification notification-warning">
|
||||
There are currently no redirects created you can manage.
|
||||
</div>
|
||||
<?php endif; ?>
|
|
@ -1,72 +1,102 @@
|
|||
<?php
|
||||
<?php
|
||||
|
||||
if(isset($_GET['deleted']) && $_GET['deleted'] == "1"){
|
||||
add_message("success", "User deleted successfully.");
|
||||
Message::getInstance()->success("User deleted successfully.");
|
||||
}
|
||||
else if(isset($_GET['created']) && $_GET['created'] == "1"){
|
||||
add_message("success", "User created successfully.");
|
||||
Message::getInstance()->success("User created successfully.");
|
||||
}
|
||||
else if(isset($_GET['edited']) && $_GET['edited'] == "1"){
|
||||
add_message("success", "User edited successfully.");
|
||||
Message::getInstance()->success("User edited successfully.");
|
||||
}
|
||||
else if(isset($_GET['adm_del']) && $_GET['adm_del'] == "1"){
|
||||
add_message("fail", "Admin user cannot be deleted.");
|
||||
Message::getInstance()->fail("Admin user cannot be deleted.");
|
||||
}
|
||||
else if(isset($_GET['missing-permission']) && $_GET['missing-permission'] == "1"){
|
||||
Message::getInstance()->fail("You don't have the permission to edit/delete users of that domain.");
|
||||
}
|
||||
|
||||
$sql = "SELECT * FROM `".DBT_USERS."` ORDER BY `".DBC_USERS_DOMAIN."`, `".DBC_USERS_USERNAME."` ASC;";
|
||||
|
||||
if(!$result = $db->query($sql)){
|
||||
dbError($db->error);
|
||||
}
|
||||
$users = User::getByLimitedDomains();
|
||||
|
||||
?>
|
||||
|
||||
<h1>List of all mailbox accounts</h1>
|
||||
|
||||
<div class="buttons">
|
||||
<a class="button button-small" href="<?php echo url('admin/edituser'); ?>">Create new user</a>
|
||||
</div>
|
||||
|
||||
<?php output_messages(); ?>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Username</th>
|
||||
<th>Domain</th>
|
||||
<?php if(defined('DBC_USERS_MAILBOXLIMIT')): ?>
|
||||
<th>Mailbox Limit</th>
|
||||
<?php endif; ?>
|
||||
<th>Role</th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php while($row = $result->fetch_assoc()): ?>
|
||||
<tr>
|
||||
<td><?php echo strip_tags($row[DBC_USERS_USERNAME]); ?></td>
|
||||
<td><?php echo strip_tags($row[DBC_USERS_DOMAIN]); ?></td>
|
||||
<?php if(defined('DBC_USERS_MAILBOXLIMIT')):
|
||||
$limit = strip_tags($row[DBC_USERS_MAILBOXLIMIT]);
|
||||
?>
|
||||
<td style="text-align: right"><?php echo ($limit > 0) ? $limit.' MB' : 'No limit'; ?></td>
|
||||
<?php endif;?>
|
||||
<td><?php echo in_array($row[DBC_USERS_USERNAME].'@'.$row[DBC_USERS_DOMAIN], $admins) ? 'Admin' : 'User'; ?></td>
|
||||
<td>
|
||||
<a href="<?php echo url('admin/edituser/?id='.$row[DBC_USERS_ID]); ?>">[Edit]</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="<?php echo url('admin/deleteuser/?id='.$row[DBC_USERS_ID]); ?>">[Delete]</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endwhile; ?>
|
||||
</tbody>
|
||||
<?php if ($result->num_rows > 0): ?>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th><?php echo $result->num_rows;?> User</th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
<?php if(!(Auth::getUser()->isDomainLimited() && count(Domain::getByLimitedDomains()) === 0)): ?>
|
||||
<div class="buttons">
|
||||
<a class="button button-small" href="<?php echo Router::url('admin/edituser'); ?>">Create new user</a>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="notification notification-warning">
|
||||
You are listed for limited access to domains, but it seems there are no domains listed you can access.
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php echo Message::getInstance()->render(); ?>
|
||||
|
||||
<?php if($users->count() > 0): ?>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Username</th>
|
||||
<th>Domain</th>
|
||||
<?php if(Config::get('options.enable_mailbox_limits', false)): ?>
|
||||
<th>Mailbox Limit</th>
|
||||
<?php endif; ?>
|
||||
<th>Redirect count</th>
|
||||
<?php if(Config::get('options.enable_user_redirects', false)): ?>
|
||||
<th>User Redirects</th>
|
||||
<?php endif; ?>
|
||||
<th>Role</th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach($users as $user): /** @var User $user */ ?>
|
||||
<tr<?php echo !is_null($user->getConflictingRedirect()) ? ' class="warning"' : ''; ?>>
|
||||
<td>
|
||||
<?php if(!is_null($user->getConflictingRedirect())): ?>
|
||||
<strong>This mailbox is overridden by a redirect.</strong><br>
|
||||
<?php endif; ?>
|
||||
<?php echo $user->getUsername(); ?>
|
||||
</td>
|
||||
<td><?php echo $user->getDomain(); ?></td>
|
||||
<?php if(Config::get('options.enable_mailbox_limits', false)): ?>
|
||||
<td style="text-align: right"><?php echo ($user->getMailboxLimit() > 0) ? $user->getMailboxLimit().' MB' : 'No limit'; ?></td>
|
||||
<?php endif; ?>
|
||||
<td style="text-align: right">
|
||||
<?php echo $user->getRedirects()->count(); ?>
|
||||
</td>
|
||||
<?php if(Config::get('options.enable_user_redirects', false)): ?>
|
||||
<td>
|
||||
<?php if($user->getMaxUserRedirects() < 0): ?>
|
||||
Not Allowed
|
||||
<?php elseif($user->getMaxUserRedirects() > 0): ?>
|
||||
Limited (<?php echo $user->getMaxUserRedirects(); ?>)
|
||||
<?php else: ?>
|
||||
Unlimited
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<?php endif; ?>
|
||||
<td><?php echo ($user->getRole() === User::ROLE_ADMIN) ? 'Admin' : 'User'; ?></td>
|
||||
<td>
|
||||
<a href="<?php echo Router::url('admin/edituser/?id='.$user->getId()); ?>">[Edit]</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="<?php echo Router::url('admin/deleteuser/?id='.$user->getId()); ?>">[Delete]</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th><?php echo textValue('_ user', $users->count()); ?></th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
<?php elseif(!(Auth::getUser()->isDomainLimited() && count(Domain::getByLimitedDomains()) === 0)): ?>
|
||||
<div class="notification notification-warning">
|
||||
There are currently no users created you can manage.
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</table>
|
|
@ -1,9 +1,11 @@
|
|||
<h1>Admin Dashboard</h1>
|
||||
|
||||
<div class="buttons buttons-horizontal button-large">
|
||||
<a class="button" href="<?php echo url('admin/listusers'); ?>">Manage users</a>
|
||||
<a class="button" href="<?php echo Router::url('admin/listusers'); ?>">Manage users</a>
|
||||
|
||||
<a class="button" href="<?php echo url('admin/listdomains'); ?>">Manage domains</a>
|
||||
<?php if(!Auth::getUser()->isDomainLimited()): ?>
|
||||
<a class="button" href="<?php echo Router::url('admin/listdomains'); ?>">Manage domains</a>
|
||||
<?php endif; ?>
|
||||
|
||||
<a class="button" href="<?php echo url('admin/listredirects'); ?>">Manage redirects</a>
|
||||
<a class="button" href="<?php echo Router::url('admin/listredirects'); ?>">Manage redirects</a>
|
||||
</div>
|
|
@ -1,35 +1,39 @@
|
|||
<?php
|
||||
|
||||
if(isset($_POST['email']) && isset($_POST['password'])){
|
||||
// Start login
|
||||
$login_success = $user->login($_POST['email'], $_POST['password']);
|
||||
if($login_success){
|
||||
redirect("private");
|
||||
}
|
||||
// If login is not successful
|
||||
else{
|
||||
//Log error message
|
||||
writeLog("WebMUM login failed for IP ".$_SERVER['REMOTE_ADDR']);
|
||||
add_message("fail", "Sorry, couldn't log you in :(");
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
// If user is already logged in, redirect to start.
|
||||
if($user->isLoggedIn()){
|
||||
redirect("private");
|
||||
if(Auth::isLoggedIn()){
|
||||
Router::redirect("private");
|
||||
}
|
||||
|
||||
if(isset($_POST['email']) && isset($_POST['password'])){
|
||||
if(empty($_POST['email']) || empty($_POST['password'])){
|
||||
Message::getInstance()->fail('Please fill out both email and password fields.');
|
||||
}
|
||||
else {
|
||||
// Start login
|
||||
if(Auth::login($_POST['email'], $_POST['password'])){
|
||||
Router::redirect("private");
|
||||
}
|
||||
// If login isn't successful
|
||||
else{
|
||||
//Log error message
|
||||
writeLog("WebMUM login failed for IP ".$_SERVER['REMOTE_ADDR']);
|
||||
Message::getInstance()->fail("Sorry, but we cannot log you in with this combination of email and password, there might be a typo.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<h1>Login</h1>
|
||||
|
||||
<?php output_messages(); ?>
|
||||
<?php echo Message::getInstance()->render(); ?>
|
||||
|
||||
<form class="form" action="" method="post">
|
||||
<div class="input-group">
|
||||
<label>Email address</label>
|
||||
<div class="input">
|
||||
<input type="text" name="email" placeholder="Your email address" autofocus required/><br>
|
||||
<input type="text" name="email" placeholder="Your email address" value="<?php echo isset($_POST['email']) ? $_POST['email'] : ''; ?>" autofocus required/><br>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
<?php
|
||||
require_once 'include/php/default.inc.php';
|
||||
|
||||
session_destroy();
|
||||
redirect('/');
|
||||
?>
|
|
@ -1,5 +0,0 @@
|
|||
<h1>Not allowed!</h1>
|
||||
|
||||
<p>
|
||||
Sorry, you are not allowed to access this page.
|
||||
</p>
|
|
@ -1,14 +1,13 @@
|
|||
<?php
|
||||
|
||||
if(isset($_POST['password']) && isset($_POST['password_repeat'])){
|
||||
// User tries to change password
|
||||
$change_pass_success = $user->change_password($_POST['password'], $_POST['password_repeat']);
|
||||
|
||||
if($change_pass_success === true){
|
||||
add_message("success", "Password changed successfully!");
|
||||
try{
|
||||
Auth::getUser()->changePassword($_POST['password'], $_POST['password_repeat']);
|
||||
|
||||
Message::getInstance()->success("Password changed successfully!");
|
||||
}
|
||||
else if($change_pass_success === false){
|
||||
add_message("fail", "Error while changing password! ".$PASS_ERR_MSG);
|
||||
catch(AuthException $passwordInvalidException){
|
||||
Message::getInstance()->fail($passwordInvalidException->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,21 +16,23 @@ if(isset($_POST['password']) && isset($_POST['password_repeat'])){
|
|||
<h1>Change password</h1>
|
||||
|
||||
<div class="buttons">
|
||||
<a class="button" href="<?php echo url('private'); ?>">❬ Back to personal dashboard</a>
|
||||
<a class="button" href="<?php echo Router::url('private'); ?>">❬ Back to personal dashboard</a>
|
||||
</div>
|
||||
|
||||
<?php output_messages(); ?>
|
||||
<?php echo Message::getInstance()->render(); ?>
|
||||
|
||||
<form class="form" action="" method="post" autocomplete="off">
|
||||
<div class="input-group">
|
||||
<label for="password">Password</label>
|
||||
<div class="input-info">Your new password must be at least <?php echo MIN_PASS_LENGTH; ?> characters long.</div>
|
||||
<?php if(Config::has('password.min_length')): ?>
|
||||
<div class="input-info">Your new password must be at least <?php echo Config::get('password.min_length'); ?> characters long.</div>
|
||||
<?php endif; ?>
|
||||
<div class="input input-action">
|
||||
<input type="password" name="password" placeholder="New password" required minlength="<?php echo MIN_PASS_LENGTH; ?>" autofocus/>
|
||||
<input type="password" name="password" placeholder="New password" required minlength="<?php echo Config::get('password.min_length', 0); ?>" autofocus/>
|
||||
<button type="button" class="button" onclick="pass=generatePassword();this.form.password.value=pass;this.form.password_repeat.value=pass;this.form.password.type='text';this.form.password_repeat.type='text'">Generate password</button>
|
||||
</div>
|
||||
<div class="input">
|
||||
<input type="password" name="password_repeat" placeholder="Repeat password" required minlength="<?php echo MIN_PASS_LENGTH; ?>"/>
|
||||
<input type="password" name="password_repeat" placeholder="Repeat password" required minlength="<?php echo Config::get('password.min_length', 0); ?>"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
133
include/php/pages/private/createredirect.php
Normal file
133
include/php/pages/private/createredirect.php
Normal file
|
@ -0,0 +1,133 @@
|
|||
<?php
|
||||
|
||||
if(!Config::get('options.enable_user_redirects', false)
|
||||
|| !Auth::getUser()->canCreateUserRedirects()
|
||||
){
|
||||
Router::redirect('private/redirects');
|
||||
}
|
||||
|
||||
if(isset($_POST['source'])){
|
||||
|
||||
$destination = Auth::getUser()->getEmail();
|
||||
$domain = Auth::getUser()->getDomain();
|
||||
|
||||
$inputSources = stringToEmails($_POST['source']);
|
||||
|
||||
// validate emails
|
||||
$emailErrors = array();
|
||||
|
||||
// basic email validation isn't working 100% correct though
|
||||
foreach($inputSources as $email){
|
||||
if(strpos($email, '@') === false){
|
||||
$emailErrors[$email] = "Address \"{$email}\" isn't a valid email address.";
|
||||
}
|
||||
}
|
||||
|
||||
// validate source emails are on domains
|
||||
if(Config::get('options.enable_validate_aliases_source_domain', true)){
|
||||
$domains = Domain::getByLimitedDomains();
|
||||
|
||||
foreach($inputSources as $email){
|
||||
if(isset($emailErrors[$email])){
|
||||
continue;
|
||||
}
|
||||
|
||||
$emailParts = explode('@', $email);
|
||||
if($emailParts[1] != $domain){
|
||||
$emailErrors[$email] = "Domain of source address \"{$email}\" must be \"{$domain}\".";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// validate no redirect loops
|
||||
if(in_array($destination, $inputSources)){
|
||||
$emailErrors[$destination] = "Address \"{$destination}\" cannot be in source and destination in same redirect.";
|
||||
}
|
||||
|
||||
|
||||
if(count($emailErrors) > 0){
|
||||
Message::getInstance()->fail(implode("<br>", $emailErrors));
|
||||
}
|
||||
elseif(count($inputSources) !== 1){
|
||||
Message::getInstance()->fail("Only one email address as source.");
|
||||
}
|
||||
else{
|
||||
if(count($inputSources) > 0){
|
||||
|
||||
$existingRedirects = AbstractRedirect::findWhere(
|
||||
array(AbstractRedirect::attr('source'), 'IN', $inputSources)
|
||||
);
|
||||
|
||||
if($existingRedirects->count() > 0){
|
||||
$errorMessages = array();
|
||||
/** @var AbstractRedirect $existingRedirect */
|
||||
foreach($existingRedirects as $existingRedirect){
|
||||
$errorMessages[] = "Source address \"{$existingRedirect->getSource()}\" is already redirected to some destination.";
|
||||
}
|
||||
|
||||
Message::getInstance()->fail(implode("<br>", $errorMessages));
|
||||
}
|
||||
else{
|
||||
foreach($inputSources as $inputSource){
|
||||
$data = array(
|
||||
AbstractRedirect::attr('source') => $inputSource,
|
||||
AbstractRedirect::attr('destination') => $destination,
|
||||
AbstractRedirect::attr('multi_hash') => null,
|
||||
AbstractRedirect::attr('is_created_by_user') => true,
|
||||
);
|
||||
|
||||
$a = Alias::createAndSave($data);
|
||||
}
|
||||
|
||||
// Redirect created, redirect to overview
|
||||
Router::redirect('private/redirects');
|
||||
}
|
||||
}
|
||||
else{
|
||||
Message::getInstance()->fail("Redirect couldn't be created. Fill out all fields.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$domains = Domain::getByLimitedDomains();
|
||||
?>
|
||||
|
||||
<h1>Create Redirect</h1>
|
||||
|
||||
<div class="buttons">
|
||||
<a class="button" href="<?php echo Router::url('private/redirects'); ?>">❬ Back to your redirects</a>
|
||||
</div>
|
||||
|
||||
<?php echo Message::getInstance()->render(); ?>
|
||||
|
||||
<form class="form" action="" method="post" autocomplete="off">
|
||||
|
||||
<div class="input-group">
|
||||
<label for="source">Source</label>
|
||||
<div class="input-info">
|
||||
<?php if($domains->count() > 0): ?>
|
||||
You can only create redirects with this domain:
|
||||
<ul>
|
||||
<li><?php echo Auth::getUser()->getDomain(); ?></li>
|
||||
</ul>
|
||||
<?php else: ?>
|
||||
There are no domains managed by WebMUM yet.
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="input">
|
||||
<input type="text" name="source" placeholder="Source address" required autofocus value="<?php echo formatEmailsForm(isset($_POST['source']) ? $_POST['source'] : ''); ?>"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="destination">Destination</label>
|
||||
<div class="input">
|
||||
<?php echo formatEmailsText(Auth::getUser()->getEmail()); ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="buttons">
|
||||
<button type="submit" class="button button-primary">Create redirect</button>
|
||||
</div>
|
||||
</form>
|
82
include/php/pages/private/deleteredirect.php
Normal file
82
include/php/pages/private/deleteredirect.php
Normal file
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
if(!Config::get('options.enable_user_redirects', false)
|
||||
|| !Auth::getUser()->isAllowedToCreateUserRedirects()
|
||||
){
|
||||
Router::redirect('private/redirects');
|
||||
}
|
||||
|
||||
if(!isset($_GET['id'])){
|
||||
// Redirect id not set, redirect to overview
|
||||
Router::redirect('private/redirects');
|
||||
}
|
||||
|
||||
$id = $_GET['id'];
|
||||
|
||||
/** @var AbstractRedirect $redirect */
|
||||
$redirect = AbstractRedirect::findMultiWhereFirst(
|
||||
array(
|
||||
array(AbstractRedirect::attr('id'), $id),
|
||||
array(AbstractRedirect::attr('is_created_by_user'), true),
|
||||
array(AbstractRedirect::attr('destination'), Auth::getUser()->getEmail()),
|
||||
)
|
||||
);
|
||||
|
||||
if(is_null($redirect)){
|
||||
// Redirect doesn't exist, redirect to overview
|
||||
Router::redirect('private/redirects');
|
||||
}
|
||||
|
||||
if(isset($_POST['confirm'])){
|
||||
$confirm = $_POST['confirm'];
|
||||
|
||||
if($confirm === "yes"){
|
||||
|
||||
$redirect->delete();
|
||||
|
||||
// Delete redirect successfull, redirect to overview
|
||||
Router::redirect('private/redirects/?deleted=1');
|
||||
}
|
||||
else{
|
||||
// Choose to not delete redirect, redirect to overview
|
||||
Router::redirect('private/redirects');
|
||||
}
|
||||
}
|
||||
|
||||
else{
|
||||
?>
|
||||
|
||||
<h1>Delete redirection?</h1>
|
||||
|
||||
<div class="buttons">
|
||||
<a class="button" href="<?php echo Router::url('private/redirects'); ?>">❬ Back to your redirects</a>
|
||||
</div>
|
||||
|
||||
<form class="form" action="" method="post" autocomplete="off">
|
||||
<div class="input-group">
|
||||
<label>Source</label>
|
||||
<div class="input-info"><?php echo formatEmailsText($redirect->getSource()); ?></div>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label>Destination</label>
|
||||
<div class="input-info"><?php echo formatEmailsText($redirect->getDestination()); ?></div>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="confirm">Do you realy want to delete this redirection?</label>
|
||||
<div class="input">
|
||||
<select name="confirm" autofocus required>
|
||||
<option value="no">No!</option>
|
||||
<option value="yes">Yes!</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="buttons">
|
||||
<button type="submit" class="button button-primary">Delete</button>
|
||||
</div>
|
||||
</form>
|
||||
<?php
|
||||
}
|
||||
?>
|
|
@ -5,5 +5,9 @@
|
|||
</p>
|
||||
|
||||
<div class="buttons buttons-horizontal button-large">
|
||||
<a class="button" href="<?php echo url('private/changepass'); ?>">Change e-mail account password</a>
|
||||
<a class="button" href="<?php echo Router::url('private/changepass'); ?>">Change your password</a>
|
||||
</div>
|
||||
|
||||
<div class="buttons buttons-horizontal button-large">
|
||||
<a class="button" href="<?php echo Router::url('private/redirects'); ?>">Redirects to your mailbox</a>
|
||||
</div>
|
88
include/php/pages/private/yourredirects.php
Normal file
88
include/php/pages/private/yourredirects.php
Normal file
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
$user = Auth::getUser();
|
||||
$activateUserRedirects = Config::get('options.enable_user_redirects', false) && $user->isAllowedToCreateUserRedirects();
|
||||
|
||||
$redirects = $user->getAnonymizedRedirects();
|
||||
|
||||
$userRedirectsCount = $user->getSelfCreatedRedirects()->count();
|
||||
?>
|
||||
|
||||
<h1>Redirects to your mailbox</h1>
|
||||
|
||||
<div class="buttons">
|
||||
<a class="button" href="<?php echo Router::url('private'); ?>">❬ Back to personal dashboard</a>
|
||||
<?php if($activateUserRedirects): ?>
|
||||
<?php if($user->canCreateUserRedirects()): ?>
|
||||
<a class="button" href="<?php echo Router::url('private/redirect/create'); ?>">Create new redirect</a>
|
||||
<?php else: ?>
|
||||
<a class="button button-disabled" title="You reached your user redirect limit of <?php echo $user->getMaxUserRedirects(); ?>.">Create new redirect</a>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php echo Message::getInstance()->render(); ?>
|
||||
|
||||
<?php if($activateUserRedirects): ?>
|
||||
<div class="notifications notification">
|
||||
You are allowed to create <strong><?php echo $user->getMaxUserRedirects() === 0 ? 'unlimited user redirects' : textValue('up to _ user redirect', $user->getMaxUserRedirects()); ?></strong> on your own.
|
||||
<?php if($user->getMaxUserRedirects() > 0): ?>
|
||||
<?php if($user->canCreateUserRedirects()): ?>
|
||||
<br><br>You can still create <strong><?php echo textValue('_ more user redirect', $user->getMaxUserRedirects() - $userRedirectsCount); ?></strong>.
|
||||
<?php else: ?>
|
||||
<br><br>You cannot create anymore redirects as your limit is reached.
|
||||
<br>Consider deleting unused redirects or ask an admin to extend your limit.
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if($redirects->count() > 0): ?>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Source</th>
|
||||
<th>Destination</th>
|
||||
<?php if($activateUserRedirects): ?>
|
||||
<th>Created by you</th>
|
||||
<th></th>
|
||||
<?php endif; ?>
|
||||
<tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach($redirects as $redirect): /** @var AbstractRedirect $redirect */ ?>
|
||||
<tr>
|
||||
<td><?php echo formatEmailsText($redirect->getSource()); ?></td>
|
||||
<td><?php echo formatEmailsText($redirect->getDestination()); ?></td>
|
||||
<?php if($activateUserRedirects): ?>
|
||||
<td><?php echo $redirect->isCreatedByUser() ? 'Yes' : 'No'; ?></td>
|
||||
<td>
|
||||
<?php if($redirect->isCreatedByUser()): ?>
|
||||
<a href="<?php echo Router::url('private/redirect/delete/?id='.$redirect->getId()); ?>">[Delete]</a>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<?php endif; ?>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th><?php echo textValue('_ redirect', $redirects->count()); ?></th>
|
||||
<?php if($activateUserRedirects): ?>
|
||||
<th></th>
|
||||
<th>
|
||||
<?php if($user->getMaxUserRedirects() === 0): ?>
|
||||
<?php echo textValue('_ user redirect', $userRedirectsCount); ?>
|
||||
<?php else: ?>
|
||||
<?php echo $userRedirectsCount.textValue(' of _ user redirect', $user->getMaxUserRedirects()); ?>
|
||||
<?php endif; ?>
|
||||
</th>
|
||||
<?php endif; ?>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
<?php else: ?>
|
||||
<div class="notification notification-warning">
|
||||
There are currently no redirects to your mailbox.
|
||||
</div>
|
||||
<?php endif; ?>
|
|
@ -1,17 +1,17 @@
|
|||
<?php
|
||||
if($user->isLoggedIn() === true){
|
||||
redirect("private/");
|
||||
if(Auth::isLoggedIn()){
|
||||
Router::redirect("private");
|
||||
}
|
||||
?>
|
||||
|
||||
<h1>WebMUM</h1>
|
||||
|
||||
<p>
|
||||
WebMUM is an easy to use webinterface for managing user accounts on your mailserver's MySQL user backend.<br/>
|
||||
WebMUM is an easy to use web interface for managing user accounts on your e-mail server with a MySQL user backend.<br/>
|
||||
Users of your server can log in here to change their passwords.
|
||||
</p>
|
||||
|
||||
<div class="buttons buttons-horizontal">
|
||||
<a class="button" href="<?php echo url('login'); ?>">Log in</a>
|
||||
<a class="button" href="<?php echo Router::url('login'); ?>">Log in</a>
|
||||
</div>
|
||||
|
||||
|
|
45
include/php/routes.inc.php
Normal file
45
include/php/routes.inc.php
Normal file
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
// Home
|
||||
Router::addGet('/', 'include/php/pages/start.php');
|
||||
|
||||
/**
|
||||
* Auth
|
||||
*/
|
||||
Router::addMixed('/login', 'include/php/pages/login.php');
|
||||
Router::addGet('/logout', function(){
|
||||
Auth::logout();
|
||||
Router::redirect('/');
|
||||
return;
|
||||
});
|
||||
|
||||
/**
|
||||
* Private area
|
||||
*/
|
||||
Router::addGet('/private', 'include/php/pages/private/start.php', User::ROLE_USER);
|
||||
Router::addMixed('/private/changepass', 'include/php/pages/private/changepass.php', User::ROLE_USER);
|
||||
Router::addGet('/private/redirects', 'include/php/pages/private/yourredirects.php', User::ROLE_USER);
|
||||
if(Config::get('options.enable_user_redirects', false)){
|
||||
Router::addMixed('/private/redirect/create', 'include/php/pages/private/createredirect.php', User::ROLE_USER);
|
||||
Router::addMixed('/private/redirect/delete', 'include/php/pages/private/deleteredirect.php', User::ROLE_USER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin area
|
||||
*/
|
||||
Router::addGet('/admin', 'include/php/pages/admin/start.php', User::ROLE_ADMIN);
|
||||
|
||||
// Users / Mailboxes
|
||||
Router::addGet('/admin/listusers', 'include/php/pages/admin/listusers.php', User::ROLE_ADMIN);
|
||||
Router::addMixed('/admin/edituser', 'include/php/pages/admin/edituser.php', User::ROLE_ADMIN);
|
||||
Router::addMixed('/admin/deleteuser', 'include/php/pages/admin/deleteuser.php', User::ROLE_ADMIN);
|
||||
|
||||
// Domains
|
||||
Router::addGet('/admin/listdomains', 'include/php/pages/admin/listdomains.php', User::ROLE_ADMIN);
|
||||
Router::addMixed('/admin/deletedomain', 'include/php/pages/admin/deletedomain.php', User::ROLE_ADMIN);
|
||||
Router::addMixed('/admin/createdomain', 'include/php/pages/admin/createdomain.php', User::ROLE_ADMIN);
|
||||
|
||||
// Redirects
|
||||
Router::addGet('/admin/listredirects', 'include/php/pages/admin/listredirects.php', User::ROLE_ADMIN);
|
||||
Router::addMixed('/admin/editredirect', 'include/php/pages/admin/editredirect.php', User::ROLE_ADMIN);
|
||||
Router::addMixed('/admin/deleteredirect', 'include/php/pages/admin/deleteredirect.php', User::ROLE_ADMIN);
|
5
include/php/template/error/not-allowed.php
Normal file
5
include/php/template/error/not-allowed.php
Normal file
|
@ -0,0 +1,5 @@
|
|||
<h1>Not allowed!</h1>
|
||||
|
||||
<p>
|
||||
Sorry, you aren't allowed to access this page.
|
||||
</p>
|
5
include/php/template/error/not-found.php
Normal file
5
include/php/template/error/not-found.php
Normal file
|
@ -0,0 +1,5 @@
|
|||
<h1>This page does not exist.</h1>
|
||||
|
||||
<p>
|
||||
Sorry, the page you requested couldn't be found.
|
||||
</p>
|
|
@ -1,8 +0,0 @@
|
|||
</div> <!-- Closing content -->
|
||||
|
||||
<div id="footer">
|
||||
Software by Thomas Leister and contributors, 2015<br/> WebMUM on GitHub:
|
||||
<a href="https://github.com/ThomasLeister/webmum">https://github.com/ThomasLeister/webmum</a> | License: GNU-GPL 3.0
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,40 +0,0 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>WebMUM</title>
|
||||
<link rel=stylesheet href="<?php echo url('include/css/style.css'); ?>" type="text/css" media=screen>
|
||||
<script type="text/javascript">
|
||||
function generatePassword() {
|
||||
var length = <?php echo MIN_PASS_LENGTH + 1; ?>,
|
||||
charset = "abcdefghijklnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#",
|
||||
retVal = "";
|
||||
for (var i = 0, n = charset.length; i < length; ++i) {
|
||||
retVal += charset.charAt(Math.floor(Math.random() * n));
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="header">
|
||||
<div class="title"><a href="<?php echo url('/'); ?>">WebMUM - Web Mailserver User Manager</a></div>
|
||||
<div class="header-menu">
|
||||
<?php if(user_has_permission("admin")): ?>
|
||||
<div class="header-button">
|
||||
<a href="<?php echo url('admin'); ?>">[Admin Dashboard]</a>
|
||||
</div>
|
||||
<div class="header-button">
|
||||
<a href="<?php echo url('private'); ?>">[Personal Dashboard]</a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if($user->isLoggedIn()): ?>
|
||||
<div class="header-button">
|
||||
Logged in as <?php echo $_SESSION['email']; ?>
|
||||
<a href="<?php echo url('logout'); ?>">[Logout]</a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="content"> <!-- Opening content -->
|
54
include/php/template/layout.php
Normal file
54
include/php/template/layout.php
Normal file
|
@ -0,0 +1,54 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>WebMUM</title>
|
||||
<link rel=stylesheet href="<?php echo Router::url('include/css/style.css'); ?>" type="text/css" media=screen>
|
||||
<script type="text/javascript">
|
||||
function generatePassword() {
|
||||
var length = <?php echo Config::get('password.min_length', 8) + 1; ?>,
|
||||
charset = "abcdefghijklnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#",
|
||||
retVal = "";
|
||||
for (var i = 0, n = charset.length; i < length; ++i) {
|
||||
retVal += charset.charAt(Math.floor(Math.random() * n));
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="header">
|
||||
<div class="title"><a href="<?php echo Router::url('/'); ?>">WebMUM - Web Mailserver User Manager</a></div>
|
||||
<div class="header-menu">
|
||||
<?php if(Auth::hasPermission(User::ROLE_ADMIN)): ?>
|
||||
<div class="header-button">
|
||||
<a href="<?php echo Router::url('admin'); ?>">[Admin Dashboard]</a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if(Auth::hasPermission(User::ROLE_USER)): ?>
|
||||
<div class="header-button">
|
||||
<a href="<?php echo Router::url('private'); ?>">[Personal Dashboard]</a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if(Auth::isLoggedIn()): ?>
|
||||
<div class="header-button">
|
||||
Logged in as <?php echo Auth::getUser()->getEmail(); ?>
|
||||
<a href="<?php echo Router::url('logout'); ?>">[Logout]</a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
<?php echo $content; ?>
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
<ul>
|
||||
<li>Powered by WebMUM (<a target="_blank" href="https://git.io/vwXhh">https://github.com/ohartl/webmum</a>).</li>
|
||||
<li>Developed by Oliver Hartl, Thomas Leister and contributors.</li>
|
||||
<li>License: MIT</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
165
index.php
165
index.php
|
@ -1,136 +1,49 @@
|
|||
<?php
|
||||
/*
|
||||
* #################### This is WebMUM Version 0.1.9 ######################
|
||||
*
|
||||
* Project on GitHub: https://github.com/ThomasLeister/webmum
|
||||
* Author's Blog: https://thomas-leister.de
|
||||
*
|
||||
* Please report bugs on GitHub.
|
||||
*
|
||||
* Copyright (C) 2014 Thomas Leister
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
define("BACKEND_BASE_PATH", preg_replace("#index.php#", "", $_SERVER['SCRIPT_FILENAME']));
|
||||
require_once 'include/php/default.inc.php';
|
||||
|
||||
require_once 'include/php/template/header.php';
|
||||
|
||||
function load_page($p){
|
||||
|
||||
if(preg_match("/^\/private(.*)$/", $p) == 1){
|
||||
// Page is user page
|
||||
if(user_has_permission("user")){
|
||||
switch($p){
|
||||
case "/private/":
|
||||
return "include/php/pages/private/start.php";
|
||||
break;
|
||||
case "/private/changepass/":
|
||||
return "include/php/pages/private/changepass.php";
|
||||
break;
|
||||
default:
|
||||
return "include/php/pages/404.php";
|
||||
}
|
||||
}
|
||||
else{ return "include/php/pages/not-allowed.php"; }
|
||||
}
|
||||
|
||||
else if(preg_match("/^\/admin(.*)$/", $p) == 1){
|
||||
// Page is admin page
|
||||
if(user_has_permission("admin")){
|
||||
switch($p){
|
||||
case "/admin/":
|
||||
return "include/php/pages/admin/start.php";
|
||||
break;
|
||||
case "/admin/listusers/":
|
||||
return "include/php/pages/admin/listusers.php";
|
||||
break;
|
||||
case "/admin/edituser/":
|
||||
return "include/php/pages/admin/edituser.php";
|
||||
break;
|
||||
case "/admin/deleteuser/":
|
||||
return "include/php/pages/admin/deleteuser.php";
|
||||
break;
|
||||
case "/admin/listdomains/":
|
||||
return "include/php/pages/admin/listdomains.php";
|
||||
break;
|
||||
case "/admin/deletedomain/":
|
||||
return "include/php/pages/admin/deletedomain.php";
|
||||
break;
|
||||
case "/admin/createdomain/":
|
||||
return "include/php/pages/admin/createdomain.php";
|
||||
break;
|
||||
case "/admin/listredirects/":
|
||||
return "include/php/pages/admin/listredirects.php";
|
||||
break;
|
||||
case "/admin/editredirect/":
|
||||
return "include/php/pages/admin/editredirect.php";
|
||||
break;
|
||||
case "/admin/deleteredirect/":
|
||||
return "include/php/pages/admin/deleteredirect.php";
|
||||
break;
|
||||
default:
|
||||
return "include/php/pages/404.php";
|
||||
}
|
||||
}
|
||||
else{ return "include/php/pages/not-allowed.php"; }
|
||||
}
|
||||
|
||||
else{
|
||||
// Page is public accessible
|
||||
switch($p){
|
||||
case "/login/":
|
||||
return "include/php/pages/login.php";
|
||||
break;
|
||||
case "/logout/":
|
||||
return "include/php/pages/logout.php";
|
||||
break;
|
||||
case "/":
|
||||
return "include/php/pages/start.php";
|
||||
break;
|
||||
default:
|
||||
return "include/php/pages/404.php";
|
||||
}
|
||||
if (php_sapi_name() == "cli-server") {
|
||||
// running under built-in server
|
||||
$extensions = array("php", "jpg", "jpeg", "css", "js");
|
||||
$path = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
|
||||
$ext = pathinfo($path, PATHINFO_EXTENSION);
|
||||
if (in_array($ext, $extensions)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
/**
|
||||
* Loading system
|
||||
*/
|
||||
require_once 'include/php/default.inc.php';
|
||||
|
||||
$path = $_SERVER["REQUEST_URI"];
|
||||
// Remove GET Parameters
|
||||
$path = preg_replace('/\?.*/', '', $path);
|
||||
// Remove prescending directory part e.g. webmum/ defined in SUBDIR
|
||||
$path = preg_replace("#".SUBDIR."#", '', $path);
|
||||
|
||||
// Webserver should add trailing slash, but if there is no trailing slash for any reason, add one ;)
|
||||
if(strrpos($path,"/") != strlen($path)-1){
|
||||
$path = $path."/";
|
||||
if(defined('INSTALLER_ENABLED')){
|
||||
/**
|
||||
* Load installer
|
||||
*/
|
||||
$content = Router::loadAndBufferOutput('installer/index.php');
|
||||
}
|
||||
else {
|
||||
/**
|
||||
* Handle request
|
||||
*/
|
||||
$content = Router::executeCurrentRequest();
|
||||
}
|
||||
}
|
||||
catch(DatabaseException $e){
|
||||
$content = '<div class="notification notification-fail">Faulty database query: "'.$e->getQuery().'".</div>';
|
||||
}
|
||||
catch(Exception $e){
|
||||
$content = '<div class="notification notification-fail">'.$e->getMessage().'</div>';
|
||||
}
|
||||
|
||||
if(defined('USING_OLD_CONFIG')){
|
||||
$content = '<div class="notification notification-fail"><strong>Your WebMUM installation is still using the old deprecated config style!</strong><br><br>Please update your config to the new style (an example config can be found in <cite>config.php.example</cite>)<br>and delete your old <cite>config.inc.php</cite> and <cite>config.inc.php.example</cite>.</div>'.$content;
|
||||
}
|
||||
|
||||
/*
|
||||
* Include page content here
|
||||
*/
|
||||
|
||||
include load_page($path);
|
||||
|
||||
|
||||
/*
|
||||
* End of dynamic content
|
||||
*/
|
||||
|
||||
require_once 'include/php/template/footer.php';
|
||||
include_once 'include/php/db_close.inc.php';
|
||||
?>
|
||||
|
||||
|
||||
echo Router::loadAndBufferOutput(
|
||||
'include/php/template/layout.php',
|
||||
array(
|
||||
'content' => $content,
|
||||
)
|
||||
);
|
1
installer/.htaccess
Normal file
1
installer/.htaccess
Normal file
|
@ -0,0 +1 @@
|
|||
Deny from all
|
125
installer/index.php
Normal file
125
installer/index.php
Normal file
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
|
||||
if(strpos($_SERVER['REQUEST_URI'], 'installer/') !== false){
|
||||
die('You cannot directly access the installer files.');
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
|
||||
define('INSTALLER_MAX_STEP', 6);
|
||||
|
||||
define('INSTALLER_TYPE_CREATE', 0);
|
||||
define('INSTALLER_TYPE_MAP', 1);
|
||||
|
||||
$installerStepTitles = array(
|
||||
'Requirements',
|
||||
'Database connection',
|
||||
'Database schema',
|
||||
'Your first admin user',
|
||||
'General settings',
|
||||
'Optional features',
|
||||
'Finish installation',
|
||||
);
|
||||
|
||||
$installerStepMapping = array(
|
||||
0 => 0,
|
||||
1 => 1,
|
||||
2 => 2,
|
||||
3 => 2,
|
||||
4 => 3,
|
||||
5 => 4,
|
||||
6 => 5,
|
||||
7 => 6,
|
||||
);
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
|
||||
function installer_reset()
|
||||
{
|
||||
global $_SESSION;
|
||||
|
||||
$_SESSION['installer'] = array(
|
||||
'lastStep' => 0,
|
||||
'step' => 0,
|
||||
'config' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
function installer_message($setMessage = null)
|
||||
{
|
||||
global $_SESSION;
|
||||
|
||||
if(!is_null($setMessage)){
|
||||
$_SESSION['installer']['message'] = $setMessage;
|
||||
}
|
||||
elseif(isset($_SESSION['installer']['message'])){
|
||||
$m = '<div class="notification notification-success">'.$_SESSION['installer']['message'].'</div>';
|
||||
unset($_SESSION['installer']['message']);
|
||||
|
||||
return $m;
|
||||
}
|
||||
|
||||
return $setMessage;
|
||||
}
|
||||
|
||||
function installer_prev($thisStep, $stepSize = 1)
|
||||
{
|
||||
$s = ($thisStep < 0) ? 0 : ($thisStep - $stepSize);
|
||||
|
||||
$_SESSION['installer']['lastStep'] = $thisStep;
|
||||
$_SESSION['installer']['step'] = $s;
|
||||
|
||||
Router::redirect('/?step='.$s);
|
||||
}
|
||||
|
||||
function installer_next($thisStep, $stepSize = 1)
|
||||
{
|
||||
$s = ($thisStep > 8) ? 8 : ($thisStep + $stepSize);
|
||||
|
||||
$_SESSION['installer']['lastStep'] = $thisStep;
|
||||
$_SESSION['installer']['step'] = $s;
|
||||
|
||||
Router::redirect('/?step='.$s);
|
||||
}
|
||||
|
||||
if(!isset($_SESSION['installer'])){
|
||||
installer_reset();
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
|
||||
$step = (isset($_GET['step']) && is_numeric($_GET['step'])) ? intval($_GET['step']) : 0;
|
||||
|
||||
echo '<h1>Installation of WebMUM</h1>';
|
||||
|
||||
if($step > 0){
|
||||
?>
|
||||
<ol style="font-size: 1.1em;">
|
||||
<?php for($s = 1; $s <= INSTALLER_MAX_STEP; $s++): ?>
|
||||
<li>
|
||||
<?php if(isset($installerStepMapping[$step]) && $s < $installerStepMapping[$step]): ?>
|
||||
<span style="color: #999;"><?php echo $installerStepTitles[$s]; ?></span>
|
||||
<?php elseif(isset($installerStepMapping[$step]) && $s === $installerStepMapping[$step]): ?>
|
||||
<strong><?php echo $installerStepTitles[$s]; ?></strong>
|
||||
<?php else: ?>
|
||||
<?php echo $installerStepTitles[$s]; ?>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
<?php endfor; ?>
|
||||
</ol>
|
||||
<?php
|
||||
}
|
||||
|
||||
try{
|
||||
$stepFile = __DIR__.'/step'.$step.'.php';
|
||||
if(file_exists($stepFile)){
|
||||
include_once $stepFile;
|
||||
}
|
||||
else{
|
||||
installer_reset();
|
||||
echo 'Wizard step '.$step.' is missing.';
|
||||
}
|
||||
}
|
||||
catch(Exception $e){
|
||||
echo $e->getMessage();
|
||||
}
|
103
installer/step0.php
Normal file
103
installer/step0.php
Normal file
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
if(strpos($_SERVER['REQUEST_URI'], 'installer/') !== false){
|
||||
die('You cannot directly access the installer files.');
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
|
||||
$thisStep = 0;
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
|
||||
$requirements = array();
|
||||
$numberOfRequirements = 5;
|
||||
if(version_compare(phpversion(), '5.4.0', '>=')){
|
||||
$requirements[] = 'php_version';
|
||||
}
|
||||
if(function_exists('mysqli_connect')){
|
||||
$requirements[] = 'php_extension_mysqli';
|
||||
}
|
||||
if(session_status() != PHP_SESSION_DISABLED){
|
||||
$requirements[] = 'php_session_enabled';
|
||||
}
|
||||
if(file_exists('config') && is_dir('config')){
|
||||
$requirements[] = 'config_directory';
|
||||
}
|
||||
if(file_exists('config/config.php.example')){
|
||||
$requirements[] = 'config_example';
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
|
||||
if(isset($_GET['go']) && $_GET['go'] == 'next'){
|
||||
if(count($requirements) === $numberOfRequirements){
|
||||
installer_message('All requirements fulfilled, let\'s get started with the installation!');
|
||||
|
||||
installer_next($thisStep);
|
||||
}
|
||||
}
|
||||
?>
|
||||
<?php echo installer_message(); ?>
|
||||
|
||||
<h2>Getting started</h2>
|
||||
|
||||
<p>By following this wizard you will install and configure your new WebMUM installation.</p>
|
||||
|
||||
<hr>
|
||||
|
||||
<strong>System Info:</strong>
|
||||
<ul>
|
||||
<li>System: <strong><?php echo php_uname(); ?></strong></li>
|
||||
<li>Hostname: <strong><?php echo isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'n/a'; ?></strong></li>
|
||||
<li>IP: <strong><?php echo isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : (isset($_SERVER['SERVER_NAME']) ? gethostbyname($_SERVER['SERVER_NAME']) : 'n/a'); ?></strong></li>
|
||||
<li>PHP version: <strong><?php echo phpversion(); ?></strong></li>
|
||||
<li>Server API: <strong><?php echo php_sapi_name(); ?></strong></li>
|
||||
<li>WebMUM directory: <strong><?php echo dirname($_SERVER['SCRIPT_FILENAME']); ?></strong></li>
|
||||
</ul>
|
||||
|
||||
<strong>Server requirements</strong>
|
||||
<ul>
|
||||
<?php if(in_array('php_version', $requirements)): ?>
|
||||
<li class="text-success">PHP version (>=5.4.0 or >=7.0.0): <strong><?php echo phpversion(); ?> ✓</strong></li>
|
||||
<?php else: ?>
|
||||
<li class="text-fail">PHP version (>=5.4.0 or >=7.0.0): <strong><?php echo phpversion(); ?> ❌</strong></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
|
||||
<strong>Required PHP settings</strong>
|
||||
<ul>
|
||||
<?php if(in_array('php_extension_mysqli', $requirements)): ?>
|
||||
<li class="text-success">Database extension (mysqli): <strong>enabled ✓</strong></li>
|
||||
<?php else: ?>
|
||||
<li class="text-fail">Database extension (mysqli): <strong>disabled ❌</strong></li>
|
||||
<?php endif; ?>
|
||||
<?php if(in_array('php_session_enabled', $requirements)): ?>
|
||||
<li class="text-success">Session support: <strong>enabled ✓</strong></li>
|
||||
<?php else: ?>
|
||||
<li class="text-fail">Session support: <strong>disabled ❌</strong></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
|
||||
<strong>Directories and files</strong>
|
||||
<ul>
|
||||
<?php if(in_array('config_directory', $requirements)): ?>
|
||||
<li class="text-success">"config/": <strong>exists ✓</strong></li>
|
||||
<?php else: ?>
|
||||
<li class="text-fail">"config/": <strong>is missing ❌</strong></li>
|
||||
<?php endif; ?>
|
||||
<?php if(in_array('config_example', $requirements)): ?>
|
||||
<li class="text-success">"config/config.php.example": <strong>exists ✓</strong></li>
|
||||
<?php else: ?>
|
||||
<li class="text-fail">"config/config.php.example": <strong>is missing ❌</strong></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
|
||||
<?php if(count($requirements) === $numberOfRequirements):?>
|
||||
<p>Click on the Start button to continue.</p>
|
||||
<a class="button button-primary" href="/?step=<?php echo $thisStep; ?>&go=next">Start</a>
|
||||
<?php else:?>
|
||||
<p class="notification notification-fail">Some requirements aren't fulfilled.</p>
|
||||
<?php endif; ?>
|
131
installer/step1.php
Normal file
131
installer/step1.php
Normal file
|
@ -0,0 +1,131 @@
|
|||
<?php
|
||||
|
||||
if(strpos($_SERVER['REQUEST_URI'], 'installer/') !== false){
|
||||
die('You cannot directly access the installer files.');
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
|
||||
$thisStep = 1;
|
||||
|
||||
$error = null;
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
|
||||
if(isset($_GET['go'])){
|
||||
if($_GET['go'] == 'next' && $_SERVER['REQUEST_METHOD'] == 'POST'){
|
||||
try{
|
||||
// testing db settings
|
||||
Database::init($_POST);
|
||||
|
||||
// saving information
|
||||
$_SESSION['installer']['config']['mysql'] = array(
|
||||
'host' => $_POST['host'],
|
||||
'user' => $_POST['user'],
|
||||
'password' => $_POST['password'],
|
||||
'database' => $_POST['database'],
|
||||
);
|
||||
$_SESSION['installer']['type'] = (isset($_POST['install_type']) && $_POST['install_type'] == INSTALLER_TYPE_MAP)
|
||||
? INSTALLER_TYPE_MAP
|
||||
: INSTALLER_TYPE_CREATE;
|
||||
|
||||
installer_message('Database connection was successfully established.');
|
||||
|
||||
installer_next($thisStep, ($_SESSION['installer']['type'] === INSTALLER_TYPE_MAP) ? 2 : 1);
|
||||
}
|
||||
catch(InvalidArgumentException $e){
|
||||
$error = 'Some fields are missing.';
|
||||
}
|
||||
catch(Exception $e){
|
||||
$error = $e->getMessage();
|
||||
}
|
||||
}
|
||||
elseif($_GET['go'] == 'prev'){
|
||||
// reset
|
||||
unset($_SESSION['installer']['config']['mysql']);
|
||||
unset($_SESSION['installer']['type']);
|
||||
|
||||
installer_prev($thisStep);
|
||||
}
|
||||
}
|
||||
|
||||
function getAttr($name, $default = null)
|
||||
{
|
||||
global $_SESSION, $_POST;
|
||||
|
||||
if(isset($_POST[$name])){
|
||||
return strip_tags($_POST[$name]);
|
||||
}
|
||||
elseif(isset($_SESSION['installer']['config']['mysql'][$name])){
|
||||
return $_SESSION['installer']['config']['mysql'][$name];
|
||||
}
|
||||
elseif($name === 'install_type' && isset($_SESSION['installer']['type'])){
|
||||
return $_SESSION['installer']['type'];
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
?>
|
||||
<?php echo installer_message(); ?>
|
||||
|
||||
<h2>Step 1 of <?php echo INSTALLER_MAX_STEP; ?>: Database connection.</h2>
|
||||
|
||||
<?php if(!empty($error)): ?>
|
||||
<div class="notification notification-fail"><?php echo $error; ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form class="form" action="/?step=<?php echo $thisStep; ?>&go=next" method="post">
|
||||
|
||||
<p>Setup your MySQL database connection.</p>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="host">Database Host</label>
|
||||
<div class="input">
|
||||
<input type="text" name="host" value="<?php echo getAttr('host', 'localhost'); ?>" autofocus/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="database">Database Name</label>
|
||||
<div class="input">
|
||||
<input type="text" name="database" value="<?php echo getAttr('database'); ?>"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="user">Database Username</label>
|
||||
<div class="input">
|
||||
<input type="text" name="user" value="<?php echo getAttr('user'); ?>"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="password">Database Password</label>
|
||||
<div class="input">
|
||||
<input type="password" name="password" value="<?php echo getAttr('password'); ?>"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="install_type">Installation Type</label>
|
||||
<div class="input-info">Be sure to select the correct option.</div>
|
||||
<div class="input">
|
||||
<input type="radio" name="install_type" id="install_type_0" value="0" <?php echo getAttr('install_type', 0) == 0 ? 'checked' : ''; ?>/>
|
||||
<label for="install_type_0">Create new database schema</label>
|
||||
</div>
|
||||
<div class="input">
|
||||
<input type="radio" name="install_type" id="install_type_1" value="1" <?php echo getAttr('install_type', 0) == 1 ? 'checked' : ''; ?>/>
|
||||
<label for="install_type_1">Map existing database schema</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="invisible">
|
||||
|
||||
<div class="buttons">
|
||||
<a class="button" href="/?step=<?php echo $thisStep; ?>&go=prev">Back</a>
|
||||
<button class="button button-primary" type="submit">Continue</button>
|
||||
</div>
|
||||
</form>
|
209
installer/step2.php
Normal file
209
installer/step2.php
Normal file
|
@ -0,0 +1,209 @@
|
|||
<?php
|
||||
|
||||
if(strpos($_SERVER['REQUEST_URI'], 'installer/') !== false){
|
||||
die('You cannot directly access the installer files.');
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
|
||||
$thisStep = 2;
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
|
||||
$exampleConfigValues = require_once 'config/config.php.example';
|
||||
|
||||
$tablesInDatabase = array();
|
||||
try{
|
||||
Database::init($_SESSION['installer']['config']['mysql']);
|
||||
|
||||
$tablesResult = Database::getInstance()->query("SELECT table_name FROM information_schema.tables WHERE table_schema='".$_SESSION['installer']['config']['mysql']['database']."';");
|
||||
foreach($tablesResult->fetch_all() as $row){
|
||||
$tablesInDatabase[] = $row[0];
|
||||
}
|
||||
}
|
||||
catch(Exception $e){
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
|
||||
$databaseSchema = array(
|
||||
'domains' => "CREATE TABLE IF NOT EXISTS ___database___.___table___ (___id___ INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, ___domain___ VARCHAR(128) NOT NULL, PRIMARY KEY (___domain___), UNIQUE KEY ___id___ (___id___)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;",
|
||||
'users' => "CREATE TABLE IF NOT EXISTS ___database___.___table___ (___id___ INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, ___username___ VARCHAR(128) NOT NULL DEFAULT '', ___domain___ VARCHAR(128) NOT NULL DEFAULT '', ___password___ VARCHAR(128) NOT NULL DEFAULT '', ___mailbox_limit___ INT(10) NOT NULL DEFAULT '128', ___max_user_redirects___ INT(10) NOT NULL DEFAULT '0', PRIMARY KEY (___username___,___domain___), UNIQUE KEY ___id___ (___id___)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;",
|
||||
'aliases' => "CREATE TABLE IF NOT EXISTS ___database___.___table___ (___id___ INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, ___source___ VARCHAR(128) NOT NULL, ___destination___ TEXT NOT NULL, ___multi_source___ VARCHAR(32) DEFAULT NULL, ___is_created_by_user___ INT(1) NOT NULL DEFAULT '0', PRIMARY KEY (___source___), UNIQUE KEY ___id___ (___id___)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;",
|
||||
);
|
||||
|
||||
/**
|
||||
* @param string $stmt
|
||||
* @param string $database
|
||||
* @param string $table
|
||||
* @param array $attributes
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function prepareSchemaTableStmt($stmt, $database, $table, $attributes)
|
||||
{
|
||||
$attributes['database'] = $database;
|
||||
$attributes['table'] = $table;
|
||||
|
||||
foreach($attributes as $search => $replace){
|
||||
$stmt = str_replace('___'.$search.'___', '`'.Database::getInstance()->escape($replace).'`', $stmt);
|
||||
}
|
||||
|
||||
return $stmt;
|
||||
}
|
||||
|
||||
$preparedSchemaStmt = '';
|
||||
$allTablesFromSchemaExist = true;
|
||||
|
||||
foreach($databaseSchema as $table => $stmt){
|
||||
$preparedSchemaStmt .= prepareSchemaTableStmt(
|
||||
$stmt,
|
||||
$_SESSION['installer']['config']['mysql']['database'],
|
||||
$exampleConfigValues['schema']['tables'][$table],
|
||||
$exampleConfigValues['schema']['attributes'][$table]
|
||||
).PHP_EOL;
|
||||
|
||||
// check if tables exist, should be enough for now
|
||||
if(!in_array($exampleConfigValues['schema']['tables'][$table], $tablesInDatabase)){
|
||||
$allTablesFromSchemaExist = false;
|
||||
}
|
||||
}
|
||||
|
||||
$commandDenied = false;
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
|
||||
if(isset($_GET['go'])){
|
||||
if($_GET['go'] == 'next' && $_SERVER['REQUEST_METHOD'] == 'POST'){
|
||||
if(isset($_POST['manual'])){
|
||||
if($_POST['manual'] == 1){
|
||||
// display SQL
|
||||
}
|
||||
elseif($_POST['manual'] == 2){
|
||||
// check if schema was created
|
||||
if($allTablesFromSchemaExist){
|
||||
// saving information
|
||||
$_SESSION['installer']['config']['schema'] = $exampleConfigValues['schema'];
|
||||
|
||||
installer_message('Database schema was manually created.');
|
||||
|
||||
installer_next($thisStep, 2);
|
||||
}
|
||||
else{
|
||||
$_POST['manual'] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
if(!$allTablesFromSchemaExist){
|
||||
try{
|
||||
foreach(explode(PHP_EOL, $preparedSchemaStmt) as $stmt){
|
||||
Database::getInstance()->query($stmt);
|
||||
}
|
||||
|
||||
// saving information
|
||||
$_SESSION['installer']['config']['schema'] = $exampleConfigValues['schema'];
|
||||
|
||||
installer_message('Database schema was automatically created.');
|
||||
|
||||
installer_next($thisStep, 2);
|
||||
}
|
||||
catch(Exception $e){
|
||||
if(strpos($e->getMessage(), 'command denied') !== false){
|
||||
$commandDenied = true;
|
||||
}
|
||||
else{
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif($_GET['go'] == 'prev'){
|
||||
// reset
|
||||
unset($_SESSION['installer']['config']['schema']);
|
||||
|
||||
installer_prev($thisStep);
|
||||
}
|
||||
}
|
||||
?>
|
||||
<?php echo installer_message(); ?>
|
||||
|
||||
<h2>Step 2 of <?php echo INSTALLER_MAX_STEP; ?>: Create database schema.</h2>
|
||||
|
||||
<?php if($allTablesFromSchemaExist): ?>
|
||||
<div class="notification notification-fail">
|
||||
The schema already exists in database "<?php echo $_SESSION['installer']['config']['mysql']['database']; ?>".
|
||||
</div>
|
||||
|
||||
<div>
|
||||
Your next possible steps:
|
||||
<ul>
|
||||
<li>Either <strong>delete</strong> the existing schema.</li>
|
||||
<li>Go Back and <strong>change</strong> the used database.</li>
|
||||
<li>Go Back and <strong>start mapping</strong> the existing database schema.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="buttons">
|
||||
<a class="button" href="/?step=<?php echo $thisStep; ?>&go=prev">Back</a>
|
||||
<a class="button" href="/?step=<?php echo $thisStep; ?>">Retry</a>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<form class="form" action="/?step=<?php echo $thisStep; ?>&go=next" method="post">
|
||||
<?php if(isset($_POST['manual']) && $_POST['manual'] == 1): ?>
|
||||
<textarea readonly style="width: 100%; height: 170px"><?php echo $preparedSchemaStmt; ?></textarea>
|
||||
|
||||
<div class="notification notification-warning">
|
||||
Copy the SQL-Code above and import it into your database "<?php echo $_SESSION['installer']['config']['mysql']['database']; ?>".
|
||||
</div>
|
||||
|
||||
<hr class="invisible">
|
||||
|
||||
<p>Once you have imported the schema, you can continue by clicking on the Continue button.</p>
|
||||
|
||||
<div class="buttons">
|
||||
<a class="button" href="/?step=<?php echo $thisStep; ?>">Back</a>
|
||||
<button class="button button-primary" name="manual" value="2" type="submit">Continue</button>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="notification notification-warning">
|
||||
The following database schema will be created in
|
||||
<strong>database "<?php echo $_SESSION['installer']['config']['mysql']['database']; ?>"</strong>.
|
||||
<br><strong>Please make sure that "<?php echo $_SESSION['installer']['config']['mysql']['database']; ?>" is clean / empty database!</strong>
|
||||
</div>
|
||||
|
||||
<?php if($commandDenied): ?>
|
||||
<div class="notification notification-fail">The
|
||||
<strong>user "<?php echo $_SESSION['installer']['config']['mysql']['user']; ?>" is missing the permission</strong> to execute MySQL "CREATE" commands.
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="notification notification-warning">
|
||||
Also <strong>make sure</strong> that the database
|
||||
<strong>user "<?php echo $_SESSION['installer']['config']['mysql']['user']; ?>" has the privileges to create</strong> the schema.
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php foreach($exampleConfigValues['schema']['tables'] as $table => $mappedTable): ?>
|
||||
<div>
|
||||
<strong>Table "<?php echo $table; ?>"</strong>
|
||||
<ul>
|
||||
<?php foreach($exampleConfigValues['schema']['attributes'][$table] as $attribute => $mappedAttribute): ?>
|
||||
<li><?php echo $attribute; ?></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<hr class="invisible">
|
||||
|
||||
<p>Click on the Continue button to try creating the schema automatically.</p>
|
||||
|
||||
<div class="buttons">
|
||||
<a class="button" href="/?step=<?php echo $thisStep; ?>&go=prev">Back</a>
|
||||
<button class="button" name="manual" value="1" type="submit">Import schema manually</button>
|
||||
<button class="button button-primary" type="submit">Continue</button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
<?php endif; ?>
|
306
installer/step3.php
Normal file
306
installer/step3.php
Normal file
|
@ -0,0 +1,306 @@
|
|||
<?php
|
||||
|
||||
if(strpos($_SERVER['REQUEST_URI'], 'installer/') !== false){
|
||||
die('You cannot directly access the installer files.');
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
|
||||
$thisStep = 3;
|
||||
|
||||
if($_SESSION['installer']['lastStep'] > $thisStep){
|
||||
$_SESSION['installer']['subStep'] = 1;
|
||||
}
|
||||
elseif($_SESSION['installer']['lastStep'] < $thisStep || !isset($_SESSION['installer']['subStep'])){
|
||||
$_SESSION['installer']['subStep'] = 0;
|
||||
}
|
||||
|
||||
$error = null;
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
|
||||
$exampleConfigValues = require_once 'config/config.php.example';
|
||||
|
||||
$tablesInDatabase = array();
|
||||
try{
|
||||
Database::init($_SESSION['installer']['config']['mysql']);
|
||||
|
||||
$db = Database::getInstance();
|
||||
$tablesResult = $db->query(
|
||||
"SELECT TABLE_NAME FROM information_schema.tables "
|
||||
."WHERE TABLE_SCHEMA='".$db->escape($_SESSION['installer']['config']['mysql']['database'])."';"
|
||||
);
|
||||
|
||||
foreach($tablesResult->fetch_all() as $row){
|
||||
$tablesInDatabase[] = $row[0];
|
||||
}
|
||||
}
|
||||
catch(Exception $e){
|
||||
}
|
||||
|
||||
function getTableAttributes($table)
|
||||
{
|
||||
global $_SESSION;
|
||||
$attributes = array();
|
||||
|
||||
if(Database::isInitialized()){
|
||||
try{
|
||||
$db = Database::getInstance();
|
||||
$tablesResult = $db->query(
|
||||
"SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_DEFAULT, COLUMN_KEY, EXTRA FROM information_schema.columns "
|
||||
."WHERE TABLE_SCHEMA = '".$db->escape($_SESSION['installer']['config']['mysql']['database'])."' "
|
||||
."AND TABLE_NAME = '".$db->escape($table)."' "
|
||||
."ORDER BY TABLE_NAME,ORDINAL_POSITION;"
|
||||
);
|
||||
|
||||
foreach($tablesResult->fetch_all() as $row){
|
||||
$s = $row[0];
|
||||
|
||||
if(!empty($row[1])){
|
||||
$s .= ' : '.$row[1];
|
||||
}
|
||||
|
||||
if($row[2] == 'NO'){
|
||||
$s .= ', NOT NULL';
|
||||
}
|
||||
|
||||
if(!is_null($row[3])){
|
||||
$s .= ', DEFAULT \''.$row[3].'\'';
|
||||
}
|
||||
|
||||
if(!empty($row[4])){
|
||||
if(strpos($row[4], 'PR') !== false){
|
||||
$s .= ', PRIMARY KEY';
|
||||
}
|
||||
if(strpos($row[4], 'UN') !== false){
|
||||
$s .= ', UNIQUE KEY';
|
||||
}
|
||||
}
|
||||
|
||||
if(!empty($row[5]) && strpos($row[5], 'auto_inc') !== false){
|
||||
$s .= ', AUTO_INCREMENT';
|
||||
}
|
||||
|
||||
$attributes[$row[0]] = $s;
|
||||
}
|
||||
}
|
||||
catch(Exception $e){
|
||||
}
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
$optionalAttributes = array(
|
||||
'users' => array('mailbox_limit', 'max_user_redirects'),
|
||||
'aliases' => array('multi_source', 'is_created_by_user'),
|
||||
);
|
||||
|
||||
define('ATTR_SEP', '---');
|
||||
|
||||
function getAttr($name, $default = null)
|
||||
{
|
||||
global $_SESSION, $_POST;
|
||||
|
||||
if(isset($_POST[$name])){
|
||||
return strip_tags($_POST[$name]);
|
||||
}
|
||||
elseif(strpos($name, ATTR_SEP) !== false){
|
||||
list($table, $attribute) = explode(ATTR_SEP, $name);
|
||||
|
||||
if(isset($_SESSION['installer']['config']['schema']['attributes'][$table][$attribute])){
|
||||
return $_SESSION['installer']['config']['schema']['attributes'][$table][$attribute];
|
||||
}
|
||||
}
|
||||
elseif(isset($_SESSION['installer']['config']['schema']['tables'][$name])){
|
||||
return $_SESSION['installer']['config']['schema']['tables'][$name];
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
|
||||
if(isset($_GET['go'])){
|
||||
if($_GET['go'] == 'next' && $_SERVER['REQUEST_METHOD'] == 'POST'){
|
||||
try{
|
||||
if($_SESSION['installer']['subStep'] === 0){
|
||||
|
||||
$tables = array();
|
||||
foreach($exampleConfigValues['schema']['tables'] as $table => $mappedTable){
|
||||
if(!isset($_POST[$table])
|
||||
|| !in_array($_POST[$table], $tablesInDatabase)
|
||||
){
|
||||
throw new InvalidArgumentException('Missing mapping for table "'.$table.'".');
|
||||
}
|
||||
|
||||
if(in_array($_POST[$table], array_values($tables))){
|
||||
throw new Exception('You cannot map table "'.$_POST[$table].'" twice.');
|
||||
}
|
||||
|
||||
$tables[$table] = $_POST[$table];
|
||||
}
|
||||
|
||||
// saving information
|
||||
$_SESSION['installer']['config']['schema'] = array();
|
||||
$_SESSION['installer']['config']['schema']['tables'] = $tables;
|
||||
|
||||
installer_message('Database tables were successfully mapped.');
|
||||
|
||||
$_SESSION['installer']['subStep'] = 1;
|
||||
installer_next($thisStep, 0);
|
||||
}
|
||||
elseif($_SESSION['installer']['subStep'] === 1){
|
||||
|
||||
$attributes = array();
|
||||
foreach($_SESSION['installer']['config']['schema']['tables'] as $table => $mappedTable){
|
||||
|
||||
$attributes[$table] = array();
|
||||
|
||||
$attributesInDatabase = getTableAttributes($table);
|
||||
|
||||
foreach($exampleConfigValues['schema']['attributes'][$table] as $attribute => $mappedAttribute){
|
||||
$key = $table.'---'.$attribute;
|
||||
|
||||
if(isset($optionalAttributes[$table])
|
||||
&& in_array($attribute, $optionalAttributes[$table])
|
||||
&& !isset($attributesInDatabase[$_POST[$key]])
|
||||
){
|
||||
$attributes[$table][$attribute] = '';
|
||||
}
|
||||
else{
|
||||
if(!isset($_POST[$key]) || !isset($attributesInDatabase[$_POST[$key]])){
|
||||
throw new InvalidArgumentException('Missing mapping for attribute "'.$attribute.'" on table "'.$table.'".');
|
||||
}
|
||||
|
||||
if(in_array($_POST[$key], $attributes[$table])){
|
||||
throw new Exception('You cannot map attribute "'.$_POST[$key].'" twice on table "'.$table.'".');
|
||||
}
|
||||
|
||||
$attributes[$table][$attribute] = $_POST[$key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// saving information
|
||||
$_SESSION['installer']['config']['schema']['attributes'] = $attributes;
|
||||
|
||||
installer_message('Database attributes were successfully mapped.');
|
||||
|
||||
unset($_SESSION['installer']['subStep']);
|
||||
installer_next($thisStep);
|
||||
}
|
||||
}
|
||||
catch(Exception $e){
|
||||
$error = $e->getMessage();
|
||||
}
|
||||
}
|
||||
elseif($_GET['go'] == 'prev'){
|
||||
|
||||
// reset
|
||||
if(isset($_SESSION['installer']['config']['schema']['tables'])){
|
||||
if($_SESSION['installer']['subStep'] === 0){
|
||||
unset($_SESSION['installer']['config']['schema']);
|
||||
}
|
||||
elseif($_SESSION['installer']['subStep'] === 1){
|
||||
unset($_SESSION['installer']['config']['schema']['attributes']);
|
||||
}
|
||||
}
|
||||
|
||||
if($_SESSION['installer']['subStep'] === 0){
|
||||
unset($_SESSION['installer']['subStep']);
|
||||
installer_prev($thisStep, ($_SESSION['installer']['type'] === INSTALLER_TYPE_MAP) ? 2 : 1);
|
||||
}
|
||||
else{
|
||||
$_SESSION['installer']['subStep'] = 0;
|
||||
installer_prev($thisStep, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<?php echo installer_message(); ?>
|
||||
|
||||
<h2>
|
||||
Step 2 of <?php echo INSTALLER_MAX_STEP; ?>:
|
||||
<?php if($_SESSION['installer']['subStep'] === 0): ?>
|
||||
Database - table mapping.
|
||||
<?php elseif($_SESSION['installer']['subStep'] === 1): ?>
|
||||
Database - attribute mapping.
|
||||
<?php else: ?>
|
||||
Wrong turn
|
||||
<?php endif; ?>
|
||||
</h2>
|
||||
|
||||
<?php if(!empty($error)): ?>
|
||||
<div class="notification notification-fail"><?php echo $error; ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if($_SESSION['installer']['subStep'] === 0): ?>
|
||||
<form class="form" action="/?step=<?php echo $thisStep; ?>&go=next" method="post">
|
||||
<?php foreach($exampleConfigValues['schema']['tables'] as $table => $mappedTable): ?>
|
||||
<div class="input-group">
|
||||
<label for="<?php echo $table; ?>">Table "<?php echo $table; ?>"</label>
|
||||
<div class="input">
|
||||
<select name="<?php echo $table; ?>">
|
||||
<option value="">-- Not mapped --</option>
|
||||
<?php foreach($tablesInDatabase as $t): ?>
|
||||
<option value="<?php echo $t; ?>" <?php echo getAttr($table, $mappedTable) == $t ? 'selected' : ''; ?>><?php echo $t; ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<hr class="invisible">
|
||||
|
||||
<div class="buttons">
|
||||
<a class="button" href="/?step=<?php echo $thisStep; ?>&go=prev">Back</a>
|
||||
<button class="button button-primary" type="submit">Continue</button>
|
||||
</div>
|
||||
</form>
|
||||
<?php elseif($_SESSION['installer']['subStep'] === 1): ?>
|
||||
<form class="form" action="/?step=<?php echo $thisStep; ?>&go=next" method="post">
|
||||
<?php
|
||||
$lastTable = array_keys($_SESSION['installer']['config']['schema']['tables']);
|
||||
$lastTable = $lastTable[count($lastTable) - 1];
|
||||
foreach($_SESSION['installer']['config']['schema']['tables'] as $table => $mappedTable):
|
||||
$attributesInDatabase = getTableAttributes($mappedTable);
|
||||
?>
|
||||
<h3>
|
||||
Table "<?php echo $table; ?>"
|
||||
<div class="sub-header">Has been mapped to table "<?php echo $mappedTable; ?>".</div>
|
||||
</h3>
|
||||
<div style="margin-left: 25px;">
|
||||
<?php foreach($exampleConfigValues['schema']['attributes'][$table] as $attribute => $mappedAttribute): ?>
|
||||
<div class="input-group">
|
||||
<label for="<?php echo $table.ATTR_SEP.$attribute; ?>">Attribute "<?php echo $attribute; ?>"</label>
|
||||
<?php if(isset($optionalAttributes[$table]) && in_array($attribute, $optionalAttributes[$table])): ?>
|
||||
<div class="input-info">This attribute is optional (used by optional features) and doesn't need to be mapped.</div>
|
||||
<?php endif; ?>
|
||||
<div class="input">
|
||||
<select name="<?php echo $table.ATTR_SEP.$attribute; ?>">
|
||||
<option value="">-- Not mapped --</option>
|
||||
<?php foreach($attributesInDatabase as $dbAttr => $dbAttrText): ?>
|
||||
<option value="<?php echo $dbAttr; ?>" <?php echo getAttr($table.ATTR_SEP.$attribute, $mappedAttribute) == $dbAttr ? 'selected' : ''; ?>><?php echo $dbAttrText; ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php if($table != $lastTable): ?>
|
||||
<hr>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<hr class="invisible">
|
||||
|
||||
<div class="buttons">
|
||||
<a class="button" href="/?step=<?php echo $thisStep; ?>&go=prev">Back</a>
|
||||
<button class="button button-primary" type="submit">Continue</button>
|
||||
</div>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<div class="notification notification-fail">You took the wrong turn, <a href="/">restart installation</a>.</div>
|
||||
<?php endif; ?>
|
216
installer/step4.php
Normal file
216
installer/step4.php
Normal file
|
@ -0,0 +1,216 @@
|
|||
<?php
|
||||
|
||||
if(strpos($_SERVER['REQUEST_URI'], 'installer/') !== false){
|
||||
die('You cannot directly access the installer files.');
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
|
||||
$thisStep = 4;
|
||||
|
||||
$error = null;
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
|
||||
$exampleConfigValues = require_once 'config/config.php.example';
|
||||
|
||||
$hashAlgorithms = array(
|
||||
'SHA-512',
|
||||
'SHA-256',
|
||||
'BLOWFISH',
|
||||
);
|
||||
|
||||
Database::init($_SESSION['installer']['config']['mysql']);
|
||||
|
||||
$databaseUserCount = Database::getInstance()->count(
|
||||
$_SESSION['installer']['config']['schema']['tables']['users'],
|
||||
$_SESSION['installer']['config']['schema']['attributes']['users']['id']
|
||||
);
|
||||
|
||||
function getAttr($name, $default = null)
|
||||
{
|
||||
global $_SESSION, $_POST;
|
||||
|
||||
if(isset($_POST[$name])){
|
||||
return strip_tags($_POST[$name]);
|
||||
}
|
||||
elseif(isset($_SESSION['installer']['config']['password'][$name])){
|
||||
return $_SESSION['installer']['config']['password'][$name];
|
||||
}
|
||||
elseif($name === 'admin_user' && isset($_SESSION['installer']['user']['user'])){
|
||||
return $_SESSION['installer']['user']['user'];
|
||||
}
|
||||
elseif($name === 'admin_password' && isset($_SESSION['installer']['user']['password'])){
|
||||
return $_SESSION['installer']['user']['password'];
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
|
||||
if(isset($_GET['go'])){
|
||||
|
||||
if($_GET['go'] == 'next' && $_SERVER['REQUEST_METHOD'] == 'POST'){
|
||||
try{
|
||||
if(!isset($_POST['hash_algorithm']) || !isset($_POST['min_length']) || !isset($_POST['admin_user']) || !isset($_POST['admin_password'])){
|
||||
throw new InvalidArgumentException;
|
||||
}
|
||||
|
||||
$passwordConfig = array(
|
||||
'hash_algorithm' => in_array($_POST['hash_algorithm'], $hashAlgorithms) ? $_POST['hash_algorithm'] : $exampleConfigValues['password']['hash_algorithm'],
|
||||
'min_length' => intval($_POST['min_length']),
|
||||
);
|
||||
|
||||
// init system for testing
|
||||
Config::init(array('password' => $passwordConfig));
|
||||
|
||||
// handle user
|
||||
if($databaseUserCount > 0){
|
||||
// testing existing login
|
||||
|
||||
$validLogin = Auth::login($_POST['admin_user'], $_POST['admin_password']);
|
||||
unset($_SESSION[Auth::SESSION_IDENTIFIER]);
|
||||
|
||||
if(!$validLogin){
|
||||
throw new Exception('Invalid combination of user and password.');
|
||||
}
|
||||
}
|
||||
else{
|
||||
// create user in database
|
||||
|
||||
if(strpos($_POST['admin_user'], '@') === false){
|
||||
throw new Exception('The field "Your user" must be an email address.');
|
||||
}
|
||||
else{
|
||||
list($username, $domain) = explode('@', $_POST['admin_user']);
|
||||
$passwordHash = Auth::generatePasswordHash($_POST['admin_password']);
|
||||
|
||||
$hasDomain = Database::getInstance()->count(
|
||||
$_SESSION['installer']['config']['schema']['tables']['domains'],
|
||||
$_SESSION['installer']['config']['schema']['attributes']['domains']['id'],
|
||||
array($_SESSION['installer']['config']['schema']['attributes']['domains']['domain'], $domain)
|
||||
);
|
||||
if($hasDomain === 0){
|
||||
Database::getInstance()->insert(
|
||||
$_SESSION['installer']['config']['schema']['tables']['domains'],
|
||||
array(
|
||||
$_SESSION['installer']['config']['schema']['attributes']['domains']['domain'] => $domain,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Database::getInstance()->insert(
|
||||
$_SESSION['installer']['config']['schema']['tables']['users'],
|
||||
array(
|
||||
$_SESSION['installer']['config']['schema']['attributes']['users']['username'] => $username,
|
||||
$_SESSION['installer']['config']['schema']['attributes']['users']['domain'] => $domain,
|
||||
$_SESSION['installer']['config']['schema']['attributes']['users']['password'] => $passwordHash,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// saving information
|
||||
$_SESSION['installer']['config']['password'] = $passwordConfig;
|
||||
$_SESSION['installer']['config']['admins'] = array($_POST['admin_user']);
|
||||
$_SESSION['installer']['config']['admin_domain_limits'] = array();
|
||||
$_SESSION['installer']['user'] = array(
|
||||
'user' => $_POST['admin_user'],
|
||||
'password' => $_POST['admin_password'],
|
||||
);
|
||||
|
||||
installer_message('You have successfully added your first admin user.');
|
||||
|
||||
installer_next($thisStep);
|
||||
}
|
||||
catch(InvalidArgumentException $e){
|
||||
$error = 'Some fields are missing.';
|
||||
}
|
||||
catch(Exception $e){
|
||||
$error = $e->getMessage();
|
||||
}
|
||||
}
|
||||
elseif($_GET['go'] == 'prev'){
|
||||
// reset
|
||||
unset($_SESSION['installer']['config']['password']);
|
||||
unset($_SESSION['installer']['config']['admins']);
|
||||
unset($_SESSION['installer']['config']['admin_domain_limits']);
|
||||
unset($_SESSION['installer']['user']);
|
||||
|
||||
installer_prev($thisStep, ($_SESSION['installer']['type'] === INSTALLER_TYPE_MAP) ? 1 : 2);
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<?php echo installer_message(); ?>
|
||||
|
||||
<h2>Step 3 of <?php echo INSTALLER_MAX_STEP; ?>: Your first admin user.</h2>
|
||||
|
||||
<?php if(!empty($error)): ?>
|
||||
<div class="notification notification-fail"><?php echo $error; ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form class="form" action="/?step=<?php echo $thisStep; ?>&go=next" method="post">
|
||||
<div class="input-group">
|
||||
<label for="password">Password hash algorithm</label>
|
||||
<div class="input-info">Hash algorithm that you chose in your mailserver installation process.</div>
|
||||
<div class="input">
|
||||
<select name="hash_algorithm">
|
||||
<?php foreach($hashAlgorithms as $algo): ?>
|
||||
<option value="<?php echo $algo; ?>" <?php echo getAttr('hash_algorithm', $exampleConfigValues['password']['hash_algorithm']) == $algo ? 'selected' : ''; ?>>
|
||||
<?php echo $algo; ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="min_length">Minimum password length</label>
|
||||
<div class="input">
|
||||
<div class="input input-labeled input-labeled-right">
|
||||
<input name="min_length" type="number" value="<?php echo getAttr('min_length', $exampleConfigValues['password']['min_length']); ?>" placeholder="Mailbox limit in MB" min="0"/>
|
||||
<span class="input-label">chars</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<?php if($databaseUserCount === 0): ?>
|
||||
<div class="notification notification-warning">
|
||||
There is no user created yet, please create one now as your admin user.
|
||||
<br>Please note that once the user is created you will have to remember the password.
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<p>This user will be mark as an admin in the configuration.</p>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="admin_user">Your user</label>
|
||||
<div class="input-info">
|
||||
Must be an email address (user@domain).<br>
|
||||
<?php if($databaseUserCount > 0): ?>
|
||||
This user must have been added in mailserver installation process.<br>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="input">
|
||||
<input type="text" name="admin_user" value="<?php echo getAttr('admin_user'); ?>"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="admin_password">Your password</label>
|
||||
<div class="input">
|
||||
<input type="password" name="admin_password" value="<?php echo getAttr('admin_password'); ?>"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="invisible">
|
||||
|
||||
<div class="buttons">
|
||||
<a class="button" href="/?step=<?php echo $thisStep; ?>&go=prev">Back</a>
|
||||
<button class="button button-primary" type="submit">Continue</button>
|
||||
</div>
|
||||
</form>
|
150
installer/step5.php
Normal file
150
installer/step5.php
Normal file
|
@ -0,0 +1,150 @@
|
|||
<?php
|
||||
|
||||
if(strpos($_SERVER['REQUEST_URI'], 'installer/') !== false){
|
||||
die('You cannot directly access the installer files.');
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
|
||||
$thisStep = 5;
|
||||
|
||||
$error = null;
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
|
||||
$exampleConfigValues = require_once 'config/config.php.example';
|
||||
|
||||
$possibleEmailSeparatorsText = array(', ', '; ', "\n");
|
||||
$possibleEmailSeparatorsForm = array(',', ';', "\n");
|
||||
|
||||
function getAttr($name, $default = null)
|
||||
{
|
||||
global $_SESSION, $_POST;
|
||||
|
||||
if(isset($_POST[$name])){
|
||||
return strip_tags($_POST[$name]);
|
||||
}
|
||||
elseif($name === 'base_url' && isset($_SESSION['installer']['config']['base_url'])){
|
||||
return $_SESSION['installer']['config']['base_url'];
|
||||
}
|
||||
elseif(isset($_SESSION['installer']['config']['frontend_options'][$name])){
|
||||
return $_SESSION['installer']['config']['frontend_options'][$name];
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
|
||||
if(isset($_GET['go'])){
|
||||
if($_GET['go'] == 'next' && $_SERVER['REQUEST_METHOD'] == 'POST'){
|
||||
try{
|
||||
if(!isset($_POST['base_url']) || empty($_POST['base_url'])){
|
||||
throw new Exception('The field URL isn\'t filled out yet.');
|
||||
}
|
||||
if(!isset($_POST['email_separator_text'])
|
||||
|| !is_numeric($_POST['email_separator_text'])
|
||||
|| !isset($possibleEmailSeparatorsText[$_POST['email_separator_text']])
|
||||
|| !isset($_POST['email_separator_form'])
|
||||
|| !is_numeric($_POST['email_separator_form'])
|
||||
|| !isset($possibleEmailSeparatorsForm[$_POST['email_separator_form']])
|
||||
){
|
||||
throw new InvalidArgumentException;
|
||||
}
|
||||
|
||||
// saving information
|
||||
$_SESSION['installer']['config']['base_url'] = $_POST['base_url'];
|
||||
$_SESSION['installer']['config']['frontend_options'] = array(
|
||||
'email_separator_text' => $possibleEmailSeparatorsText[$_POST['email_separator_text']],
|
||||
'email_separator_form' => $possibleEmailSeparatorsForm[$_POST['email_separator_form']],
|
||||
);
|
||||
|
||||
installer_message('General settings saved.');
|
||||
|
||||
installer_next($thisStep);
|
||||
}
|
||||
catch(InvalidArgumentException $e){
|
||||
$error = 'Some field is missing.';
|
||||
}
|
||||
catch(Exception $e){
|
||||
$error = $e->getMessage();
|
||||
}
|
||||
}
|
||||
elseif($_GET['go'] == 'prev'){
|
||||
// reset
|
||||
unset($_SESSION['installer']['config']['base_url']);
|
||||
unset($_SESSION['installer']['config']['frontend_options']);
|
||||
|
||||
installer_prev($thisStep);
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<?php echo installer_message(); ?>
|
||||
|
||||
<h2>Step 4 of <?php echo INSTALLER_MAX_STEP; ?>: General settings</h2>
|
||||
|
||||
<?php if(!empty($error)): ?>
|
||||
<div class="notification notification-fail"><?php echo $error; ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form class="form" action="/?step=<?php echo $thisStep; ?>&go=next" method="post">
|
||||
|
||||
<div class="input-group">
|
||||
<label for="base_url">URL to this WebMUM installation</label>
|
||||
<div class="input-info">
|
||||
The URL your WebMUM installation is accessible from outside including subdirectories, ports and the protocol.
|
||||
<br><br>Some examples:
|
||||
<ul style="margin: 2px 0">
|
||||
<li>http://localhost/webmum</li>
|
||||
<li>http://webmum.mydomain.tld</li>
|
||||
<li>https://mydomain.tld/dir</li>
|
||||
<li>http://mydomain.tld:8080</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="input">
|
||||
<input type="text" name="base_url" value="<?php echo getAttr('base_url'); ?>"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="input-group">
|
||||
<label>Separator for email lists</label>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="email_separator_text">… in texts.</label>
|
||||
<div class="input">
|
||||
<input type="radio" name="email_separator_text" id="email_separator_text_0" value="0" <?php echo (getAttr('email_separator_text', 0) == 0) ? 'checked' : ''; ?>>
|
||||
<label for="email_separator_text_0">comma: <code>', '</code></label>
|
||||
|
||||
<input type="radio" name="email_separator_text" id="email_separator_text_1" value="1" <?php echo (getAttr('email_separator_text', 0) == 1) ? 'checked' : ''; ?>>
|
||||
<label for="email_separator_text_1">semicolon: <code>'; '</code></label>
|
||||
|
||||
<input type="radio" name="email_separator_text" id="email_separator_text_2" value="2" <?php echo (getAttr('email_separator_text', 0) == 2) ? 'checked' : ''; ?>>
|
||||
<label for="email_separator_text_2">newline: <code>'<br>'</code></label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="email_separator_form">… in forms.</label>
|
||||
<div class="input">
|
||||
<input type="radio" name="email_separator_form" id="email_separator_form_0" value="0" <?php echo (getAttr('email_separator_form', 0) == 0) ? 'checked' : ''; ?>>
|
||||
<label for="email_separator_form_0">comma: <code>','</code></label>
|
||||
|
||||
<input type="radio" name="email_separator_form" id="email_separator_form_1" value="1" <?php echo (getAttr('email_separator_form', 0) == 1) ? 'checked' : ''; ?>>
|
||||
<label for="email_separator_form_1">semicolon: <code>';'</code></label>
|
||||
|
||||
<input type="radio" name="email_separator_form" id="email_separator_form_2" value="2" <?php echo (getAttr('email_separator_form', 0) == 2) ? 'checked' : ''; ?>>
|
||||
<label for="email_separator_form_2">newline: <code>'\n'</code></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="invisible">
|
||||
|
||||
<div class="buttons">
|
||||
<a class="button" href="/?step=<?php echo $thisStep; ?>&go=prev">Back</a>
|
||||
<button class="button button-primary" type="submit">Continue</button>
|
||||
</div>
|
||||
</form>
|
270
installer/step6.php
Normal file
270
installer/step6.php
Normal file
|
@ -0,0 +1,270 @@
|
|||
<?php
|
||||
|
||||
if(strpos($_SERVER['REQUEST_URI'], 'installer/') !== false){
|
||||
die('You cannot directly access the installer files.');
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
|
||||
$thisStep = 6;
|
||||
|
||||
$error = null;
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
|
||||
$exampleConfigValues = require_once 'config/config.php.example';
|
||||
|
||||
function getAttr($name, $default = null)
|
||||
{
|
||||
global $_SESSION, $_POST;
|
||||
|
||||
if(isset($_POST[$name])){
|
||||
return strip_tags($_POST[$name]);
|
||||
}
|
||||
elseif(isset($_SESSION['installer']['config']['options'][$name])){
|
||||
return $_SESSION['installer']['config']['options'][$name];
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
|
||||
if(isset($_GET['go'])){
|
||||
|
||||
if($_GET['go'] == 'next' && $_SERVER['REQUEST_METHOD'] == 'POST'){
|
||||
try{
|
||||
$options = array();
|
||||
|
||||
// Mailbox limits
|
||||
if(isset($_POST['enable_mailbox_limits']) && $_POST['enable_mailbox_limits'] == 1){
|
||||
if(empty($_SESSION['installer']['config']['schema']['attributes']['users']['mailbox_limit'])){
|
||||
throw new Exception('Mailbox limits couldn\'t be enabled, because the attribute "mailbox_limit" in database table "users" is missing or not mapped yet');
|
||||
}
|
||||
else{
|
||||
$options['enable_mailbox_limits'] = true;
|
||||
}
|
||||
}
|
||||
else{
|
||||
$options['enable_mailbox_limits'] = false;
|
||||
}
|
||||
|
||||
// Validate source addresses in redirects
|
||||
if(isset($_POST['enable_validate_aliases_source_domain']) && $_POST['enable_validate_aliases_source_domain'] == 1){
|
||||
$options['enable_validate_aliases_source_domain'] = true;
|
||||
}
|
||||
else{
|
||||
$options['enable_validate_aliases_source_domain'] = false;
|
||||
}
|
||||
|
||||
// Multiple source redirect support
|
||||
if(isset($_POST['enable_multi_source_redirects']) && $_POST['enable_multi_source_redirects'] == 1){
|
||||
if(empty($_SESSION['installer']['config']['schema']['attributes']['aliases']['multi_source'])){
|
||||
throw new Exception('Multiple source redirect support couldn\'t be enabled, because the attribute "multi_source" in database table "aliases" is missing or not mapped yet');
|
||||
}
|
||||
else{
|
||||
$options['enable_multi_source_redirects'] = true;
|
||||
}
|
||||
}
|
||||
else{
|
||||
$options['enable_multi_source_redirects'] = false;
|
||||
}
|
||||
|
||||
// Admin domain limits
|
||||
if(isset($_POST['enable_admin_domain_limits']) && $_POST['enable_admin_domain_limits'] == 1){
|
||||
$options['enable_admin_domain_limits'] = true;
|
||||
}
|
||||
else{
|
||||
$options['enable_admin_domain_limits'] = false;
|
||||
}
|
||||
|
||||
// Users redirects
|
||||
if(isset($_POST['enable_user_redirects']) && $_POST['enable_user_redirects'] == 1){
|
||||
if(empty($_SESSION['installer']['config']['schema']['attributes']['users']['max_user_redirects'])
|
||||
|| empty($_SESSION['installer']['config']['schema']['attributes']['aliases']['is_created_by_user'])
|
||||
){
|
||||
throw new Exception('Users redirects couldn\'t be enabled, because some database attributes are missing or not mapped yet');
|
||||
}
|
||||
else{
|
||||
$options['enable_user_redirects'] = true;
|
||||
}
|
||||
}
|
||||
else{
|
||||
$options['enable_user_redirects'] = false;
|
||||
}
|
||||
|
||||
// Logging for failed login attempts
|
||||
$logPath = '';
|
||||
if(isset($_POST['enable_logging']) && $_POST['enable_logging'] == 1){
|
||||
$options['enable_logging'] = true;
|
||||
|
||||
if(!isset($_POST['log_path']) || empty($_POST['log_path'])){
|
||||
throw new Exception('You need to set the log path if you enabled logging.');
|
||||
}
|
||||
|
||||
$logPath = $_POST['log_path'];
|
||||
|
||||
if(!file_exists($_POST['log_path'])){
|
||||
throw new Exception('The log path you set doesn\'t exist.');
|
||||
}
|
||||
|
||||
if(!is_writable($_POST['log_path'])){
|
||||
throw new Exception('The log path you set isn\'t writable.');
|
||||
}
|
||||
}
|
||||
else{
|
||||
$options['enable_logging'] = false;
|
||||
}
|
||||
|
||||
// saving information
|
||||
$_SESSION['installer']['config']['options'] = $options;
|
||||
$_SESSION['installer']['config']['log_path'] = $logPath;
|
||||
|
||||
installer_message('Saved settings for optional features.');
|
||||
|
||||
installer_next($thisStep);
|
||||
}
|
||||
catch(Exception $e){
|
||||
$error = $e->getMessage();
|
||||
}
|
||||
}
|
||||
elseif($_GET['go'] == 'prev'){
|
||||
// reset
|
||||
unset($_SESSION['installer']['config']['options']);
|
||||
unset($_SESSION['installer']['config']['log_path']);
|
||||
|
||||
installer_prev($thisStep);
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<?php echo installer_message(); ?>
|
||||
|
||||
<h2>Step 5 of <?php echo INSTALLER_MAX_STEP; ?>: Optional features</h2>
|
||||
|
||||
<?php if(!empty($error)): ?>
|
||||
<div class="notification notification-fail"><?php echo $error; ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form class="form" action="/?step=<?php echo $thisStep; ?>&go=next" method="post">
|
||||
|
||||
<div class="input-group">
|
||||
<label for="enable_mailbox_limits">Mailbox limits</label>
|
||||
<div class="input-info">Limit the maximum size of mailbox for users.</div>
|
||||
<?php if(empty($_SESSION['installer']['config']['schema']['attributes']['users']['mailbox_limit'])): ?>
|
||||
<p class="text-warning">
|
||||
<strong>This feature cannot be enabled because the attribute "mailbox_limit" in database table "users" is missing or not mapped yet.</strong>
|
||||
<br><br>You could go back and create / map the missing attribute.
|
||||
</p>
|
||||
<?php else: ?>
|
||||
<div class="input">
|
||||
<input type="checkbox" name="enable_mailbox_limits" id="enable_mailbox_limits" value="1" <?php echo getAttr('enable_mailbox_limits', false) ? 'checked' : ''; ?>>
|
||||
<label for="enable_mailbox_limits">Enable feature</label>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="enable_validate_aliases_source_domain">Validate source addresses in redirects</label>
|
||||
<div class="input-info">Only email addresses ending with a domain from domains will be allowed.</div>
|
||||
<div class="input">
|
||||
<input type="checkbox" name="enable_validate_aliases_source_domain" id="enable_validate_aliases_source_domain" value="1" <?php echo getAttr('enable_validate_aliases_source_domain', true) ? 'checked' : ''; ?>>
|
||||
<label for="enable_validate_aliases_source_domain">Enable feature</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="enable_multi_source_redirects">Multiple source redirect support</label>
|
||||
<div class="input-info">Redirects can have multiple source addresses. This enables you to enter multiple redirects to a destination at once.</div>
|
||||
<?php if(empty($_SESSION['installer']['config']['schema']['attributes']['aliases']['multi_source'])): ?>
|
||||
<p class="text-warning">
|
||||
<strong>This feature cannot be enabled because the attribute "multi_source" in database table "aliases" is missing or not mapped yet.</strong>
|
||||
<br><br>You could go back and create / map the missing attribute.
|
||||
</p>
|
||||
<?php else: ?>
|
||||
<div class="input">
|
||||
<input type="checkbox" name="enable_multi_source_redirects" id="enable_multi_source_redirects" value="1" <?php echo getAttr('enable_multi_source_redirects', false) ? 'checked' : ''; ?>>
|
||||
<label for="enable_multi_source_redirects">Enable feature</label>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="enable_admin_domain_limits">Admin domain limits</label>
|
||||
<div class="input-info">
|
||||
Limit certain admins to have access to certain domains only.
|
||||
<br>Note: This needs to be manually configured in the <code>'admin_domain_limits'</code> config variable.
|
||||
</div>
|
||||
<div class="input">
|
||||
<input type="checkbox" name="enable_admin_domain_limits" id="enable_admin_domain_limits" value="1" <?php echo getAttr('enable_admin_domain_limits', false) ? 'checked' : ''; ?>>
|
||||
<label for="enable_admin_domain_limits">Enable feature</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="enable_user_redirects">Users redirects</label>
|
||||
<div class="input-info">
|
||||
Enable users to create their redirects on their own.
|
||||
<br>Users can also be limited to a maximum number of redirects they can create.
|
||||
</div>
|
||||
<?php if(empty($_SESSION['installer']['config']['schema']['attributes']['users']['max_user_redirects']) || empty($_SESSION['installer']['config']['schema']['attributes']['aliases']['is_created_by_user'])): ?>
|
||||
<p class="text-warning">
|
||||
<strong>This feature cannot be enabled because,
|
||||
<?php if(empty($_SESSION['installer']['config']['schema']['attributes']['users']['max_user_redirects']) && empty($_SESSION['installer']['config']['schema']['attributes']['aliases']['is_created_by_user'])): ?>
|
||||
there are missing attributes in two database tables:</strong>
|
||||
<ul>
|
||||
<li>"max_user_redirects" in "users"</li>
|
||||
<li>"is_created_by_user" in "aliases"</li>
|
||||
</ul>
|
||||
<br>You could go back and create / map the missing attributes.
|
||||
<?php else: ?>
|
||||
the attribute <?php echo empty($_SESSION['installer']['config']['schema']['attributes']['users']['max_user_redirects']) ? '"max_user_redirects" in database table "users"' : '"is_created_by_user" in database table "aliases"'; ?> is missing or not mapped yet.
|
||||
<?php endif; ?>
|
||||
</strong>
|
||||
<br><br>You could go back and create / map the missing attributes.
|
||||
</p>
|
||||
<?php else: ?>
|
||||
<div class="input">
|
||||
<input type="checkbox" name="enable_user_redirects" id="enable_user_redirects" value="1" <?php echo getAttr('enable_user_redirects', false) ? 'checked' : ''; ?>>
|
||||
<label for="enable_user_redirects">Enable feature</label>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="enable_logging">Logging for failed login attempts</label>
|
||||
<div class="input-info">
|
||||
WebMUM will write messages into the logfile.
|
||||
<br>The logfile could be used by <strong>Fail2ban</strong> to block brute-forcing attacks.
|
||||
</div>
|
||||
<div class="input">
|
||||
<input type="checkbox" name="enable_logging" id="enable_logging" value="1" <?php echo getAttr('enable_logging', false) ? 'checked' : ''; ?>>
|
||||
<label for="enable_logging">Enable feature</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="log_path">Log path</label>
|
||||
<div class="input-info">Directory where the <code>webmum.log</code> should be written to:</div>
|
||||
<div class="input">
|
||||
<input type="text" name="log_path" value="<?php echo getAttr('log_path'); ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="invisible">
|
||||
|
||||
<div class="buttons">
|
||||
<a class="button" href="/?step=<?php echo $thisStep; ?>&go=prev">Back</a>
|
||||
<button class="button button-primary" type="submit">Continue</button>
|
||||
</div>
|
||||
</form>
|
152
installer/step7.php
Normal file
152
installer/step7.php
Normal file
|
@ -0,0 +1,152 @@
|
|||
<?php
|
||||
|
||||
if(strpos($_SERVER['REQUEST_URI'], 'installer/') !== false){
|
||||
die('You cannot directly access the installer files.');
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
|
||||
$thisStep = 7;
|
||||
|
||||
$error = '';
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
|
||||
$configPath = dirname(__DIR__).DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'config.php';
|
||||
|
||||
$configString = '<?php'.PHP_EOL
|
||||
.'// This config has been automatically generated by the WebMUM installer.'.PHP_EOL.PHP_EOL
|
||||
.'return '.var_export($_SESSION['installer']['config'], true).';'.PHP_EOL;
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
|
||||
if(isset($_SESSION['installer']['finished'])){
|
||||
if(!file_exists($configPath)){
|
||||
unset($_SESSION['installer']['finished']);
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($_GET['go'])){
|
||||
|
||||
if($_GET['go'] == 'next' && $_SERVER['REQUEST_METHOD'] == 'POST'){
|
||||
try{
|
||||
if(isset($_POST['automatic']) && $_POST['automatic'] == 1){
|
||||
if(file_exists($configPath)){
|
||||
throw new Exception('The file "'.$configPath.'"" already exists, if you\'ve already written the config manually then complete manually.');
|
||||
}
|
||||
|
||||
if(!file_exists(dirname($configPath)) || !is_dir(dirname($configPath))){
|
||||
throw new Exception('The directory "'.dirname($configPath).'"" is missing.');
|
||||
}
|
||||
|
||||
// Write config
|
||||
if(file_put_contents($configPath, $configString) === false){
|
||||
throw new Exception('Couldn\'t automatically write config to "'.$configPath.'", please write the config on your own.');
|
||||
}
|
||||
|
||||
$_SESSION['installer']['finished'] = true;
|
||||
}
|
||||
elseif(isset($_POST['manual']) && $_POST['manual'] == 1){
|
||||
|
||||
if(!file_exists($configPath)){
|
||||
throw new Exception('You need to write the config file first before you can manually complete the installation.');
|
||||
}
|
||||
|
||||
$configValues = require_once 'config/config.php';
|
||||
if(!is_array($configValues)){
|
||||
throw new Exception('The data in the config file is invalid, please try again and be sure to use the config provided below.');
|
||||
}
|
||||
|
||||
$_SESSION['installer']['finished'] = true;
|
||||
}
|
||||
}
|
||||
catch(Exception $e){
|
||||
$error = $e->getMessage();
|
||||
}
|
||||
}
|
||||
elseif($_GET['go'] == 'finish'){
|
||||
try{
|
||||
if(isset($_SESSION['installer']['finished'])){
|
||||
// Load config
|
||||
$configValues = include_once 'config/config.php';
|
||||
if(!is_array($configValues)){
|
||||
throw new Exception('Error writing the config, please manually write the config to "'.$configPath.'".');
|
||||
}
|
||||
|
||||
// Init system
|
||||
Config::init($configValues);
|
||||
Database::init(Config::get('mysql'));
|
||||
Auth::init();
|
||||
|
||||
// Login user
|
||||
Auth::login($_SESSION['installer']['user']['user'], $_SESSION['installer']['user']['password']);
|
||||
|
||||
// Reset installer
|
||||
unset($_SESSION['installer']);
|
||||
|
||||
Router::redirect('/');
|
||||
}
|
||||
}
|
||||
catch(Exception $e){
|
||||
$error = $e->getMessage();
|
||||
}
|
||||
}
|
||||
elseif($_GET['go'] == 'prev'){
|
||||
installer_prev($thisStep);
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<?php if(isset($_SESSION['installer']['finished'])): ?>
|
||||
<form class="form" action="/?step=<?php echo $thisStep; ?>&go=finish" method="post">
|
||||
<div class="notification notification-success">You finished your installation!</div>
|
||||
|
||||
<h2 style="text-align: center;">Welcome to WebMUM - Web Mailserver User Manager.</h2>
|
||||
|
||||
<div>
|
||||
If you like this project, be sure to give us a Star and Follow the project on GitHub <a target="_blank" href="https://git.io/vwXhh">https://github.com/ohartl/webmum</a>
|
||||
|
||||
<ol>
|
||||
<li>To change the configuration you have to edit to config file "<?php echo $configPath; ?>" (see <a target="_blank" href="https://git.io/vwXhh#webmum-configuration">the README</a> for further instructions.</li>
|
||||
<li>If you've found a bug or got a great idea, feel free to submit an issue on GitHub <a target="_blank" href="https://git.io/vrnOM">here</a>.</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<hr class="invisible">
|
||||
|
||||
<p>By clicking Finish you will end the installation process and get logged in automatically.</p>
|
||||
|
||||
<div class="buttons">
|
||||
<button class="button button-primary" type="submit">Finish & Start using WebMUM</button>
|
||||
</div>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<?php echo installer_message(); ?>
|
||||
|
||||
<h2>Step 6 of <?php echo INSTALLER_MAX_STEP; ?>: Write the config & finish the installation!</h2>
|
||||
|
||||
<?php if(!empty($error)): ?>
|
||||
<div class="notification notification-fail"><?php echo $error; ?></div>
|
||||
<?php endif; ?>
|
||||
<form class="form" action="/?step=<?php echo $thisStep; ?>&go=next" method="post">
|
||||
<p>The following config needs to be written to <code><?php echo $configPath; ?></code>.</p>
|
||||
|
||||
<textarea readonly style="width: 100%; height: 500px;"><?php echo $configString; ?></textarea>
|
||||
|
||||
<hr class="invisible">
|
||||
|
||||
<div>
|
||||
This is the last step, you are almost there!<br>
|
||||
<ul>
|
||||
<li>Click "Already manually completed" if you already wrote the config to "<?php echo $configPath; ?>".</li>
|
||||
<li>Or click "Complete automatically" if you want the installer to do the work for you.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="buttons">
|
||||
<a class="button" href="/?step=<?php echo $thisStep; ?>&go=prev">Back</a>
|
||||
<button class="button" name="manual" value="1" type="submit">Already manually completed</button>
|
||||
<button class="button button-primary" name="automatic" value="1" type="submit">Complete automatically!</button>
|
||||
</div>
|
||||
</form>
|
||||
<?php endif; ?>
|
13
phpunit.xml
Normal file
13
phpunit.xml
Normal file
|
@ -0,0 +1,13 @@
|
|||
<phpunit bootstrap="include/php/default.inc.php">
|
||||
<testsuites>
|
||||
<testsuite name="webmum">
|
||||
<directory>tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">include/php/classes</directory>
|
||||
<directory suffix=".php">include/php/models</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
202
tests/AuthTest.php
Normal file
202
tests/AuthTest.php
Normal file
|
@ -0,0 +1,202 @@
|
|||
<?php
|
||||
|
||||
require_once 'TestCase.php';
|
||||
|
||||
/**
|
||||
* @covers Auth
|
||||
*/
|
||||
class AuthTest extends TestCase
|
||||
{
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
Auth::logout();
|
||||
$_SESSION = array();
|
||||
}
|
||||
|
||||
|
||||
public function testInitGuest()
|
||||
{
|
||||
$_SESSION = array();
|
||||
|
||||
Auth::init();
|
||||
|
||||
$this->assertFalse(Auth::isLoggedIn());
|
||||
$this->assertNull(Auth::getUser());
|
||||
$this->assertFalse(Auth::hasPermission(User::ROLE_USER));
|
||||
$this->assertFalse(Auth::hasPermission(User::ROLE_ADMIN));
|
||||
}
|
||||
|
||||
public function testInitUser()
|
||||
{
|
||||
$_SESSION = array(
|
||||
Auth::SESSION_IDENTIFIER => self::USER_ROLE_USER_ID
|
||||
);
|
||||
|
||||
Auth::init();
|
||||
|
||||
$this->assertTrue(Auth::isLoggedIn());
|
||||
$this->assertInstanceOf('User', Auth::getUser());
|
||||
$this->assertTrue(Auth::hasPermission(User::ROLE_USER));
|
||||
$this->assertFalse(Auth::hasPermission(User::ROLE_ADMIN));
|
||||
}
|
||||
|
||||
|
||||
public function testInitAdmin()
|
||||
{
|
||||
$_SESSION = array(
|
||||
Auth::SESSION_IDENTIFIER => self::USER_ROLE_ADMIN_ID
|
||||
);
|
||||
|
||||
Auth::init();
|
||||
|
||||
$this->assertTrue(Auth::isLoggedIn());
|
||||
$this->assertInstanceOf('User', Auth::getUser());
|
||||
$this->assertTrue(Auth::hasPermission(User::ROLE_USER));
|
||||
$this->assertTrue(Auth::hasPermission(User::ROLE_ADMIN));
|
||||
}
|
||||
|
||||
|
||||
public function testLogin()
|
||||
{
|
||||
$_SESSION = array();
|
||||
|
||||
Auth::init();
|
||||
|
||||
$this->assertFalse(Auth::isLoggedIn());
|
||||
|
||||
$this->assertTrue(Auth::login('user@domain.tld', 'testtest'));
|
||||
|
||||
$this->assertTrue(Auth::isLoggedIn());
|
||||
}
|
||||
|
||||
|
||||
public function testLoginInvalidEmail()
|
||||
{
|
||||
$_SESSION = array();
|
||||
|
||||
Auth::init();
|
||||
|
||||
$this->assertFalse(Auth::isLoggedIn());
|
||||
|
||||
$this->assertFalse(Auth::login('domain.tld', 'test'));
|
||||
|
||||
$this->assertFalse(Auth::isLoggedIn());
|
||||
}
|
||||
|
||||
|
||||
public function testLoginInvalidUser()
|
||||
{
|
||||
$_SESSION = array();
|
||||
|
||||
Auth::init();
|
||||
|
||||
$this->assertFalse(Auth::isLoggedIn());
|
||||
|
||||
$this->assertFalse(Auth::login('no.user@domain.tld', 'test'));
|
||||
|
||||
$this->assertFalse(Auth::isLoggedIn());
|
||||
}
|
||||
|
||||
|
||||
public function testLogout()
|
||||
{
|
||||
$_SESSION = array(
|
||||
Auth::SESSION_IDENTIFIER => self::USER_ROLE_USER_ID
|
||||
);
|
||||
|
||||
Auth::init();
|
||||
|
||||
$this->assertTrue(Auth::isLoggedIn());
|
||||
|
||||
Auth::logout();
|
||||
|
||||
$this->assertFalse(Auth::isLoggedIn());
|
||||
$this->assertArrayNotHasKey(Auth::SESSION_IDENTIFIER, $_SESSION);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param int $length
|
||||
* @return string
|
||||
*/
|
||||
protected static function genTestPw($length)
|
||||
{
|
||||
return substr(str_shuffle("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-+=_,!@$#*%<>[]{}"), 0, $length);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @expectedException AuthException
|
||||
* @expectedExceptionCode 2
|
||||
*/
|
||||
public function testValidateNewPasswordFirstEmpty()
|
||||
{
|
||||
Auth::validateNewPassword('', static::genTestPw(Config::get('password.min_length', 8)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @expectedException AuthException
|
||||
* @expectedExceptionCode 2
|
||||
*/
|
||||
public function testValidateNewPasswordLastEmpty()
|
||||
{
|
||||
Auth::validateNewPassword(static::genTestPw(Config::get('password.min_length', 8)), '');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @expectedException AuthException
|
||||
* @expectedExceptionCode 3
|
||||
*/
|
||||
public function testValidateNewPasswordNotEqual()
|
||||
{
|
||||
$pw = static::genTestPw(Config::get('password.min_length', 8));
|
||||
Auth::validateNewPassword($pw, $pw.'neq');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @expectedException AuthException
|
||||
* @expectedExceptionCode 4
|
||||
*/
|
||||
public function testValidateNewPasswordTooShort()
|
||||
{
|
||||
$pw = static::genTestPw(Config::get('password.min_length', 8) - 1);
|
||||
Auth::validateNewPassword($pw, $pw);
|
||||
}
|
||||
|
||||
|
||||
public function testValidateNewPasswordOk()
|
||||
{
|
||||
$pw = static::genTestPw(Config::get('password.min_length', 8));
|
||||
Auth::validateNewPassword($pw, $pw);
|
||||
}
|
||||
|
||||
|
||||
public function testGeneratePasswordHash()
|
||||
{
|
||||
Auth::generatePasswordHash(static::genTestPw(Config::get('password.min_length', 8)));
|
||||
}
|
||||
|
||||
|
||||
public function testGeneratePasswordHashAlgorithmFallback()
|
||||
{
|
||||
Config::set('password.hash_algorithm', '--not-an-algorithm--');
|
||||
Auth::generatePasswordHash(static::genTestPw(Config::get('password.min_length', 8)));
|
||||
}
|
||||
|
||||
|
||||
public function testChangeUserPassword()
|
||||
{
|
||||
$this->assertTrue(Auth::login('user@domain.tld', 'testtest'));
|
||||
|
||||
Auth::changeUserPassword(static::USER_ROLE_USER_ID, 'newpassword');
|
||||
|
||||
$this->assertFalse(Auth::login('user@domain.tld', 'testtest'));
|
||||
|
||||
$this->assertTrue(Auth::login('user@domain.tld', 'newpassword'));
|
||||
}
|
||||
|
||||
}
|
69
tests/ConfigTest.php
Normal file
69
tests/ConfigTest.php
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @covers Config
|
||||
*/
|
||||
class ConfigTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
Config::init(
|
||||
array(
|
||||
'test-value' => 123,
|
||||
'test' => array(
|
||||
'deep' => array(
|
||||
'deeper' => 'value',
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function testInit()
|
||||
{
|
||||
$this->assertEquals(Config::get('test-value'), 123);
|
||||
$this->assertEquals(Config::get('test.deep.deeper'), 'value');
|
||||
}
|
||||
|
||||
|
||||
public function testSet()
|
||||
{
|
||||
$this->assertEquals(Config::get('test-value'), 123);
|
||||
Config::set('test-value', false);
|
||||
$this->assertEquals(Config::get('test-value'), false);
|
||||
|
||||
$this->assertEquals(Config::get('test.deep.deeper'), 'value');
|
||||
Config::set('test.deep.deeper', 'other');
|
||||
$this->assertEquals(Config::get('test.deep.deeper'), 'other');
|
||||
|
||||
Config::set('test.new.deep.deeper', true);
|
||||
$this->assertEquals(Config::get('test.new.deep.deeper'), true);
|
||||
}
|
||||
|
||||
|
||||
public function testGet()
|
||||
{
|
||||
$this->assertTrue(is_array(Config::get(null)));
|
||||
|
||||
$this->assertEquals(Config::get('test-value'), 123);
|
||||
|
||||
$this->assertEquals(Config::get('test.deep.deeper'), 'value');
|
||||
|
||||
$this->assertEquals(Config::get('test-default', 123456), 123456);
|
||||
}
|
||||
|
||||
|
||||
public function testHas()
|
||||
{
|
||||
$this->assertTrue(Config::has('test-value'));
|
||||
|
||||
$this->assertTrue(Config::has('test.deep.deeper'));
|
||||
|
||||
$this->assertFalse(Config::has('test-default'));
|
||||
|
||||
Config::init(null);
|
||||
$this->assertFalse(Config::has(null));
|
||||
}
|
||||
}
|
44
tests/MessageTest.php
Normal file
44
tests/MessageTest.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @covers Message
|
||||
*/
|
||||
class MessageTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
||||
public function testAdd()
|
||||
{
|
||||
Message::getInstance()->add(Message::TYPE_SUCCESS, 'lorem');
|
||||
|
||||
$out = Message::getInstance()->render();
|
||||
|
||||
$this->assertContains(Message::TYPE_SUCCESS, $out);
|
||||
$this->assertContains('lorem', $out);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @expectedException InvalidArgumentException
|
||||
*/
|
||||
public function testAddRestrictTypes()
|
||||
{
|
||||
Message::getInstance()->add('wrong-type', 'lorem');
|
||||
}
|
||||
|
||||
|
||||
public function testAddShortcuts()
|
||||
{
|
||||
Message::getInstance()->fail('lorem');
|
||||
$this->assertContains(Message::TYPE_FAIL, Message::getInstance()->render());
|
||||
|
||||
Message::getInstance()->error('lorem');
|
||||
$this->assertContains(Message::TYPE_ERROR, Message::getInstance()->render());
|
||||
|
||||
Message::getInstance()->warning('lorem');
|
||||
$this->assertContains(Message::TYPE_WARNING, Message::getInstance()->render());
|
||||
|
||||
Message::getInstance()->success('lorem');
|
||||
$this->assertContains(Message::TYPE_SUCCESS, Message::getInstance()->render());
|
||||
}
|
||||
|
||||
}
|
147
tests/RouterTest.php
Normal file
147
tests/RouterTest.php
Normal file
|
@ -0,0 +1,147 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @covers Router
|
||||
*/
|
||||
class RouterTest extends TestCase
|
||||
{
|
||||
|
||||
const BASE_URL = 'http://test.tld/somedir';
|
||||
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
Config::set('base_url', self::BASE_URL);
|
||||
Router::init(array());
|
||||
}
|
||||
|
||||
|
||||
public function testUrl()
|
||||
{
|
||||
$this->assertEquals(self::BASE_URL, Router::url());
|
||||
$this->assertEquals(self::BASE_URL, Router::url('/'));
|
||||
|
||||
$this->assertEquals(self::BASE_URL.'/this/sub/dir?get=123', Router::url('this/sub/dir?get=123'));
|
||||
}
|
||||
|
||||
|
||||
public function testAdd()
|
||||
{
|
||||
Router::addRoute(Router::METHOD_GET, 'test-get', 'test-get-file');
|
||||
Router::execute('test-get', Router::METHOD_GET);
|
||||
|
||||
|
||||
Router::addRoute(Router::METHOD_POST, 'test-post', 'test-post-file');
|
||||
Router::execute('test-post', Router::METHOD_POST);
|
||||
|
||||
|
||||
Router::addRoute(array(Router::METHOD_GET, Router::METHOD_POST), 'test-mixed', 'test-mixed-file');
|
||||
Router::execute('test-mixed', Router::METHOD_GET);
|
||||
Router::execute('test-mixed', Router::METHOD_POST);
|
||||
}
|
||||
|
||||
|
||||
public function testAddCallback()
|
||||
{
|
||||
$reachedCallback = false;
|
||||
Router::addRoute(Router::METHOD_GET, 'test-callback', function() use(&$reachedCallback) {
|
||||
$reachedCallback = true;
|
||||
});
|
||||
Router::execute('test-callback', Router::METHOD_GET);
|
||||
|
||||
$this->assertTrue($reachedCallback);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @expectedException Exception
|
||||
* @expectedExceptionMessageRegExp /unsupported/i
|
||||
*/
|
||||
public function testAddMethodUnsupported()
|
||||
{
|
||||
Router::addRoute('not-a-method', 'test-fail', 'test-fail-file');
|
||||
}
|
||||
|
||||
|
||||
public function testAddShortcuts()
|
||||
{
|
||||
Router::addGet('test-get', 'test-get-file');
|
||||
Router::execute('test-get', Router::METHOD_GET);
|
||||
|
||||
Router::addPost('test-post', 'test-post-file');
|
||||
Router::execute('test-post', Router::METHOD_POST);
|
||||
|
||||
Router::addMixed('test-mixed', 'test-mixed-file');
|
||||
Router::execute('test-mixed', Router::METHOD_GET);
|
||||
Router::execute('test-mixed', Router::METHOD_POST);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @expectedException Exception
|
||||
* @expectedExceptionMessageRegExp /unsupported/i
|
||||
*/
|
||||
public function testExecuteMethodUnsupported()
|
||||
{
|
||||
Router::execute('test-fail', 'not-a-method');
|
||||
}
|
||||
|
||||
|
||||
public function testExecuteCurrentRequest()
|
||||
{
|
||||
$_SERVER['REQUEST_METHOD'] = 'GET';
|
||||
$_SERVER['REQUEST_URI'] = '/somedir/test-get';
|
||||
|
||||
Router::executeCurrentRequest();
|
||||
}
|
||||
|
||||
|
||||
public function testRouteWithPermission()
|
||||
{
|
||||
$this->assertFalse(Auth::isLoggedIn());
|
||||
|
||||
$reachedCallback = false;
|
||||
Router::addRoute(Router::METHOD_GET, 'test-perm-admin', function() use(&$reachedCallback) {
|
||||
$reachedCallback = true;
|
||||
}, User::ROLE_ADMIN);
|
||||
|
||||
Router::addRoute(Router::METHOD_GET, 'test-perm-user', function() use(&$reachedCallback) {
|
||||
$reachedCallback = true;
|
||||
}, User::ROLE_USER);
|
||||
|
||||
|
||||
$reachedCallback = false;
|
||||
Router::execute('test-perm-admin', Router::METHOD_GET);
|
||||
$this->assertFalse($reachedCallback);
|
||||
|
||||
$reachedCallback = false;
|
||||
Router::execute('test-perm-user', Router::METHOD_GET);
|
||||
$this->assertFalse($reachedCallback);
|
||||
|
||||
// Now auth as admin and try again
|
||||
Auth::login('admin@domain.tld', 'testtest');
|
||||
|
||||
$reachedCallback = false;
|
||||
Router::execute('test-perm-admin', Router::METHOD_GET);
|
||||
$this->assertTrue($reachedCallback);
|
||||
|
||||
$reachedCallback = false;
|
||||
Router::execute('test-perm-user', Router::METHOD_GET);
|
||||
$this->assertTrue($reachedCallback);
|
||||
|
||||
Auth::logout();
|
||||
|
||||
// Now auth as user and try again
|
||||
Auth::login('user@domain.tld', 'testtest');
|
||||
|
||||
$reachedCallback = false;
|
||||
Router::execute('test-perm-admin', Router::METHOD_GET);
|
||||
$this->assertFalse($reachedCallback);
|
||||
|
||||
$reachedCallback = false;
|
||||
Router::execute('test-perm-user', Router::METHOD_GET);
|
||||
$this->assertTrue($reachedCallback);
|
||||
|
||||
Auth::logout();
|
||||
}
|
||||
}
|
77
tests/TestCase.php
Normal file
77
tests/TestCase.php
Normal file
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @covers Auth
|
||||
*/
|
||||
abstract class TestCase extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
const USER_ROLE_ADMIN_ID = 100001;
|
||||
const USER_ROLE_ADMIN_ID_LIMITED_NO_ACCESS = 100002;
|
||||
const USER_ROLE_ADMIN_ID_LIMITED_HAS_ACCESS = 100003;
|
||||
const USER_ROLE_USER_ID = 100013;
|
||||
|
||||
|
||||
public static function setUpBeforeClass()
|
||||
{
|
||||
|
||||
Database::getInstance()->insert(
|
||||
'users',
|
||||
array(
|
||||
'id' => static::USER_ROLE_ADMIN_ID,
|
||||
'username' => 'admin',
|
||||
'domain' => 'domain.tld',
|
||||
'password' => Auth::generatePasswordHash('testtest'),
|
||||
'mailbox_limit' => 0,
|
||||
)
|
||||
);
|
||||
|
||||
Database::getInstance()->insert(
|
||||
'users',
|
||||
array(
|
||||
'id' => static::USER_ROLE_ADMIN_ID_LIMITED_NO_ACCESS,
|
||||
'username' => 'no-access-limited-admin',
|
||||
'domain' => 'domain.tld',
|
||||
'password' => Auth::generatePasswordHash('testtest'),
|
||||
'mailbox_limit' => 0,
|
||||
)
|
||||
);
|
||||
|
||||
Database::getInstance()->insert(
|
||||
'users',
|
||||
array(
|
||||
'id' => static::USER_ROLE_ADMIN_ID_LIMITED_HAS_ACCESS,
|
||||
'username' => 'has-access-limited-admin',
|
||||
'domain' => 'domain.tld',
|
||||
'password' => Auth::generatePasswordHash('testtest'),
|
||||
'mailbox_limit' => 0,
|
||||
)
|
||||
);
|
||||
|
||||
Database::getInstance()->insert(
|
||||
'users',
|
||||
array(
|
||||
'id' => static::USER_ROLE_USER_ID,
|
||||
'username' => 'user',
|
||||
'domain' => 'domain.tld',
|
||||
'password' => Auth::generatePasswordHash('testtest'),
|
||||
'mailbox_limit' => 64,
|
||||
)
|
||||
);
|
||||
|
||||
Config::set('admins', array('admin@domain.tld', 'limited-admin@domain.tld'));
|
||||
Config::set('admin_domain_limits', array(
|
||||
'no-access-limited-admin@domain.tld' => array(),
|
||||
'has-access-limited-admin@domain.tld' => array('his-domain.tld'),
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
public static function tearDownAfterClass()
|
||||
{
|
||||
Database::getInstance()->delete('users', 'id', static::USER_ROLE_ADMIN_ID);
|
||||
Database::getInstance()->delete('users', 'id', static::USER_ROLE_ADMIN_ID_LIMITED_NO_ACCESS);
|
||||
Database::getInstance()->delete('users', 'id', static::USER_ROLE_ADMIN_ID_LIMITED_HAS_ACCESS);
|
||||
Database::getInstance()->delete('users', 'id', static::USER_ROLE_USER_ID);
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue