Bläddra i källkod

Merge pull request #30 from ohartl/feature-multi-source-redirects

Feature optional multi source redirects
Oliver Hartl 9 år sedan
förälder
incheckning
8ff0dd17e9

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+config/config_override.inc.php

+ 19 - 0
config/config.inc.php

@@ -34,6 +34,15 @@ define("DBC_DOMAINS_DOMAIN", "domain");
 define("DBC_ALIASES_ID", "id");
 define("DBC_ALIASES_ID", "id");
 define("DBC_ALIASES_SOURCE", "source");
 define("DBC_ALIASES_SOURCE", "source");
 define("DBC_ALIASES_DESTINATION", "destination");
 define("DBC_ALIASES_DESTINATION", "destination");
+// Enable multi source redirects, needs a new column in aliase table with VARCHAR(32)
+//define("DBC_ALIASES_MULTI_SOURCE", "multi_source");	// (Optional, Recommended)
+
+
+/*
+ * General options
+ */
+// Enable validating that the source addresses are ending with domain from domains
+//define("VALIDATE_ALIASES_SOURCE_DOMAIN_ENABLED", true);	// (Optional, Recommended)
 
 
 
 
 /*
 /*
@@ -77,4 +86,14 @@ define("MIN_PASS_LENGTH", 8);
 // define("WRITE_LOG", true);
 // define("WRITE_LOG", true);
 // define("WRITE_LOG_PATH","/var/www/webmum/log/");
 // define("WRITE_LOG_PATH","/var/www/webmum/log/");
 
 
+
+/*
+ * Frontend options
+ */
+
+// Separator for email lists
+define("FRONTEND_EMAIL_SEPARATOR_TEXT", ', '); // possible values: ', ' (default), '; ', PHP_EOL (newline)
+define("FRONTEND_EMAIL_SEPARATOR_FORM", ','); // possible values: ',' (default), ';', PHP_EOL (newline)
+
+
 ?>
 ?>

+ 96 - 16
include/php/global.inc.php

@@ -1,4 +1,4 @@
-<?php 
+<?php
 
 
 /*
 /*
  * Message manager
  * Message manager
@@ -10,7 +10,8 @@
 
 
 $MESSAGES = array();
 $MESSAGES = array();
 
 
-function add_message($type, $message){
+function add_message($type, $message)
+{
 	global $MESSAGES;
 	global $MESSAGES;
 	$newmessage = array();
 	$newmessage = array();
 	$newmessage['type'] = $type;
 	$newmessage['type'] = $type;
@@ -19,7 +20,8 @@ function add_message($type, $message){
 	$MESSAGES[] = $newmessage;
 	$MESSAGES[] = $newmessage;
 }
 }
 
 
-function output_messages(){
+function output_messages()
+{
 	echo "<div class=\"messages\">";
 	echo "<div class=\"messages\">";
 	global $MESSAGES;
 	global $MESSAGES;
 	foreach($MESSAGES as $message){
 	foreach($MESSAGES as $message){
@@ -29,8 +31,6 @@ function output_messages(){
 }
 }
 
 
 
 
-
-
 /*
 /*
  * Function checks password input for new password
  * Function checks password input for new password
  * Return codes:
  * Return codes:
@@ -40,7 +40,8 @@ function output_messages(){
  * 4: Passwort is too snort
  * 4: Passwort is too snort
  */
  */
 
 
-function check_new_pass($pass1, $pass2){
+function check_new_pass($pass1, $pass2)
+{
 	global $PASS_ERR;
 	global $PASS_ERR;
 	global $PASS_ERR_MSG;
 	global $PASS_ERR_MSG;
 	// Check if one passwort input is empty
 	// Check if one passwort input is empty
@@ -56,6 +57,7 @@ function check_new_pass($pass1, $pass2){
 				// Password is not long enough
 				// Password is not long enough
 				$PASS_ERR = 4;
 				$PASS_ERR = 4;
 				$PASS_ERR_MSG = "Password is not long enough. Please enter a password which has ".MIN_PASS_LENGTH." characters or more.";
 				$PASS_ERR_MSG = "Password is not long enough. Please enter a password which has ".MIN_PASS_LENGTH." characters or more.";
+
 				return $PASS_ERR;
 				return $PASS_ERR;
 			}
 			}
 		}
 		}
@@ -63,6 +65,7 @@ function check_new_pass($pass1, $pass2){
 			// Passwords are not equal
 			// Passwords are not equal
 			$PASS_ERR = 3;
 			$PASS_ERR = 3;
 			$PASS_ERR_MSG = "Passwords are not equal.";
 			$PASS_ERR_MSG = "Passwords are not equal.";
+
 			return $PASS_ERR;
 			return $PASS_ERR;
 		}
 		}
 	}
 	}
@@ -70,36 +73,39 @@ function check_new_pass($pass1, $pass2){
 		// One password is empty.
 		// One password is empty.
 		$PASS_ERR = 2;
 		$PASS_ERR = 2;
 		$PASS_ERR_MSG = "Not all password fields were filled out";
 		$PASS_ERR_MSG = "Not all password fields were filled out";
+
 		return $PASS_ERR;
 		return $PASS_ERR;
 	}
 	}
 }
 }
 
 
 function get_hash()
 function get_hash()
 {
 {
-	switch(PASS_HASH_SCHEMA)
-	{
+	switch(PASS_HASH_SCHEMA){
 		case "SHA-512":
 		case "SHA-512":
 			return '$6$rounds=5000$';
 			return '$6$rounds=5000$';
-		break;
+			break;
 		
 		
 		case "SHA-256":
 		case "SHA-256":
 			return '$5$rounds=5000$';
 			return '$5$rounds=5000$';
-		break;
+			break;
 		
 		
 		case "BLOWFISH":
 		case "BLOWFISH":
 			return '$2a$09$';
 			return '$2a$09$';
-		break;
+			break;
 	}
 	}
 }
 }
 
 
-function gen_pass_hash($pass){
-	$salt = base64_encode(rand(1,1000000) + microtime());
+function gen_pass_hash($pass)
+{
+	$salt = base64_encode(rand(1, 1000000) + microtime());
 	$hash_schema = get_hash();
 	$hash_schema = get_hash();
 	$pass_hash = crypt($pass, $hash_schema.$salt.'$');
 	$pass_hash = crypt($pass, $hash_schema.$salt.'$');
+
 	return $pass_hash;
 	return $pass_hash;
 }
 }
 
 
-function write_pass_hash_to_db($pass_hash, $uid){
+function write_pass_hash_to_db($pass_hash, $uid)
+{
 	global $db;
 	global $db;
 	$uid = $db->escape_string($uid);
 	$uid = $db->escape_string($uid);
 	$pass_hash = $db->escape_string($pass_hash);
 	$pass_hash = $db->escape_string($pass_hash);
@@ -110,7 +116,8 @@ function write_pass_hash_to_db($pass_hash, $uid){
 /*
 /*
  * Add message to logfile
  * Add message to logfile
  */
  */
-function writeLog($text){
+function writeLog($text)
+{
 	if(defined('WRITE_LOG') && defined('WRITE_LOG_PATH')){
 	if(defined('WRITE_LOG') && defined('WRITE_LOG_PATH')){
 		$logdestination = realpath(WRITE_LOG_PATH).DIRECTORY_SEPARATOR."webmum.log";
 		$logdestination = realpath(WRITE_LOG_PATH).DIRECTORY_SEPARATOR."webmum.log";
 		if(is_writable(WRITE_LOG_PATH)){
 		if(is_writable(WRITE_LOG_PATH)){
@@ -128,10 +135,83 @@ function writeLog($text){
 /**
 /**
  * @param string $url
  * @param string $url
  */
  */
-function redirect($url){
+function redirect($url)
+{
 	header("Location: ".FRONTEND_BASE_PATH.$url);
 	header("Location: ".FRONTEND_BASE_PATH.$url);
 	exit;
 	exit;
 }
 }
 
 
 
 
+/**
+ * Split comma, semicolon or newline separated list of emails to string
+ *
+ * @param string $input
+ *
+ * @return array
+ */
+function stringToEmails($input)
+{
+	$separators = array(',', ';', "\r\n", "\r", "\n", '|', ':');
+
+	$list = explode('|', str_replace($separators, '|', $input));
+	foreach($list as $i => &$email){
+		if(empty($email)){
+			unset($list[$i]);
+		}
+		else{
+			$email = trim($email);
+		}
+	}
+
+	return array_values(
+		array_map(
+			'strtolower',
+			array_unique(
+				$list
+			)
+		)
+	);
+}
+
+/**
+ * List of emails to comma or $glue separated list string
+ *
+ * @param array $list
+ * @param string $glue
+ *
+ * @return string
+ */
+function emailsToString($list, $glue = ',')
+{
+	return implode($glue, $list);
+}
+
+/**
+ * Format single email address
+ *
+ * @param string $input
+ * @return string
+ */
+function formatEmail($input)
+{
+	return strtolower(trim($input));
+}
+
+/**
+ * Format email addresses (single, multiple in separated list, or array of email addresses)
+ *
+ * @param string|array $input
+ * @param string $glue
+ * @return string
+ */
+function formatEmails($input, $glue)
+{
+	if(!is_array($input)){
+		$input = stringToEmails($input);
+	}
+
+	return emailsToString($input, $glue);
+}
+
+
 ?>
 ?>

+ 62 - 30
include/php/pages/admin/deleteredirect.php

@@ -7,12 +7,55 @@ if(!isset($_GET['id'])){
 
 
 $id = $db->escape_string($_GET['id']);
 $id = $db->escape_string($_GET['id']);
 
 
+if(defined('DBC_ALIASES_MULTI_SOURCE')){
+	$sql = "SELECT r.* FROM (
+		SELECT
+			group_concat(g.`".DBC_ALIASES_ID."` ORDER BY g.`".DBC_ALIASES_ID."` SEPARATOR ',') AS `".DBC_ALIASES_ID."`,
+			group_concat(g.`".DBC_ALIASES_SOURCE."` SEPARATOR ',') AS `".DBC_ALIASES_SOURCE."`,
+			g.`".DBC_ALIASES_DESTINATION."`,
+			g.`".DBC_ALIASES_MULTI_SOURCE."`
+		FROM `".DBT_ALIASES."` AS g
+		WHERE g.`".DBC_ALIASES_MULTI_SOURCE."` IS NOT NULL
+		GROUP BY g.`".DBC_ALIASES_MULTI_SOURCE."`
+	UNION
+		SELECT
+			s.`".DBC_ALIASES_ID."`,
+			s.`".DBC_ALIASES_SOURCE."`,
+			s.`".DBC_ALIASES_DESTINATION."`,
+			s.`".DBC_ALIASES_MULTI_SOURCE."`
+		FROM `".DBT_ALIASES."` AS s
+		WHERE s.`".DBC_ALIASES_MULTI_SOURCE."` IS NULL
+	) AS r
+	WHERE `".DBC_ALIASES_ID."` = '$id' LIMIT 1;";
+}
+else{
+	$sql = "SELECT `".DBC_ALIASES_ID."`, `".DBC_ALIASES_SOURCE."`, `".DBC_ALIASES_DESTINATION."` FROM `".DBT_ALIASES."` WHERE `".DBC_ALIASES_ID."` = '$id' LIMIT 1;";
+}
+
+if(!$result = $db->query($sql)){
+	dbError($db->error);
+}
+
+if($result->num_rows !== 1){
+	// Redirect does not exist, redirect to overview
+	redirect("admin/listredirects/");
+}
+
+$redirect = $result->fetch_assoc();
+
 if(isset($_POST['confirm'])){
 if(isset($_POST['confirm'])){
 	$confirm = $_POST['confirm'];
 	$confirm = $_POST['confirm'];
-	
+
 	if($confirm === "yes"){
 	if($confirm === "yes"){
-		$sql = "DELETE FROM `".DBT_ALIASES."` WHERE `".DBC_ALIASES_ID."` = '$id'";
-			
+
+		$key = DBC_ALIASES_ID;
+		if(defined('DBC_ALIASES_MULTI_SOURCE') && !empty($redirect[DBC_ALIASES_MULTI_SOURCE])){
+			$key = DBC_ALIASES_MULTI_SOURCE;
+		}
+		$value = $redirect[$key];
+
+		$sql = "DELETE FROM `".DBT_ALIASES."` WHERE `$key` = '$value'";
+
 		if(!$result = $db->query($sql)){
 		if(!$result = $db->query($sql)){
 			dbError($db->error);
 			dbError($db->error);
 		}
 		}
@@ -28,41 +71,30 @@ if(isset($_POST['confirm'])){
 }
 }
 
 
 else{
 else{
-	//Load user data from DB
-	$sql = "SELECT `".DBC_ALIASES_SOURCE."`, `".DBC_ALIASES_DESTINATION."` FROM `".DBT_ALIASES."` WHERE `".DBC_ALIASES_ID."` = '$id' LIMIT 1;";
-	
-	if(!$result = $db->query($sql)){
-		dbError($db->error);
-	}
-
-	if($result->num_rows !== 1){
-		// Redirect does not exist, redirect to overview
-		redirect("admin/listredirects/");
-	}
+	$source = $redirect[DBC_ALIASES_SOURCE];
+	$destination = $redirect[DBC_ALIASES_DESTINATION];
+	?>
+	<h1>Delete redirection?</h1>
 
 
-	$row = $result->fetch_assoc();
-
-	$source = $row[DBC_ALIASES_SOURCE];
-	$destination = $row[DBC_ALIASES_DESTINATION];
-}
-?>
-
-<h1>Delete redirection?</h1>
-
-<p>
 	<table>
 	<table>
-	<tr> <th>From</th> <th>To</th> </tr>
-	<tr> <td><?php echo $source; ?></td> <td><?php echo $destination; ?></td> </tr>
+		<tr>
+			<th>Source</th>
+			<th>Destination</th>
+		</tr>
+		<tr>
+			<td><?php echo strip_tags(formatEmails($source, FRONTEND_EMAIL_SEPARATOR_TEXT)); ?></td>
+			<td><?php echo strip_tags(formatEmails($destination, FRONTEND_EMAIL_SEPARATOR_TEXT)); ?></td>
+		</tr>
 	</table>
 	</table>
-</p>
 
 
-<p>
 	<form action="" method="post">
 	<form action="" method="post">
 		<select name="confirm">
 		<select name="confirm">
 			<option value="no">No!</option>
 			<option value="no">No!</option>
 			<option value="yes">Yes!</option>
 			<option value="yes">Yes!</option>
 		</select>
 		</select>
-		
+
 		<input type="submit" class="button button-small" value="Okay"/>
 		<input type="submit" class="button button-small" value="Okay"/>
 	</form>
 	</form>
-</p>
+	<?php
+}
+?>

+ 221 - 89
include/php/pages/admin/editredirect.php

@@ -1,63 +1,211 @@
-<?php 
-	if(isset($_POST['savemode'])){
-		$savemode = $_POST['savemode'];
-		
-		if($savemode === "edit"){
-
-			if(!isset($_POST['id'])){
-				// Redirect id not set, redirect to overview
-				redirect("admin/listredirects/");
+<?php
+
+$id = null;
+$redirect = null;
+
+if(isset($_GET['id'])){
+	$id = $db->escape_string($_GET['id']);
+
+	if(defined('DBC_ALIASES_MULTI_SOURCE')){
+		$sql = "SELECT r.* FROM (
+			SELECT
+				group_concat(g.`".DBC_ALIASES_ID."` ORDER BY g.`".DBC_ALIASES_ID."` SEPARATOR ',') AS `".DBC_ALIASES_ID."`,
+				group_concat(g.`".DBC_ALIASES_SOURCE."` SEPARATOR ',') AS `".DBC_ALIASES_SOURCE."`,
+				g.`".DBC_ALIASES_DESTINATION."`,
+				g.`".DBC_ALIASES_MULTI_SOURCE."`
+			FROM `".DBT_ALIASES."` AS g
+			WHERE g.`".DBC_ALIASES_MULTI_SOURCE."` IS NOT NULL
+			GROUP BY g.`".DBC_ALIASES_MULTI_SOURCE."`
+		UNION
+			SELECT
+				s.`".DBC_ALIASES_ID."`,
+				s.`".DBC_ALIASES_SOURCE."`,
+				s.`".DBC_ALIASES_DESTINATION."`,
+				s.`".DBC_ALIASES_MULTI_SOURCE."`
+			FROM `".DBT_ALIASES."` AS s
+			WHERE s.`".DBC_ALIASES_MULTI_SOURCE."` IS NULL
+		) AS r
+		WHERE `".DBC_ALIASES_ID."` = '$id' LIMIT 1;";
+	}
+	else{
+		$sql = "SELECT `".DBC_ALIASES_ID."`, `".DBC_ALIASES_SOURCE."`, `".DBC_ALIASES_DESTINATION."` FROM `".DBT_ALIASES."` WHERE `".DBC_ALIASES_ID."` = '$id' LIMIT 1;";
+	}
+
+	if(!$result = $db->query($sql)){
+		dbError($db->error);
+	}
+
+	if($result->num_rows !== 1){
+		// Redirect does not exist, redirect to overview
+		redirect("admin/listredirects/");
+	}
+
+	$redirect = $result->fetch_assoc();
+
+	$sources = stringToEmails($redirect[DBC_ALIASES_SOURCE]);
+	$destinations = stringToEmails($redirect[DBC_ALIASES_DESTINATION]);
+}
+
+if(isset($_POST['savemode'])){
+	$savemode = $_POST['savemode'];
+
+	$sources = stringToEmails($_POST['source']);
+	$destinations = stringToEmails($_POST['destination']);
+
+	// validate emails
+	$emailErrors = array();
+
+	// basic email validation is not working 100% correct though
+	foreach(array_merge($sources, $destinations) as $email){
+		if(!filter_var($email, FILTER_VALIDATE_EMAIL)){
+			$emailErrors[$email] = "Address \"$email\" is not a valid email address.";
+		}
+	}
+
+	if(defined('VALIDATE_ALIASES_SOURCE_ENABLED')){
+		$sql = "SELECT GROUP_CONCAT(`".DBC_DOMAINS_DOMAIN."` SEPARATOR ',') as `".DBC_DOMAINS_DOMAIN."` FROM `".DBT_DOMAINS."`";
+		if(!$resultDomains = $db->query($sql)){
+			dbError($db->error);
+		}
+		$domainRow = $resultDomains->fetch_assoc();
+		$domains = explode(',', $domainRow[DBC_DOMAINS_DOMAIN]);
+
+		// validate source emails are on domains
+		foreach($sources as $email){
+			if(isset($emailErrors[$email])){
+				continue;
 			}
 			}
+			$splited = explode('@', $email);
+			if(count($splited) !== 2 || !in_array($splited[1], $domains)){
+				$emailErrors[$email] = "Domain of source address \"$email\" not in domains.";
+			}
+		}
+	}
+
+	if(count($emailErrors) > 0){
+		add_message("fail", implode("<br>", $emailErrors));
+	}
+	else{
+		if(count($emailErrors) === 0 && $savemode === "edit" && !is_null($redirect)){
+
+			if(count($sources) > 0 && count($destinations) > 0){
+				$destination = $db->escape_string(emailsToString($destinations));
+				$source = $db->escape_string(emailsToString($sources));
+
+				$key = DBC_ALIASES_ID;
+				if(defined('DBC_ALIASES_MULTI_SOURCE') && !empty($redirect[DBC_ALIASES_MULTI_SOURCE])){
+					$key = DBC_ALIASES_MULTI_SOURCE;
+				}
+				$value = $redirect[$key];
 
 
-			$id = $db->escape_string($_POST['id']);
-			
-			$source = $db->escape_string($_POST['source']);
-			$source = strtolower($source);
-			$destination = $db->escape_string($_POST['destination']);
-			$destination = strtolower($destination);
-			
-			if($source !== "" && $destination !== ""){
-
-				$sql = "SELECT `".DBC_ALIASES_ID."` FROM `".DBT_ALIASES."` WHERE `".DBC_ALIASES_ID."` = '$id' LIMIT 1;";
-				if(!$resultExists = $db->query($sql)){
+				$sql = "SELECT `".DBC_ALIASES_ID."`, `".DBC_ALIASES_SOURCE."` FROM `".DBT_ALIASES."` WHERE `$key` = '$value'";
+				if(!$resultExisting = $db->query($sql)){
 					dbError($db->error);
 					dbError($db->error);
 				}
 				}
 
 
-				if($resultExists->num_rows !== 1){
-					// Redirect does not exist, redirect to overview
-					redirect("admin/listredirects/");
+				$sourceIdMap = array();
+				while($existingRedirect = $resultExisting->fetch_assoc()){
+					$sourceIdMap[$existingRedirect[DBC_ALIASES_SOURCE]] = $existingRedirect[DBC_ALIASES_ID];
 				}
 				}
 
 
-				$sql = "UPDATE `".DBT_ALIASES."` SET `".DBC_ALIASES_SOURCE."` = '$source', `".DBC_ALIASES_DESTINATION."` = '$destination' WHERE `".DBC_ALIASES_ID."` = '$id'";
-				
-				if(!$result = $db->query($sql)){
-					dbError($db->error);
+				// multi source handling
+				$hash = (count($sources) === 1) ? "NULL" : "'".md5($source)."'";
+
+				foreach($sources as $sourceAddress){
+					$sourceAddress = $db->escape_string(formatEmail($sourceAddress));
+
+					if(isset($sourceIdMap[$sourceAddress])){
+						// edit existing source
+						$id = $sourceIdMap[$sourceAddress];
+
+						$additionalSql = defined('DBC_ALIASES_MULTI_SOURCE') ? ", `".DBC_ALIASES_MULTI_SOURCE."` = $hash " : "";
+						$sql = "UPDATE `".DBT_ALIASES."` SET `".DBC_ALIASES_SOURCE."` = '$sourceAddress', `".DBC_ALIASES_DESTINATION."` = '$destination' $additionalSql WHERE `".DBC_ALIASES_ID."` = '$id';";
+
+						if(!$result = $db->query($sql)){
+							dbError($db->error);
+						}
+
+						unset($sourceIdMap[$sourceAddress]); // mark updated
+					}
+					else{
+						// add new source
+						$additionalSql = defined('DBC_ALIASES_MULTI_SOURCE') ? ", `".DBC_ALIASES_MULTI_SOURCE."`" : "";
+						$additionalSqlValue = defined('DBC_ALIASES_MULTI_SOURCE') ? ", $hash" : "";
+						$sql = "INSERT INTO `".DBT_ALIASES."` (`".DBC_ALIASES_SOURCE."`, `".DBC_ALIASES_DESTINATION."` $additionalSql) VALUES ('$sourceAddress', '$destination' $additionalSqlValue);";
+
+						if(!$result = $db->query($sql)){
+							dbError($db->error);
+						}
+					}
 				}
 				}
-				else{
-					// Edit successfull, redirect to overview
-					redirect("admin/listredirects/?edited=1");
+
+				// delete none updated redirect
+				foreach($sourceIdMap as $source => $id){
+					$sql = "DELETE FROM `".DBT_ALIASES."` WHERE `".DBC_ALIASES_ID."` = '$id';";
+
+					if(!$result = $db->query($sql)){
+						dbError($db->error);
+					}
 				}
 				}
+
+				// Edit successfull, redirect to overview
+				redirect("admin/listredirects/?edited=1");
 			}
 			}
 			else{
 			else{
 				add_message("fail", "Redirect could not be edited. Fill out all fields.");
 				add_message("fail", "Redirect could not be edited. Fill out all fields.");
 			}
 			}
 		}
 		}
-		
-		else if($savemode === "create"){
-			$source = $db->escape_string($_POST['source']);
-			$source = strtolower($source);
-			$destination = $db->escape_string($_POST['destination']);
-			$destination = strtolower($destination);
-			
-			if($source !== "" && $destination !== ""){
-				$sql = "INSERT INTO `".DBT_ALIASES."` (`".DBC_ALIASES_SOURCE."`, `".DBC_ALIASES_DESTINATION."`) VALUES ('$source', '$destination')";
-
-				if(!$result = $db->query($sql)){
+
+		else if(count($emailErrors) === 0 && $savemode === "create"){
+			if(count($sources) > 0 && count($destinations) > 0){
+
+				$values = array();
+				foreach($sources as $source){
+					$values[] = "'$source'";
+				}
+				$sql = "SELECT `".DBC_ALIASES_SOURCE."` FROM `".DBT_ALIASES."` WHERE `".DBC_ALIASES_SOURCE."` IN (".implode(',', $values).");";
+				if(!$resultExisting = $db->query($sql)){
 					dbError($db->error);
 					dbError($db->error);
 				}
 				}
+
+				$errorExisting = array();
+				while($existingRedirect = $resultExisting->fetch_assoc()){
+					$email = $existingRedirect[DBC_ALIASES_SOURCE];
+					$errorExisting[] = "Source address \"$email\" is already redirected to some destination.";
+				}
+
+				if(count($errorExisting) > 0){
+					add_message("fail", implode("<br>", $errorExisting));
+				}
 				else{
 				else{
-					// Redirect created, redirect to overview
-					redirect("admin/listredirects/?created=1");
+					$destination = $db->escape_string(emailsToString($destinations));
+					$source = $db->escape_string(emailsToString($sources));
+
+					$values = array();
+					if(count($sources) === 1){
+						$values[] = "('$source', '$destination', NULL)";
+					}
+					else{
+						// multi source handling
+						$hash = md5($source);
+
+						foreach($sources as $sourceAddress){
+							$sourceAddress = $db->escape_string(formatEmail($sourceAddress));
+							$additionalSqlValue = defined('DBC_ALIASES_MULTI_SOURCE') ? ", '$hash'" : "";
+							$values[] = "('$sourceAddress', '$destination' $additionalSqlValue)";
+						}
+					}
+
+					$additionalSql = defined('DBC_ALIASES_MULTI_SOURCE') ? ", `".DBC_ALIASES_MULTI_SOURCE."`" : "";
+					$sql = "INSERT INTO `".DBT_ALIASES."` (`".DBC_ALIASES_SOURCE."`, `".DBC_ALIASES_DESTINATION."` $additionalSql) VALUES ".implode(',', $values).";";
+
+					if(!$result = $db->query($sql)){
+						dbError($db->error);
+					}
+					else{
+						// Redirect created, redirect to overview
+						redirect("admin/listredirects/?created=1");
+					}
 				}
 				}
 			}
 			}
 			else{
 			else{
@@ -65,66 +213,50 @@
 			}
 			}
 		}
 		}
 	}
 	}
-	
-	
-	// Select mode 
-	$mode = "create";	
-	if(isset($_GET['id'])){
-		$mode = "edit";
-		$id = $db->escape_string($_GET['id']);
-	}
-	
-	if($mode === "edit"){
-		//Load user data from DB
-		$sql = "SELECT `".DBC_ALIASES_SOURCE."`, `".DBC_ALIASES_DESTINATION."` FROM `".DBT_ALIASES."` WHERE `".DBC_ALIASES_ID."` = '$id' LIMIT 1;";
-		
-		if(!$result = $db->query($sql)){
-			dbError($db->error);
-		}
-
-		if($result->num_rows !== 1){
-			// Redirect does not exist, redirect to overview
-			redirect("admin/listredirects/");
-		}
+}
 
 
-		$row = $result->fetch_assoc();
 
 
-		$source = $row[DBC_ALIASES_SOURCE];
-		$destination = $row[DBC_ALIASES_DESTINATION];
-	}
+// Select mode
+$mode = "create";
+if(isset($_GET['id'])){
+	$mode = "edit";
+}
 ?>
 ?>
 
 
-<h1><?php if($mode === "create") { ?> Create <?php } else {?>Edit <?php } ?>Redirect</h1>
+<h1><?php echo ($mode === "create") ? 'Create' : 'Edit'; ?> Redirect</h1>
 
 
 <?php output_messages(); ?>
 <?php output_messages(); ?>
 
 
-<p>
-Here you can edit a redirect.
-</p>
-
 <p>
 <p>
 	<a class="button button-small" href="<?php echo FRONTEND_BASE_PATH; ?>admin/listredirects/">&#10092; Back to redirects list</a>
 	<a class="button button-small" href="<?php echo FRONTEND_BASE_PATH; ?>admin/listredirects/">&#10092; Back to redirects list</a>
 </p>
 </p>
 
 
 <form action="" method="post">
 <form action="" method="post">
+	<input name="savemode" type="hidden" value="<?php echo isset($mode) ? $mode : ''; ?>"/>
+
+	<p>
+		Enter single or multiple addresses separated by comma, semicolon or newline.
+	</p>
+
 	<table>
 	<table>
-	<tr> <th>Source</th> <th>Destination</th> </tr>
-	
-	<tr>
-		<td>
-			<input type="text" name="source" class="textinput" placeholder="Source (single address)" required="required" value="<?php if(isset($source)){echo strip_tags($source);}?>" autofocus/>
-		</td>
-		
-		<td>
-			<textarea name="destination" class="textinput" placeholder="Destination (multiple addresses separated by comma possible)" required="required"><?php if(isset($destination)){echo strip_tags($destination);} ?></textarea>
-		</td>
-	</tr>
-	
+		<tr>
+			<th>Source</th>
+			<th>Destination</th>
+		</tr>
+		<tr>
+			<td>
+				<?php if(defined('DBC_ALIASES_MULTI_SOURCE')): ?>
+					<textarea name="source" class="textinput" placeholder="Source" required="required" autofocus><?php echo isset($sources) ? strip_tags(emailsToString($sources, FRONTEND_EMAIL_SEPARATOR_FORM)) : ''; ?></textarea>
+				<?php else: ?>
+					<input type="text" name="source" class="textinput" placeholder="Source (single address)" required="required" autofocus value="<?php echo isset($sources) ? strip_tags(emailsToString($sources, FRONTEND_EMAIL_SEPARATOR_FORM)) : ''; ?>"/>
+				<?php endif; ?>
+			</td>
+			<td>
+				<textarea name="destination" class="textinput" placeholder="Destination" required="required"><?php echo isset($destinations) ? strip_tags(emailsToString($destinations, FRONTEND_EMAIL_SEPARATOR_FORM)) : ''; ?></textarea>
+			</td>
+		</tr>
 	</table>
 	</table>
-	
-	<input name="savemode" type="hidden" value="<?php if(isset($mode)){echo $mode;} ?>"/>
-	<input name="id" type="hidden" value="<?php if(isset($id)){echo $id;} ?>"/>
-	
+
 	<p>
 	<p>
 		<input type="submit" class="button button-small" value="Save settings">
 		<input type="submit" class="button button-small" value="Save settings">
 	</p>
 	</p>

+ 6 - 7
include/php/pages/admin/edituser.php

@@ -176,13 +176,12 @@
 	<a class="button button-small" href="<?php echo FRONTEND_BASE_PATH; ?>admin/listusers/">&#10092; Back to user list</a>
 	<a class="button button-small" href="<?php echo FRONTEND_BASE_PATH; ?>admin/listusers/">&#10092; Back to user list</a>
 </p>
 </p>
 
 
-<p>
-<?php 
-	if($mode === "edit"){
-		echo "Username and domain cannot be edited.";
-	}
-?>
-</p>
+<?php if($mode === "edit"): ?>
+	<p>
+		<strong>Username and domain cannot be edited.</strong><br>
+		To rename or move a mailbox, you have to move in the filesystem first and create a new user here after.
+	</p>
+<?php endif; ?>
 
 
 <form action="" method="post">	
 <form action="" method="post">	
 	<table>
 	<table>

+ 42 - 9
include/php/pages/admin/listredirects.php

@@ -10,8 +10,30 @@ else if(isset($_GET['edited']) && $_GET['edited'] == "1"){
 	add_message("success", "Redirect edited successfully.");
 	add_message("success", "Redirect edited successfully.");
 }
 }
 
 
-
-$sql = "SELECT * FROM `".DBT_ALIASES."` ORDER BY `".DBC_ALIASES_SOURCE."` ASC;";
+if(defined('DBC_ALIASES_MULTI_SOURCE')){
+	$sql = "SELECT r.* FROM (
+		SELECT
+			group_concat(g.`".DBC_ALIASES_ID."` ORDER BY g.`".DBC_ALIASES_ID."` SEPARATOR ',') AS `".DBC_ALIASES_ID."`,
+			group_concat(g.`".DBC_ALIASES_SOURCE."` SEPARATOR ',') AS `".DBC_ALIASES_SOURCE."`,
+			g.`".DBC_ALIASES_DESTINATION."`,
+			g.`".DBC_ALIASES_MULTI_SOURCE."`
+		FROM `".DBT_ALIASES."` AS g
+		WHERE g.`".DBC_ALIASES_MULTI_SOURCE."` IS NOT NULL
+		GROUP BY g.`".DBC_ALIASES_MULTI_SOURCE."`
+	UNION
+		SELECT
+			s.`".DBC_ALIASES_ID."`,
+			s.`".DBC_ALIASES_SOURCE."`,
+			s.`".DBC_ALIASES_DESTINATION."`,
+			s.`".DBC_ALIASES_MULTI_SOURCE."`
+		FROM `".DBT_ALIASES."` AS s
+		WHERE s.`".DBC_ALIASES_MULTI_SOURCE."` IS NULL
+	) AS r
+	ORDER BY `".DBC_ALIASES_SOURCE."` ASC";
+}
+else{
+	$sql = "SELECT `".DBC_ALIASES_ID."`, `".DBC_ALIASES_SOURCE."`, `".DBC_ALIASES_DESTINATION."` FROM `".DBT_ALIASES."` ORDER BY `".DBC_ALIASES_SOURCE."` ASC;";
+}
 
 
 if(!$result = $db->query($sql)){
 if(!$result = $db->query($sql)){
 	dbError($db->error);
 	dbError($db->error);
@@ -28,11 +50,22 @@ if(!$result = $db->query($sql)){
 </p>
 </p>
 
 
 <table class="list">
 <table class="list">
-<tr class="head"><th>Source</th> <th>Destination</th> <th></th><th></th><tr>
-
-<?php 
-	while($row = $result->fetch_assoc()){
-		echo "<tr><td>".strip_tags($row[DBC_ALIASES_SOURCE])."</td> <td>".strip_tags($row[DBC_ALIASES_DESTINATION])."</td> <td><a href=\"".FRONTEND_BASE_PATH."admin/editredirect/?id=".$row[DBC_ALIASES_ID]."\">[Edit]</a></td> <td><a href=\"".FRONTEND_BASE_PATH."admin/deleteredirect/?id=".$row[DBC_ALIASES_ID]."\">[Delete]</a></td></tr>";
-	}
-?>
+	<tr class="head">
+		<th>Source</th>
+		<th>Destination</th>
+		<th></th>
+		<th></th>
+	<tr>
+<?php while($row = $result->fetch_assoc()): ?>
+	<tr>
+		<td><?php echo strip_tags(formatEmails($row[DBC_ALIASES_SOURCE], FRONTEND_EMAIL_SEPARATOR_TEXT)); ?></td>
+		<td><?php echo strip_tags(formatEmails($row[DBC_ALIASES_DESTINATION], FRONTEND_EMAIL_SEPARATOR_TEXT)); ?></td>
+		<td>
+			<a href="<?php echo FRONTEND_BASE_PATH; ?>admin/editredirect/?id=<?php echo $row[DBC_ALIASES_ID]; ?>">[Edit]</a>
+		</td>
+		<td>
+			<a href="<?php echo FRONTEND_BASE_PATH; ?>admin/deleteredirect/?id=<?php echo $row[DBC_ALIASES_ID]; ?>">[Delete]</a>
+		</td>
+	</tr>
+<?php endwhile; ?>
 </table>
 </table>