diff --git a/www/activity.php b/www/activity.php
index dec810b..387245e 100644
--- a/www/activity.php
+++ b/www/activity.php
@@ -1,38 +1,41 @@
 <?php
 # Copyright (C) 2018, 2019, 2020 Valerio Bozzolan
 # KISS Libre Hosting Panel
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as
 # published by the Free Software Foundation, either version 3 of the
 # License, or (at your option) any later version.
 #
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 # GNU Affero General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
 # along with this program. If not, see <http://www.gnu.org/licenses/>.
 
 /*
  * This is the homepage of your hosting panel
  */
 
 // load framework
 require '../load.php';
 
+// this page is not public
+require_permission( 'backend' );
+
 // spawn header
 Header::spawn();
 
 ?>
 
 	<?php ActivityPanel::spawn( [
 		'query' => [
 			'limit' => 200,
 		],
 	] ) ?>
 
 <?php
 
 Footer::spawn();
diff --git a/www/domain.php b/www/domain.php
index 97a26a3..5b6774b 100644
--- a/www/domain.php
+++ b/www/domain.php
@@ -1,105 +1,108 @@
 <?php
 # Copyright (C) 2018, 2019, 2020 Valerio Bozzolan
 # Boz Libre Hosting Panel
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as
 # published by the Free Software Foundation, either version 3 of the
 # License, or (at your option) any later version.
 #
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 # GNU Affero General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
 # along with this program. If not, see <http://www.gnu.org/licenses/>.
 
 /*
  * This is the domain edit page
  */
 
 // load framework
 require '../load.php';
 
+// this page is not public
+require_permission( 'backend' );
+
 // wanted domain
 list( $domain_name ) = url_parts( 1, 0 );
 
 $domain = null;
 
 if( $domain_name ) {
 	// retrieve domain
 	$domain = ( new DomainAPI() )
 		->whereDomainName( $domain_name )
 		->whereDomainIsEditable()
 		->joinPlan( 'LEFT' )
 		->queryRow();
 
 	// 404?
 	$domain or PageNotFound::spawn();
 } else {
 	// try to create
 
 	require_permission( 'edit-domain-all' );
 
 	if( is_action( 'add-domain' ) && isset( $_POST[ 'domain_name' ] ) ) {
 
 		// trim and normalize to max length
 		$domain_name = luser_input( $_POST[ 'domain_name' ], 64 );
 
 		// existing domain
 		$existing = ( new DomainAPI() )
 			->whereDomainName( $domain_name )
 			->queryRow();
 
 		// go to the existing one
 		if( $existing ) {
 			http_redirect( $existing->getDomainPermalink() );
 		}
 
 		query( 'START TRANSACTION' );
 
 		// insert this new domain
 		insert_row( Domain::T, [
 			new DBCol( 'domain_name',   $domain_name, 's' ),
 			new DBCol( 'domain_active', 1,            'd' ),
 			new DBCol( 'domain_born',  'NOW()',       '-' ),
 		] );
 
 		// this Domain ID
 		$domain_ID = last_inserted_ID();
 
 		// add this event in the log
 		APILog::insert( [
 			'family' => 'domain',
 			'action' => 'create',
 			'domain' => $domain_ID,
 		] );
 
 		query( 'COMMIT' );
 
 		// go to the new domain
 		http_redirect( Domain::permalink( $domain_name, true ) );
 	}
 }
 
 // spawn header
 Header::spawn( [
 	'uid' => false,
 	'title-prefix' => __( "Domain" ),
 	'title' => $domain_name ? $domain_name : __( "Add" ),
 ] );
 
 if( $domain ) {
 	// spawn the domain template
 	template( 'domain', [
 		'domain' => $domain,
 		'plan'   => $domain,
 	] );
 } else {
 	// form to create the domain
 	template( 'domain-create' );
 }
 
 // spawn the footer
 Footer::spawn();
diff --git a/www/index.php b/www/index.php
index f55416a..a183fe9 100644
--- a/www/index.php
+++ b/www/index.php
@@ -1,95 +1,98 @@
 <?php
 # Copyright (C) 2018, 2019, 2020 Valerio Bozzolan
 # KISS Libre Hosting Panel
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as
 # published by the Free Software Foundation, either version 3 of the
 # License, or (at your option) any later version.
 #
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 # GNU Affero General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
 # along with this program. If not, see <http://www.gnu.org/licenses/>.
 
 /*
  * This is the homepage of your hosting panel
  */
 
 // load framework
 require '../load.php';
 
+// this page is not public
+require_permission( 'backend' );
+
 // spawn header
 Header::spawn( [
 	'breadcrumb' => false,
 ] );
 
 // user domains
 $domains = ( new DomainAPI() )
 	->select( [
 		'domain.domain_ID',
 		'domain_name',
 		'domain_active',
 	] )
 	->whereDomainIsEditable()
 	->orderBy( 'domain_name' )
 	->queryGenerator();
 ?>
 
 	<p class="lead"><?php printf(
 		__( "Welcome in your %s dashboard!" ),
 		SITE_NAME
 	) ?></p>
 
 	<?php if( $domains->valid() ): ?>
 		<h3><?php printf(
 			__( "Your %s" ),
 			__( "domains" )
 		) ?></h3>
 		<ul>
 			<?php foreach( $domains as $domain ): ?>
 			<li>
 				<code>
 				<?php if( $domain->domain_active ): ?>
 					<?= HTML::a(
 						$domain->getDomainPermalink(),
 						$domain->domain_name
 					) ?>
 				<?php else: ?>
 					<del><?= esc_html( $domain->domain_name ) ?></del>
 				<?php endif ?>
 				</code>
 			</li>
 			<?php endforeach ?>
 		</ul>
 
 		<?php if( has_permission( 'edit-domain-all' ) ): ?>
 			<p><a class="btn btn-default" href="<?= ROOT ?>/domain.php"><?php echo __( "Add" ) ?></a></p>
 		<?php endif ?>
 	<?php endif ?>
 
 	<!-- link to users -->
 	<?php if( has_permission( 'edit-user-all' ) ): ?>
 		<h3><?= HTML::a(
 			menu_entry( 'user-list' )->getURL(),
 			__( "Users" )
 		) ?></h3>
 	<?php endif ?>
 	<!-- end link to users -->
 
 	<!-- link to users activity -->
 	<?php if( has_permission( 'monitor' ) ): ?>
 		<h3><?= HTML::a(
 			menu_entry( 'activity' )->getURL(),
 			__( "Last Activity" )
 		) ?></h3>
 	<?php endif ?>
 	<!-- end link to users activity -->
 
 <?php
 
 // spawn footer
 Footer::spawn();
diff --git a/www/mailbox.php b/www/mailbox.php
index ecb0898..9bbd3b3 100644
--- a/www/mailbox.php
+++ b/www/mailbox.php
@@ -1,191 +1,194 @@
 <?php
 # Copyright (C) 2018, 2019, 2020 Valerio Bozzolan
 # KISS Libre Hosting Panel
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as
 # published by the Free Software Foundation, either version 3 of the
 # License, or (at your option) any later version.
 #
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 # GNU Affero General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
 # along with this program. If not, see <http://www.gnu.org/licenses/>.
 
 /*
  * This is the mailbox edit page
  */
 
 // load framework
 require '../load.php';
 
+// this page is not public
+require_permission( 'backend' );
+
 // wanted domain and mailbox username
 list( $domain_name, $mailbox_username ) = url_parts( 2, 1 );
 
 // some useful information
 $domain   = null;
 $mailbox  = null;
 $plan     = null;
 $mailbox_password = null;
 
 // check if the page is about a specific Mailbox
 if( $mailbox_username ) {
 
 	// retrieve the mailbox and its domain and its Plan
 	$mailbox = ( new MailboxFullAPI() )
 		->joinPlan( 'LEFT' )
 		->whereDomainName( $domain_name )
 		->whereMailboxUsername( $mailbox_username )
 		->whereMailboxIsEditable()
 		->queryRow();
 
 	// 404?
 	$mailbox or PageNotFound::spawn();
 
 	// the mailbox object has the domain stuff - recycle it
 	$domain = $mailbox;
 
 	// the mailbox object has the Plan stuff - recycle it
 	$plan   = $mailbox;
 } else {
 
 	// retrieve just the domain and its Plan
 	$domain = ( new DomainAPI() )
 		->whereDomainName( $domain_name )
 		->whereDomainIsEditable()
 		->joinPlan( 'LEFT' )
 		->queryRow();
 
 	// 404?
 	$domain or PageNotFound::spawn();
 
 	// the domain object has the Plan stuff - recycle it
 	$plan = $domain;
 }
 
 // does the user want to create a Mailbox?
 if( !$mailbox ) {
 
 	// count the actual number of Domain Mailbox(es)
 	$mailbox_count = (int)
 		( new MailboxAPI() )
 			->select( 'COUNT(*) count' )
 			->whereDomain( $domain )
 			->queryValue( 'count' );
 
 	// check if I can add another Mailbox
 	if( $mailbox_count >= $plan->getPlanMailboxes() && !has_permission( 'edit-email-all' ) ) {
 		BadRequest::spawn( __( "Your Plan does not allow this action" ), 401 );
 	}
 
 }
 
 /*
  * Change the mailbox password
  */
 if( $mailbox && is_action( 'mailbox-password-reset' ) ) {
 	$mailbox_password = $mailbox->updateMailboxPassword();
 }
 
 /**
  * Eventually save the notes
  */
 if( $mailbox && is_action( 'save-mailbox-notes' ) ) {
 
 	// read the description
 	$description = $_POST['mailbox_description'] ?? null;
 
 	query( 'START TRANSACTION' );
 
 	// save the description
 	( new MailboxAPI() )
 		->whereMailbox( $mailbox )
 		->update( [
 			'mailbox_description' => $description,
 		] );
 
 	// remember this action in the registry
 	APILog::insert( [
 		'family'  => 'mailbox',
 		'action'  => 'description.change',
 		'mailbox' => $mailbox,
 		'domain'  => $domain,
 	] );
 
 	query( 'COMMIT' );
 
 	// POST -> redirect -> GET
 	http_redirect( $mailbox->getMailboxPermalink() );
 }
 
 /*
  * Create the mailbox
  */
 if( !$mailbox && is_action( 'mailbox-create' ) && isset( $_POST['mailbox_username'] ) ) {
 
 	// assure that the username is not too long
 	$_POST['mailbox_username'] = luser_input( $_POST['mailbox_username'], 64 );
 
 	// check if the mailbox already exist
 	$mailbox_exists = ( new MailboxFullAPI() )
 		->select( 1 )
 		->whereDomainName( $domain_name )
 		->whereMailboxUsername( $_POST['mailbox_username'] )
 		->queryRow();
 
 	// check if we can create the mailbox
 	if( !$mailbox_exists ) {
 		// assign a damn temporary password
 		$mailbox_password = generate_password();
 		$mailbox_password_safe = Mailbox::encryptPassword( $mailbox_password );
 
 		query( 'START TRANSACTION' );
 
 		// really create the mailbox
 		insert_row( 'mailbox', [
 			new DBCol( 'mailbox_username', $_POST['mailbox_username'], 's' ),
 			new DBCol( 'domain_ID',        $domain->getDomainID(),       'd' ),
 			new DBCol( 'mailbox_password', $mailbox_password_safe,       's' ),
 		] );
 
 		// register this event in the registry
 		APILog::insert( [
 			'family'  => 'mailbox',
 			'action'  => 'create',
 			'domain'  => $domain,
 			'mailbox' => last_inserted_ID(),
 		] );
 
 		query( 'COMMIT' );
 	}
 
 	// POST -> redirect -> GET
 	http_redirect( Mailbox::permalink(
 		$domain->getDomainName(),
 		$_POST['mailbox_username']
 	) );
 }
 
 // spawn header
 Header::spawn( [
 	'uid' => false,
 	'title-prefix' => __( "Mailbox" ),
 	'title' => $mailbox ? $mailbox->getMailboxAddress() : __( "create" ),
 	'breadcrumb' => [
 		new MenuEntry( null, $domain->getDomainPermalink(), $domain->getDomainName() ),
 	],
 ] );
 
 // spawn the page content
 template( 'mailbox', [
 	'mailbox'          => $mailbox,
 	'mailbox_password' => $mailbox_password,
 	'domain'           => $domain,
 	'plan'             => $plan,
 ] );
 
 // spawn the footer
 Footer::spawn();
diff --git a/www/mailforward.php b/www/mailforward.php
index 383899a..71c9a22 100644
--- a/www/mailforward.php
+++ b/www/mailforward.php
@@ -1,239 +1,242 @@
 <?php
 # Copyright (C) 2018, 2019, 2020 Valerio Bozzolan
 # KISS Libre Hosting Panel
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as
 # published by the Free Software Foundation, either version 3 of the
 # License, or (at your option) any later version.
 #
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 # GNU Affero General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
 # along with this program. If not, see <http://www.gnu.org/licenses/>.
 
 /*
  * This is the single e-mail forwarding edit page
  */
 
 // load framework
 require '../load.php';
 
+// this page is not public
+require_permission( 'backend' );
+
 // wanted informations
 $domain          = null;
 $mailforwardfrom = null;
 
 // URL paramenters (maximum both domain and mailforward source, minimum just domain)
 list( $domain_name, $mailforwardfrom_username ) = url_parts( 2, 1 );
 
 // eventually retrieve mailforward from database
 if( $mailforwardfrom_username ) {
 	$mailforwardfrom = ( new MailforwardfromQuery() )
 		->select( [
 			'domain.domain_ID',
 			'domain_name',
 			'mailforwardfrom.mailforwardfrom_ID',
 			'mailforwardfrom_username',
 		] )
 		->joinDomain()
 		->whereDomainName( $domain_name )
 		->whereMailforwardfromUsername( $mailforwardfrom_username )
 		->whereDomainIsEditable()
 		->queryRow();
 
 	// 404
 	$mailforwardfrom or PageNotFound::spawn();
 
 	// recycle the mailforward object that has domain informations
 	$domain = $mailforwardfrom;
 }
 
 // eventually retrieve domain from database
 if( ! $domain ) {
 	$domain = ( new DomainAPI() )
 		->select( [
 			'domain.domain_ID',
 			'domain.domain_name',
 		] )
 		->whereDomainName( $domain_name )
 		->whereDomainIsEditable()
 		->queryRow();
 
 	// 404
 	$domain or PageNotFound::spawn();
 }
 
 // save destination action
 if( is_action( 'mailforward-save' ) ) {
 
 	// save source only during creation
 	if( ! $mailforwardfrom ) {
 
 		// sanitize
 		if( ! isset( $_POST[ 'mailforwardfrom_username' ] ) ) {
 			BadRequest::spawn( __( "missing parameter" ) );
 		}
 		$username = luser_input( $_POST[ 'mailforwardfrom_username' ], 128 );
 		if( ! validate_mailbox_username( $username ) ) {
 			BadRequest::spawn( __( "invalid mailbox name" ) );
 		}
 
 		// check existence
 		$mailforwardfrom_exists = ( new MailforwardfromQuery() )
 			->select( 1 )
 			->whereDomain( $domain )
 			->whereMailforwardfromUsername( $username )
 			->queryRow();
 
 		// die if exists
 		if( $mailforwardfrom_exists ) {
 			BadRequest::spawn( __( "e-mail forwarding already existing" ) );
 		}
 
 		query( 'START TRANSACTION' );
 
 		// insert as new row
 		insert_row( 'mailforwardfrom', [
 			new DBCol( 'domain_ID',                $domain->getDomainID(), 'd' ),
 			new DBCol( 'mailforwardfrom_username', $username,              's' ),
 		] );
 
 		// remember this action in the registry
 		APILog::insert( [
 			'family'          => 'mailforward',
 			'action'          => 'create',
 			'domain'          => $domain,
 			'mailforwardfrom' => last_inserted_ID(),
 		] );
 
 		query( 'COMMIT' );
 
 		// POST/redirect/GET
 		http_redirect( Mailforwardfrom::permalink(
 			$domain->getDomainName(),
 			$username,
 			true
 		), 303 );
 	}
 }
 
 // delete action
 if( $mailforwardfrom ) {
 
 	// action fired when deleting a whole mailforward
 	if( is_action( 'mailforward-delete' ) ) {
 
 		query( 'START TRANSACTION' );
 
 		// mark a deletion on this Domain
 		APILog::insert( [
 			'family'          => 'mailforward',
 			'action'          => 'delete',
 			'domain'          => $domain,
 		] );
 
 		// drop the foreign key constraints
 		// See https://gitpull.it/T518
 		( new QueryLog() )
 			->whereMailforwardfrom( $mailforwardfrom )
 			->update( [
 				'mailforwardfrom_ID' => null,
 			] );
 
 		// drop the existing one
 		( new MailforwardfromQuery() )
 			->whereMailforwardfrom( $mailforwardfrom )
 			->delete();
 
 		query( 'COMMIT' );
 
 		// POST/redirect/GET
 		http_redirect( $domain->getDomainPermalink( true ), 303 );
 
 	}
 
 	// action fired when adding/removing a mailforward
 	if( ( is_action( 'mailforwardto-add' ) || is_action( 'mailforwardto-remove' ) ) && isset( $_POST[ 'address' ] ) ) {
 
 		$address = require_email( $_POST[ 'address' ] );
 
 		if( $address === $mailforwardfrom->getMailforwardfromAddress() ) {
 			BadRequest::spawn( __( "do not try to create a loop" ) );
 		}
 
 		$existing_address =
 			( new MailforwardtoAPI() )
 				->whereMailforwardfrom( $mailforwardfrom )
 				->whereMailforwardtoAddress( $address )
 				->queryRow();
 
 		// action fired when removing a mailforward
 		if( is_action( 'mailforwardto-remove' ) && $existing_address ) {
 
 			query( 'START TRANSACTION' );
 
 			// TODO refactor with query builder
 			query( sprintf(
 				"DELETE FROM %s WHERE mailforwardfrom_ID = %d and mailforwardto_address = '%s'",
 				T( 'mailforwardto' ),
 				$mailforwardfrom->getMailforwardfromID(),
 				esc_sql( $address )
 			) );
 
 			// remember that we removed an address
 			APILog::insert( [
 				'family'          => 'mailforward',
 				'action'          => 'remove.destination',
 				'domain'          => $domain,
 				'mailforwardfrom' => $mailforwardfrom,
 			] );
 
 			query( 'COMMIT' );
 		}
 
 		// action fired when adding a mailforward
 		if( is_action( 'mailforwardto-add' ) && ! $existing_address ) {
 
 			query( 'START TRANSACTION' );
 
 			insert_row( 'mailforwardto', [
 				new DBCol( 'mailforwardfrom_ID',    $mailforwardfrom->getMailforwardfromID(), 'd' ),
 				new DBCol( 'mailforwardto_address', $address,                                 's' ),
 			] );
 
 			// remember that we added an address
 			APILog::insert( [
 				'family'          => 'mailforward',
 				'action'          => 'add.destination',
 				'domain'          => $domain,
 				'mailforwardfrom' => $mailforwardfrom,
 			] );
 
 			query( 'COMMIT' );
 		}
 	}
 }
 
 // spawn header
 Header::spawn( [
 	'uid' => false,
 	'title-prefix' => __( "E-mail forwarding" ),
 	'title' => $mailforwardfrom
 		? $mailforwardfrom->getMailforwardfromAddress()
 		: __( "create" ),
 	'breadcrumb' => [
 		new MenuEntry( null, $domain->getDomainPermalink(), $domain->getDomainName() ),
 	],
 ] );
 
 // spawn the page content
 template( 'mailforward', [
 	'domain'          => $domain,
 	'mailforwardfrom' => $mailforwardfrom,
 ] );
 
 // spawn the footer
 Footer::spawn();
diff --git a/www/profile.php b/www/profile.php
index 6fbcbbf..d8ff0e2 100644
--- a/www/profile.php
+++ b/www/profile.php
@@ -1,83 +1,83 @@
 <?php
 # Copyright (C) 2018 Valerio Bozzolan
 # Boz Libre Hosting Panel
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as
 # published by the Free Software Foundation, either version 3 of the
 # License, or (at your option) any later version.
 #
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 # GNU Affero General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
 # along with this program. If not, see <http://www.gnu.org/licenses/>.
 
 /*
  * This is the user profile page
  */
 
 // load framework
 require '../load.php';
 
-// must be logged
-require_permission( 'read' );
+// this page is not public
+require_permission( 'backend' );
 
 // myself
 $user = get_user();
 
 // edit whatever user
 if( isset( $_SERVER[ 'PATH_INFO' ] ) ) {
 	require_permission( 'edit-all-user' );
 
 	list( $user_uid ) = url_parts( 1 );
 	$user = Sessionuser::factoryFromUID( $user_uid )
 		->queryRow();
 
 	if( ! $user ) {
 		PageNotFound::spaqn();
 	}
 }
 
 // spawn header
 Header::spawn( [
 	'title' => __( "Profile" ),
 ] );
 
 // user e-mail
 $email = $user->get( 'user_email' );
 
 // handle send user password action
 if( is_action( 'send-user-password' ) ) {
 
 	// generate
 	$password = generate_password();
 
 	// e-mail body
 	$mail_content = template_content( 'mail-user-password', [
 		'email'    => $email,
 		'name'     => $user->get( 'user_name' ),
 		'uid'      => $user->get( 'user_uid' ),
 		'surname'  => $user->get( 'user_surname' ),
 		'password' => $password,
 	] );
 
 	// update
 	query_update( 'user', [
 		new DBCol( 'user_password', Sessionuser::encryptPassword( $password ), 's' ),
 	], sprintf(
 		'user_ID = %d',
 		$user->get( 'user_ID' )
 	) );
 
 	// send
 	send_email( __( "Password reset" ), $mail_content, $email );
 }
 
 // spawn the profile page
 template( 'profile', [ 'email' => $email ] );
 
 // spawn footer
 Footer::spawn();
diff --git a/www/user-list.php b/www/user-list.php
index 87cf4f7..8c93daf 100644
--- a/www/user-list.php
+++ b/www/user-list.php
@@ -1,77 +1,80 @@
 <?php
 # Copyright (C) 2019 Valerio Bozzolan
 # Boz Libre Hosting Panel
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as
 # published by the Free Software Foundation, either version 3 of the
 # License, or (at your option) any later version.
 #
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 # GNU Affero General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
 # along with this program. If not, see <http://www.gnu.org/licenses/>.
 
 /*
  * This is the domain edit page
  */
 
 // load framework
 require '../load.php';
 
+// this page is not public
+require_permission( 'backend' );
+
 // spawn header
 Header::spawn( [
 	'title' => __( "Users" ),
 ] );
 
 $pager = new UserPager();
 ?>
 
 <form method="get">
 	<p>
 		<label for="user-email"><?= esc_html( __( "E-mail" ) ) ?></label>
 		<input id="user-email" type="email" name="email"<?= value( $pager->getArg( 'email' ) ) ?> />
 	</p>
 	<p>
 		<label for="user-login"><?= esc_html( __( "Login" ) ) ?></label>
 		<input id="user-login" type="text" name="uid"<?= value( $pager->getArg( 'uid' ) ) ?> />
 	</p>
 	<p>
 		<button type="submit" class="btn btn-default"><?= esc_html( __( "Search" ) ) ?></button>
 	</p>
 </form>
 
 <table class="table">
 	<thead>
 		<tr>
 			<th><?= esc_html( __( "Login"  ) ) ?></th>
 			<th><?= esc_html( __( "Surname" ) ) ?></th>
 			<th><?= esc_html( __( "Name"    ) ) ?></th>
 			<th><?= esc_html( __( "Role"    ) ) ?></th>
 		</tr>
 	</thead>
 	<tbody>
 		<?php foreach( $pager->createPagedQuery()->queryGenerator() as $user ): ?>
 			<tr>
 				<td><?= HTML::a(
 					$user->getUserPermalink(),
 					$user->getUserUID()
 				) ?></td>
 				<td><?= esc_html( $user->getUserSurname()   ) ?></td>
 				<td><?= esc_html( $user->getUserName()      ) ?></td>
 				<td><?= esc_html( $user->getUserRoleLabel() ) ?></td>
 			</tr>
 		<?php endforeach ?>
 	</tbody>
 </table>
 
 <?php if( has_permission( 'edit-user-all' ) ): ?>
 	<p><a class="btn btn-default" href="<?= ROOT ?>/user.php"><?= __( "Add" ) ?></a></p>
 <?php endif ?>
 
 <?php
 // spawn the footer
 Footer::spawn();
diff --git a/www/user.php b/www/user.php
index 917fccf..3faf5b0 100644
--- a/www/user.php
+++ b/www/user.php
@@ -1,209 +1,209 @@
 <?php
 # Copyright (C) 2019 Valerio Bozzolan
 # Boz Libre Hosting Panel
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as
 # published by the Free Software Foundation, either version 3 of the
 # License, or (at your option) any later version.
 #
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 # GNU Affero General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
 # along with this program. If not, see <http://www.gnu.org/licenses/>.
 
 /*
  * This is the single User creation/edit page
  */
 
 // load framework
 require '../load.php';
 
-// require the permission to see the backend
+// this page is not public
 require_permission( 'backend' );
 
 // wanted informations
 $user = null;
 
 // URL paramenters (user_uid)
 list( $user_uid ) = url_parts( 1, 0 );
 
 // eventually retrieve mailforward from database
 if( $user_uid ) {
 	$user = ( new UserAPI() )
 		->whereUserUID( $user_uid )
 		->whereUserIsEditable()
 		->queryRow();
 
 	// 404
 	if( !$user || !$user->isUserEditable() ) {
 		PageNotFound::spawn();
 	}
 } else {
 	// to create an FTP user, must edit all FTP users
 	require_permission( 'edit-user-all' );
 }
 
 // register save User action
 if( is_action( 'save-user' ) ) {
 
 	$email   = $_POST['email']   ?? null;
 	$uid     = $_POST['uid']     ?? null;
 	$name    = $_POST['name']    ?? null;
 	$surname = $_POST['surname'] ?? null;
 
 	if( $email && $uid && $name && $surname ) {
 		$email = (string) $email;
 
 		// data to be saved
 		$data = [];
 		$data['user_email']   = $email;
 		$data['user_name']    = $name;
 		$data['user_surname'] = $surname;
 
 		if( $user ) {
 			// update existing User
 			( new UserAPI() )
 				->whereUser( $user )
 				->update( $data );
 		} else {
 			// insert new User
 			$data['user_uid']      = $uid;
 			$data['user_active']   = 0;      // disable login as default
 			$data['user_password'] = '!';    // assign an invalid password
 			$data['user_role']     = 'user'; // assign low privileges
 			$data[] = new DBCol( 'user_registration_date', 'NOW()', '-' );
 
 			( new UserAPI() )
 				->insertRow( $data );
 		}
 
 		// POST -> redirect -> GET (See Other)
 		http_redirect( User::permalink( $uid ), 303 );
 	}
 }
 // end register Save user action
 
 // add a Domain to the user
 if( is_action( 'add-domain' ) ){
 
 	// check for permissions
 	if( !has_permission( 'edit-user-all' ) ) {
 		error_die( "Not authorized to add a Domain" );
 	}
 
 	// get the Domain by name
 	$domain_name = $_POST['domain_name'] ?? null;
 	if( !$domain_name ) {
 		die( "Please fill that damn Domain name" );
 	}
 
 	// search the Domain name
 	$domain =
 		( new DomainAPI() )
 			->whereDomainName( $domain_name )
 			->queryRow();
 
 	query( 'START TRANSACTION' );
 
 	// domain ID to be assigned to the User
 	$domain_ID = null;
 
 	// does the Domain already exist?
 	if( $domain ) {
 		$domain_ID = $domain->getDomainID();
 	} else {
 		// can I add this Domain?
 		if( has_permission( 'edit-domain-all' ) ) {
 
 			// add this Domain
 			( new DomainAPI() )
 				->insertRow( [
 					'domain_name'   => $domain_name,
 					'domain_active' => 1,
 					new DBCol( 'domain_born', 'NOW()', '-' ),
 				] );
 
 			$domain_ID = last_inserted_ID();
 		}
 	}
 
 	if( $domain_ID ) {
 
 		$is_domain_mine =
 			( new DomainUserAPI() )
 				->whereUserIsMe()
 				->whereDomainID( $domain_ID )
 				->queryRow();
 
 		// is it already mine?
 		if( !$is_domain_mine ) {
 
 			// associate this domain to myself
 			( new DomainUserAPI() )
 				->insertRow( [
 					'domain_ID' => $domain_ID,
 					'user_ID'   => $user->getUserID(),
 					new DBCol( 'domain_user_creation_date', 'NOW()', '-' ),
 				] );
 		}
 
 	} else {
 		die( "this Domain is not registered and can't be added" );
 	}
 
 	query( 'COMMIT' );
 }
 // end add Domain to User
 
 // register action to generate a new password
 $new_password = null;
 if( is_action( 'change-password' ) && $user ) {
 
 	// generate a new password and save
 	$new_password = generate_password();
 	$encrypted = User::encryptPassword( $new_password );
 	( new UserAPI() )
 		->whereUser( $user )
 		->update( [
 			User::IS_ACTIVE => 1,
 			User::PASSWORD  => $encrypted,
 		] );
 
 	// do not refresh the page
 }
 
 // expose the User domains
 $user_domains = [];
 if( $user ) {
 
 	// get User domains
 	$user_domains =
 		( new DomainUserAPI() )
 			->joinDomain()
 			->whereUser( $user )
 			->orderByDomainName()
 			->queryGenerator();
 }
 
 // spawn header
 Header::spawn( [
 	'uid' => false,
 	'title-prefix' => __( "User" ),
 	'title' => $user
 		? $user->getUserUID()
 		: __( "create" ),
 ] );
 
 // spawn the page content
 template( 'user', [
 	'user'         => $user,
 	'new_password' => $new_password,
 	'user_domains' => $user_domains,
 ] );
 
 // spawn the footer
 Footer::spawn();