2018-12-20 18:23:35 +08:00
< ? php
function relayhost ( $_action , $_data = null ) {
global $pdo ;
global $lang ;
$_data_log = $_data ;
switch ( $_action ) {
case 'add' :
if ( $_SESSION [ 'mailcow_cc_role' ] != " admin " ) {
$_SESSION [ 'return' ][] = array (
'type' => 'danger' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => 'access_denied'
);
return false ;
}
$hostname = trim ( $_data [ 'hostname' ]);
$username = str_replace ( ':' , '\:' , trim ( $_data [ 'username' ]));
$password = str_replace ( ':' , '\:' , trim ( $_data [ 'password' ]));
if ( empty ( $hostname )) {
$_SESSION [ 'return' ][] = array (
'type' => 'danger' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => array ( 'invalid_host' , htmlspecialchars ( $host ))
);
return false ;
}
try {
$stmt = $pdo -> prepare ( " INSERT INTO `relayhosts` (`hostname`, `username` ,`password`, `active`)
VALUES ( : hostname , : username , : password , : active ) " );
$stmt -> execute ( array (
':hostname' => $hostname ,
':username' => $username ,
':password' => str_replace ( ':' , '\:' , $password ),
':active' => '1'
));
}
catch ( PDOException $e ) {
$_SESSION [ 'return' ][] = array (
'type' => 'danger' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => array ( 'mysql_error' , $e )
);
return false ;
}
$_SESSION [ 'return' ][] = array (
'type' => 'success' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => array ( 'relayhost_added' , htmlspecialchars ( implode ( ', ' , $hosts )))
);
break ;
case 'edit' :
if ( $_SESSION [ 'mailcow_cc_role' ] != " admin " ) {
$_SESSION [ 'return' ][] = array (
'type' => 'danger' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => 'access_denied'
);
return false ;
}
$ids = ( array ) $_data [ 'id' ];
foreach ( $ids as $id ) {
$is_now = relayhost ( 'details' , $id );
if ( ! empty ( $is_now )) {
$hostname = ( ! empty ( $_data [ 'hostname' ])) ? trim ( $_data [ 'hostname' ]) : $is_now [ 'hostname' ];
$username = ( isset ( $_data [ 'username' ])) ? trim ( $_data [ 'username' ]) : $is_now [ 'username' ];
$password = ( isset ( $_data [ 'password' ])) ? trim ( $_data [ 'password' ]) : $is_now [ 'password' ];
2020-09-18 01:49:15 +08:00
$active = ( isset ( $_data [ 'active' ])) ? intval ( $_data [ 'active' ]) : $is_now [ 'active' ];
2018-12-20 18:23:35 +08:00
}
else {
$_SESSION [ 'return' ][] = array (
'type' => 'danger' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => array ( 'relayhost_invalid' , $id )
);
continue ;
}
try {
$stmt = $pdo -> prepare ( " UPDATE `relayhosts` SET
`hostname` = : hostname ,
`username` = : username ,
`password` = : password ,
`active` = : active
WHERE `id` = : id " );
$stmt -> execute ( array (
':id' => $id ,
':hostname' => $hostname ,
':username' => $username ,
':password' => $password ,
':active' => $active
));
}
catch ( PDOException $e ) {
$_SESSION [ 'return' ][] = array (
'type' => 'danger' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => array ( 'mysql_error' , $e )
);
continue ;
}
$_SESSION [ 'return' ][] = array (
'type' => 'success' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => array ( 'object_modified' , htmlspecialchars ( implode ( ', ' , $hostnames )))
);
}
break ;
case 'delete' :
if ( $_SESSION [ 'mailcow_cc_role' ] != " admin " ) {
$_SESSION [ 'return' ][] = array (
'type' => 'danger' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => 'access_denied'
);
return false ;
}
$ids = ( array ) $_data [ 'id' ];
foreach ( $ids as $id ) {
try {
$stmt = $pdo -> prepare ( " DELETE FROM `relayhosts` WHERE `id`= :id " );
$stmt -> execute ( array ( ':id' => $id ));
$stmt = $pdo -> prepare ( " UPDATE `domain` SET `relayhost` = '0' WHERE `relayhost`= :id " );
$stmt -> execute ( array ( ':id' => $id ));
}
catch ( PDOException $e ) {
$_SESSION [ 'return' ][] = array (
'type' => 'danger' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => array ( 'mysql_error' , $e )
);
continue ;
}
$_SESSION [ 'return' ][] = array (
'type' => 'success' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => array ( 'relayhost_removed' , htmlspecialchars ( $id ))
);
}
break ;
case 'get' :
2021-05-26 20:02:27 +08:00
if ( $_SESSION [ 'mailcow_cc_role' ] != " admin " && $_SESSION [ 'mailcow_cc_role' ] != " domainadmin " ) {
2018-12-20 18:23:35 +08:00
return false ;
}
$relayhosts = array ();
2021-05-26 20:02:27 +08:00
$stmt = $pdo -> query ( " SELECT `id`, `hostname`, `username`, `active` FROM `relayhosts` " );
2018-12-20 18:23:35 +08:00
$relayhosts = $stmt -> fetchAll ( PDO :: FETCH_ASSOC );
return $relayhosts ;
break ;
case 'details' :
if ( $_SESSION [ 'mailcow_cc_role' ] != " admin " || ! isset ( $_data )) {
return false ;
}
$relayhostdata = array ();
$stmt = $pdo -> prepare ( " SELECT `id`,
`hostname` ,
`username` ,
`password` ,
2020-09-18 01:49:15 +08:00
`active` ,
CONCAT ( LEFT ( `password` , 3 ), '...' ) AS `password_short`
2018-12-20 18:23:35 +08:00
FROM `relayhosts`
WHERE `id` = : id " );
$stmt -> execute ( array ( ':id' => $_data ));
$relayhostdata = $stmt -> fetch ( PDO :: FETCH_ASSOC );
if ( ! empty ( $relayhostdata )) {
$stmt = $pdo -> prepare ( " SELECT GROUP_CONCAT(`domain` SEPARATOR ', ') AS `used_by_domains` FROM `domain` WHERE `relayhost` = :id " );
$stmt -> execute ( array ( ':id' => $_data ));
$used_by_domains = $stmt -> fetch ( PDO :: FETCH_ASSOC )[ 'used_by_domains' ];
$used_by_domains = ( empty ( $used_by_domains )) ? '' : $used_by_domains ;
$relayhostdata [ 'used_by_domains' ] = $used_by_domains ;
2021-05-26 20:02:27 +08:00
$stmt = $pdo -> prepare ( " SELECT GROUP_CONCAT(`username` SEPARATOR ', ') AS `used_by_mailboxes` FROM `mailbox` WHERE JSON_VALUE(`attributes`, ' $ .relayhost') = :id " );
$stmt -> execute ( array ( ':id' => $_data ));
$used_by_mailboxes = $stmt -> fetch ( PDO :: FETCH_ASSOC )[ 'used_by_mailboxes' ];
$used_by_mailboxes = ( empty ( $used_by_mailboxes )) ? '' : $used_by_mailboxes ;
$relayhostdata [ 'used_by_mailboxes' ] = $used_by_mailboxes ;
2018-12-20 18:23:35 +08:00
}
return $relayhostdata ;
break ;
}
}
function transport ( $_action , $_data = null ) {
global $pdo ;
global $lang ;
$_data_log = $_data ;
switch ( $_action ) {
case 'add' :
if ( $_SESSION [ 'mailcow_cc_role' ] != " admin " ) {
$_SESSION [ 'return' ][] = array (
'type' => 'danger' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => 'access_denied'
);
return false ;
}
2019-08-31 00:54:38 +08:00
$destinations = array_map ( 'trim' , preg_split ( " /( |,|;| \n )/ " , $_data [ 'destination' ]));
2019-08-25 22:02:58 +08:00
$active = intval ( $_data [ 'active' ]);
2021-05-28 16:40:41 +08:00
$is_mx_based = intval ( $_data [ 'is_mx_based' ]);
2018-12-20 18:23:35 +08:00
$nexthop = trim ( $_data [ 'nexthop' ]);
2020-01-17 04:11:46 +08:00
if ( filter_var ( $nexthop , FILTER_VALIDATE_IP )) {
$nexthop = '[' . $nexthop . ']' ;
}
2018-12-21 19:48:30 +08:00
preg_match ( '/\[(.+)\].*/' , $nexthop , $next_hop_matches );
$next_hop_clean = ( isset ( $next_hop_matches [ 1 ])) ? $next_hop_matches [ 1 ] : $nexthop ;
2018-12-20 18:23:35 +08:00
$username = str_replace ( ':' , '\:' , trim ( $_data [ 'username' ]));
$password = str_replace ( ':' , '\:' , trim ( $_data [ 'password' ]));
if ( empty ( $nexthop )) {
$_SESSION [ 'return' ][] = array (
'type' => 'danger' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => array ( 'invalid_nexthop' )
);
return false ;
}
2018-12-21 19:48:30 +08:00
$transports = transport ( 'get' );
if ( ! empty ( $transports )) {
foreach ( $transports as $transport ) {
$transport_data = transport ( 'details' , $transport [ 'id' ]);
$existing_nh [] = $transport_data [ 'nexthop' ];
preg_match ( '/\[(.+)\].*/' , $transport_data [ 'nexthop' ], $existing_clean_nh []);
if (( $transport_data [ 'nexthop' ] == $nexthop || $transport_data [ 'nexthop' ] == $next_hop_clean ) && $transport_data [ 'username' ] != $username ) {
$_SESSION [ 'return' ][] = array (
'type' => 'danger' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => 'invalid_nexthop_authenticated'
);
return false ;
}
2019-08-31 00:54:38 +08:00
foreach ( $destinations as $d_ix => & $dest ) {
if ( empty ( $dest )) {
unset ( $destinations [ $d_ix ]);
continue ;
}
if ( $transport_data [ 'destination' ] == $dest ) {
$_SESSION [ 'return' ][] = array (
'type' => 'danger' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => array ( 'transport_dest_exists' , $dest )
);
unset ( $destinations [ $d_ix ]);
continue ;
}
// ".domain" is a valid destination, "..domain" is not
2021-05-28 16:40:41 +08:00
if ( $is_mx_based == 0 && ( empty ( $dest ) || ( is_valid_domain_name ( preg_replace ( '/^' . preg_quote ( '.' , '/' ) . '/' , '' , $dest )) === false && $dest != '*' && filter_var ( $dest , FILTER_VALIDATE_EMAIL ) === false ))) {
$_SESSION [ 'return' ][] = array (
'type' => 'danger' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => array ( 'invalid_destination' , $dest )
);
unset ( $destinations [ $d_ix ]);
continue ;
}
if ( $is_mx_based == 1 && ( empty ( $dest ) || @ preg_match ( '/' . $dest . '/' , null ) === false )) {
2019-08-31 00:54:38 +08:00
$_SESSION [ 'return' ][] = array (
'type' => 'danger' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => array ( 'invalid_destination' , $dest )
);
unset ( $destinations [ $d_ix ]);
continue ;
}
2019-04-26 00:10:28 +08:00
}
2018-12-21 19:48:30 +08:00
}
}
2020-09-21 04:21:00 +08:00
$destinations = array_filter ( array_values ( array_unique ( $destinations )));
2019-08-31 00:54:38 +08:00
if ( empty ( $destinations )) { return false ; }
2018-12-23 21:16:55 +08:00
if ( isset ( $next_hop_matches [ 1 ])) {
if ( in_array ( $next_hop_clean , $existing_nh )) {
$_SESSION [ 'return' ][] = array (
'type' => 'danger' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => array ( 'next_hop_interferes' , $next_hop_clean , $nexthop )
);
return false ;
}
}
else {
2018-12-21 19:48:30 +08:00
foreach ( $existing_clean_nh as $existing_clean_nh_each ) {
if ( $existing_clean_nh_each [ 1 ] == $nexthop ) {
$_SESSION [ 'return' ][] = array (
'type' => 'danger' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => array ( 'next_hop_interferes_any' , $nexthop )
);
return false ;
2018-12-20 18:23:35 +08:00
}
}
}
2019-08-31 00:54:38 +08:00
foreach ( $destinations as $insert_dest ) {
2021-05-28 16:40:41 +08:00
$stmt = $pdo -> prepare ( " INSERT INTO `transports` (`nexthop`, `destination`, `is_mx_based`, `username` , `password`, `active`)
VALUES ( : nexthop , : destination , : is_mx_based , : username , : password , : active ) " );
2018-12-20 18:23:35 +08:00
$stmt -> execute ( array (
':nexthop' => $nexthop ,
2019-08-31 00:54:38 +08:00
':destination' => $insert_dest ,
2021-05-28 16:40:41 +08:00
':is_mx_based' => $is_mx_based ,
2018-12-20 18:23:35 +08:00
':username' => $username ,
':password' => str_replace ( ':' , '\:' , $password ),
2019-08-25 22:02:58 +08:00
':active' => $active
2018-12-20 18:23:35 +08:00
));
}
2019-08-31 00:54:38 +08:00
$stmt = $pdo -> prepare ( " UPDATE `transports` SET
`username` = : username ,
`password` = : password
WHERE `nexthop` = : nexthop " );
$stmt -> execute ( array (
':nexthop' => $nexthop ,
':username' => $username ,
':password' => $password
));
2018-12-20 18:23:35 +08:00
$_SESSION [ 'return' ][] = array (
'type' => 'success' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => array ( 'relayhost_added' , htmlspecialchars ( implode ( ', ' , $hosts )))
);
break ;
case 'edit' :
if ( $_SESSION [ 'mailcow_cc_role' ] != " admin " ) {
$_SESSION [ 'return' ][] = array (
'type' => 'danger' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => 'access_denied'
);
return false ;
}
$ids = ( array ) $_data [ 'id' ];
foreach ( $ids as $id ) {
$is_now = transport ( 'details' , $id );
if ( ! empty ( $is_now )) {
$destination = ( ! empty ( $_data [ 'destination' ])) ? trim ( $_data [ 'destination' ]) : $is_now [ 'destination' ];
$nexthop = ( ! empty ( $_data [ 'nexthop' ])) ? trim ( $_data [ 'nexthop' ]) : $is_now [ 'nexthop' ];
$username = ( isset ( $_data [ 'username' ])) ? trim ( $_data [ 'username' ]) : $is_now [ 'username' ];
$password = ( isset ( $_data [ 'password' ])) ? trim ( $_data [ 'password' ]) : $is_now [ 'password' ];
2021-05-28 16:40:41 +08:00
$is_mx_based = ( isset ( $_data [ 'is_mx_based' ]) && $_data [ 'is_mx_based' ] != '' ) ? intval ( $_data [ 'is_mx_based' ]) : $is_now [ 'is_mx_based' ];
2020-09-18 01:49:15 +08:00
$active = ( isset ( $_data [ 'active' ]) && $_data [ 'active' ] != '' ) ? intval ( $_data [ 'active' ]) : $is_now [ 'active' ];
2018-12-20 18:23:35 +08:00
}
else {
$_SESSION [ 'return' ][] = array (
'type' => 'danger' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => array ( 'relayhost_invalid' , $id )
);
continue ;
}
2018-12-21 19:48:30 +08:00
preg_match ( '/\[(.+)\].*/' , $nexthop , $next_hop_matches );
2020-01-17 04:11:46 +08:00
if ( filter_var ( $nexthop , FILTER_VALIDATE_IP )) {
$nexthop = '[' . $nexthop . ']' ;
}
2018-12-21 19:48:30 +08:00
$next_hop_clean = ( isset ( $next_hop_matches [ 1 ])) ? $next_hop_matches [ 1 ] : $nexthop ;
$transports = transport ( 'get' );
if ( ! empty ( $transports )) {
foreach ( $transports as $transport ) {
$transport_data = transport ( 'details' , $transport [ 'id' ]);
if ( $transport [ 'id' ] == $id ) {
continue ;
}
$existing_nh [] = $transport_data [ 'nexthop' ];
preg_match ( '/\[(.+)\].*/' , $transport_data [ 'nexthop' ], $existing_clean_nh []);
2019-04-26 00:10:28 +08:00
if ( $transport_data [ 'destination' ] == $destination ) {
$_SESSION [ 'return' ][] = array (
'type' => 'danger' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => 'transport_dest_exists'
);
return false ;
}
2018-12-21 19:48:30 +08:00
}
}
2021-05-28 16:40:41 +08:00
if ( $is_mx_based == 0 && ( empty ( $destination ) || ( is_valid_domain_name ( preg_replace ( '/^' . preg_quote ( '.' , '/' ) . '/' , '' , $destination )) === false && $destination != '*' && filter_var ( $destination , FILTER_VALIDATE_EMAIL ) === false ))) {
$_SESSION [ 'return' ][] = array (
'type' => 'danger' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => array ( 'invalid_destination' , $destination )
);
return false ;
}
if ( $is_mx_based == 1 && ( empty ( $destination ) || @ preg_match ( '/' . $destination . '/' , null ) === false )) {
$_SESSION [ 'return' ][] = array (
'type' => 'danger' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => array ( 'invalid_destination' , $destination )
);
return false ;
}
2018-12-23 21:16:55 +08:00
if ( isset ( $next_hop_matches [ 1 ])) {
if ( in_array ( $next_hop_clean , $existing_nh )) {
$_SESSION [ 'return' ][] = array (
'type' => 'danger' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => array ( 'next_hop_interferes' , $next_hop_clean , $nexthop )
);
return false ;
}
}
else {
2018-12-21 19:48:30 +08:00
foreach ( $existing_clean_nh as $existing_clean_nh_each ) {
if ( $existing_clean_nh_each [ 1 ] == $nexthop ) {
$_SESSION [ 'return' ][] = array (
'type' => 'danger' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => array ( 'next_hop_interferes_any' , $nexthop )
);
return false ;
}
}
}
if ( empty ( $username )) {
$password = '' ;
}
2018-12-20 18:23:35 +08:00
try {
$stmt = $pdo -> prepare ( " UPDATE `transports` SET
`destination` = : destination ,
2021-05-28 16:40:41 +08:00
`is_mx_based` = : is_mx_based ,
2018-12-20 18:23:35 +08:00
`nexthop` = : nexthop ,
`username` = : username ,
`password` = : password ,
`active` = : active
WHERE `id` = : id " );
$stmt -> execute ( array (
':id' => $id ,
':destination' => $destination ,
2021-05-28 16:40:41 +08:00
':is_mx_based' => $is_mx_based ,
2018-12-20 18:23:35 +08:00
':nexthop' => $nexthop ,
':username' => $username ,
':password' => $password ,
':active' => $active
));
$stmt = $pdo -> prepare ( " UPDATE `transports` SET
`username` = : username ,
`password` = : password
WHERE `nexthop` = : nexthop " );
$stmt -> execute ( array (
':nexthop' => $nexthop ,
':username' => $username ,
':password' => $password
));
}
catch ( PDOException $e ) {
$_SESSION [ 'return' ][] = array (
'type' => 'danger' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => array ( 'mysql_error' , $e )
);
continue ;
}
$_SESSION [ 'return' ][] = array (
'type' => 'success' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => array ( 'object_modified' , htmlspecialchars ( implode ( ', ' , $hostnames )))
);
}
break ;
case 'delete' :
if ( $_SESSION [ 'mailcow_cc_role' ] != " admin " ) {
$_SESSION [ 'return' ][] = array (
'type' => 'danger' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => 'access_denied'
);
return false ;
}
$ids = ( array ) $_data [ 'id' ];
foreach ( $ids as $id ) {
try {
$stmt = $pdo -> prepare ( " DELETE FROM `transports` WHERE `id`= :id " );
$stmt -> execute ( array ( ':id' => $id ));
}
catch ( PDOException $e ) {
$_SESSION [ 'return' ][] = array (
'type' => 'danger' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => array ( 'mysql_error' , $e )
);
continue ;
}
$_SESSION [ 'return' ][] = array (
'type' => 'success' ,
'log' => array ( __FUNCTION__ , $_action , $_data_log ),
'msg' => array ( 'relayhost_removed' , htmlspecialchars ( $id ))
);
}
break ;
case 'get' :
if ( $_SESSION [ 'mailcow_cc_role' ] != " admin " ) {
return false ;
}
$transports = array ();
2021-05-28 16:40:41 +08:00
$stmt = $pdo -> query ( " SELECT `id`, `is_mx_based`, `destination`, `nexthop`, `username` FROM `transports` " );
2018-12-20 18:23:35 +08:00
$transports = $stmt -> fetchAll ( PDO :: FETCH_ASSOC );
return $transports ;
break ;
case 'details' :
if ( $_SESSION [ 'mailcow_cc_role' ] != " admin " || ! isset ( $_data )) {
return false ;
}
$transportdata = array ();
$stmt = $pdo -> prepare ( " SELECT `id`,
2021-05-28 16:40:41 +08:00
`is_mx_based` ,
2018-12-20 18:23:35 +08:00
`destination` ,
`nexthop` ,
`username` ,
`password` ,
2020-09-18 01:49:15 +08:00
`active` ,
CONCAT ( LEFT ( `password` , 3 ), '...' ) AS `password_short`
2018-12-20 18:23:35 +08:00
FROM `transports`
WHERE `id` = : id " );
$stmt -> execute ( array ( ':id' => $_data ));
$transportdata = $stmt -> fetch ( PDO :: FETCH_ASSOC );
return $transportdata ;
break ;
}
}