├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── config
├── .htaccess
└── config.php.example
├── include
├── css
│ └── style.css
└── php
│ ├── .htaccess
│ ├── classes
│ ├── Auth.php
│ ├── AuthException.php
│ ├── Config.php
│ ├── Database.php
│ ├── DatabaseException.php
│ ├── Message.php
│ └── Router.php
│ ├── default.inc.php
│ ├── global.inc.php
│ ├── models
│ ├── AbstractModel.php
│ ├── AbstractMultiRedirect.php
│ ├── AbstractRedirect.php
│ ├── Alias.php
│ ├── Domain.php
│ ├── DomainLimitTrait.php
│ ├── ModelCollection.php
│ ├── MultiAlias.php
│ ├── MultiRedirect.php
│ ├── Redirect.php
│ └── User.php
│ ├── pages
│ ├── admin
│ │ ├── createdomain.php
│ │ ├── deletedomain.php
│ │ ├── deleteredirect.php
│ │ ├── deleteuser.php
│ │ ├── editredirect.php
│ │ ├── edituser.php
│ │ ├── listdomains.php
│ │ ├── listredirects.php
│ │ ├── listusers.php
│ │ └── start.php
│ ├── login.php
│ ├── private
│ │ ├── changepass.php
│ │ ├── createredirect.php
│ │ ├── deleteredirect.php
│ │ ├── start.php
│ │ └── yourredirects.php
│ └── start.php
│ ├── routes.inc.php
│ └── template
│ ├── error
│ ├── not-allowed.php
│ └── not-found.php
│ └── layout.php
├── index.php
├── installer
├── .htaccess
├── index.php
├── step0.php
├── step1.php
├── step2.php
├── step3.php
├── step4.php
├── step5.php
├── step6.php
└── step7.php
├── phpunit.xml
└── tests
├── AuthTest.php
├── ConfigTest.php
├── MessageTest.php
├── RouterTest.php
└── TestCase.php
/.gitignore:
--------------------------------------------------------------------------------
1 | config/config.php
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 | php:
3 | - '5.4'
4 | - '5.5'
5 | - '5.6'
6 | - '7.0'
7 | - hhvm
8 | - nightly
9 | matrix:
10 | allow_failures:
11 | - php: hhvm
12 | - php: nightly
13 | services:
14 | - mysql
15 | before_install:
16 | # Create example schema
17 | - mysql -u root -e "CREATE USER 'vmail'@'localhost' IDENTIFIED BY 'vmail';"
18 | - mysql -u root -e "CREATE DATABASE IF NOT EXISTS vmail;"
19 | - mysql -u root -e "GRANT ALL PRIVILEGES ON vmail.* TO 'vmail'@'localhost'"
20 | - 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;"
21 | - 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;"
22 | - 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;"
23 | # Copy the example config
24 | - cp config/config.php.example config/config.php
25 | notifications:
26 | email: false
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Thomas Leister
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/config/.htaccess:
--------------------------------------------------------------------------------
1 | Deny from all
--------------------------------------------------------------------------------
/config/config.php.example:
--------------------------------------------------------------------------------
1 | 'http://localhost/webmum',
19 |
20 |
21 | /******************************************************
22 | * MySQL database connection settings
23 | */
24 |
25 | 'mysql' => array(
26 | 'host' => 'localhost',
27 | 'user' => 'vmail',
28 | 'password' => 'vmail',
29 | 'database' => 'vmail',
30 | ),
31 |
32 |
33 | /******************************************************
34 | * Database schema mapping
35 | */
36 |
37 | 'schema' => array(
38 | // Table names
39 | 'tables' => array(
40 | // Example:
41 | // 'table-keyword' => 'actual-table-name',
42 |
43 | 'users' => 'users',
44 | 'domains' => 'domains',
45 | 'aliases' => 'aliases',
46 | ),
47 |
48 | 'attributes' => array(
49 | // Example:
50 | // 'table-keyword' => array(
51 | // 'attribute-keyword' => 'actual-attribute-name',
52 | // ...
53 | // ),
54 |
55 | // Users table columns
56 | 'users' => array(
57 | 'id' => 'id',
58 | 'username' => 'username',
59 | 'domain' => 'domain',
60 | 'password' => 'password',
61 | 'mailbox_limit' => 'mailbox_limit', // (Optional see 'options.enable_mailbox_limits')
62 | 'max_user_redirects' => 'max_user_redirects', // (Optional see 'options.enable_user_redirects')
63 | ),
64 |
65 | // Domains table columns
66 | 'domains' => array(
67 | 'id' => 'id',
68 | 'domain' => 'domain',
69 | ),
70 |
71 | // Aliases table columns
72 | 'aliases' => array(
73 | 'id' => 'id',
74 | 'source' => 'source',
75 | 'destination' => 'destination',
76 | 'multi_source' => 'multi_source', // (Optional see 'options.enable_multi_source_redirects')
77 | 'is_created_by_user' => 'is_created_by_user', // (Optional see 'options.enable_user_redirects')
78 | ),
79 | ),
80 | ),
81 |
82 |
83 | /******************************************************
84 | * General options
85 | */
86 |
87 | 'options' => array(
88 |
89 | /**
90 | * Enable mailbox limits. (Default false == off)
91 | *
92 | * Needs a new db attribute in users table with INT(10).
93 | * (see 'schema.attributes.users.mailbox_limit')
94 | */
95 |
96 | 'enable_mailbox_limits' => false,
97 |
98 |
99 | /**
100 | * Enable validating that the source addresses are ending with domain from domains. (Default true == on)
101 | */
102 |
103 | 'enable_validate_aliases_source_domain' => true,
104 |
105 |
106 | /**
107 | * Enable multi source redirects. (Default false == off)
108 | *
109 | * Needs a new db attribute in aliases table with VARCHAR(32).
110 | * (see 'schema.attributes.aliases.multi_source')
111 | */
112 |
113 | 'enable_multi_source_redirects' => false,
114 |
115 |
116 | /**
117 | * Enable limited admin domain access. (Default false == off)
118 | *
119 | * Limitations can be configured under 'admin_domain_limits'.
120 | */
121 |
122 | 'enable_admin_domain_limits' => false,
123 |
124 |
125 | /**
126 | * Enable users can create own redirects. (Default false == off)
127 | *
128 | * Needs two new db attributes in users table with INT(10) and aliases table with INT(1) + DEFAULT 0
129 | * (see 'schema.attributes.users.max_user_redirects' and 'schema.attributes.aliases.is_created_by_user')
130 | *
131 | * A maximum number of redirects per user can be configured.
132 | */
133 |
134 | 'enable_user_redirects' => false,
135 |
136 |
137 | /**
138 | * Enable logging for failed login attempts. (Default false == off)
139 | *
140 | * You can monitor the logfile with fail2ban and ban attackers' IP-addresses.
141 | * Path to logfile can be configured under 'log_path'.
142 | */
143 |
144 | 'enable_logging' => false,
145 |
146 | ),
147 |
148 |
149 | /******************************************************
150 | * Admin e-mail addresses
151 | *
152 | * Users with these e-mail addresses will have admin access,
153 | * you can limit their access with the 'options.enable_admin_domain_limits' feature
154 | */
155 |
156 | 'admins' => array(
157 | 'admin@domain.tld',
158 | ),
159 |
160 |
161 | /******************************************************
162 | * Limited admin domain access (only used if 'options.enable_admin_domain_limits' is true)
163 | *
164 | * Unlisted admins have access to every domain, the admin is limited to listed domains only!
165 | * Unlisted domains are not accessible by that admin.
166 | * Note that listed admins cannot create new domains!
167 | */
168 |
169 | 'admin_domain_limits' => array(
170 | // Example:
171 | // 'low_rank_admin@domain.tld' => array('his-domain.tld', 'shared-domain.tld'),
172 | ),
173 |
174 |
175 | /******************************************************
176 | * Password
177 | */
178 |
179 | 'password' => array(
180 |
181 | // Algorithm used for password encryption
182 | 'hash_algorithm' => 'SHA-512', // Supported algorithms: SHA-512, SHA-256, BLOWFISH
183 |
184 | // Minimum length for passwords
185 | 'min_length' => 8,
186 |
187 | ),
188 |
189 |
190 | /******************************************************
191 | * Log file path (only used if 'options.enable_logging' is true)
192 | *
193 | * Make sure that PHP has permission to create the log directory and webmum.log (write permissions for php user)
194 | */
195 |
196 | 'log_path' => '/var/www/webmum/log/',
197 |
198 |
199 | /******************************************************
200 | * Frontend options
201 | */
202 |
203 | 'frontend_options' => array(
204 |
205 | // Separator for email lists
206 | 'email_separator_text' => ', ', // possible values: ', ' (default), '; ', PHP_EOL (newline)
207 | 'email_separator_form' => ',', // possible values: ',' (default), ';', PHP_EOL (newline)
208 |
209 | ),
210 |
211 | );
212 |
--------------------------------------------------------------------------------
/include/css/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: arial, serif;
3 | font-size: 12px;
4 | margin: 0;
5 | background-color: white;
6 | }
7 |
8 | hr {
9 | border: none;
10 | border-bottom: 1px solid #ccc;
11 | margin: 20px 0;
12 | }
13 |
14 | hr.invisible {
15 | border-color: transparent;
16 | }
17 |
18 | .text-fail {
19 | color: #d90000;
20 | }
21 |
22 | .text-warning {
23 | color: #ADA900;
24 | }
25 |
26 | .text-success {
27 | color: #39AD00;
28 | }
29 |
30 |
31 | #header {
32 | position: relative;
33 | height: 50px;
34 | width: 100%;
35 | background: rgba(15, 15, 15, 1) linear-gradient(rgba(63, 63, 63, 1), rgba(15, 15, 15, 1));
36 | color: white;
37 | line-height: 50px;
38 | box-sizing: border-box;
39 | padding-left: 20px;
40 | padding-right: 20px;
41 | }
42 |
43 | #header div.title {
44 | float: left;
45 | height: 50px;
46 | width: auto;
47 | }
48 |
49 | #header div.title a {
50 | font-size: 15px;
51 | color: white;
52 | text-decoration: none;
53 | }
54 |
55 | #header div.title a:hover {
56 | text-decoration: underline;
57 | }
58 |
59 | #header div.header-menu {
60 | float: left;
61 | padding-left: 100px;
62 | }
63 |
64 | #header div.header-button {
65 | float: left;
66 | height: 50px;
67 | margin-right: 30px;
68 | color: white;
69 | }
70 |
71 | #header div.header-button a {
72 | color: white;
73 | text-decoration: none;
74 | }
75 |
76 | #header div.header-button a:hover {
77 | text-decoration: underline;
78 | }
79 |
80 | #content {
81 | height: auto;
82 | min-height: calc(100vh - 150px);
83 | padding: 20px;
84 | background-color: white;
85 | }
86 |
87 | #content h1 {
88 | color: rgba(62, 59, 59, 1);
89 | }
90 |
91 | #content .sub-header {
92 | font-weight: normal;
93 | font-size: .9em;
94 | color: #999;
95 | padding: 5px 0 0 5px;
96 | }
97 |
98 | #content a {
99 | color: blue;
100 | text-decoration: none;
101 | }
102 |
103 | #content a:hover {
104 | text-decoration: underline;
105 | }
106 |
107 |
108 | #content .form {
109 | margin: 25px 0;
110 | }
111 |
112 | #content .form hr {
113 | margin: 5px 0 15px;
114 | }
115 |
116 | #content .form .input-group, #content .form .buttons {
117 | padding-bottom: 10px;
118 | }
119 |
120 | #content .form .input-group > label {
121 | font-weight: bold;
122 | line-height: 22px;
123 | font-size: 13px;
124 | }
125 |
126 | #content .form .input-group > .input-info {
127 | padding-bottom: 5px;
128 | color: #999;
129 | }
130 |
131 | #content .form .input-group > .input-group {
132 | padding-left: 25px;
133 | }
134 |
135 | #content .form .input-group > .input-group:first-of-type {
136 | padding-top: 10px;
137 | }
138 |
139 | #content .form .input-group > .input-group > label {
140 | font-size: 12px;
141 | }
142 |
143 | #content .form .input {
144 | }
145 |
146 | #content .form .input input, #content .form .input textarea, #content .form .input select {
147 | background: #fefefe;
148 | border: 1px solid rgba(200, 200, 200, 1);
149 | border-radius: 3px;
150 | margin-bottom: 5px;
151 | padding: 0 10px;
152 | box-shadow: inset 1px 1px 3px rgba(230, 230, 230, 1);
153 | }
154 |
155 | #content .form .input input:focus, #content .form .input input:focus, #content .form .input select:focus {
156 | border: 1px solid rgba(137, 137, 137, 1);
157 | }
158 |
159 | #content .form .input input {
160 | min-width: 180px;
161 | }
162 |
163 | #content .form .input input[type="number"] {
164 | padding-left: 15px;
165 | padding-right: 0;
166 | min-width: 70px;
167 | width: 70px;
168 | }
169 |
170 | #content .form .input input[type="checkbox"],
171 | #content .form .input input[type="radio"] {
172 | border: none;
173 | box-shadow: none;
174 | min-width: inherit;
175 | vertical-align: middle;
176 | margin: 6px 0 8px 5px;
177 | cursor: pointer;
178 | }
179 |
180 | #content .form .input input[type="checkbox"]+label,
181 | #content .form .input input[type="radio"]+label {
182 | padding-left: 3px;
183 | margin-right: 15px;
184 | cursor: pointer;
185 | -webkit-user-select: none;
186 | -moz-user-select: none;
187 | -ms-user-select: none;
188 | user-select: none;
189 | }
190 |
191 | #content .form .input textarea {
192 | min-height: 150px;
193 | min-width: 400px;
194 | line-height: 18px !important;
195 | padding-top: 8px;
196 | padding-bottom: 8px;
197 | }
198 |
199 | #content .form .input input,
200 | #content .form .input textarea,
201 | #content .form .input.input-labeled.input-labeled-left > *:first-child,
202 | #content .form .input.input-labeled.input-labeled-right > *:last-child {
203 | line-height: 33px;
204 | }
205 |
206 | #content .form .input select {
207 | padding: 8px 10px 9px;
208 | min-width: 200px;
209 | }
210 |
211 | #content .form .input select option[value=""] {
212 | color: #ccc;
213 | }
214 |
215 | #content .form .input.input-labeled.input-labeled-left > *:first-child,
216 | #content .form .input.input-labeled.input-labeled-right > *:last-child {
217 | background: #eee;
218 | border: 1px solid rgba(200, 200, 200, 1);
219 | border-radius: 3px;
220 | display: inline-block;
221 | margin: 0 0 0 3px;
222 | padding: 0 10px;
223 | position: absolute;
224 | }
225 |
226 | #content .form .input.input-action > *:first-child,
227 | #content .form .input.input-labeled > *:first-child {
228 | border-top-right-radius: 0 !important;
229 | border-bottom-right-radius: 0 !important;
230 | margin-right: -4px;
231 | }
232 | #content .form .input.input-action > *:last-child,
233 | #content .form .input.input-labeled > *:last-child {
234 | border-top-left-radius: 0 !important;
235 | border-bottom-left-radius: 0 !important;
236 | }
237 |
238 |
239 | #content .buttons {
240 | margin: 15px 0;
241 | }
242 |
243 | #content .form .buttons {
244 | margin: 0;
245 | }
246 |
247 | #content .buttons.buttons-horizontal .button {
248 | display: block;
249 | margin-top: 10px;
250 | }
251 |
252 | #content .buttons.buttons-horizontal .button:first-child {
253 | margin-top: 0;
254 | }
255 |
256 | #content .button {
257 | background: #dddddd;
258 | background: linear-gradient(#ffffff, #eaeaea);
259 | border: 1px solid rgba(200, 200, 200, 1);
260 | border-radius: 3px;
261 | font-family: arial, serif;
262 | transition: all 0.2s;
263 |
264 | height: auto;
265 | min-width: 200px;
266 | width: 200px;
267 | line-height: 31px;
268 | font-size: 13px;
269 | text-align: center;
270 | color: rgba(57, 57, 57, 1);
271 | text-decoration: none;
272 |
273 | -webkit-user-select: none;
274 | -moz-user-select: none;
275 | -ms-user-select: none;
276 | user-select: none;
277 | }
278 |
279 | #content a.button {
280 | display: inline-block;
281 | }
282 |
283 | #content .button:hover {
284 | box-shadow: 1px 1px 4px #dbdbdb;
285 | text-decoration: none;
286 | cursor: pointer;
287 | }
288 |
289 | #content .button.button-large,
290 | #content .buttons.button-large .button {
291 | min-width: 300px;
292 | width: 300px;
293 | font-size: 15px;
294 | line-height: 45px;
295 | color: rgba(57, 57, 57, 1);
296 | }
297 |
298 | #content .button.button-primary,
299 | #content .buttons.button-primary .button {
300 | background: #666;
301 | background: linear-gradient(#999, #666);
302 | border-color: #444;
303 | color: #fff;
304 | }
305 |
306 | #content .button.button-primary:hover,
307 | #content .button.button-primary:active,
308 | #content .buttons.button-primary .button:hover,
309 | #content .buttons.button-primary .button:active {
310 | background: #777;
311 | background: linear-gradient(#777777, #444);
312 | border-color: #333;
313 | color: #fff;
314 | }
315 |
316 | #content .button.button-disabled {
317 | background: #f1f1f1;
318 | border-color: #f1f1f1;
319 | box-shadow: none;
320 | cursor: not-allowed;
321 | color: #bbb;
322 | }
323 |
324 |
325 | #content .table {
326 | margin: 25px 0;
327 | border-collapse: collapse;
328 | border: none;
329 | }
330 |
331 | #content .table-compact {
332 | margin: 5px 0;
333 | }
334 |
335 | #content .table thead th {
336 | line-height: 38px;
337 | padding: 2px 15px 0;
338 | border: 1px solid rgba(179, 176, 176, 1);
339 | background: #eeeeee;
340 | background: linear-gradient(#ffffff, #eaeaea);
341 | font-size: 13px;
342 | }
343 |
344 | #content .table tfoot th {
345 | line-height: 33px;
346 | text-align: left;
347 | padding: 0 10px;
348 | font-weight: normal;
349 | color: #999;
350 | }
351 |
352 | #content .table tbody td {
353 | line-height: 21px;
354 | padding: 9px 10px;
355 | border: 1px solid rgba(179, 176, 176, 1);
356 | }
357 |
358 | #content .table tbody > tr:hover {
359 | background-color: rgba(234, 234, 234, 1);
360 | }
361 |
362 | #content .table tbody > tr.warning {
363 | background-color: #fcf897;
364 | }
365 |
366 | #content .table a {
367 | color: rgb(148, 148, 255);
368 | }
369 | #content .table tbody > tr:hover a {
370 | color: blue;
371 | }
372 |
373 |
374 | #content .notifications {
375 | }
376 |
377 | #content .notification {
378 | height: auto;
379 | width: 100%;
380 | margin: 15px 0;
381 | text-align: center;
382 | border: 1px solid;
383 | border-radius: 3px;
384 | padding: 15px 10px;
385 | box-sizing: border-box;
386 | }
387 |
388 | #content .notification.notification-fail {
389 | background-color: #fcacac;
390 | border-color: red;
391 | }
392 |
393 | #content .notification.notification-warning {
394 | background-color: #fcf897;
395 | border-color: #ffe600;
396 | }
397 |
398 | #content .notification.notification-success {
399 | background-color: rgba(182, 255, 183, 1);
400 | border-color: green;
401 | }
402 |
403 |
404 | #footer {
405 | position: relative;
406 | width: 100%;
407 | background-color: white;
408 | padding: 15px 20px;
409 | box-sizing: border-box;
410 | color: grey;
411 | }
412 |
413 | #footer ul {
414 | list-style: none;
415 | }
416 |
417 | #footer li {
418 | display: inline-block;
419 | }
420 |
421 | #footer li:not(:last-child):after {
422 | content: '|';
423 | color: #444444;
424 | padding: 0 6px;
425 | }
426 |
427 | #footer a {
428 | color: #888888;
429 | }
430 |
431 | #footer a:hover {
432 | color: #666666;
433 | }
--------------------------------------------------------------------------------
/include/php/.htaccess:
--------------------------------------------------------------------------------
1 | Deny from all
--------------------------------------------------------------------------------
/include/php/classes/Auth.php:
--------------------------------------------------------------------------------
1 | getPasswordHash())){
106 |
107 | static::loginUserByModel($user);
108 |
109 | $_SESSION[static::SESSION_IDENTIFIER] = $user->getId();
110 |
111 | return true;
112 | }
113 | }
114 |
115 | return false;
116 | }
117 |
118 |
119 | /**
120 | * @return void
121 | */
122 | public static function logout()
123 | {
124 | unset($_SESSION[static::SESSION_IDENTIFIER]);
125 |
126 | static::$loggedInUser = null;
127 |
128 | if(session_status() === PHP_SESSION_ACTIVE){
129 | session_destroy();
130 | }
131 | }
132 |
133 |
134 | /**
135 | * Check if current user has a certain role, but User::ROLE_ADMIN will have access to all
136 | *
137 | * @param string $requiredRole
138 | *
139 | * @return bool
140 | */
141 | public static function hasPermission($requiredRole)
142 | {
143 | if(static::isLoggedIn()){
144 | $user = static::getUser();
145 |
146 | return $user->getRole() === $requiredRole
147 | || $user->getRole() === User::ROLE_ADMIN;
148 | }
149 |
150 | return false;
151 | }
152 |
153 |
154 | /**
155 | * Checks the new password entered by user on certain criteria, and throws an exception if its invalid.
156 | *
157 | * @param string $password
158 | * @param string $passwordRepeated
159 | *
160 | * @throws AuthException Codes explained below
161 | * 2: One password field is empty
162 | * 3: Passwords aren't equal
163 | * 4: Passwort is too snort
164 | */
165 | public static function validateNewPassword($password, $passwordRepeated)
166 | {
167 | // Check if one passwort input is empty
168 | if(empty($password)){
169 | throw new AuthException("First password field was'nt filled out.", 2);
170 | }
171 | if(empty($passwordRepeated)){
172 | throw new AuthException("Repeat password field was'nt filled out.", 2);
173 | }
174 |
175 | // Check if password are equal
176 | if($password !== $passwordRepeated){
177 | throw new AuthException("The repeated password must be equal to the first one.", 3);
178 | }
179 |
180 | // Check if password length is okay
181 | if(Config::has('password.min_length')
182 | && strlen($password) < Config::get('password.min_length')
183 | ){
184 | throw new AuthException("Passwords must be at least ".Config::get('password.min_length')." characters long.", 4);
185 | }
186 | }
187 |
188 |
189 | /**
190 | * @param string $password
191 | * @param string $hash
192 | *
193 | * @return bool
194 | */
195 | public static function checkPasswordByHash($password, $hash)
196 | {
197 | return crypt($password, $hash) === $hash;
198 | }
199 |
200 |
201 | /**
202 | * @return string
203 | */
204 | private static function getPasswordSchemaPrefix()
205 | {
206 | $map = array(
207 | 'SHA-256' => '$5$rounds=5000$',
208 | 'BLOWFISH' => '$2a$09$',
209 | 'SHA-512' => '$6$rounds=5000$',
210 | );
211 |
212 | $key = Config::get('password.hash_algorithm', 'SHA-512');
213 |
214 | if(!isset($map[$key])){
215 | $key = 'SHA-512';
216 | }
217 |
218 | return $map[$key];
219 | }
220 |
221 |
222 | /**
223 | * @param string $password
224 | *
225 | * @return string
226 | */
227 | public static function generatePasswordHash($password)
228 | {
229 | if(function_exists('mt_rand')){
230 | mt_srand(time());
231 | $num = mt_rand(1, 100000);
232 | }
233 | else{
234 | srand(time());
235 | $num = rand(1, 100000);
236 | }
237 |
238 | $salt = base64_encode($num);
239 | $schemaPrefix = static::getPasswordSchemaPrefix();
240 |
241 | $hash = crypt($password, $schemaPrefix.$salt.'$');
242 |
243 | return $hash;
244 | }
245 |
246 |
247 | /**
248 | * @param string $userId
249 | * @param $password
250 | */
251 | public static function changeUserPassword($userId, $password)
252 | {
253 | $passwordHash = static::generatePasswordHash($password);
254 |
255 | /** @var User $user */
256 | $user = User::find($userId);
257 |
258 | if(!is_null($user)){
259 | $user->setPasswordHash($passwordHash);
260 | $user->save();
261 | }
262 | }
263 | }
264 |
--------------------------------------------------------------------------------
/include/php/classes/AuthException.php:
--------------------------------------------------------------------------------
1 | 1){
52 | $key = array_shift($keys);
53 |
54 | if(!isset($array[$key]) || !is_array($array[$key])){
55 | $array[$key] = array();
56 | }
57 |
58 | $array =& $array[$key];
59 | }
60 |
61 | $array[array_shift($keys)] = $value;
62 |
63 | return $array;
64 | }
65 |
66 |
67 | /**
68 | * Get a config value using "dot" notation.
69 | *
70 | * @param string $key
71 | * @param mixed $default
72 | * @return mixed
73 | */
74 | public static function get($key, $default = null)
75 | {
76 | if(is_null($key)) return static::$config;
77 |
78 | if(isset(static::$config[$key])) return static::$config[$key];
79 |
80 | $pointer = static::$config;
81 | foreach(explode('.', $key) as $segment){
82 | if(!is_array($pointer) || !array_key_exists($segment, $pointer)){
83 | return $default;
84 | }
85 |
86 | $pointer = $pointer[$segment];
87 | }
88 |
89 | return $pointer;
90 | }
91 |
92 |
93 | /**
94 | * Check if a config value exists using "dot" notation.
95 | *
96 | * @param string $key
97 | * @return bool
98 | */
99 | public static function has($key)
100 | {
101 | if(empty(static::$config) || is_null($key)) return false;
102 |
103 | if(array_key_exists($key, static::$config)) return true;
104 |
105 | $pointer = static::$config;
106 | foreach(explode('.', $key) as $segment){
107 | if(!is_array($pointer) || !array_key_exists($segment, $pointer)){
108 | return false;
109 | }
110 |
111 | $pointer = $pointer[$segment];
112 | }
113 |
114 | return true;
115 | }
116 |
117 | }
--------------------------------------------------------------------------------
/include/php/classes/DatabaseException.php:
--------------------------------------------------------------------------------
1 | query = $query;
18 |
19 | return $this;
20 | }
21 |
22 | /**
23 | * Get the executed SQL query
24 | *
25 | * @return string
26 | */
27 | public function getQuery()
28 | {
29 | return $this->query;
30 | }
31 | }
--------------------------------------------------------------------------------
/include/php/classes/Message.php:
--------------------------------------------------------------------------------
1 | messages[] = array(
70 | 'type' => $type,
71 | 'message' => $text,
72 | );
73 | }
74 |
75 |
76 | /**
77 | * Add a new success message
78 | *
79 | * @param string $text
80 | */
81 | public function fail($text)
82 | {
83 | $this->add(static::TYPE_FAIL, $text);
84 | }
85 |
86 |
87 | /**
88 | * Add a new success message
89 | *
90 | * @param string $text
91 | */
92 | public function error($text)
93 | {
94 | $this->add(static::TYPE_ERROR, $text);
95 | }
96 |
97 |
98 | /**
99 | * Add a new success message
100 | *
101 | * @param string $text
102 | */
103 | public function warning($text)
104 | {
105 | $this->add(static::TYPE_WARNING, $text);
106 | }
107 |
108 |
109 | /**
110 | * Add a new success message
111 | *
112 | * @param string $text
113 | */
114 | public function success($text)
115 | {
116 | $this->add(static::TYPE_SUCCESS, $text);
117 | }
118 |
119 |
120 | /**
121 | * Render all messages
122 | *
123 | * @param null|string $type null = render all
124 | *
125 | * @return string
126 | */
127 | public function render($type = null)
128 | {
129 | $out = '';
130 |
131 | if(count($this->messages) > 0){
132 | $out .= '
';
133 |
134 | foreach($this->messages as $message){
135 | if(is_null($type) || $type == $message['type']){
136 | $out .= '
'.$message['message'].'
';
137 | }
138 | }
139 |
140 | $out .= '
';
141 | }
142 |
143 | return $out;
144 | }
145 |
146 | }
--------------------------------------------------------------------------------
/include/php/classes/Router.php:
--------------------------------------------------------------------------------
1 | 'include/php/template/error/not-found.php',
20 | 403 => 'include/php/template/error/not-allowed.php'
21 | );
22 |
23 |
24 | /**
25 | * @codeCoverageIgnore
26 | */
27 | private function __construct()
28 | {
29 | }
30 |
31 |
32 | /**
33 | * @codeCoverageIgnore
34 | */
35 | private function __clone()
36 | {
37 | }
38 |
39 |
40 | /**
41 | * @param array $routes
42 | */
43 | public static function init($routes)
44 | {
45 | static::$routes = $routes;
46 | }
47 |
48 |
49 | /**
50 | * @param string $method
51 | *
52 | * @return bool
53 | */
54 | protected static function isValidMethod($method)
55 | {
56 | return in_array(
57 | $method,
58 | array(
59 | static::METHOD_GET,
60 | static::METHOD_POST
61 | )
62 | );
63 | }
64 |
65 |
66 | /**
67 | * @param string|array $methods
68 | * @param string $pattern
69 | * @param callable|array|string $routeConfig
70 | * @param array $permission
71 | *
72 | * @throws Exception
73 | */
74 | public static function addRoute($methods, $pattern, $routeConfig, $permission = null)
75 | {
76 | if(!is_array($methods)){
77 | $methods = array($methods);
78 | }
79 |
80 | $config = array(
81 | 'pattern' => $pattern,
82 | 'config' => $routeConfig,
83 | 'permission' => $permission,
84 | );
85 |
86 | foreach($methods as $method){
87 | $method = strtoupper($method);
88 |
89 | if(!static::isValidMethod($method)){
90 | throw new Exception('Unsupported HTTP method "'.$method.'".');
91 | }
92 |
93 | if(!isset(static::$routes[$method])){
94 | static::$routes[$method] = array();
95 | }
96 |
97 | static::$routes[$method][] = $config;
98 | }
99 | }
100 |
101 |
102 | /**
103 | * @param string $pattern
104 | * @param callable|string $routeConfig
105 | * @param array $permission
106 | */
107 | public static function addGet($pattern, $routeConfig, $permission = null)
108 | {
109 | static::addRoute(static::METHOD_GET, $pattern, $routeConfig, $permission);
110 | }
111 |
112 |
113 | /**
114 | * @param string $pattern
115 | * @param callable|string $routeConfig
116 | * @param array $permission
117 | */
118 | public static function addPost($pattern, $routeConfig, $permission = null)
119 | {
120 | static::addRoute(static::METHOD_POST, $pattern, $routeConfig, $permission);
121 | }
122 |
123 |
124 | /**
125 | * @param string $pattern
126 | * @param callable|string $routeConfig
127 | * @param array $permission
128 | */
129 | public static function addMixed($pattern, $routeConfig, $permission = null)
130 | {
131 | static::addRoute(array(static::METHOD_GET, static::METHOD_POST), $pattern, $routeConfig, $permission);
132 | }
133 |
134 |
135 | /**
136 | * @param string $url
137 | * @param string $method
138 | *
139 | * @return string
140 | *
141 | * @throws Exception
142 | */
143 | public static function execute($url, $method = self::METHOD_GET)
144 | {
145 | $method = strtoupper($method);
146 |
147 | if(!static::isValidMethod($method) && !isset(self::$routes[$method])){
148 | throw new Exception('Unsupported HTTP method "'.$method.'".');
149 | }
150 |
151 | if(isset(self::$routes[$method])){
152 | foreach(self::$routes[$method] as $route){
153 | if(rtrim($route['pattern'], '/') === rtrim($url, '/')){
154 | if(!is_null($route['permission'])){
155 | if(!Auth::isLoggedIn() || !Auth::hasPermission($route['permission'])){
156 | return static::loadAndBufferOutput(static::$errorPages[403]);
157 | }
158 | }
159 |
160 | return static::resolveRouteConfig($route['config']);
161 | }
162 | }
163 | }
164 |
165 | return static::loadAndBufferOutput(static::$errorPages[404]);
166 | }
167 |
168 | /**
169 | * @return string
170 | */
171 | public static function executeCurrentRequest()
172 | {
173 | return static::execute(
174 | static::getCurrentUrlPath(),
175 | isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : static::METHOD_GET
176 | );
177 | }
178 |
179 |
180 | /**
181 | * @param int $errorNumber
182 | *
183 | * @return string|null
184 | *
185 | * @codeCoverageIgnore
186 | */
187 | public static function displayError($errorNumber)
188 | {
189 | $errorPage = isset(static::$errorPages[$errorNumber])
190 | ? static::loadAndBufferOutput(static::$errorPages[$errorNumber])
191 | : '';
192 |
193 | echo Router::loadAndBufferOutput(
194 | 'include/php/template/layout.php',
195 | array(
196 | 'content' => $errorPage,
197 | )
198 | );
199 | exit;
200 | }
201 |
202 |
203 | /**
204 | * @param bool $removeGetParameters
205 | *
206 | * @return string
207 | */
208 | public static function getCurrentUrlPath($removeGetParameters = true)
209 | {
210 | $baseUrl = parse_url(Config::get('base_url'));
211 | $basePath = isset($baseUrl['path']) ? rtrim($baseUrl['path'], '/') : '';
212 |
213 | $url = $_SERVER['REQUEST_URI'];
214 |
215 | if($removeGetParameters){
216 | $url = preg_replace('/\?.*/', '', $url); // Trim GET Parameters
217 | }
218 |
219 | // Trim all leading slashes
220 | $url = rtrim($url, '/');
221 |
222 | if(!empty($basePath) && ($basePathPos = strpos($url, $basePath)) === 0){
223 | $url = substr($url, strlen($basePath));
224 | }
225 |
226 | return $url;
227 | }
228 |
229 |
230 | /**
231 | * @param array $config
232 | *
233 | * @return string
234 | */
235 | public static function resolveRouteConfig($config)
236 | {
237 | if(is_string($config)){
238 | if(file_exists($config)){
239 | return static::loadAndBufferOutput($config);
240 | }
241 | }
242 | elseif(is_callable($config) && $config instanceof Closure){
243 | return $config();
244 | }
245 |
246 | return static::loadAndBufferOutput(static::$errorPages[404]);
247 | }
248 |
249 | /**
250 | * @param string $file
251 | * @param array $variables
252 | *
253 | * @return string
254 | */
255 | public static function loadAndBufferOutput($file, $variables = array())
256 | {
257 | ob_start();
258 |
259 | extract($variables);
260 |
261 | require $file;
262 |
263 | return ob_get_clean();
264 | }
265 |
266 |
267 | /**
268 | * Generate full url
269 | *
270 | * @param string $url
271 | *
272 | * @return string
273 | */
274 | public static function url($url = '')
275 | {
276 | return rtrim(
277 | sprintf(
278 | '%s/%s',
279 | rtrim(Config::get('base_url'), '/'),
280 | trim($url, '/')
281 | ),
282 | '/'
283 | );
284 | }
285 |
286 |
287 | /**
288 | * Redirect user to an url
289 | *
290 | * @param string $url
291 | *
292 | * @codeCoverageIgnore
293 | */
294 | public static function redirect($url)
295 | {
296 | header("Location: ".static::url($url));
297 | exit;
298 | }
299 | }
--------------------------------------------------------------------------------
/include/php/default.inc.php:
--------------------------------------------------------------------------------
1 | &$email){
52 | if(empty($email)){
53 | unset($list[$i]);
54 | }
55 | }
56 |
57 | $emails = array_values(
58 | array_unique(
59 | array_map(
60 | 'formatEmail',
61 | $list
62 | )
63 | )
64 | );
65 |
66 | asort($emails);
67 |
68 | return $emails;
69 | }
70 |
71 | /**
72 | * List of emails to comma or $glue separated list string
73 | *
74 | * @param array $list
75 | * @param string $glue
76 | *
77 | * @return string
78 | */
79 | function emailsToString($list, $glue = ',')
80 | {
81 | if(is_string($list)){
82 | return $list;
83 | }
84 |
85 | return implode($glue, $list);
86 | }
87 |
88 | /**
89 | * Format single email address
90 | *
91 | * @param string $input
92 | *
93 | * @return string
94 | */
95 | function formatEmail($input)
96 | {
97 | return strtolower(trim($input));
98 | }
99 |
100 | /**
101 | * Format email addresses (single, multiple in separated list, or array of email addresses)
102 | *
103 | * @param string|array $input
104 | * @param string $glue
105 | *
106 | * @return string
107 | */
108 | function formatEmails($input, $glue)
109 | {
110 | if(!is_array($input)){
111 | $input = stringToEmails($input);
112 | }
113 |
114 | return emailsToString($input, $glue);
115 | }
116 |
117 | /**
118 | * Format email addresses for text output (not in an input field)
119 | *
120 | * @param string|array $input
121 | * @return string
122 | */
123 | function formatEmailsText($input)
124 | {
125 | return formatEmails(
126 | $input,
127 | str_replace(PHP_EOL, ' ', Config::get('frontend_options.email_separator_text', ', '))
128 | );
129 | }
130 |
131 |
132 | /**
133 | * Format email addresses for form output (in an input field)
134 | *
135 | * @param string|array $input
136 | * @return string
137 | */
138 | function formatEmailsForm($input)
139 | {
140 | return strip_tags(
141 | formatEmails(
142 | $input,
143 | Config::get('frontend_options.email_separator_form', ',')
144 | )
145 | );
146 | }
147 |
148 | /**
149 | * @param string $textPattern
150 | * @param int|mixed $value
151 | * @return string
152 | */
153 | function textValue($textPattern, $value)
154 | {
155 | $text = str_replace(
156 | array('_', ':val:', ':value:'),
157 | $value,
158 | $textPattern
159 | );
160 |
161 | if(is_numeric($value) && $value > 1){
162 | $text .= 's';
163 | }
164 |
165 | return $text;
166 | }
--------------------------------------------------------------------------------
/include/php/models/AbstractModel.php:
--------------------------------------------------------------------------------
1 | setId($id);
93 | }
94 | }
95 |
96 |
97 | /**
98 | * Create a model from data
99 | *
100 | * @param array $data
101 | *
102 | * @return static|null The Model
103 | */
104 | public static function create($data)
105 | {
106 | if(count($data) > 0){
107 | return new static($data);
108 | }
109 |
110 | return null;
111 | }
112 |
113 |
114 | /**
115 | * Create a model collection from data
116 | *
117 | * @param array $multiData
118 | *
119 | * @return ModelCollection|static[]
120 | */
121 | public static function createMultiple($multiData = array())
122 | {
123 | $collection = new ModelCollection();
124 |
125 | foreach($multiData as $data){
126 | $model = static::create($data);
127 |
128 | if(!is_null($model)){
129 | if(is_null($model->getId())){
130 | $collection->add($model);
131 | }
132 | else{
133 | $collection->add($model, $model->getId());
134 | }
135 | }
136 | }
137 |
138 | return $collection;
139 | }
140 |
141 |
142 | /**
143 | * @see create
144 | *
145 | * @param array $data
146 | *
147 | * @return AbstractModel|null
148 | */
149 | public static function createAndSave($data)
150 | {
151 | $model = static::create($data);
152 |
153 | if(!is_null($model)){
154 | $model->save();
155 |
156 | return $model;
157 | }
158 |
159 | return null;
160 | }
161 |
162 |
163 | /**
164 | * @see createMultiple
165 | *
166 | * @param array $multiData
167 | *
168 | * @return ModelCollection|static[]
169 | */
170 | public static function createMultipleAndSave($multiData = array())
171 | {
172 | $collection = new ModelCollection();
173 |
174 | foreach($multiData as $data){
175 | $model = static::createAndSave($data);
176 |
177 | if(!is_null($model)){
178 | $collection->add($model);
179 | }
180 | }
181 |
182 | return $collection;
183 | }
184 |
185 |
186 | /**
187 | * Create a model from mysqli result
188 | *
189 | * @param mysqli_result $result
190 | *
191 | * @return static|null
192 | */
193 | public static function createFromDbResult($result)
194 | {
195 | if($result->num_rows === 0){
196 | return null;
197 | }
198 |
199 | return static::create($result->fetch_assoc());
200 | }
201 |
202 |
203 | /**
204 | * Create a model collection from mysqli result
205 | *
206 | * @param mysqli_result $result
207 | *
208 | * @return ModelCollection|static[]
209 | */
210 | public static function createMultipleFromDbResult($result)
211 | {
212 | $rows = array();
213 |
214 | while($row = $result->fetch_assoc()){
215 | $rows[] = $row;
216 | }
217 |
218 | return static::createMultiple($rows);
219 | }
220 |
221 |
222 | /**
223 | * @param string $attribute
224 | * @param mixed $value
225 | */
226 | public function setAttribute($attribute, $value)
227 | {
228 | $this->data[$attribute] = $value;
229 | }
230 |
231 |
232 | /**
233 | * @param string $attribute
234 | *
235 | * @return mixed|null
236 | */
237 | public function getAttribute($attribute)
238 | {
239 | if(isset($this->data[$attribute])){
240 | $value = $this->data[$attribute];
241 |
242 | if(is_array($value)){
243 | return array_map('strip_tags', $value);
244 | }
245 | elseif(is_string($value)){
246 | return strip_tags($value);
247 | }
248 |
249 | return $value;
250 | }
251 |
252 | return null;
253 | }
254 |
255 |
256 | /**
257 | * @return mixed
258 | */
259 | public function getId()
260 | {
261 | return $this->getAttribute('id');
262 | }
263 |
264 |
265 | /**
266 | * @param mixed $value
267 | */
268 | protected function setId($value)
269 | {
270 | $this->setAttribute('id', $value);
271 | }
272 |
273 |
274 | /**
275 | * Find all models by raw sql
276 | *
277 | * @param $sql
278 | * @param null|string $useSpecificModel
279 | *
280 | * @return ModelCollection|static[]
281 | */
282 | public static function findAllRaw($sql, $useSpecificModel = null)
283 | {
284 | $result = Database::getInstance()->query($sql);
285 |
286 | if(is_null($useSpecificModel)){
287 | return static::createMultipleFromDbResult($result);
288 | }
289 | elseif(class_exists($useSpecificModel)){
290 | return call_user_func_array(array($useSpecificModel, 'createMultipleFromDbResult'), array($result));
291 | }
292 |
293 | return new ModelCollection();
294 | }
295 |
296 |
297 | /**
298 | * Find a model by raw sql
299 | *
300 | * @param $sql
301 | * @param null|string $useSpecificModel
302 | *
303 | * @return AbstractModel
304 | */
305 | public static function findRaw($sql, $useSpecificModel = null)
306 | {
307 | $result = Database::getInstance()->query($sql);
308 |
309 | if(is_null($useSpecificModel)){
310 | return static::createFromDbResult($result);
311 | }
312 | elseif(class_exists($useSpecificModel)){
313 | return call_user_func_array(array($useSpecificModel, 'createFromDbResult'), array($result));
314 | }
315 |
316 | return null;
317 | }
318 |
319 |
320 | /**
321 | * Find models by a condition
322 | *
323 | * @param array $conditions see helperConditionArray
324 | * @param string $conditionConnector see helperConditionArray
325 | * @param array|null $orderBy
326 | * @param int $limit see helperLimit
327 | *
328 | * @return ModelCollection|static[]|AbstractModel|null
329 | */
330 | public static function findWhere($conditions = array(), $conditionConnector = 'AND', $orderBy = null, $limit = 0)
331 | {
332 | static::initModel();
333 |
334 | $result = Database::getInstance()->select(static::$table, $conditions, $conditionConnector, $orderBy, $limit);
335 |
336 | if($limit === 1){
337 | return static::createFromDbResult($result);
338 | }
339 |
340 | return static::createMultipleFromDbResult($result);
341 | }
342 |
343 |
344 | /**
345 | * Find all models
346 | *
347 | * @param array|null $orderBy see helperOrderBy
348 | *
349 | * @return ModelCollection|static[]
350 | */
351 | public static function findAll($orderBy = null)
352 | {
353 | return static::findWhere(array(), 'AND', $orderBy);
354 | }
355 |
356 |
357 | /**
358 | * Find first model matching a condition
359 | *
360 | * @param array $conditions see helperConditionArray
361 | * @param string $conditionConnector see helperConditionArray
362 | * @param array|null $orderBy
363 | *
364 | * @return AbstractModel|null
365 | */
366 | public static function findWhereFirst($conditions = array(), $conditionConnector = 'AND', $orderBy = null)
367 | {
368 | return static::findWhere($conditions, $conditionConnector, $orderBy, 1);
369 | }
370 |
371 |
372 | /**
373 | * Find a model by id
374 | *
375 | * @param mixed $id
376 | *
377 | * @return AbstractModel|null
378 | */
379 | public static function find($id)
380 | {
381 | static::initModel();
382 |
383 | return static::findWhereFirst(array(static::$idAttribute, $id));
384 | }
385 |
386 |
387 | /**
388 | * Save model data to database
389 | */
390 | public function save()
391 | {
392 | $data = $this->preSave($this->data);
393 |
394 | $values = array();
395 | foreach(static::$attributeDbAttributeMapping as $attribute => $sqlAttribute){
396 | if($sqlAttribute === static::$idAttribute){
397 | continue;
398 | }
399 |
400 | $values[$sqlAttribute] = $data[$attribute];
401 | }
402 |
403 | if(is_null($this->getId())){
404 | $insertId = Database::getInstance()->insert(static::$table, $values);
405 |
406 | $this->setId(intval($insertId));
407 | }
408 | else{
409 | Database::getInstance()->update(static::$table, $values, array(static::$idAttribute, $this->getId()));
410 | }
411 | }
412 |
413 | /**
414 | * Delete model from database
415 | *
416 | * @return bool
417 | */
418 | public function delete()
419 | {
420 | if(!is_null($this->getId())){
421 |
422 | Database::getInstance()->delete(static::$table, static::$idAttribute, $this->getId());
423 |
424 | return true;
425 | }
426 |
427 | return false;
428 | }
429 |
430 |
431 | /**
432 | * Count models by a condition
433 | *
434 | * @param array $conditions see helperConditionArray
435 | * @param string $conditionConnector see helperConditionArray
436 | *
437 | * @return int
438 | */
439 | public static function countWhere($conditions = array(), $conditionConnector = 'AND')
440 | {
441 | static::initModel();
442 |
443 | return Database::getInstance()->count(static::$table, static::$idAttribute, $conditions, $conditionConnector);
444 | }
445 |
446 |
447 | /**
448 | * Count all models
449 | *
450 | * @return int
451 | */
452 | public static function count()
453 | {
454 | return static::countWhere();
455 | }
456 |
457 | }
458 |
--------------------------------------------------------------------------------
/include/php/models/AbstractMultiRedirect.php:
--------------------------------------------------------------------------------
1 | Config::get('schema.attributes.aliases.id', 'id'),
49 | 'source' => Config::get('schema.attributes.aliases.source', 'source'),
50 | 'destination' => Config::get('schema.attributes.aliases.destination', 'destination'),
51 | 'multi_hash' => Config::get('schema.attributes.aliases.multi_source', 'multi_source'),
52 | );
53 |
54 | if(Config::get('options.enable_user_redirects', false)){
55 | static::$attributeDbAttributeMapping['is_created_by_user'] = Config::get('schema.attributes.aliases.is_created_by_user', 'is_created_by_user');
56 | }
57 | }
58 | }
59 |
60 |
61 | /**
62 | * @inheritdoc
63 | */
64 | protected function preSave($data)
65 | {
66 | $data = parent::preSave($data);
67 |
68 | $data['source'] = emailsToString($data['source']);
69 | $data['destination'] = emailsToString($data['destination']);
70 |
71 | if(Config::get('options.enable_user_redirects', false)){
72 | $data['is_created_by_user'] = $data['is_created_by_user'] ? 1 : 0;
73 | }
74 |
75 | return $data;
76 | }
77 |
78 |
79 | /**
80 | * @inheritdoc
81 | */
82 | protected function __construct($data)
83 | {
84 | parent::__construct($data);
85 |
86 | $source = stringToEmails($data[static::attr('source')]);
87 | $destination = stringToEmails($data[static::attr('destination')]);
88 |
89 | if(get_called_class() === 'Alias' || get_called_class() === 'Redirect'){
90 | $source = $source[0];
91 | }
92 |
93 | if(get_called_class() === 'Alias' || get_called_class() === 'MultiAlias'){
94 | $destination = $destination[0];
95 | }
96 |
97 | $this->setSource($source);
98 | $this->setDestination($destination);
99 |
100 | if(Config::get('options.enable_multi_source_redirects', false)){
101 | $this->setMultiHash($data[static::attr('multi_hash')]);
102 | }
103 |
104 | if(Config::get('options.enable_user_redirects', false)){
105 | $this->setIsCreatedByUser($data[static::attr('is_created_by_user')]);
106 | }
107 | }
108 |
109 |
110 | /**
111 | * @inheritdoc
112 | */
113 | public static function create($data)
114 | {
115 | if(get_called_class() !== 'AbstractRedirect'){
116 | return parent::create($data);
117 | }
118 |
119 | $hasMultipleSources = array_key_exists(static::attr('source'), $data)
120 | && strpos($data[static::attr('source')], ',') !== false;
121 |
122 | $hasMultipleDestinations = array_key_exists(static::attr('destination'), $data)
123 | && strpos($data[static::attr('destination')], ',') !== false;
124 |
125 | if(Config::get('options.enable_multi_source_redirects', false) && $hasMultipleSources
126 | ){
127 | if($hasMultipleDestinations){
128 | return MultiRedirect::create($data);
129 | }
130 | else{
131 | return MultiAlias::create($data);
132 | }
133 | }
134 | else{
135 | if($hasMultipleDestinations){
136 | return Redirect::create($data);
137 | }
138 | else{
139 | return Alias::create($data);
140 | }
141 | }
142 | }
143 |
144 |
145 | /**
146 | * @return array|string
147 | */
148 | public function getSource()
149 | {
150 | return $this->getAttribute('source');
151 | }
152 |
153 |
154 | /**
155 | * @param string|array $value
156 | */
157 | public function setSource($value)
158 | {
159 | if(is_array($value)){
160 | $this->setAttribute('source', array_map('strtolower', $value));
161 | }
162 | else{
163 | $this->setAttribute('source', strtolower($value));
164 | }
165 | }
166 |
167 |
168 | /**
169 | * @return array|string
170 | */
171 | public function getDestination()
172 | {
173 | return $this->getAttribute('destination');
174 | }
175 |
176 |
177 | /**
178 | * @param string|array $value
179 | */
180 | public function setDestination($value)
181 | {
182 | if(is_array($value)){
183 | $this->setAttribute('destination', array_map('strtolower', $value));
184 | }
185 | else{
186 | $this->setAttribute('destination', strtolower($value));
187 | }
188 | }
189 |
190 |
191 | /**
192 | * @return string
193 | */
194 | public function getMultiHash()
195 | {
196 | return $this->getAttribute('multi_hash');
197 | }
198 |
199 |
200 | /**
201 | * @param string $value
202 | */
203 | public function setMultiHash($value)
204 | {
205 | $this->setAttribute('multi_hash', $value);
206 | }
207 |
208 |
209 | /**
210 | * @return bool
211 | */
212 | public function isCreatedByUser()
213 | {
214 | return $this->getAttribute('is_created_by_user');
215 | }
216 |
217 |
218 | /**n
219 | * @param bool $value
220 | */
221 | public function setIsCreatedByUser($value)
222 | {
223 | $this->setAttribute('is_created_by_user', $value ? true : false);
224 | }
225 |
226 |
227 | /**
228 | * @return array
229 | */
230 | protected function getDomain()
231 | {
232 | $sources = $this->getSource();
233 | if(is_string($sources)){
234 | $sources = array($sources);
235 | }
236 |
237 | $domains = array();
238 | foreach($sources as $source){
239 | $emailParts = explode('@', $source);
240 | if(count($emailParts) === 2){
241 | $domains[] = $emailParts[1];
242 | }
243 | }
244 |
245 | return array_unique($domains);
246 | }
247 |
248 |
249 | /**
250 | * @return ModelCollection
251 | */
252 | public function getConflictingUsers()
253 | {
254 | if(is_null($this->conflictingUsers)){
255 | $sources = $this->getSource();
256 |
257 | if(is_string($sources)){
258 | $sources = array($sources);
259 | }
260 |
261 | $this->conflictingUsers = new ModelCollection();
262 | foreach($sources as $source){
263 | $user = User::findByEmail($source);
264 | if(!is_null($user)){
265 | $this->conflictingUsers->add($user);
266 | }
267 | }
268 | }
269 |
270 | return $this->conflictingUsers;
271 | }
272 |
273 |
274 | /**
275 | * @param string $template
276 | *
277 | * @return array|string
278 | */
279 | public function getConflictingMarkedSource($template = "%email% ")
280 | {
281 | $conflictingUsers = $this->getConflictingUsers();
282 |
283 | $sources = $this->getSource();
284 |
285 | if(is_string($sources)){
286 | $sources = array($sources);
287 | }
288 |
289 | foreach($conflictingUsers as $user){
290 | if(($key = array_search($user->getEmail(), $sources)) !== false){
291 | $sources[$key] = str_replace('%email%', $sources[$key], $template);
292 | }
293 | }
294 |
295 | return $sources;
296 | }
297 |
298 |
299 | /**
300 | * @inheritdoc
301 | */
302 | public static function findAll($orderBy = null)
303 | {
304 | if(is_null($orderBy)){
305 | $orderBy = array(static::attr('source'));
306 | }
307 |
308 | return parent::findAll($orderBy);
309 | }
310 |
311 |
312 | /**
313 | * @return string
314 | */
315 | private static function generateRedirectBaseQuery()
316 | {
317 | if(Config::get('options.enable_multi_source_redirects', false)){
318 | return "SELECT r.* FROM (
319 | SELECT
320 | GROUP_CONCAT(g.`".static::$idAttribute."` ORDER BY g.`".static::$idAttribute."` SEPARATOR ',') AS `".static::$idAttribute."`,
321 | GROUP_CONCAT(g.`".static::attr('source')."` SEPARATOR ',') AS `".static::attr('source')."`,
322 | g.`".static::attr('destination')."`,
323 | g.`".static::attr('multi_hash')."`
324 | ".(Config::get('options.enable_user_redirects', false) ? ", g.`".static::attr('is_created_by_user')."`" : "")."
325 | FROM `".static::$table."` AS g
326 | WHERE g.`".static::attr('multi_hash')."` IS NOT NULL
327 | GROUP BY g.`".static::attr('multi_hash')."`
328 | UNION
329 | SELECT
330 | s.`".static::$idAttribute."`,
331 | s.`".static::attr('source')."`,
332 | s.`".static::attr('destination')."`,
333 | s.`".static::attr('multi_hash')."`
334 | ".(Config::get('options.enable_user_redirects', false) ? ", s.`".static::attr('is_created_by_user')."`" : "")."
335 | FROM `".static::$table."` AS s
336 | WHERE s.`".static::attr('multi_hash')."` IS NULL
337 | ) AS r";
338 | }
339 | else{
340 | return "SELECT * FROM `".static::$table."`";
341 | }
342 | }
343 |
344 |
345 | public static function findMultiAll($orderBy = null)
346 | {
347 | static::initModel();
348 |
349 | if(is_null($orderBy)){
350 | $orderBy = array(static::attr('source'));
351 | }
352 |
353 | $sql = static::generateRedirectBaseQuery()
354 | .Database::helperOrderBy($orderBy);
355 |
356 | return static::findAllRaw($sql);
357 | }
358 |
359 |
360 | public static function findMultiWhere($conditions = array(), $conditionConnector = 'AND', $orderBy = null, $limit = 0)
361 | {
362 | $sql = static::generateRedirectBaseQuery()
363 | .Database::helperWhere($conditions, $conditionConnector)
364 | .Database::helperOrderBy($orderBy)
365 | .Database::helperLimit($limit);
366 |
367 | if($limit === 1){
368 | return static::findRaw($sql);
369 | }
370 |
371 | return static::findAllRaw($sql);
372 | }
373 |
374 |
375 | public static function findMultiWhereFirst($conditions = array(), $conditionConnector = 'AND', $orderBy = null)
376 | {
377 | return static::findMultiWhere($conditions, $conditionConnector, $orderBy, 1);
378 | }
379 |
380 |
381 | public static function findMulti($id)
382 | {
383 | static::initModel();
384 |
385 | return static::findMultiWhereFirst(array(static::$idAttribute, $id));
386 | }
387 |
388 |
389 | /**
390 | * @param array|User|null $limitedBy
391 | *
392 | * @return ModelCollection|static[]
393 | */
394 | public static function getMultiByLimitedDomains($limitedBy = null)
395 | {
396 | return static::filterModelCollectionByLimitedDomains(static::findMultiAll(), $limitedBy);
397 | }
398 | }
399 |
--------------------------------------------------------------------------------
/include/php/models/Alias.php:
--------------------------------------------------------------------------------
1 | Config::get('schema.attributes.domains.id', 'id'),
43 | 'domain' => Config::get('schema.attributes.domains.domain', 'domain'),
44 | );
45 | }
46 | }
47 |
48 |
49 | /**
50 | * @inheritdoc
51 | */
52 | protected function __construct($data)
53 | {
54 | parent::__construct($data);
55 |
56 | $this->setDomain($data[static::attr('domain')]);
57 | }
58 |
59 |
60 | /**
61 | * @return string
62 | */
63 | public function getDomain()
64 | {
65 | return $this->getAttribute('domain');
66 | }
67 |
68 |
69 | /**
70 | * @param string $value
71 | */
72 | public function setDomain($value)
73 | {
74 | $this->setAttribute('domain', strtolower($value));
75 | }
76 |
77 |
78 | /**
79 | * @return int
80 | */
81 | public function countUsers()
82 | {
83 | return User::countWhere(
84 | array(User::attr('domain'), $this->getDomain())
85 | );
86 | }
87 |
88 |
89 | /**
90 | * @return int
91 | */
92 | public function countRedirects()
93 | {
94 | return AbstractRedirect::countWhere(
95 | array(
96 | array(AbstractRedirect::attr('source'), 'LIKE', "%@{$this->getDomain()}"),
97 | array(AbstractRedirect::attr('destination'), 'LIKE', "%@{$this->getDomain()}")
98 | ),
99 | 'OR'
100 | );
101 | }
102 |
103 | }
104 |
--------------------------------------------------------------------------------
/include/php/models/DomainLimitTrait.php:
--------------------------------------------------------------------------------
1 | isDomainLimited() || static::isInLimitedDomains($limitedBy->getDomainLimits());
21 | }
22 |
23 | if(!is_array($limitedBy)){
24 | throw new InvalidArgumentException;
25 | }
26 |
27 | /** @var string|array|string[] $domain */
28 | $domain = $this->getDomain();
29 |
30 | if(is_string($domain)) {
31 | return in_array($domain, $limitedBy);
32 | }
33 |
34 | foreach($domain as $d){
35 | if(!in_array($d, $limitedBy)) {
36 | return false;
37 | }
38 | }
39 |
40 | return true;
41 | }
42 |
43 |
44 | /**
45 | * @param ModelCollection|static[] $collection
46 | * @param array|User|null $limitedBy
47 | *
48 | * @return ModelCollection|static[]
49 | */
50 | protected static function filterModelCollectionByLimitedDomains($collection, $limitedBy = null)
51 | {
52 | return $collection->searchAll(function($model) use ($limitedBy){
53 | /** @var static $model */
54 | //var_dump($model->isInLimitedDomains($limitedBy), $model->getDomain());
55 | return $model->isInLimitedDomains($limitedBy);
56 | });
57 | }
58 |
59 |
60 | /**
61 | * @param array|User|null $limitedBy
62 | *
63 | * @return ModelCollection|static[]
64 | */
65 | public static function getByLimitedDomains($limitedBy = null)
66 | {
67 | return static::filterModelCollectionByLimitedDomains(static::findAll(), $limitedBy);
68 | }
69 | }
--------------------------------------------------------------------------------
/include/php/models/ModelCollection.php:
--------------------------------------------------------------------------------
1 | isNumericArray($array)){
20 | foreach($array as $model){
21 | $this->add($model);
22 | }
23 | }
24 | else{
25 | foreach($array as $key => $model){
26 | $this->add($model, $key);
27 | }
28 | }
29 | }
30 |
31 |
32 | /**
33 | * @param array $array
34 | *
35 | * @return bool
36 | */
37 | protected function isNumericArray($array)
38 | {
39 | return array_keys($array) === range(0, count($array) - 1)
40 | && count(array_filter($array, 'is_string')) === 0;
41 | }
42 |
43 |
44 | /**
45 | * Adds a model to the collection,
46 | * but won't replace if it exists with that key
47 | *
48 | * @param AbstractModel $model
49 | * @param mixed|null $key
50 | */
51 | public function add($model, $key = null)
52 | {
53 | if(is_null($model) || !($model instanceof AbstractModel)){
54 | return;
55 | }
56 |
57 | if(is_null($key)){
58 | $this->models[] = $model;
59 | }
60 | elseif(!$this->has($key)){
61 | $this->models[$key] = $model;
62 | }
63 | }
64 |
65 |
66 | /**
67 | * Replace a model with given key
68 | *
69 | * @param AbstractModel $model
70 | * @param mixed $key
71 | */
72 | public function replace($model, $key)
73 | {
74 | if(is_null($model) || !($model instanceof AbstractModel)){
75 | return;
76 | }
77 |
78 | $model[$key] = $model;
79 | }
80 |
81 |
82 | /**
83 | * Delete a model by key
84 | *
85 | * @param mixed $key
86 | */
87 | public function delete($key)
88 | {
89 | if($this->has($key)){
90 | unset($this->models[$key]);
91 | }
92 | }
93 |
94 |
95 | /**
96 | * Check if collection has a model by key
97 | *
98 | * @param mixed $key
99 | *
100 | * @return bool
101 | */
102 | public function has($key)
103 | {
104 | return isset($this->models[$key]);
105 | }
106 |
107 |
108 | /**
109 | * Get a model from the collection by key
110 | *
111 | * @param mixed $key
112 | *
113 | * @return AbstractModel|null
114 | */
115 | public function get($key)
116 | {
117 | if($this->has($key)){
118 | return $this->models[$key];
119 | }
120 |
121 | return null;
122 | }
123 |
124 |
125 | /**
126 | * Search a model in collection with a condition
127 | *
128 | * @param callable $callable Gives back if the search matches
129 | *
130 | * @return AbstractModel|null
131 | */
132 | public function search($callable)
133 | {
134 | if(is_callable($callable)){
135 | foreach($this->models as $model){
136 | if($callable($model)){
137 | return $model;
138 | }
139 | }
140 | }
141 |
142 | return null;
143 | }
144 |
145 |
146 | /**
147 | * Search all models in collection with a condition
148 | *
149 | * @param callable $callable Gives back if the search matches
150 | *
151 | * @return static
152 | */
153 | public function searchAll($callable)
154 | {
155 | $collection = new static;
156 |
157 | if(is_callable($callable)){
158 | foreach($this->models as $model){
159 | if($callable($model)){
160 | $collection->add($model);
161 | }
162 | }
163 | }
164 |
165 | return $collection;
166 | }
167 |
168 |
169 | /**
170 | * Convert models to an array of strings
171 | *
172 | * @param callable $callable Gives back a string for a model
173 | *
174 | * @return array|string[]
175 | */
176 | public function toStringArray($callable)
177 | {
178 | $strings = array();
179 |
180 | if(is_callable($callable)){
181 | foreach($this->models as $model){
182 | $strings[] = $callable($model);
183 | }
184 | }
185 |
186 | return $strings;
187 | }
188 |
189 |
190 | /**
191 | * @inheritdoc
192 | */
193 | public function current()
194 | {
195 | return current($this->models);
196 | }
197 |
198 |
199 | /**
200 | * @inheritdoc
201 | */
202 | public function next()
203 | {
204 | return next($this->models);
205 | }
206 |
207 |
208 | /**
209 | * @inheritdoc
210 | */
211 | public function key()
212 | {
213 | return key($this->models);
214 | }
215 |
216 |
217 | /**
218 | * @inheritdoc
219 | */
220 | public function valid()
221 | {
222 | return $this->current() !== false;
223 | }
224 |
225 |
226 | /**
227 | * @inheritdoc
228 | */
229 | public function rewind()
230 | {
231 | reset($this->models);
232 | }
233 |
234 |
235 | /**
236 | * @inheritdoc
237 | */
238 | public function offsetExists($offset)
239 | {
240 | return $this->has($offset);
241 | }
242 |
243 |
244 | /**
245 | * @inheritdoc
246 | */
247 | public function offsetGet($offset)
248 | {
249 | return $this->get($offset);
250 | }
251 |
252 |
253 | /**
254 | * @inheritdoc
255 | */
256 | public function offsetSet($offset, $value)
257 | {
258 | $this->add($value, $offset);
259 | }
260 |
261 |
262 | /**
263 | * @inheritdoc
264 | */
265 | public function offsetUnset($offset)
266 | {
267 | $this->delete($offset);
268 | }
269 |
270 |
271 | /**
272 | * @inheritdoc
273 | */
274 | public function count()
275 | {
276 | return count($this->models);
277 | }
278 | }
279 |
--------------------------------------------------------------------------------
/include/php/models/MultiAlias.php:
--------------------------------------------------------------------------------
1 | Config::get('schema.attributes.users.id', 'id'),
59 | 'username' => Config::get('schema.attributes.users.username', 'username'),
60 | 'domain' => Config::get('schema.attributes.users.domain', 'domain'),
61 | 'password_hash' => Config::get('schema.attributes.users.password', 'password'),
62 | );
63 |
64 | if(Config::get('options.enable_mailbox_limits', false)){
65 | static::$attributeDbAttributeMapping['mailbox_limit'] = Config::get('schema.attributes.users.mailbox_limit');
66 | }
67 |
68 | if(Config::get('options.enable_user_redirects', false)){
69 | static::$attributeDbAttributeMapping['max_user_redirects'] = Config::get('schema.attributes.users.max_user_redirects');
70 | }
71 | }
72 | }
73 |
74 |
75 | /**
76 | * @inheritdoc
77 | */
78 | protected function __construct($data)
79 | {
80 | parent::__construct($data);
81 |
82 | $this->setUsername($data[static::attr('username')]);
83 | $this->setDomain($data[static::attr('domain')]);
84 | $this->setPasswordHash($data[static::attr('password_hash')]);
85 |
86 | if(Config::get('options.enable_mailbox_limits', false)){
87 | $this->setMailboxLimit($data[static::attr('mailbox_limit')]);
88 | }
89 |
90 | if(Config::get('options.enable_user_redirects', false)){
91 | $this->setMaxUserRedirects($data[static::attr('max_user_redirects')]);
92 | }
93 |
94 | $this->setAttribute('role', static::getRoleByEmail($this->getEmail()));
95 | }
96 |
97 |
98 | /**
99 | * @return string
100 | */
101 | public function getUsername()
102 | {
103 | return $this->getAttribute('username');
104 | }
105 |
106 |
107 | /**
108 | * @param string $value
109 | */
110 | public function setUsername($value)
111 | {
112 | $this->setAttribute('username', strtolower($value));
113 | }
114 |
115 |
116 | /**
117 | * @return string
118 | */
119 | public function getDomain()
120 | {
121 | return $this->getAttribute('domain');
122 | }
123 |
124 |
125 | /**
126 | * @param string $value
127 | */
128 | public function setDomain($value)
129 | {
130 | $this->setAttribute('domain', strtolower($value));
131 | }
132 |
133 |
134 | /**
135 | * @return string
136 | */
137 | public function getEmail()
138 | {
139 | return $this->getUsername().'@'.$this->getDomain();
140 | }
141 |
142 |
143 | /**
144 | * @return string
145 | */
146 | public function getPasswordHash()
147 | {
148 | return $this->getAttribute('password_hash');
149 | }
150 |
151 |
152 | /**
153 | * @param string $value
154 | */
155 | public function setPasswordHash($value)
156 | {
157 | $this->setAttribute('password_hash', $value);
158 | }
159 |
160 |
161 | /**
162 | * @return int
163 | */
164 | public function getMailboxLimit()
165 | {
166 | return $this->getAttribute('mailbox_limit');
167 | }
168 |
169 |
170 | /**
171 | * @param int $value
172 | */
173 | public function setMailboxLimit($value)
174 | {
175 | $this->setAttribute('mailbox_limit', intval($value));
176 | }
177 |
178 |
179 | /**
180 | * @return int
181 | */
182 | public function getMaxUserRedirects()
183 | {
184 | return $this->getAttribute('max_user_redirects');
185 | }
186 |
187 |
188 | /**
189 | * @param int $value
190 | */
191 | public function setMaxUserRedirects($value)
192 | {
193 | $this->setAttribute('max_user_redirects', intval($value));
194 | }
195 |
196 |
197 | /**
198 | * @param string $attr
199 | * @param mixed $default
200 | *
201 | * @return mixed
202 | *
203 | * @throws Exception
204 | */
205 | protected static function getAttributeDefaultValue($attr, $default)
206 | {
207 | static::initModel();
208 |
209 | $sql = "SELECT DEFAULT(".static::attr($attr).") FROM `".static::$table."` LIMIT 1";
210 |
211 | try {
212 | $result = Database::getInstance()->query($sql);
213 |
214 | if($result->num_rows === 1){
215 | $row = $result->fetch_array();
216 |
217 | return $row[0];
218 | }
219 | }
220 | catch(Exception $e) {
221 | if (strpos($e->getMessage(), 'doesn\'t have a default') !== false) {
222 | throw new Exception('Database table "'.static::$table.'" is missing a default value for attribute "'.static::attr($attr).'".');
223 | }
224 |
225 | return $default;
226 | }
227 |
228 | return $default;
229 | }
230 |
231 |
232 | /**
233 | * Get mailbox limit default via database default value
234 | *
235 | * @return int
236 | */
237 | public static function getMailboxLimitDefault()
238 | {
239 | if(Config::get('options.enable_mailbox_limits', false)){
240 | return intval(static::getAttributeDefaultValue('mailbox_limit', 0));
241 | }
242 |
243 | return 0;
244 | }
245 |
246 |
247 | /**
248 | * Get max user redirects default via database default value
249 | *
250 | * @return int
251 | */
252 | public static function getMaxUserRedirectsDefault()
253 | {
254 | if(Config::get('options.enable_user_redirects', false)){
255 | return intval(static::getAttributeDefaultValue('max_user_redirects', 0));
256 | }
257 |
258 | return 0;
259 | }
260 |
261 |
262 | /**
263 | * @return string
264 | */
265 | public function getRole()
266 | {
267 | return $this->getAttribute('role');
268 | }
269 |
270 |
271 | /**
272 | * @param string $email
273 | *
274 | * @return string
275 | */
276 | private static function getRoleByEmail($email)
277 | {
278 | if(in_array($email, Config::get('admins', array()))){
279 | return static::ROLE_ADMIN;
280 | }
281 |
282 | return static::ROLE_USER;
283 | }
284 |
285 |
286 | /**
287 | * Is user limited by domain limits?
288 | *
289 | * @return bool
290 | */
291 | public function isDomainLimited()
292 | {
293 | $adminDomainLimits = Config::get('admin_domain_limits', array());
294 |
295 | return Config::get('options.enable_admin_domain_limits', false)
296 | && is_array($adminDomainLimits) && isset($adminDomainLimits[$this->getEmail()]);
297 | }
298 |
299 |
300 | /**
301 | * Get domain limits, returns an empty array if user has no limits or ADMIN_DOMAIN_LIMITS_ENABLED is disabled
302 | *
303 | * @return array
304 | */
305 | public function getDomainLimits()
306 | {
307 | if($this->isDomainLimited()){
308 | $adminDomainLimits = Config::get('admin_domain_limits', array());
309 |
310 | if(!is_array($adminDomainLimits[$this->getEmail()])){
311 | throw new InvalidArgumentException('Config value of admin domain limits for email "'.$this->getEmail().'" needs to be of type array.');
312 | }
313 |
314 | return $adminDomainLimits[$this->getEmail()];
315 | }
316 |
317 | return array();
318 | }
319 |
320 |
321 | /**
322 | * @return bool
323 | */
324 | public function isAllowedToCreateUserRedirects()
325 | {
326 | return $this->getMaxUserRedirects() >= 0;
327 | }
328 |
329 |
330 | /**
331 | * @return bool
332 | */
333 | public function canCreateUserRedirects()
334 | {
335 | if(!$this->isAllowedToCreateUserRedirects()
336 | || (
337 | $this->getMaxUserRedirects() > 0
338 | && $this->getSelfCreatedRedirects()->count() >= $this->getMaxUserRedirects()
339 | )
340 | ){
341 | return false;
342 | }
343 |
344 | return true;
345 | }
346 |
347 |
348 | /**
349 | * @return AbstractRedirect
350 | */
351 | public function getConflictingRedirect()
352 | {
353 | if(is_null($this->conflictingRedirect)){
354 | $this->conflictingRedirect = AbstractRedirect::findWhereFirst(
355 | array(AbstractRedirect::attr('source'), $this->getEmail())
356 | );
357 | }
358 |
359 | return $this->conflictingRedirect;
360 | }
361 |
362 |
363 | /**
364 | * @return ModelCollection|AbstractRedirect[]
365 | */
366 | public function getRedirects()
367 | {
368 | if(is_null($this->redirects)){
369 | $this->redirects = AbstractRedirect::findMultiWhere(
370 | array(AbstractRedirect::attr('destination'), 'LIKE', '%'.$this->getEmail().'%')
371 | );
372 | }
373 |
374 | return $this->redirects;
375 | }
376 |
377 |
378 | /**
379 | * @return ModelCollection|AbstractRedirect[]
380 | */
381 | public function getAnonymizedRedirects()
382 | {
383 | $redirects = $this->getRedirects();
384 |
385 | foreach($redirects as $redirect){
386 | $emails = $redirect->getDestination();
387 |
388 | if(is_array($emails) && count($emails) > 1){
389 | $redirect->setDestination(array($this->getEmail(), '…'));
390 | }
391 | }
392 |
393 | return $redirects;
394 | }
395 |
396 |
397 | /**
398 | * @return ModelCollection|AbstractRedirect[]
399 | */
400 | public function getSelfCreatedRedirects()
401 | {
402 | $redirects = $this->getRedirects();
403 |
404 | return $redirects->searchAll(
405 | function($redirect) {
406 | /** @var AbstractRedirect $redirect */
407 | return $redirect->isCreatedByUser();
408 | }
409 | );
410 | }
411 |
412 |
413 | /**
414 | * Change this users password, throws Exception if password is invalid.
415 | *
416 | * @param string $password
417 | * @param string $passwordRepeated
418 | *
419 | * @throws AuthException
420 | */
421 | public function changePassword($password, $passwordRepeated)
422 | {
423 | Auth::validateNewPassword($password, $passwordRepeated);
424 |
425 | $passwordHash = Auth::generatePasswordHash($password);
426 |
427 | $this->setPasswordHash($passwordHash);
428 | $this->save();
429 | }
430 |
431 |
432 | /**
433 | * @inheritdoc
434 | */
435 | public static function findAll($orderBy = null)
436 | {
437 | if(is_null($orderBy)){
438 | $orderBy = array(static::attr('domain'), static::attr('username'));
439 | }
440 |
441 | return parent::findAll($orderBy);
442 | }
443 |
444 |
445 | /**
446 | * @param string $email
447 | *
448 | * @return User|null
449 | */
450 | public static function findByEmail($email)
451 | {
452 | $emailInParts = explode("@", $email);
453 | if(count($emailInParts) !== 2){
454 | return null;
455 | }
456 | $username = $emailInParts[0];
457 | $domain = $emailInParts[1];
458 |
459 | return static::findWhereFirst(
460 | array(
461 | array(static::attr('username'), $username),
462 | array(static::attr('domain'), $domain)
463 | )
464 | );
465 | }
466 |
467 | }
468 |
--------------------------------------------------------------------------------
/include/php/pages/admin/createdomain.php:
--------------------------------------------------------------------------------
1 | isDomainLimited()){
4 | Router::displayError(403);
5 | }
6 |
7 | if(isset($_POST['domain'])){
8 | $inputDomain = $_POST['domain'];
9 |
10 | if(!empty($inputDomain)){
11 |
12 | $existingDomain = Domain::findWhere(array(Domain::attr('domain'), $inputDomain));
13 |
14 | if(!is_null($existingDomain)){
15 |
16 | Domain::createAndSave(
17 | array(
18 | Domain::attr('domain') => $inputDomain,
19 | )
20 | );
21 |
22 | // Created domain successfull, redirect to overview
23 | Router::redirect("admin/listdomains/?created=1");
24 | }
25 | else{
26 | Message::getInstance()->fail("Domain already exists in database.");
27 | }
28 | }
29 | else{
30 | Message::getInstance()->fail("Empty domain couldn't be created.");
31 | }
32 | }
33 |
34 | ?>
35 |
36 | Create new domain
37 |
38 | render(); ?>
39 |
40 |
43 |
44 |
--------------------------------------------------------------------------------
/include/php/pages/admin/deletedomain.php:
--------------------------------------------------------------------------------
1 | isDomainLimited()){
4 | Router::displayError(403);
5 | }
6 |
7 | if(!isset($_GET['id'])){
8 | // Domain id not set, redirect to overview
9 | Router::redirect("admin/listdomains");
10 | }
11 |
12 | $id = $_GET['id'];
13 |
14 | /** @var Domain $domain */
15 | $domain = Domain::find($id);
16 |
17 | if(is_null($domain)){
18 | // Domain doesn't exist, redirect to overview
19 | Router::redirect("admin/listdomains");
20 | }
21 |
22 | if(!$domain->isInLimitedDomains()){
23 | Router::redirect("admin/listdomains/?missing-permission=1");
24 | }
25 |
26 | // Delete domain
27 | if(isset($_POST['confirm'])){
28 | $confirm = $_POST['confirm'];
29 |
30 | if($confirm === "yes"){
31 |
32 | // Check if admin domain is affected
33 | $isAdminDomain = false;
34 | foreach(Config::get('admins', array()) as $admin){
35 | $parts = explode("@", $admin);
36 | if(count($parts) === 2 && $parts[2] === $domain->getDomain()){
37 | $isAdminDomain = true;
38 | break;
39 | }
40 | }
41 |
42 | if(!$isAdminDomain){
43 |
44 | $users = User::findWhere(array(User::attr('domain'), $domain->getDomain()));
45 |
46 | /** @var User $user */
47 | foreach($users as $user){
48 | $user->delete();
49 | }
50 |
51 | $domain->delete();
52 |
53 | // Delete domain successfull, redirect to overview
54 | Router::redirect("admin/listdomains/?deleted=1");
55 | }
56 | else{
57 | // Cannot delete domain with admin emails, redirect to overview
58 | Router::redirect("admin/listdomains/?adm_del=1");
59 | }
60 | }
61 |
62 | else{
63 | // Choose to not delete domain, redirect to overview
64 | Router::redirect("admin/listdomains");
65 | }
66 | }
67 | ?>
68 |
69 | Delete domain "getDomain() ?>"?
70 |
71 |
74 |
75 |
--------------------------------------------------------------------------------
/include/php/pages/admin/deleteredirect.php:
--------------------------------------------------------------------------------
1 | isInLimitedDomains()){
19 | Router::redirect("admin/listredirects/?missing-permission=1");
20 | }
21 |
22 | if(isset($_POST['confirm'])){
23 | $confirm = $_POST['confirm'];
24 |
25 | if($confirm === "yes"){
26 |
27 | if ($redirect instanceof AbstractMultiRedirect){
28 |
29 | // Get single source rows of multi source redirect/alias instead
30 | $hash = $redirect->getMultiHash();
31 | $singleRedirects = AbstractRedirect::findWhere(array(AbstractRedirect::attr('multi_hash'), $hash));
32 |
33 | /** @var AbstractRedirect $redirectToDelete */
34 | foreach($singleRedirects as $redirectToDelete){
35 | $redirectToDelete->delete();
36 | }
37 | }
38 | else {
39 | $redirect->delete();
40 | }
41 |
42 | // Delete redirect successfull, redirect to overview
43 | Router::redirect("admin/listredirects/?deleted=1");
44 | }
45 | else{
46 | // Choose to not delete redirect, redirect to overview
47 | Router::redirect("admin/listredirects");
48 | }
49 | }
50 |
51 | else{
52 | ?>
53 |
54 | Delete redirection?
55 |
56 |
59 |
60 |
85 |
--------------------------------------------------------------------------------
/include/php/pages/admin/deleteuser.php:
--------------------------------------------------------------------------------
1 | isInLimitedDomains()){
19 | Router::redirect('admin/listusers/?missing-permission=1');
20 | }
21 |
22 | // Delete user
23 | if(isset($_POST['confirm'])){
24 | $confirm = $_POST['confirm'];
25 |
26 | if($confirm === 'yes'){
27 | // Check if admin is affected
28 | if(!in_array($user->getEmail(), Config::get('admins', array()))){
29 |
30 | // Delete redirects of this user
31 | if(isset($_POST['delete_redirects']) && $_POST['delete_redirects'] === 'yes'
32 | && isset($_POST['selected_redirects']) && is_array($_POST['selected_redirects'])
33 | ){
34 | $redirectMultiIds = $_POST['selected_redirects'];
35 |
36 | foreach($redirectMultiIds as $redirectMultiId){
37 | $redirectIds = explode(',', $redirectMultiId);
38 |
39 | foreach($redirectIds as $redirectId){
40 |
41 | // Note: No Multi* selected, so there is only Alias & Redirect
42 | $redirects = AbstractRedirect::findWhere(
43 | array(
44 | array(AbstractRedirect::attr('id'), $redirectId),
45 | array(AbstractRedirect::attr('destination'), 'LIKE', '%'.$user->getEmail().'%')
46 | )
47 | );
48 |
49 | /** @var AbstractRedirect $redirect */
50 | foreach($redirects as $redirect){
51 | if($redirect instanceof Alias) {
52 | $redirect->delete();
53 | }
54 | elseif($redirect instanceof Redirect) {
55 | $redirect->setDestination(
56 | array_diff(
57 | $redirect->getDestination(),
58 | array($user->getEmail())
59 | )
60 | );
61 | $redirect->save();
62 | }
63 | }
64 | }
65 | }
66 | }
67 |
68 | $user->delete();
69 |
70 | // Delete user successful, redirect to overview
71 | Router::redirect('admin/listusers/?deleted=1');
72 | }
73 | else{
74 | // Admin tried to delete himself, redirect to overview
75 | Router::redirect('admin/listusers/?adm_del=1');
76 | }
77 | }
78 | else{
79 | // Choose to not delete user, redirect to overview
80 | Router::redirect('admin/listusers');
81 | }
82 | }
83 |
84 | $redirects = $user->getAnonymizedRedirects();
85 |
86 | ?>
87 |
88 | Delete user "getEmail() ?>"?
89 |
90 |
93 |
94 |
--------------------------------------------------------------------------------
/include/php/pages/admin/editredirect.php:
--------------------------------------------------------------------------------
1 | isInLimitedDomains()){
18 | Router::redirect("admin/listredirects/?missing-permission=1");
19 | }
20 | }
21 |
22 | if(isset($_POST['savemode'])){
23 | $savemode = $_POST['savemode'];
24 |
25 | $inputSources = stringToEmails($_POST['source']);
26 | $inputDestinations = stringToEmails($_POST['destination']);
27 |
28 | // validate emails
29 | $emailErrors = array();
30 |
31 | // basic email validation isn't working 100% correct though
32 | foreach(array_merge($inputSources, $inputDestinations) as $email){
33 | if(strpos($email, '@') === false){
34 | $emailErrors[$email] = "Address \"{$email}\" isn't a valid email address.";
35 | }
36 | }
37 |
38 | // validate source emails are on domains
39 | if(Config::get('options.enable_validate_aliases_source_domain', true)){
40 | $domains = Domain::getByLimitedDomains();
41 |
42 | foreach($inputSources as $email){
43 | if(isset($emailErrors[$email])){
44 | continue;
45 | }
46 |
47 | $emailParts = explode('@', $email);
48 | $searchResult = $domains->search(
49 | function($domain) use ($emailParts){
50 | /** @var Domain $domain */
51 | return $domain->getDomain() === $emailParts[1];
52 | }
53 | );
54 |
55 | if(is_null($searchResult)){
56 | $emailErrors[$email] = "Domain of source address \"{$email}\" not in your domains.";
57 | }
58 | }
59 | }
60 |
61 | // validate no redirect loops
62 | foreach(array_intersect($inputSources, $inputDestinations) as $email){
63 | $emailErrors[$email] = "Address \"{$email}\" cannot be in source and destination in same redirect.";
64 | }
65 |
66 |
67 | if(count($emailErrors) > 0){
68 | Message::getInstance()->fail(implode(" ", $emailErrors));
69 | }
70 | else{
71 | if(count($emailErrors) === 0 && $savemode === "edit" && !is_null($redirect)){
72 |
73 | if(count($inputSources) > 0 && count($inputDestinations) > 0){
74 |
75 | if(Config::get('options.enable_multi_source_redirects', false) && $redirect instanceof AbstractMultiRedirect){
76 | $existingRedirectsToEdit = AbstractRedirect::findWhere(
77 | array(AbstractRedirect::attr('multi_hash'), $redirect->getMultiHash())
78 | );
79 | }
80 | else{
81 | $existingRedirectsToEdit = AbstractRedirect::findWhere(
82 | array(AbstractRedirect::attr('id'), $redirect->getId())
83 | );
84 | }
85 |
86 | $emailsToCheck = $inputSources;
87 | foreach($existingRedirectsToEdit as $r){
88 | $key = array_search($r->getSource(), $emailsToCheck);
89 | if($key !== false){
90 | unset($emailsToCheck[$key]);
91 | }
92 | }
93 |
94 | if(count($emailsToCheck) > 0){
95 | $existingRedirectsOther = AbstractRedirect::findWhere(
96 | array(
97 | array(AbstractRedirect::attr('source'), 'IN', $emailsToCheck)
98 | )
99 | );
100 | }
101 | else{
102 | $existingRedirectsOther = null;
103 | }
104 |
105 | if(!is_null($existingRedirectsOther) && $existingRedirectsOther->count() > 0){
106 | $errorMessages = array();
107 | /** @var AbstractRedirect $existingRedirect */
108 | foreach($existingRedirectsOther as $id => $existingRedirect){
109 | if(!$existingRedirectsToEdit->has($id)){
110 | $errorMessages[] = "Source address \"{$existingRedirect->getSource()}\" is already redirected to some destination.";
111 | }
112 | }
113 |
114 | Message::getInstance()->fail(implode(" ", $errorMessages));
115 | }
116 | else{
117 | // multi source handling
118 | $hash = (count($inputSources) === 1) ? null : md5(emailsToString($inputSources));
119 |
120 | foreach($inputSources as $sourceAddress){
121 | $sourceAddress = formatEmail($sourceAddress);
122 |
123 | /** @var AbstractRedirect $thisRedirect */
124 | $thisRedirect = $existingRedirectsToEdit->search(
125 | function($model) use ($sourceAddress){
126 | /** @var AbstractRedirect $model */
127 | return $model->getSource() === $sourceAddress;
128 | }
129 | );
130 |
131 | if(!is_null($thisRedirect)){
132 | // edit existing source
133 |
134 | $thisRedirect->setSource($sourceAddress);
135 | $thisRedirect->setDestination($inputDestinations);
136 | $thisRedirect->setMultiHash($hash);
137 | // Don't set 'isCreatedByUser' here, it will overwrite redirects created by user
138 | $thisRedirect->save();
139 |
140 | $existingRedirectsToEdit->delete($thisRedirect->getId()); // mark updated
141 | }
142 | else{
143 | $data = array(
144 | AbstractRedirect::attr('source') => $sourceAddress,
145 | AbstractRedirect::attr('destination') => emailsToString($inputDestinations),
146 | AbstractRedirect::attr('multi_hash') => $hash,
147 | );
148 |
149 | if(Config::get('options.enable_user_redirects', false)){
150 | $data[AbstractRedirect::attr('is_created_by_user')] = false;
151 | }
152 |
153 | AbstractRedirect::createAndSave($data);
154 | }
155 | }
156 |
157 | // Delete none updated redirect
158 | foreach($existingRedirectsToEdit as $redirect){
159 | $redirect->delete();
160 | }
161 |
162 | // Edit successfull, redirect to overview
163 | Router::redirect("admin/listredirects/?edited=1");
164 | }
165 | }
166 | else{
167 | Message::getInstance()->fail("Redirect couldn't be edited. Fill out all fields.");
168 | }
169 | }
170 |
171 | else if(count($emailErrors) === 0 && $savemode === "create"){
172 | if(count($inputSources) > 0 && count($inputDestinations) > 0){
173 |
174 | $existingRedirects = AbstractRedirect::findWhere(
175 | array(AbstractRedirect::attr('source'), 'IN', $inputSources)
176 | );
177 |
178 | if($existingRedirects->count() > 0){
179 | $errorMessages = array();
180 | /** @var AbstractRedirect $existingRedirect */
181 | foreach($existingRedirects as $existingRedirect){
182 | $errorMessages[] = "Source address \"{$existingRedirect->getSource()}\" is already redirected to some destination.";
183 | }
184 |
185 | Message::getInstance()->fail(implode(" ", $errorMessages));
186 | }
187 | else{
188 | $inputDestination = emailsToString($inputDestinations);
189 | $hash = (count($inputSources) === 1) ? null : md5(emailsToString($inputSources));
190 |
191 | foreach($inputSources as $inputSource){
192 | $data = array(
193 | AbstractRedirect::attr('source') => $inputSource,
194 | AbstractRedirect::attr('destination') => $inputDestination,
195 | AbstractRedirect::attr('multi_hash') => $hash,
196 | );
197 |
198 | if(Config::get('options.enable_user_redirects', false)){
199 | $data[AbstractRedirect::attr('is_created_by_user')] = false;
200 | }
201 |
202 | $a = AbstractRedirect::createAndSave($data);
203 | }
204 |
205 | // Redirect created, redirect to overview
206 | Router::redirect("admin/listredirects/?created=1");
207 | }
208 | }
209 | else{
210 | Message::getInstance()->fail("Redirect couldn't be created. Fill out all fields.");
211 | }
212 | }
213 | }
214 | }
215 |
216 |
217 | // Select mode
218 | $mode = "create";
219 | if(isset($_GET['id'])){
220 | $mode = "edit";
221 | }
222 |
223 | $domains = Domain::getByLimitedDomains();
224 | ?>
225 |
226 | Redirect
227 |
228 |
231 |
232 |
233 | Please note that mailservers will prefer to deliver mails to redirects over mailboxes.
234 | So make sure you don't accidentally override a mailbox with a redirect.
235 |
236 |
237 | render(); ?>
238 |
239 | isDomainLimited() && $domains->count() === 0): ?>
240 |
241 | You are listed for limited access to domains, but it seems there are no domains listed you can access.
242 |
243 |
244 |
290 |
--------------------------------------------------------------------------------
/include/php/pages/admin/listdomains.php:
--------------------------------------------------------------------------------
1 | isDomainLimited()){
4 | Router::displayError(403);
5 | }
6 |
7 | if(isset($_GET['deleted']) && $_GET['deleted'] == "1"){
8 | Message::getInstance()->success("Domain deleted successfully.");
9 | }
10 | else if(isset($_GET['created']) && $_GET['created'] == "1"){
11 | Message::getInstance()->success("Domain created successfully.");
12 | }
13 | else if(isset($_GET['adm_del']) && $_GET['adm_del'] == "1"){
14 | Message::getInstance()->fail("Domain couldn't be deleted because admin account would be affected.");
15 | }
16 | else if(isset($_GET['missing-permission']) && $_GET['missing-permission'] == "1"){
17 | Message::getInstance()->fail("You don't have the permission to delete that domain.");
18 | }
19 |
20 | $domains = Domain::findAll();
21 |
22 | ?>
23 |
24 | Domains
25 |
26 | isDomainLimited()): ?>
27 |
30 |
31 |
32 | render(); ?>
33 |
34 | count() > 0): ?>
35 |
36 |
37 |
38 | Domain
39 | User count
40 | Redirect count
41 |
42 |
43 |
44 |
45 |
46 |
47 | getDomain(); ?>
48 | countUsers(); ?>
49 | countRedirects(); ?>
50 |
51 | [Delete]
52 |
53 |
54 |
55 |
56 |
57 |
58 | count()); ?>
59 |
60 |
61 |
62 |
63 |
64 | There are currently no domains created you can manage.
65 |
66 |
--------------------------------------------------------------------------------
/include/php/pages/admin/listredirects.php:
--------------------------------------------------------------------------------
1 | success("Redirect deleted successfully.");
5 | }
6 | else if(isset($_GET['created']) && $_GET['created'] == "1"){
7 | Message::getInstance()->success("Redirect created successfully.");
8 | }
9 | else if(isset($_GET['edited']) && $_GET['edited'] == "1"){
10 | Message::getInstance()->success("Redirect edited successfully.");
11 | }
12 | else if(isset($_GET['missing-permission']) && $_GET['missing-permission'] == "1"){
13 | Message::getInstance()->fail("You don't have the permission to edit/delete redirects of that domain.");
14 | }
15 |
16 | $redirects = AbstractRedirect::getMultiByLimitedDomains();
17 | ?>
18 |
19 | Redirects
20 |
21 | isDomainLimited() && count(Domain::getByLimitedDomains()) === 0)): ?>
22 |
25 |
26 |
27 | You are listed for limited access to domains, but it seems there are no domains listed you can access.
28 |
29 |
30 |
31 | render(); ?>
32 |
33 | count() > 0): ?>
34 |
35 |
36 |
37 | Source
38 | Destination
39 |
40 | Created by user
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | getConflictingUsers()->count() > 0 ? ' class="warning"' : ''; ?>>
49 |
50 | getConflictingUsers()->count() > 0): ?>
51 | getConflictingUsers()->count() === 1 ? 'The marked redirect overrides a mailbox.' : 'The marked redirects override mailboxes.'; ?>
52 |
53 | getConflictingMarkedSource()); ?>
54 |
55 | getDestination()); ?>
56 |
57 | isCreatedByUser() ? 'Yes' : 'No'; ?>
58 |
59 |
60 | [Edit]
61 |
62 |
63 | [Delete]
64 |
65 |
66 |
67 |
68 |
69 |
70 | count()); ?>
71 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | isDomainLimited() && count(Domain::getByLimitedDomains()) === 0)): ?>
83 |
84 | There are currently no redirects created you can manage.
85 |
86 |
--------------------------------------------------------------------------------
/include/php/pages/admin/listusers.php:
--------------------------------------------------------------------------------
1 | success("User deleted successfully.");
5 | }
6 | else if(isset($_GET['created']) && $_GET['created'] == "1"){
7 | Message::getInstance()->success("User created successfully.");
8 | }
9 | else if(isset($_GET['edited']) && $_GET['edited'] == "1"){
10 | Message::getInstance()->success("User edited successfully.");
11 | }
12 | else if(isset($_GET['adm_del']) && $_GET['adm_del'] == "1"){
13 | Message::getInstance()->fail("Admin user cannot be deleted.");
14 | }
15 | else if(isset($_GET['missing-permission']) && $_GET['missing-permission'] == "1"){
16 | Message::getInstance()->fail("You don't have the permission to edit/delete users of that domain.");
17 | }
18 |
19 | $users = User::getByLimitedDomains();
20 |
21 | ?>
22 |
23 | List of all mailbox accounts
24 |
25 | isDomainLimited() && count(Domain::getByLimitedDomains()) === 0)): ?>
26 |
29 |
30 |
31 | You are listed for limited access to domains, but it seems there are no domains listed you can access.
32 |
33 |
34 |
35 | render(); ?>
36 |
37 | count() > 0): ?>
38 |
39 |
40 |
41 | Username
42 | Domain
43 |
44 | Mailbox Limit
45 |
46 | Redirect count
47 |
48 | User Redirects
49 |
50 | Role
51 |
52 |
53 |
54 |
55 |
56 |
57 | getConflictingRedirect()) ? ' class="warning"' : ''; ?>>
58 |
59 | getConflictingRedirect())): ?>
60 | This mailbox is overridden by a redirect.
61 |
62 | getUsername(); ?>
63 |
64 | getDomain(); ?>
65 |
66 | getMailboxLimit() > 0) ? $user->getMailboxLimit().' MB' : 'No limit'; ?>
67 |
68 |
69 | getRedirects()->count(); ?>
70 |
71 |
72 |
73 | getMaxUserRedirects() < 0): ?>
74 | Not Allowed
75 | getMaxUserRedirects() > 0): ?>
76 | Limited (getMaxUserRedirects(); ?>)
77 |
78 | Unlimited
79 |
80 |
81 |
82 | getRole() === User::ROLE_ADMIN) ? 'Admin' : 'User'; ?>
83 |
84 | [Edit]
85 |
86 |
87 | [Delete]
88 |
89 |
90 |
91 |
92 |
93 |
94 | count()); ?>
95 |
96 |
97 |
98 | isDomainLimited() && count(Domain::getByLimitedDomains()) === 0)): ?>
99 |
100 | There are currently no users created you can manage.
101 |
102 |
103 |
--------------------------------------------------------------------------------
/include/php/pages/admin/start.php:
--------------------------------------------------------------------------------
1 | Admin Dashboard
2 |
3 |
--------------------------------------------------------------------------------
/include/php/pages/login.php:
--------------------------------------------------------------------------------
1 | fail('Please fill out both email and password fields.');
11 | }
12 | else {
13 | // Start login
14 | if(Auth::login($_POST['email'], $_POST['password'])){
15 | Router::redirect("private");
16 | }
17 | // If login isn't successful
18 | else{
19 | //Log error message
20 | writeLog("WebMUM login failed for IP ".$_SERVER['REMOTE_ADDR']);
21 | Message::getInstance()->fail("Sorry, but we cannot log you in with this combination of email and password, there might be a typo.");
22 | }
23 | }
24 | }
25 |
26 | ?>
27 |
28 | Login
29 |
30 | render(); ?>
31 |
32 |
33 |
39 |
40 |
46 |
47 |
48 | Log in
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/include/php/pages/private/changepass.php:
--------------------------------------------------------------------------------
1 | changePassword($_POST['password'], $_POST['password_repeat']);
6 |
7 | Message::getInstance()->success("Password changed successfully!");
8 | }
9 | catch(AuthException $passwordInvalidException){
10 | Message::getInstance()->fail($passwordInvalidException->getMessage());
11 | }
12 | }
13 |
14 | ?>
15 |
16 | Change password
17 |
18 |
21 |
22 | render(); ?>
23 |
24 |
25 |
38 |
39 |
40 | Change password
41 |
42 |
--------------------------------------------------------------------------------
/include/php/pages/private/createredirect.php:
--------------------------------------------------------------------------------
1 | canCreateUserRedirects()
5 | ){
6 | Router::redirect('private/redirects');
7 | }
8 |
9 | if(isset($_POST['source'])){
10 |
11 | $destination = Auth::getUser()->getEmail();
12 | $domain = Auth::getUser()->getDomain();
13 |
14 | $inputSources = stringToEmails($_POST['source']);
15 |
16 | // validate emails
17 | $emailErrors = array();
18 |
19 | // basic email validation isn't working 100% correct though
20 | foreach($inputSources as $email){
21 | if(strpos($email, '@') === false){
22 | $emailErrors[$email] = "Address \"{$email}\" isn't a valid email address.";
23 | }
24 | }
25 |
26 | // validate source emails are on domains
27 | if(Config::get('options.enable_validate_aliases_source_domain', true)){
28 | $domains = Domain::getByLimitedDomains();
29 |
30 | foreach($inputSources as $email){
31 | if(isset($emailErrors[$email])){
32 | continue;
33 | }
34 |
35 | $emailParts = explode('@', $email);
36 | if($emailParts[1] != $domain){
37 | $emailErrors[$email] = "Domain of source address \"{$email}\" must be \"{$domain}\".";
38 | }
39 | }
40 | }
41 |
42 | // validate no redirect loops
43 | if(in_array($destination, $inputSources)){
44 | $emailErrors[$destination] = "Address \"{$destination}\" cannot be in source and destination in same redirect.";
45 | }
46 |
47 |
48 | if(count($emailErrors) > 0){
49 | Message::getInstance()->fail(implode(" ", $emailErrors));
50 | }
51 | elseif(count($inputSources) !== 1){
52 | Message::getInstance()->fail("Only one email address as source.");
53 | }
54 | else{
55 | if(count($inputSources) > 0){
56 |
57 | $existingRedirects = AbstractRedirect::findWhere(
58 | array(AbstractRedirect::attr('source'), 'IN', $inputSources)
59 | );
60 |
61 | if($existingRedirects->count() > 0){
62 | $errorMessages = array();
63 | /** @var AbstractRedirect $existingRedirect */
64 | foreach($existingRedirects as $existingRedirect){
65 | $errorMessages[] = "Source address \"{$existingRedirect->getSource()}\" is already redirected to some destination.";
66 | }
67 |
68 | Message::getInstance()->fail(implode(" ", $errorMessages));
69 | }
70 | else{
71 | foreach($inputSources as $inputSource){
72 | $data = array(
73 | AbstractRedirect::attr('source') => $inputSource,
74 | AbstractRedirect::attr('destination') => $destination,
75 | AbstractRedirect::attr('multi_hash') => null,
76 | AbstractRedirect::attr('is_created_by_user') => true,
77 | );
78 |
79 | $a = Alias::createAndSave($data);
80 | }
81 |
82 | // Redirect created, redirect to overview
83 | Router::redirect('private/redirects');
84 | }
85 | }
86 | else{
87 | Message::getInstance()->fail("Redirect couldn't be created. Fill out all fields.");
88 | }
89 | }
90 | }
91 |
92 |
93 | $domains = Domain::getByLimitedDomains();
94 | ?>
95 |
96 | Create Redirect
97 |
98 |
101 |
102 | render(); ?>
103 |
104 |
105 |
106 |
122 |
123 |
129 |
130 |
131 | Create redirect
132 |
133 |
--------------------------------------------------------------------------------
/include/php/pages/private/deleteredirect.php:
--------------------------------------------------------------------------------
1 | isAllowedToCreateUserRedirects()
5 | ){
6 | Router::redirect('private/redirects');
7 | }
8 |
9 | if(!isset($_GET['id'])){
10 | // Redirect id not set, redirect to overview
11 | Router::redirect('private/redirects');
12 | }
13 |
14 | $id = $_GET['id'];
15 |
16 | /** @var AbstractRedirect $redirect */
17 | $redirect = AbstractRedirect::findMultiWhereFirst(
18 | array(
19 | array(AbstractRedirect::attr('id'), $id),
20 | array(AbstractRedirect::attr('is_created_by_user'), true),
21 | array(AbstractRedirect::attr('destination'), Auth::getUser()->getEmail()),
22 | )
23 | );
24 |
25 | if(is_null($redirect)){
26 | // Redirect doesn't exist, redirect to overview
27 | Router::redirect('private/redirects');
28 | }
29 |
30 | if(isset($_POST['confirm'])){
31 | $confirm = $_POST['confirm'];
32 |
33 | if($confirm === "yes"){
34 |
35 | $redirect->delete();
36 |
37 | // Delete redirect successfull, redirect to overview
38 | Router::redirect('private/redirects/?deleted=1');
39 | }
40 | else{
41 | // Choose to not delete redirect, redirect to overview
42 | Router::redirect('private/redirects');
43 | }
44 | }
45 |
46 | else{
47 | ?>
48 |
49 | Delete redirection?
50 |
51 |
54 |
55 |
56 |
60 |
61 |
65 |
66 |
75 |
76 |
77 | Delete
78 |
79 |
80 |
--------------------------------------------------------------------------------
/include/php/pages/private/start.php:
--------------------------------------------------------------------------------
1 | Welcome to your dashboard!
2 |
3 |
4 | Please choose an action.
5 |
6 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/include/php/pages/private/yourredirects.php:
--------------------------------------------------------------------------------
1 | isAllowedToCreateUserRedirects();
5 |
6 | $redirects = $user->getAnonymizedRedirects();
7 |
8 | $userRedirectsCount = $user->getSelfCreatedRedirects()->count();
9 | ?>
10 |
11 | Redirects to your mailbox
12 |
13 |
23 |
24 | render(); ?>
25 |
26 |
27 |
28 | You are allowed to create getMaxUserRedirects() === 0 ? 'unlimited user redirects' : textValue('up to _ user redirect', $user->getMaxUserRedirects()); ?> on your own.
29 | getMaxUserRedirects() > 0): ?>
30 | canCreateUserRedirects()): ?>
31 | You can still create getMaxUserRedirects() - $userRedirectsCount); ?> .
32 |
33 | You cannot create anymore redirects as your limit is reached.
34 | Consider deleting unused redirects or ask an admin to extend your limit.
35 |
36 |
37 |
38 |
39 |
40 | count() > 0): ?>
41 |
42 |
43 |
44 | Source
45 | Destination
46 |
47 | Created by you
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | getSource()); ?>
56 | getDestination()); ?>
57 |
58 | isCreatedByUser() ? 'Yes' : 'No'; ?>
59 |
60 | isCreatedByUser()): ?>
61 | [Delete]
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | count()); ?>
71 |
72 |
73 |
74 | getMaxUserRedirects() === 0): ?>
75 |
76 |
77 | getMaxUserRedirects()); ?>
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | There are currently no redirects to your mailbox.
87 |
88 |
--------------------------------------------------------------------------------
/include/php/pages/start.php:
--------------------------------------------------------------------------------
1 |
6 |
7 | WebMUM
8 |
9 |
10 | WebMUM is an easy to use web interface for managing user accounts on your e-mail server with a MySQL user backend.
11 | Users of your server can log in here to change their passwords.
12 |
13 |
14 |
17 |
18 |
--------------------------------------------------------------------------------
/include/php/routes.inc.php:
--------------------------------------------------------------------------------
1 | Not allowed!
2 |
3 |
4 | Sorry, you aren't allowed to access this page.
5 |
--------------------------------------------------------------------------------
/include/php/template/error/not-found.php:
--------------------------------------------------------------------------------
1 | This page does not exist.
2 |
3 |
4 | Sorry, the page you requested couldn't be found.
5 |
--------------------------------------------------------------------------------
/include/php/template/layout.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WebMUM
5 |
6 |
17 |
18 |
19 |
20 |
41 |
42 |
43 |
44 |
45 |
46 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/index.php:
--------------------------------------------------------------------------------
1 | Faulty database query: "'.$e->getQuery().'".';
35 | }
36 | catch(Exception $e){
37 | $content = ''.$e->getMessage().'
';
38 | }
39 |
40 | if(defined('USING_OLD_CONFIG')){
41 | $content = 'Your WebMUM installation is still using the old deprecated config style! Please update your config to the new style (an example config can be found in config.php.example ) and delete your old config.inc.php and config.inc.php.example .
'.$content;
42 | }
43 |
44 | echo Router::loadAndBufferOutput(
45 | 'include/php/template/layout.php',
46 | array(
47 | 'content' => $content,
48 | )
49 | );
--------------------------------------------------------------------------------
/installer/.htaccess:
--------------------------------------------------------------------------------
1 | Deny from all
--------------------------------------------------------------------------------
/installer/index.php:
--------------------------------------------------------------------------------
1 | 0,
26 | 1 => 1,
27 | 2 => 2,
28 | 3 => 2,
29 | 4 => 3,
30 | 5 => 4,
31 | 6 => 5,
32 | 7 => 6,
33 | );
34 |
35 | /*-----------------------------------------------------------------------------*/
36 |
37 | function installer_reset()
38 | {
39 | global $_SESSION;
40 |
41 | $_SESSION['installer'] = array(
42 | 'lastStep' => 0,
43 | 'step' => 0,
44 | 'config' => array(),
45 | );
46 | }
47 |
48 | function installer_message($setMessage = null)
49 | {
50 | global $_SESSION;
51 |
52 | if(!is_null($setMessage)){
53 | $_SESSION['installer']['message'] = $setMessage;
54 | }
55 | elseif(isset($_SESSION['installer']['message'])){
56 | $m = ''.$_SESSION['installer']['message'].'
';
57 | unset($_SESSION['installer']['message']);
58 |
59 | return $m;
60 | }
61 |
62 | return $setMessage;
63 | }
64 |
65 | function installer_prev($thisStep, $stepSize = 1)
66 | {
67 | $s = ($thisStep < 0) ? 0 : ($thisStep - $stepSize);
68 |
69 | $_SESSION['installer']['lastStep'] = $thisStep;
70 | $_SESSION['installer']['step'] = $s;
71 |
72 | Router::redirect('/?step='.$s);
73 | }
74 |
75 | function installer_next($thisStep, $stepSize = 1)
76 | {
77 | $s = ($thisStep > 8) ? 8 : ($thisStep + $stepSize);
78 |
79 | $_SESSION['installer']['lastStep'] = $thisStep;
80 | $_SESSION['installer']['step'] = $s;
81 |
82 | Router::redirect('/?step='.$s);
83 | }
84 |
85 | if(!isset($_SESSION['installer'])){
86 | installer_reset();
87 | }
88 |
89 | /*-----------------------------------------------------------------------------*/
90 |
91 | $step = (isset($_GET['step']) && is_numeric($_GET['step'])) ? intval($_GET['step']) : 0;
92 |
93 | echo 'Installation of WebMUM ';
94 |
95 | if($step > 0){
96 | ?>
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 | getMessage();
125 | }
126 |
--------------------------------------------------------------------------------
/installer/step0.php:
--------------------------------------------------------------------------------
1 | =')){
16 | $requirements[] = 'php_version';
17 | }
18 | if(function_exists('mysqli_connect')){
19 | $requirements[] = 'php_extension_mysqli';
20 | }
21 | if(session_status() != PHP_SESSION_DISABLED){
22 | $requirements[] = 'php_session_enabled';
23 | }
24 | if(file_exists('config') && is_dir('config')){
25 | $requirements[] = 'config_directory';
26 | }
27 | if(file_exists('config/config.php.example')){
28 | $requirements[] = 'config_example';
29 | }
30 |
31 | /*-----------------------------------------------------------------------------*/
32 |
33 | if(isset($_GET['go']) && $_GET['go'] == 'next'){
34 | if(count($requirements) === $numberOfRequirements){
35 | installer_message('All requirements fulfilled, let\'s get started with the installation!');
36 |
37 | installer_next($thisStep);
38 | }
39 | }
40 | ?>
41 |
42 |
43 | Getting started
44 |
45 | By following this wizard you will install and configure your new WebMUM installation.
46 |
47 |
48 |
49 | System Info:
50 |
51 | System:
52 | Hostname:
53 | IP:
54 | PHP version:
55 | Server API:
56 | WebMUM directory:
57 |
58 |
59 | Server requirements
60 |
61 |
62 | PHP version (>=5.4.0 or >=7.0.0): ✓
63 |
64 | PHP version (>=5.4.0 or >=7.0.0): ❌
65 |
66 |
67 |
68 | Required PHP settings
69 |
70 |
71 | Database extension (mysqli): enabled ✓
72 |
73 | Database extension (mysqli): disabled ❌
74 |
75 |
76 | Session support: enabled ✓
77 |
78 | Session support: disabled ❌
79 |
80 |
81 |
82 | Directories and files
83 |
84 |
85 | "config/": exists ✓
86 |
87 | "config/": is missing ❌
88 |
89 |
90 | "config/config.php.example": exists ✓
91 |
92 | "config/config.php.example": is missing ❌
93 |
94 |
95 |
96 |
97 |
98 |
99 | Click on the Start button to continue.
100 | Start
101 |
102 | Some requirements aren't fulfilled.
103 |
--------------------------------------------------------------------------------
/installer/step1.php:
--------------------------------------------------------------------------------
1 | $_POST['host'],
24 | 'user' => $_POST['user'],
25 | 'password' => $_POST['password'],
26 | 'database' => $_POST['database'],
27 | );
28 | $_SESSION['installer']['type'] = (isset($_POST['install_type']) && $_POST['install_type'] == INSTALLER_TYPE_MAP)
29 | ? INSTALLER_TYPE_MAP
30 | : INSTALLER_TYPE_CREATE;
31 |
32 | installer_message('Database connection was successfully established.');
33 |
34 | installer_next($thisStep, ($_SESSION['installer']['type'] === INSTALLER_TYPE_MAP) ? 2 : 1);
35 | }
36 | catch(InvalidArgumentException $e){
37 | $error = 'Some fields are missing.';
38 | }
39 | catch(Exception $e){
40 | $error = $e->getMessage();
41 | }
42 | }
43 | elseif($_GET['go'] == 'prev'){
44 | // reset
45 | unset($_SESSION['installer']['config']['mysql']);
46 | unset($_SESSION['installer']['type']);
47 |
48 | installer_prev($thisStep);
49 | }
50 | }
51 |
52 | function getAttr($name, $default = null)
53 | {
54 | global $_SESSION, $_POST;
55 |
56 | if(isset($_POST[$name])){
57 | return strip_tags($_POST[$name]);
58 | }
59 | elseif(isset($_SESSION['installer']['config']['mysql'][$name])){
60 | return $_SESSION['installer']['config']['mysql'][$name];
61 | }
62 | elseif($name === 'install_type' && isset($_SESSION['installer']['type'])){
63 | return $_SESSION['installer']['type'];
64 | }
65 |
66 | return $default;
67 | }
68 |
69 | ?>
70 |
71 |
72 | Step 1 of : Database connection.
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | Setup your MySQL database connection.
81 |
82 |
88 |
89 |
95 |
96 |
102 |
103 |
109 |
110 |
111 |
112 |
124 |
125 |
126 |
127 |
131 |
--------------------------------------------------------------------------------
/installer/step2.php:
--------------------------------------------------------------------------------
1 | query("SELECT table_name FROM information_schema.tables WHERE table_schema='".$_SESSION['installer']['config']['mysql']['database']."';");
20 | foreach($tablesResult->fetch_all() as $row){
21 | $tablesInDatabase[] = $row[0];
22 | }
23 | }
24 | catch(Exception $e){
25 | }
26 |
27 | /*-----------------------------------------------------------------------------*/
28 |
29 | $databaseSchema = array(
30 | '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;",
31 | '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;",
32 | '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;",
33 | );
34 |
35 | /**
36 | * @param string $stmt
37 | * @param string $database
38 | * @param string $table
39 | * @param array $attributes
40 | *
41 | * @return string
42 | */
43 | function prepareSchemaTableStmt($stmt, $database, $table, $attributes)
44 | {
45 | $attributes['database'] = $database;
46 | $attributes['table'] = $table;
47 |
48 | foreach($attributes as $search => $replace){
49 | $stmt = str_replace('___'.$search.'___', '`'.Database::getInstance()->escape($replace).'`', $stmt);
50 | }
51 |
52 | return $stmt;
53 | }
54 |
55 | $preparedSchemaStmt = '';
56 | $allTablesFromSchemaExist = true;
57 |
58 | foreach($databaseSchema as $table => $stmt){
59 | $preparedSchemaStmt .= prepareSchemaTableStmt(
60 | $stmt,
61 | $_SESSION['installer']['config']['mysql']['database'],
62 | $exampleConfigValues['schema']['tables'][$table],
63 | $exampleConfigValues['schema']['attributes'][$table]
64 | ).PHP_EOL;
65 |
66 | // check if tables exist, should be enough for now
67 | if(!in_array($exampleConfigValues['schema']['tables'][$table], $tablesInDatabase)){
68 | $allTablesFromSchemaExist = false;
69 | }
70 | }
71 |
72 | $commandDenied = false;
73 |
74 | /*-----------------------------------------------------------------------------*/
75 |
76 | if(isset($_GET['go'])){
77 | if($_GET['go'] == 'next' && $_SERVER['REQUEST_METHOD'] == 'POST'){
78 | if(isset($_POST['manual'])){
79 | if($_POST['manual'] == 1){
80 | // display SQL
81 | }
82 | elseif($_POST['manual'] == 2){
83 | // check if schema was created
84 | if($allTablesFromSchemaExist){
85 | // saving information
86 | $_SESSION['installer']['config']['schema'] = $exampleConfigValues['schema'];
87 |
88 | installer_message('Database schema was manually created.');
89 |
90 | installer_next($thisStep, 2);
91 | }
92 | else{
93 | $_POST['manual'] = 1;
94 | }
95 | }
96 | }
97 | else{
98 | if(!$allTablesFromSchemaExist){
99 | try{
100 | foreach(explode(PHP_EOL, $preparedSchemaStmt) as $stmt){
101 | Database::getInstance()->query($stmt);
102 | }
103 |
104 | // saving information
105 | $_SESSION['installer']['config']['schema'] = $exampleConfigValues['schema'];
106 |
107 | installer_message('Database schema was automatically created.');
108 |
109 | installer_next($thisStep, 2);
110 | }
111 | catch(Exception $e){
112 | if(strpos($e->getMessage(), 'command denied') !== false){
113 | $commandDenied = true;
114 | }
115 | else{
116 | throw $e;
117 | }
118 | }
119 | }
120 | }
121 | }
122 | elseif($_GET['go'] == 'prev'){
123 | // reset
124 | unset($_SESSION['installer']['config']['schema']);
125 |
126 | installer_prev($thisStep);
127 | }
128 | }
129 | ?>
130 |
131 |
132 | Step 2 of : Create database schema.
133 |
134 |
135 |
136 | The schema already exists in database "".
137 |
138 |
139 |
140 | Your next possible steps:
141 |
142 | Either delete the existing schema.
143 | Go Back and change the used database.
144 | Go Back and start mapping the existing database schema.
145 |
146 |
147 |
148 |
152 |
153 |
154 |
155 |
156 |
157 |
158 | Copy the SQL-Code above and import it into your database "".
159 |
160 |
161 |
162 |
163 | Once you have imported the schema, you can continue by clicking on the Continue button.
164 |
165 |
169 |
170 |
171 | The following database schema will be created in
172 | database "" .
173 | Please make sure that "" is clean / empty database!
174 |
175 |
176 |
177 | The
178 | user "" is missing the permission to execute MySQL "CREATE" commands.
179 |
180 |
181 |
182 | Also make sure that the database
183 | user "" has the privileges to create the schema.
184 |
185 |
186 |
187 | $mappedTable): ?>
188 |
189 |
Table ""
190 |
191 | $mappedAttribute): ?>
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 | Click on the Continue button to try creating the schema automatically.
201 |
202 |
207 |
208 |
209 |
--------------------------------------------------------------------------------
/installer/step3.php:
--------------------------------------------------------------------------------
1 | $thisStep){
12 | $_SESSION['installer']['subStep'] = 1;
13 | }
14 | elseif($_SESSION['installer']['lastStep'] < $thisStep || !isset($_SESSION['installer']['subStep'])){
15 | $_SESSION['installer']['subStep'] = 0;
16 | }
17 |
18 | $error = null;
19 |
20 | /*-----------------------------------------------------------------------------*/
21 |
22 | $exampleConfigValues = require_once 'config/config.php.example';
23 |
24 | $tablesInDatabase = array();
25 | try{
26 | Database::init($_SESSION['installer']['config']['mysql']);
27 |
28 | $db = Database::getInstance();
29 | $tablesResult = $db->query(
30 | "SELECT TABLE_NAME FROM information_schema.tables "
31 | ."WHERE TABLE_SCHEMA='".$db->escape($_SESSION['installer']['config']['mysql']['database'])."';"
32 | );
33 |
34 | foreach($tablesResult->fetch_all() as $row){
35 | $tablesInDatabase[] = $row[0];
36 | }
37 | }
38 | catch(Exception $e){
39 | }
40 |
41 | function getTableAttributes($table)
42 | {
43 | global $_SESSION;
44 | $attributes = array();
45 |
46 | if(Database::isInitialized()){
47 | try{
48 | $db = Database::getInstance();
49 | $tablesResult = $db->query(
50 | "SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_DEFAULT, COLUMN_KEY, EXTRA FROM information_schema.columns "
51 | ."WHERE TABLE_SCHEMA = '".$db->escape($_SESSION['installer']['config']['mysql']['database'])."' "
52 | ."AND TABLE_NAME = '".$db->escape($table)."' "
53 | ."ORDER BY TABLE_NAME,ORDINAL_POSITION;"
54 | );
55 |
56 | foreach($tablesResult->fetch_all() as $row){
57 | $s = $row[0];
58 |
59 | if(!empty($row[1])){
60 | $s .= ' : '.$row[1];
61 | }
62 |
63 | if($row[2] == 'NO'){
64 | $s .= ', NOT NULL';
65 | }
66 |
67 | if(!is_null($row[3])){
68 | $s .= ', DEFAULT \''.$row[3].'\'';
69 | }
70 |
71 | if(!empty($row[4])){
72 | if(strpos($row[4], 'PR') !== false){
73 | $s .= ', PRIMARY KEY';
74 | }
75 | if(strpos($row[4], 'UN') !== false){
76 | $s .= ', UNIQUE KEY';
77 | }
78 | }
79 |
80 | if(!empty($row[5]) && strpos($row[5], 'auto_inc') !== false){
81 | $s .= ', AUTO_INCREMENT';
82 | }
83 |
84 | $attributes[$row[0]] = $s;
85 | }
86 | }
87 | catch(Exception $e){
88 | }
89 | }
90 |
91 | return $attributes;
92 | }
93 |
94 | $optionalAttributes = array(
95 | 'users' => array('mailbox_limit', 'max_user_redirects'),
96 | 'aliases' => array('multi_source', 'is_created_by_user'),
97 | );
98 |
99 | define('ATTR_SEP', '---');
100 |
101 | function getAttr($name, $default = null)
102 | {
103 | global $_SESSION, $_POST;
104 |
105 | if(isset($_POST[$name])){
106 | return strip_tags($_POST[$name]);
107 | }
108 | elseif(strpos($name, ATTR_SEP) !== false){
109 | list($table, $attribute) = explode(ATTR_SEP, $name);
110 |
111 | if(isset($_SESSION['installer']['config']['schema']['attributes'][$table][$attribute])){
112 | return $_SESSION['installer']['config']['schema']['attributes'][$table][$attribute];
113 | }
114 | }
115 | elseif(isset($_SESSION['installer']['config']['schema']['tables'][$name])){
116 | return $_SESSION['installer']['config']['schema']['tables'][$name];
117 | }
118 |
119 | return $default;
120 | }
121 |
122 | /*-----------------------------------------------------------------------------*/
123 |
124 | if(isset($_GET['go'])){
125 | if($_GET['go'] == 'next' && $_SERVER['REQUEST_METHOD'] == 'POST'){
126 | try{
127 | if($_SESSION['installer']['subStep'] === 0){
128 |
129 | $tables = array();
130 | foreach($exampleConfigValues['schema']['tables'] as $table => $mappedTable){
131 | if(!isset($_POST[$table])
132 | || !in_array($_POST[$table], $tablesInDatabase)
133 | ){
134 | throw new InvalidArgumentException('Missing mapping for table "'.$table.'".');
135 | }
136 |
137 | if(in_array($_POST[$table], array_values($tables))){
138 | throw new Exception('You cannot map table "'.$_POST[$table].'" twice.');
139 | }
140 |
141 | $tables[$table] = $_POST[$table];
142 | }
143 |
144 | // saving information
145 | $_SESSION['installer']['config']['schema'] = array();
146 | $_SESSION['installer']['config']['schema']['tables'] = $tables;
147 |
148 | installer_message('Database tables were successfully mapped.');
149 |
150 | $_SESSION['installer']['subStep'] = 1;
151 | installer_next($thisStep, 0);
152 | }
153 | elseif($_SESSION['installer']['subStep'] === 1){
154 |
155 | $attributes = array();
156 | foreach($_SESSION['installer']['config']['schema']['tables'] as $table => $mappedTable){
157 |
158 | $attributes[$table] = array();
159 |
160 | $attributesInDatabase = getTableAttributes($table);
161 |
162 | foreach($exampleConfigValues['schema']['attributes'][$table] as $attribute => $mappedAttribute){
163 | $key = $table.'---'.$attribute;
164 |
165 | if(isset($optionalAttributes[$table])
166 | && in_array($attribute, $optionalAttributes[$table])
167 | && !isset($attributesInDatabase[$_POST[$key]])
168 | ){
169 | $attributes[$table][$attribute] = '';
170 | }
171 | else{
172 | if(!isset($_POST[$key]) || !isset($attributesInDatabase[$_POST[$key]])){
173 | throw new InvalidArgumentException('Missing mapping for attribute "'.$attribute.'" on table "'.$table.'".');
174 | }
175 |
176 | if(in_array($_POST[$key], $attributes[$table])){
177 | throw new Exception('You cannot map attribute "'.$_POST[$key].'" twice on table "'.$table.'".');
178 | }
179 |
180 | $attributes[$table][$attribute] = $_POST[$key];
181 | }
182 | }
183 | }
184 |
185 | // saving information
186 | $_SESSION['installer']['config']['schema']['attributes'] = $attributes;
187 |
188 | installer_message('Database attributes were successfully mapped.');
189 |
190 | unset($_SESSION['installer']['subStep']);
191 | installer_next($thisStep);
192 | }
193 | }
194 | catch(Exception $e){
195 | $error = $e->getMessage();
196 | }
197 | }
198 | elseif($_GET['go'] == 'prev'){
199 |
200 | // reset
201 | if(isset($_SESSION['installer']['config']['schema']['tables'])){
202 | if($_SESSION['installer']['subStep'] === 0){
203 | unset($_SESSION['installer']['config']['schema']);
204 | }
205 | elseif($_SESSION['installer']['subStep'] === 1){
206 | unset($_SESSION['installer']['config']['schema']['attributes']);
207 | }
208 | }
209 |
210 | if($_SESSION['installer']['subStep'] === 0){
211 | unset($_SESSION['installer']['subStep']);
212 | installer_prev($thisStep, ($_SESSION['installer']['type'] === INSTALLER_TYPE_MAP) ? 2 : 1);
213 | }
214 | else{
215 | $_SESSION['installer']['subStep'] = 0;
216 | installer_prev($thisStep, 0);
217 | }
218 | }
219 | }
220 | ?>
221 |
222 |
223 |
224 |
225 | Step 2 of :
226 |
227 | Database - table mapping.
228 |
229 | Database - attribute mapping.
230 |
231 | Wrong turn
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 | $mappedTable): ?>
242 |
253 |
254 |
255 |
256 |
257 |
261 |
262 |
263 |
264 | $mappedTable):
268 | $attributesInDatabase = getTableAttributes($mappedTable);
269 | ?>
270 |
271 | Table ""
272 |
273 |
274 |
275 | $mappedAttribute): ?>
276 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
303 |
304 |
305 |
306 |
307 |
--------------------------------------------------------------------------------
/installer/step4.php:
--------------------------------------------------------------------------------
1 | count(
26 | $_SESSION['installer']['config']['schema']['tables']['users'],
27 | $_SESSION['installer']['config']['schema']['attributes']['users']['id']
28 | );
29 |
30 | function getAttr($name, $default = null)
31 | {
32 | global $_SESSION, $_POST;
33 |
34 | if(isset($_POST[$name])){
35 | return strip_tags($_POST[$name]);
36 | }
37 | elseif(isset($_SESSION['installer']['config']['password'][$name])){
38 | return $_SESSION['installer']['config']['password'][$name];
39 | }
40 | elseif($name === 'admin_user' && isset($_SESSION['installer']['user']['user'])){
41 | return $_SESSION['installer']['user']['user'];
42 | }
43 | elseif($name === 'admin_password' && isset($_SESSION['installer']['user']['password'])){
44 | return $_SESSION['installer']['user']['password'];
45 | }
46 |
47 | return $default;
48 | }
49 |
50 | /*-----------------------------------------------------------------------------*/
51 |
52 | if(isset($_GET['go'])){
53 |
54 | if($_GET['go'] == 'next' && $_SERVER['REQUEST_METHOD'] == 'POST'){
55 | try{
56 | if(!isset($_POST['hash_algorithm']) || !isset($_POST['min_length']) || !isset($_POST['admin_user']) || !isset($_POST['admin_password'])){
57 | throw new InvalidArgumentException;
58 | }
59 |
60 | $passwordConfig = array(
61 | 'hash_algorithm' => in_array($_POST['hash_algorithm'], $hashAlgorithms) ? $_POST['hash_algorithm'] : $exampleConfigValues['password']['hash_algorithm'],
62 | 'min_length' => intval($_POST['min_length']),
63 | );
64 |
65 | // init system for testing
66 | Config::init(array('password' => $passwordConfig));
67 |
68 | // handle user
69 | if($databaseUserCount > 0){
70 | // testing existing login
71 |
72 | $validLogin = Auth::login($_POST['admin_user'], $_POST['admin_password']);
73 | unset($_SESSION[Auth::SESSION_IDENTIFIER]);
74 |
75 | if(!$validLogin){
76 | throw new Exception('Invalid combination of user and password.');
77 | }
78 | }
79 | else{
80 | // create user in database
81 |
82 | if(strpos($_POST['admin_user'], '@') === false){
83 | throw new Exception('The field "Your user" must be an email address.');
84 | }
85 | else{
86 | list($username, $domain) = explode('@', $_POST['admin_user']);
87 | $passwordHash = Auth::generatePasswordHash($_POST['admin_password']);
88 |
89 | $hasDomain = Database::getInstance()->count(
90 | $_SESSION['installer']['config']['schema']['tables']['domains'],
91 | $_SESSION['installer']['config']['schema']['attributes']['domains']['id'],
92 | array($_SESSION['installer']['config']['schema']['attributes']['domains']['domain'], $domain)
93 | );
94 | if($hasDomain === 0){
95 | Database::getInstance()->insert(
96 | $_SESSION['installer']['config']['schema']['tables']['domains'],
97 | array(
98 | $_SESSION['installer']['config']['schema']['attributes']['domains']['domain'] => $domain,
99 | )
100 | );
101 | }
102 |
103 | Database::getInstance()->insert(
104 | $_SESSION['installer']['config']['schema']['tables']['users'],
105 | array(
106 | $_SESSION['installer']['config']['schema']['attributes']['users']['username'] => $username,
107 | $_SESSION['installer']['config']['schema']['attributes']['users']['domain'] => $domain,
108 | $_SESSION['installer']['config']['schema']['attributes']['users']['password'] => $passwordHash,
109 | )
110 | );
111 | }
112 | }
113 |
114 | // saving information
115 | $_SESSION['installer']['config']['password'] = $passwordConfig;
116 | $_SESSION['installer']['config']['admins'] = array($_POST['admin_user']);
117 | $_SESSION['installer']['config']['admin_domain_limits'] = array();
118 | $_SESSION['installer']['user'] = array(
119 | 'user' => $_POST['admin_user'],
120 | 'password' => $_POST['admin_password'],
121 | );
122 |
123 | installer_message('You have successfully added your first admin user.');
124 |
125 | installer_next($thisStep);
126 | }
127 | catch(InvalidArgumentException $e){
128 | $error = 'Some fields are missing.';
129 | }
130 | catch(Exception $e){
131 | $error = $e->getMessage();
132 | }
133 | }
134 | elseif($_GET['go'] == 'prev'){
135 | // reset
136 | unset($_SESSION['installer']['config']['password']);
137 | unset($_SESSION['installer']['config']['admins']);
138 | unset($_SESSION['installer']['config']['admin_domain_limits']);
139 | unset($_SESSION['installer']['user']);
140 |
141 | installer_prev($thisStep, ($_SESSION['installer']['type'] === INSTALLER_TYPE_MAP) ? 1 : 2);
142 | }
143 | }
144 | ?>
145 |
146 |
147 |
148 | Step 3 of : Your first admin user.
149 |
150 |
151 |
152 |
153 |
154 |
155 |
168 |
169 |
178 |
179 |
180 |
181 |
182 |
183 | There is no user created yet, please create one now as your admin user.
184 | Please note that once the user is created you will have to remember the password.
185 |
186 |
187 |
188 | This user will be mark as an admin in the configuration.
189 |
190 |
202 |
203 |
209 |
210 |
211 |
212 |
216 |
--------------------------------------------------------------------------------
/installer/step5.php:
--------------------------------------------------------------------------------
1 | $possibleEmailSeparatorsText[$_POST['email_separator_text']],
59 | 'email_separator_form' => $possibleEmailSeparatorsForm[$_POST['email_separator_form']],
60 | );
61 |
62 | installer_message('General settings saved.');
63 |
64 | installer_next($thisStep);
65 | }
66 | catch(InvalidArgumentException $e){
67 | $error = 'Some field is missing.';
68 | }
69 | catch(Exception $e){
70 | $error = $e->getMessage();
71 | }
72 | }
73 | elseif($_GET['go'] == 'prev'){
74 | // reset
75 | unset($_SESSION['installer']['config']['base_url']);
76 | unset($_SESSION['installer']['config']['frontend_options']);
77 |
78 | installer_prev($thisStep);
79 | }
80 | }
81 | ?>
82 |
83 |
84 |
85 | Step 4 of : General settings
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
109 |
110 |
111 |
112 |
143 |
144 |
145 |
146 |
150 |
--------------------------------------------------------------------------------
/installer/step6.php:
--------------------------------------------------------------------------------
1 | getMessage();
129 | }
130 | }
131 | elseif($_GET['go'] == 'prev'){
132 | // reset
133 | unset($_SESSION['installer']['config']['options']);
134 | unset($_SESSION['installer']['config']['log_path']);
135 |
136 | installer_prev($thisStep);
137 | }
138 | }
139 | ?>
140 |
141 |
142 |
143 | Step 5 of : Optional features
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
166 |
167 |
168 |
169 |
177 |
178 |
179 |
180 |
195 |
196 |
197 |
198 |
209 |
210 |
211 |
212 |
241 |
242 |
243 |
244 |
255 |
256 |
263 |
264 |
265 |
266 |
270 |
--------------------------------------------------------------------------------
/installer/step7.php:
--------------------------------------------------------------------------------
1 | getMessage();
65 | }
66 | }
67 | elseif($_GET['go'] == 'finish'){
68 | try{
69 | if(isset($_SESSION['installer']['finished'])){
70 | // Load config
71 | $configValues = include_once 'config/config.php';
72 | if(!is_array($configValues)){
73 | throw new Exception('Error writing the config, please manually write the config to "'.$configPath.'".');
74 | }
75 |
76 | // Init system
77 | Config::init($configValues);
78 | Database::init(Config::get('mysql'));
79 | Auth::init();
80 |
81 | // Login user
82 | Auth::login($_SESSION['installer']['user']['user'], $_SESSION['installer']['user']['password']);
83 |
84 | // Reset installer
85 | unset($_SESSION['installer']);
86 |
87 | Router::redirect('/');
88 | }
89 | }
90 | catch(Exception $e){
91 | $error = $e->getMessage();
92 | }
93 | }
94 | elseif($_GET['go'] == 'prev'){
95 | installer_prev($thisStep);
96 | }
97 | }
98 | ?>
99 |
100 |
101 |
102 | You finished your installation!
103 |
104 | Welcome to WebMUM - Web Mailserver User Manager.
105 |
106 |
107 | If you like this project, be sure to give us a Star and Follow the project on GitHub
https://github.com/ohartl/webmum
108 |
109 |
110 | To change the configuration you have to edit to config file "" (see the README for further instructions.
111 | If you've found a bug or got a great idea, feel free to submit an issue on GitHub here .
112 |
113 |
114 |
115 |
116 |
117 | By clicking Finish you will end the installation process and get logged in automatically.
118 |
119 |
120 | Finish & Start using WebMUM
121 |
122 |
123 |
124 |
125 |
126 | Step 6 of : Write the config & finish the installation!
127 |
128 |
129 |
130 |
131 |
132 | The following config needs to be written to
.
133 |
134 |
135 |
136 |
137 |
138 |
139 | This is the last step, you are almost there!
140 |
141 | Click "Already manually completed" if you already wrote the config to "".
142 | Or click "Complete automatically" if you want the installer to do the work for you.
143 |
144 |
145 |
146 |
151 |
152 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | tests
5 |
6 |
7 |
8 |
9 | include/php/classes
10 | include/php/models
11 |
12 |
13 |
--------------------------------------------------------------------------------
/tests/AuthTest.php:
--------------------------------------------------------------------------------
1 | assertFalse(Auth::isLoggedIn());
25 | $this->assertNull(Auth::getUser());
26 | $this->assertFalse(Auth::hasPermission(User::ROLE_USER));
27 | $this->assertFalse(Auth::hasPermission(User::ROLE_ADMIN));
28 | }
29 |
30 | public function testInitUser()
31 | {
32 | $_SESSION = array(
33 | Auth::SESSION_IDENTIFIER => self::USER_ROLE_USER_ID
34 | );
35 |
36 | Auth::init();
37 |
38 | $this->assertTrue(Auth::isLoggedIn());
39 | $this->assertInstanceOf('User', Auth::getUser());
40 | $this->assertTrue(Auth::hasPermission(User::ROLE_USER));
41 | $this->assertFalse(Auth::hasPermission(User::ROLE_ADMIN));
42 | }
43 |
44 |
45 | public function testInitAdmin()
46 | {
47 | $_SESSION = array(
48 | Auth::SESSION_IDENTIFIER => self::USER_ROLE_ADMIN_ID
49 | );
50 |
51 | Auth::init();
52 |
53 | $this->assertTrue(Auth::isLoggedIn());
54 | $this->assertInstanceOf('User', Auth::getUser());
55 | $this->assertTrue(Auth::hasPermission(User::ROLE_USER));
56 | $this->assertTrue(Auth::hasPermission(User::ROLE_ADMIN));
57 | }
58 |
59 |
60 | public function testLogin()
61 | {
62 | $_SESSION = array();
63 |
64 | Auth::init();
65 |
66 | $this->assertFalse(Auth::isLoggedIn());
67 |
68 | $this->assertTrue(Auth::login('user@domain.tld', 'testtest'));
69 |
70 | $this->assertTrue(Auth::isLoggedIn());
71 | }
72 |
73 |
74 | public function testLoginInvalidEmail()
75 | {
76 | $_SESSION = array();
77 |
78 | Auth::init();
79 |
80 | $this->assertFalse(Auth::isLoggedIn());
81 |
82 | $this->assertFalse(Auth::login('domain.tld', 'test'));
83 |
84 | $this->assertFalse(Auth::isLoggedIn());
85 | }
86 |
87 |
88 | public function testLoginInvalidUser()
89 | {
90 | $_SESSION = array();
91 |
92 | Auth::init();
93 |
94 | $this->assertFalse(Auth::isLoggedIn());
95 |
96 | $this->assertFalse(Auth::login('no.user@domain.tld', 'test'));
97 |
98 | $this->assertFalse(Auth::isLoggedIn());
99 | }
100 |
101 |
102 | public function testLogout()
103 | {
104 | $_SESSION = array(
105 | Auth::SESSION_IDENTIFIER => self::USER_ROLE_USER_ID
106 | );
107 |
108 | Auth::init();
109 |
110 | $this->assertTrue(Auth::isLoggedIn());
111 |
112 | Auth::logout();
113 |
114 | $this->assertFalse(Auth::isLoggedIn());
115 | $this->assertArrayNotHasKey(Auth::SESSION_IDENTIFIER, $_SESSION);
116 | }
117 |
118 |
119 | /**
120 | * @param int $length
121 | * @return string
122 | */
123 | protected static function genTestPw($length)
124 | {
125 | return substr(str_shuffle("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-+=_,!@$#*%<>[]{}"), 0, $length);
126 | }
127 |
128 |
129 | /**
130 | * @expectedException AuthException
131 | * @expectedExceptionCode 2
132 | */
133 | public function testValidateNewPasswordFirstEmpty()
134 | {
135 | Auth::validateNewPassword('', static::genTestPw(Config::get('password.min_length', 8)));
136 | }
137 |
138 |
139 | /**
140 | * @expectedException AuthException
141 | * @expectedExceptionCode 2
142 | */
143 | public function testValidateNewPasswordLastEmpty()
144 | {
145 | Auth::validateNewPassword(static::genTestPw(Config::get('password.min_length', 8)), '');
146 | }
147 |
148 |
149 | /**
150 | * @expectedException AuthException
151 | * @expectedExceptionCode 3
152 | */
153 | public function testValidateNewPasswordNotEqual()
154 | {
155 | $pw = static::genTestPw(Config::get('password.min_length', 8));
156 | Auth::validateNewPassword($pw, $pw.'neq');
157 | }
158 |
159 |
160 | /**
161 | * @expectedException AuthException
162 | * @expectedExceptionCode 4
163 | */
164 | public function testValidateNewPasswordTooShort()
165 | {
166 | $pw = static::genTestPw(Config::get('password.min_length', 8) - 1);
167 | Auth::validateNewPassword($pw, $pw);
168 | }
169 |
170 |
171 | public function testValidateNewPasswordOk()
172 | {
173 | $pw = static::genTestPw(Config::get('password.min_length', 8));
174 | Auth::validateNewPassword($pw, $pw);
175 | }
176 |
177 |
178 | public function testGeneratePasswordHash()
179 | {
180 | Auth::generatePasswordHash(static::genTestPw(Config::get('password.min_length', 8)));
181 | }
182 |
183 |
184 | public function testGeneratePasswordHashAlgorithmFallback()
185 | {
186 | Config::set('password.hash_algorithm', '--not-an-algorithm--');
187 | Auth::generatePasswordHash(static::genTestPw(Config::get('password.min_length', 8)));
188 | }
189 |
190 |
191 | public function testChangeUserPassword()
192 | {
193 | $this->assertTrue(Auth::login('user@domain.tld', 'testtest'));
194 |
195 | Auth::changeUserPassword(static::USER_ROLE_USER_ID, 'newpassword');
196 |
197 | $this->assertFalse(Auth::login('user@domain.tld', 'testtest'));
198 |
199 | $this->assertTrue(Auth::login('user@domain.tld', 'newpassword'));
200 | }
201 |
202 | }
--------------------------------------------------------------------------------
/tests/ConfigTest.php:
--------------------------------------------------------------------------------
1 | 123,
14 | 'test' => array(
15 | 'deep' => array(
16 | 'deeper' => 'value',
17 | )
18 | )
19 | )
20 | );
21 | }
22 |
23 |
24 | public function testInit()
25 | {
26 | $this->assertEquals(Config::get('test-value'), 123);
27 | $this->assertEquals(Config::get('test.deep.deeper'), 'value');
28 | }
29 |
30 |
31 | public function testSet()
32 | {
33 | $this->assertEquals(Config::get('test-value'), 123);
34 | Config::set('test-value', false);
35 | $this->assertEquals(Config::get('test-value'), false);
36 |
37 | $this->assertEquals(Config::get('test.deep.deeper'), 'value');
38 | Config::set('test.deep.deeper', 'other');
39 | $this->assertEquals(Config::get('test.deep.deeper'), 'other');
40 |
41 | Config::set('test.new.deep.deeper', true);
42 | $this->assertEquals(Config::get('test.new.deep.deeper'), true);
43 | }
44 |
45 |
46 | public function testGet()
47 | {
48 | $this->assertTrue(is_array(Config::get(null)));
49 |
50 | $this->assertEquals(Config::get('test-value'), 123);
51 |
52 | $this->assertEquals(Config::get('test.deep.deeper'), 'value');
53 |
54 | $this->assertEquals(Config::get('test-default', 123456), 123456);
55 | }
56 |
57 |
58 | public function testHas()
59 | {
60 | $this->assertTrue(Config::has('test-value'));
61 |
62 | $this->assertTrue(Config::has('test.deep.deeper'));
63 |
64 | $this->assertFalse(Config::has('test-default'));
65 |
66 | Config::init(null);
67 | $this->assertFalse(Config::has(null));
68 | }
69 | }
--------------------------------------------------------------------------------
/tests/MessageTest.php:
--------------------------------------------------------------------------------
1 | add(Message::TYPE_SUCCESS, 'lorem');
12 |
13 | $out = Message::getInstance()->render();
14 |
15 | $this->assertContains(Message::TYPE_SUCCESS, $out);
16 | $this->assertContains('lorem', $out);
17 | }
18 |
19 |
20 | /**
21 | * @expectedException InvalidArgumentException
22 | */
23 | public function testAddRestrictTypes()
24 | {
25 | Message::getInstance()->add('wrong-type', 'lorem');
26 | }
27 |
28 |
29 | public function testAddShortcuts()
30 | {
31 | Message::getInstance()->fail('lorem');
32 | $this->assertContains(Message::TYPE_FAIL, Message::getInstance()->render());
33 |
34 | Message::getInstance()->error('lorem');
35 | $this->assertContains(Message::TYPE_ERROR, Message::getInstance()->render());
36 |
37 | Message::getInstance()->warning('lorem');
38 | $this->assertContains(Message::TYPE_WARNING, Message::getInstance()->render());
39 |
40 | Message::getInstance()->success('lorem');
41 | $this->assertContains(Message::TYPE_SUCCESS, Message::getInstance()->render());
42 | }
43 |
44 | }
--------------------------------------------------------------------------------
/tests/RouterTest.php:
--------------------------------------------------------------------------------
1 | assertEquals(self::BASE_URL, Router::url());
22 | $this->assertEquals(self::BASE_URL, Router::url('/'));
23 |
24 | $this->assertEquals(self::BASE_URL.'/this/sub/dir?get=123', Router::url('this/sub/dir?get=123'));
25 | }
26 |
27 |
28 | public function testAdd()
29 | {
30 | Router::addRoute(Router::METHOD_GET, 'test-get', 'test-get-file');
31 | Router::execute('test-get', Router::METHOD_GET);
32 |
33 |
34 | Router::addRoute(Router::METHOD_POST, 'test-post', 'test-post-file');
35 | Router::execute('test-post', Router::METHOD_POST);
36 |
37 |
38 | Router::addRoute(array(Router::METHOD_GET, Router::METHOD_POST), 'test-mixed', 'test-mixed-file');
39 | Router::execute('test-mixed', Router::METHOD_GET);
40 | Router::execute('test-mixed', Router::METHOD_POST);
41 | }
42 |
43 |
44 | public function testAddCallback()
45 | {
46 | $reachedCallback = false;
47 | Router::addRoute(Router::METHOD_GET, 'test-callback', function() use(&$reachedCallback) {
48 | $reachedCallback = true;
49 | });
50 | Router::execute('test-callback', Router::METHOD_GET);
51 |
52 | $this->assertTrue($reachedCallback);
53 | }
54 |
55 |
56 | /**
57 | * @expectedException Exception
58 | * @expectedExceptionMessageRegExp /unsupported/i
59 | */
60 | public function testAddMethodUnsupported()
61 | {
62 | Router::addRoute('not-a-method', 'test-fail', 'test-fail-file');
63 | }
64 |
65 |
66 | public function testAddShortcuts()
67 | {
68 | Router::addGet('test-get', 'test-get-file');
69 | Router::execute('test-get', Router::METHOD_GET);
70 |
71 | Router::addPost('test-post', 'test-post-file');
72 | Router::execute('test-post', Router::METHOD_POST);
73 |
74 | Router::addMixed('test-mixed', 'test-mixed-file');
75 | Router::execute('test-mixed', Router::METHOD_GET);
76 | Router::execute('test-mixed', Router::METHOD_POST);
77 | }
78 |
79 |
80 | /**
81 | * @expectedException Exception
82 | * @expectedExceptionMessageRegExp /unsupported/i
83 | */
84 | public function testExecuteMethodUnsupported()
85 | {
86 | Router::execute('test-fail', 'not-a-method');
87 | }
88 |
89 |
90 | public function testExecuteCurrentRequest()
91 | {
92 | $_SERVER['REQUEST_METHOD'] = 'GET';
93 | $_SERVER['REQUEST_URI'] = '/somedir/test-get';
94 |
95 | Router::executeCurrentRequest();
96 | }
97 |
98 |
99 | public function testRouteWithPermission()
100 | {
101 | $this->assertFalse(Auth::isLoggedIn());
102 |
103 | $reachedCallback = false;
104 | Router::addRoute(Router::METHOD_GET, 'test-perm-admin', function() use(&$reachedCallback) {
105 | $reachedCallback = true;
106 | }, User::ROLE_ADMIN);
107 |
108 | Router::addRoute(Router::METHOD_GET, 'test-perm-user', function() use(&$reachedCallback) {
109 | $reachedCallback = true;
110 | }, User::ROLE_USER);
111 |
112 |
113 | $reachedCallback = false;
114 | Router::execute('test-perm-admin', Router::METHOD_GET);
115 | $this->assertFalse($reachedCallback);
116 |
117 | $reachedCallback = false;
118 | Router::execute('test-perm-user', Router::METHOD_GET);
119 | $this->assertFalse($reachedCallback);
120 |
121 | // Now auth as admin and try again
122 | Auth::login('admin@domain.tld', 'testtest');
123 |
124 | $reachedCallback = false;
125 | Router::execute('test-perm-admin', Router::METHOD_GET);
126 | $this->assertTrue($reachedCallback);
127 |
128 | $reachedCallback = false;
129 | Router::execute('test-perm-user', Router::METHOD_GET);
130 | $this->assertTrue($reachedCallback);
131 |
132 | Auth::logout();
133 |
134 | // Now auth as user and try again
135 | Auth::login('user@domain.tld', 'testtest');
136 |
137 | $reachedCallback = false;
138 | Router::execute('test-perm-admin', Router::METHOD_GET);
139 | $this->assertFalse($reachedCallback);
140 |
141 | $reachedCallback = false;
142 | Router::execute('test-perm-user', Router::METHOD_GET);
143 | $this->assertTrue($reachedCallback);
144 |
145 | Auth::logout();
146 | }
147 | }
--------------------------------------------------------------------------------
/tests/TestCase.php:
--------------------------------------------------------------------------------
1 | insert(
18 | 'users',
19 | array(
20 | 'id' => static::USER_ROLE_ADMIN_ID,
21 | 'username' => 'admin',
22 | 'domain' => 'domain.tld',
23 | 'password' => Auth::generatePasswordHash('testtest'),
24 | 'mailbox_limit' => 0,
25 | )
26 | );
27 |
28 | Database::getInstance()->insert(
29 | 'users',
30 | array(
31 | 'id' => static::USER_ROLE_ADMIN_ID_LIMITED_NO_ACCESS,
32 | 'username' => 'no-access-limited-admin',
33 | 'domain' => 'domain.tld',
34 | 'password' => Auth::generatePasswordHash('testtest'),
35 | 'mailbox_limit' => 0,
36 | )
37 | );
38 |
39 | Database::getInstance()->insert(
40 | 'users',
41 | array(
42 | 'id' => static::USER_ROLE_ADMIN_ID_LIMITED_HAS_ACCESS,
43 | 'username' => 'has-access-limited-admin',
44 | 'domain' => 'domain.tld',
45 | 'password' => Auth::generatePasswordHash('testtest'),
46 | 'mailbox_limit' => 0,
47 | )
48 | );
49 |
50 | Database::getInstance()->insert(
51 | 'users',
52 | array(
53 | 'id' => static::USER_ROLE_USER_ID,
54 | 'username' => 'user',
55 | 'domain' => 'domain.tld',
56 | 'password' => Auth::generatePasswordHash('testtest'),
57 | 'mailbox_limit' => 64,
58 | )
59 | );
60 |
61 | Config::set('admins', array('admin@domain.tld', 'limited-admin@domain.tld'));
62 | Config::set('admin_domain_limits', array(
63 | 'no-access-limited-admin@domain.tld' => array(),
64 | 'has-access-limited-admin@domain.tld' => array('his-domain.tld'),
65 | ));
66 | }
67 |
68 |
69 | public static function tearDownAfterClass()
70 | {
71 | Database::getInstance()->delete('users', 'id', static::USER_ROLE_ADMIN_ID);
72 | Database::getInstance()->delete('users', 'id', static::USER_ROLE_ADMIN_ID_LIMITED_NO_ACCESS);
73 | Database::getInstance()->delete('users', 'id', static::USER_ROLE_ADMIN_ID_LIMITED_HAS_ACCESS);
74 | Database::getInstance()->delete('users', 'id', static::USER_ROLE_USER_ID);
75 | }
76 |
77 | }
--------------------------------------------------------------------------------