enhance domain-importer, fixes #1512

Signed-off-by: Michael Kaufmann (d00p) <d00p@froxlor.org>
This commit is contained in:
Michael Kaufmann (d00p)
2015-03-06 20:28:41 +01:00
parent 5dce0db661
commit c150d559c7

View File

@@ -24,442 +24,523 @@
* @author Michael Kaufmann (d00p) <d00p@froxlor.org> * @author Michael Kaufmann (d00p) <d00p@froxlor.org>
* *
*/ */
class DomainBulkAction { class DomainBulkAction
{
/** /**
* complete path including filename of file to be imported * complete path including filename of file to be imported
* *
* @var string * @var string
*/ */
private $_impFile = null; private $_impFile = null;
/** /**
* customer id of the user the domains are being added to * customer id of the user the domains are being added to
* *
* @var int * @var int
*/ */
private $_custId = null; private $_custId = null;
/** /**
* array of customer data read from the database * array of customer data read from the database
* *
* @var array * @var array
*/ */
private $_custData = null; private $_custData = null;
/** /**
* array of already known domains from the database * array of already known domains from the database
* *
* @var array * @var array
*/ */
private $_knownDomains = null; private $_knownDomains = null;
/** /**
* array of known ip/port combinations * array of known ip/port combinations
* *
* @var array * @var array
*/ */
private $_knownIpPort = null; private $_knownIpPort = null;
/** /**
* array of known IP's to check against * array of known IP's to check against
* *
* @var array * @var array
*/ */
private $_knownIpPortChk = null; private $_knownIpPortChk = null;
/** /**
* array of fields to import to panel_domains * array of fields to import to panel_domains
* *
* @var array * @var array
*/ */
private $_required_fields = array ( private $_required_fields = array (
'domain', /* 1 */ 'domain',
'documentroot', /* 2 */ 'documentroot',
'isbinddomain', /* 3 */ 'aliasdomain',
'isemaildomain', /* 4 */ 'isbinddomain',
'email_only', /* 5 */ 'isemaildomain',
'iswildcarddomain', /* 6 */ 'email_only',
'subcanemaildomain', /* 7 */ 'iswildcarddomain',
'caneditdomain', /* 8 */ 'subcanemaildomain',
'wwwserveralias', /* 9 */ 'caneditdomain',
'specialsettings', /* 10 */ 'zonefile',
'ssl_redirect', /* 11 */ 'wwwserveralias',
'registration_date', /* 12 */ 'openbasedir',
'ips', /* 13 */ 'speciallogfile',
'adminid', /* 14 */ 'specialsettings',
'customerid', /* 15 */ 'ssl_redirect',
'add_date' /* 16 */ 'use_ssl',
); /* 17 */ 'registration_date',
/* 18 */ 'ips',
/* automatically added */
'adminid',
'customerid',
'add_date'
);
/** /**
* prepared statements for each domain * prepared statements for each domain
* *
* @var PDOStatement * @var PDOStatement
*/ */
private $_ins_stmt = null; private $_ins_stmt = null;
private $_ipp_ins_stmt = null;
/** private $_ipp_ins_stmt = null;
* class constructor, optionally sets file and customer-id
*
* @param string $import_file
* @param int $customer_id
*
* @return object DomainBulkAction instance
*/
public function __construct($import_file = null, $customer_id = 0) {
if (!empty($import_file)) { /**
$this->_impFile = makeCorrectFile($import_file); * class constructor, optionally sets file and customer-id
} *
$this->_custId = $customer_id; * @param string $import_file
* @param int $customer_id
*
* @return object DomainBulkAction instance
*/
public function __construct($import_file = null, $customer_id = 0)
{
if (! empty($import_file)) {
$this->_impFile = makeCorrectFile($import_file);
}
$this->_custId = $customer_id;
}
} /**
* import the parsed import file data with an optional separator other then semicolon
* and offset (maybe for header-line in csv or similar)
*
* @param string $separator
* @param int $offset
*
* @return array 'all' => amount of records processed, 'imported' => number of imported records
*/
public function doImport($separator = ";", $offset = 0)
{
/** // get the admins userinfo to check for domains_used, etc.
* import the parsed import file data with an optional separator other then semicolon global $userinfo;
* and offset (maybe for header-line in csv or similar)
*
* @param string $separator
* @param int $offset
*
* @return array 'all' => amount of records processed, 'imported' => number of imported records
*/
public function doImport($separator = ";", $offset = 0) {
// get the admins userinfo to check for domains_used, etc. if ($userinfo['domains'] == "-1") {
global $userinfo; $dom_unlimited = true;
} else {
$dom_unlimited = false;
}
if ($userinfo['domains'] == "-1") { $domains_used = (int) $userinfo['domains_used'];
$dom_unlimited = true; $domains_avail = (int) $userinfo['domains'];
} else {
$dom_unlimited = false;
}
$domains_used = (int)$userinfo['domains_used']; if (empty($separator) || strlen($separator) != 1) {
$domains_avail = (int)$userinfo['domains']; throw new Exception("Invalid separator specified: '" . $separator . "'");
}
if (empty($separator) || strlen($separator) != 1) { if (! is_int($offset) || $offset < 0) {
throw new Exception("Invalid separator specified: '" . $separator . "'"); throw new Exception("Invalid offset specified");
} }
if (! is_int($offset) || $offset < 0) { if ($this->_custId <= 0) {
throw new Exception("Invalid offset specified"); throw new Exception("Invalid customer selected");
} }
if ($this->_custId <= 0) { $this->_readCustomerData();
throw new Exception("Invalid customer selected");
}
$this->_readCustomerData(); if (is_null($this->_custData)) {
throw new Exception("Failed to read customer data");
}
if (is_null($this->_custData)) { $this->_readIpPortData();
throw new Exception("Failed to read customer data"); $this->_readDomainData();
}
$this->_readIpPortData(); try {
$this->_readDomainData(); $domain_array = $this->_parseImportFile($separator);
} catch (Exception $e) {
throw $e;
}
try { if (count($domain_array) <= 0) {
$domain_array = $this->_parseImportFile($separator); throw new Exception("No domains were read from the file.");
} catch (Exception $e) { }
throw $e;
}
if (count($domain_array) <= 0) { // preapre insert statement as it is used a few times
throw new Exception("No domains were read from the file."); $this->_ins_stmt = Database::prepare("
}
// preapre insert statement as it is used a few times
$this->_ins_stmt = Database::prepare("
INSERT INTO `" . TABLE_PANEL_DOMAINS . "` SET INSERT INTO `" . TABLE_PANEL_DOMAINS . "` SET
`domain` = :domain, `domain` = :domain,
`adminid` = :adminid, `adminid` = :adminid,
`customerid` = :customerid, `customerid` = :customerid,
`documentroot` = :documentroot, `documentroot` = :documentroot,
`aliasdomain` = :aliasdomain,
`isbinddomain` = :isbinddomain, `isbinddomain` = :isbinddomain,
`isemaildomain` = :isemaildomain, `isemaildomain` = :isemaildomain,
`email_only` = :email_only, `email_only` = :email_only,
`iswildcarddomain` = :iswildcarddomain, `iswildcarddomain` = :iswildcarddomain,
`subcanemaildomain` = :subcanemaildomain, `subcanemaildomain` = :subcanemaildomain,
`caneditdomain` = :caneditdomain, `caneditdomain` = :caneditdomain,
`zonefile` = :zonefile,
`wwwserveralias` = :wwwserveralias, `wwwserveralias` = :wwwserveralias,
`openbasedir` = :openbasedir,
`speciallogfile` = :speciallogfile,
`specialsettings` = :specialsettings, `specialsettings` = :specialsettings,
`ssl_redirect` = :ssl_redirect, `ssl_redirect` = :ssl_redirect,
`registration_date` = :registration_date, `registration_date` = :registration_date,
`add_date` = :add_date `add_date` = :add_date
"); ");
// prepare insert statement for ip/port <> domain // prepare insert statement for ip/port <> domain
$this->_ipp_ins_stmt = Database::prepare(" $this->_ipp_ins_stmt = Database::prepare("
INSERT INTO `" . TABLE_DOMAINTOIP . "` SET INSERT INTO `" . TABLE_DOMAINTOIP . "` SET
`id_domain` = :domid, `id_domain` = :domid,
`id_ipandports` = :ipid `id_ipandports` = :ipid
"); ");
$global_counter = 0; $global_counter = 0;
$import_counter = 0; $import_counter = 0;
$note = ''; $note = '';
foreach ($domain_array as $idx => $dom) { foreach ($domain_array as $idx => $dom) {
if ($idx >= $offset) { if ($idx >= $offset) {
if ($dom_unlimited || (! $dom_unlimited && $domains_used < $domains_avail)) { if ($dom_unlimited || (! $dom_unlimited && $domains_used < $domains_avail)) {
$ins_id = $this->_addSingleDomainToDatabase($dom); $ins_id = $this->_addSingleDomainToDatabase($dom);
if ($ins_id !== false) { if ($ins_id !== false) {
$import_counter ++; $import_counter ++;
$domains_used ++; $domains_used ++;
} }
} else { } else {
$note = 'You have reached your maximum allocation of domains (' . $domains_avail . ').'; $note = 'You have reached your maximum allocation of domains (' . $domains_avail . ').';
break; break;
} }
} }
$global_counter ++; $global_counter ++;
} }
return array ( return array(
'all' => $global_counter, 'all' => $global_counter,
'imported' => $import_counter, 'imported' => $import_counter,
'notice' => $note 'notice' => $note
); );
}
} /**
* setter for import-file
*
* @param string $import_file
*
* @return void
*/
public function setImportFile($import_file = null)
{
$this->_impFile = makeCorrectFile($import_file);
}
/** /**
* setter for import-file * setter for customer-id
* *
* @param string $import_file * @param int $customer_id
* *
* @return void * @return void
*/ */
public function setImportFile($import_file = null) { public function setCustomer($customer_id = 0)
{
$this->_custId = $customer_id;
}
$this->_impFile = makeCorrectFile($import_file); /**
* adds a single domain to the database using the given array
*
* @param array $domain_data
*
* @return int last-inserted id or false on error
*/
private function _addSingleDomainToDatabase($domain_data = array())
{
} // format domain
$idna_convert = new idna_convert_wrapper();
$domain_data['domain'] = $idna_convert->encode(preg_replace(array(
'/\:(\d)+$/',
'/^https?\:\/\//'
), '', $domain_data['domain']));
/** // check if it is a valid domain
* setter for customer-id if (! validateDomain($domain_data['domain'])) {
* return false;
* @param int $customer_id }
*
* @return void
*/
public function setCustomer($customer_id = 0) {
$this->_custId = $customer_id; // no system-hostname can be added
if ($domain_data['domain'] == Settings::Get('system.hostname')) {
return false;
}
} // no existing domains can be imported
if (in_array($domain_data['domain'], $this->_knownDomains)) {
return false;
}
/** // check for alias-domain
* adds a single domain to the database using the given array if (! empty($domain_data['aliasdomain'])) {
* // format
* @param array $domain_data $domain_data['aliasdomain'] = $idna_convert->encode(preg_replace(array(
* '/\:(\d)+$/',
* @return int last-inserted id or false on error '/^https?\:\/\//'
*/ ), '', $domain_data['aliasdomain']));
private function _addSingleDomainToDatabase($domain_data = array()) { // validate alias-domain
if (! validateDomain($domain_data['aliasdomain'])) {
// invalid-domain lol - skip to be sure we dont add anything weird
return false;
}
// does the domain we want to be an alias of exists?
if (! in_array($domain_data['aliasdomain'], $this->_knownDomains)) {
// it does not - User should respect the order of import so if the domain
// he wants to alias is also part of the import is ABOVE this one
// - we'd better skip
return false;
}
}
// format domain // check for use_ssl and ssl_redirect
$idna_convert = new idna_convert_wrapper(); if (!isset($domain_data['use_ssl']) || $domain_data['use_ssl'] == 1) {
$domain_data['domain'] = $idna_convert->encode(preg_replace(array ( // if not set: default is whatever the system says
'/\:(\d)+$/', // if set to 1: set to 0 if system has no ssl enabled
'/^https?\:\/\//' $domain_data['use_ssl'] = (Settings::get('system.use_ssl') == 1 ? 1 : 0);
), '', $domain_data['domain'])); }
// check if it is a valid domain // use_ssl flag
if (! validateDomain($domain_data['domain'])) { if ($domain_data['use_ssl'] != 1) {
return false; $domain_data['use_ssl'] = 0;
} }
// no system-hostname can be added // ssl_redirect flag
if ($domain_data['domain'] == Settings::Get('system.hostname')) { if ($domain_data['ssl_redirect'] != 1) {
return false; $domain_data['ssl_redirect'] = 0;
} }
// no existing domains // if use_ssl is 0 ssl_redirect must be too (no ssl-ip => no ssl-redirect)
if (in_array($domain_data['domain'], $this->_knownDomains)) { if ($domain_data['use_ssl'] == 0 && $domain_data['ssl_redirect'] == 1) {
return false; $domain_data['ssl_redirect'] = 0;
} }
// add to known domains // add to known domains
$this->_knownDomains[] = $domain_data['domain']; $this->_knownDomains[] = $domain_data['domain'];
// docroot (URL allowed, will lead to redirect) // docroot (URL allowed, will lead to redirect)
if (! preg_match('/^https?\:\/\//', $domain_data['documentroot'])) { if (! preg_match('/^https?\:\/\//', $domain_data['documentroot'])) {
$domain_data['documentroot'] = makeCorrectDir($this->_custData['documentroot'] . "/" . $domain_data['documentroot']); $domain_data['documentroot'] = makeCorrectDir($this->_custData['documentroot'] . "/" . $domain_data['documentroot']);
} }
// is bind domain? // is bind domain?
if (! isset($domain_data['isbinddomain'])) { if (! isset($domain_data['isbinddomain'])) {
$domain_data['isbinddomain'] = (Settings::Get('system.bind_enable') == '1') ? 1 : 0; $domain_data['isbinddomain'] = (Settings::Get('system.bind_enable') == '1') ? 1 : 0;
} elseif ($domain_data['isbinddomain'] != 1) { } elseif ($domain_data['isbinddomain'] != 1) {
$domain_data['isbinddomain'] = 0; $domain_data['isbinddomain'] = 0;
} }
/* // zonefile
* automatically set values (not from the file) if (!isset($domain_data['zonefile'])) {
*/ $domain_data['zonefile'] = "";
// add date } else {
$domain_data['add_date'] = time(); if (!empty($domain_data['zonefile']) && Settings::Get('system.bind_enable') == '1') {
// set adminid $domain_data['zonefile'] = makeCorrectFile($domain_data['zonefile']);
$domain_data['adminid'] = $this->_custData['adminid']; } else {
// set customerid $domain_data['zonefile'] = "";
$domain_data['customerid'] = $this->_custId; }
}
// check for required fields // openbasedir flag
foreach ($this->_required_fields as $rfld) { if (! isset($domain_data['openbasedir'])) {
if (! isset($domain_data[$rfld])) { $domain_data['openbasedir'] = 1;
return false; } elseif ($domain_data['openbasedir'] != 1) {
} $domain_data['openbasedir'] = 0;
} }
// clean all fields that do not belong to the required fields // speciallogfile flag
$domain_data_tmp = $domain_data; if (! isset($domain_data['speciallogfile'])) {
foreach ($domain_data_tmp as $fld => $val) { $domain_data['speciallogfile'] = 0;
if (! in_array($fld, $this->_required_fields)) { } elseif ($domain_data['speciallogfile'] != 1) {
unset($domain_data[$fld]); $domain_data['speciallogfile'] = 0;
} }
}
// save iplist /*
$iplist = $domain_data['ips']; * automatically set values (not from the file)
$iplist_arr = array_unique(explode(",", $iplist)); */
$knownIPsCheck = array_unique($this->_knownIpPortChk); // add date
// check whether we actually have at least one of the used IP's in our system $domain_data['add_date'] = time();
$result_iplist = array_intersect($iplist_arr, $knownIPsCheck); // set adminid
// write back iplist $domain_data['adminid'] = (int)$this->_custData['adminid'];
$iplist = implode(",", $result_iplist); // set customerid
$domain_data['customerid'] = (int)$this->_custId;
// dont need that for the domain-insert-statement // check for required fields
unset($domain_data['ips']); foreach ($this->_required_fields as $rfld) {
if (! isset($domain_data[$rfld])) {
return false;
}
}
// finally ADD the domain to panel_domains // clean all fields that do not belong to the required fields
Database::pexecute($this->_ins_stmt, $domain_data); $domain_data_tmp = $domain_data;
foreach ($domain_data_tmp as $fld => $val) {
if (! in_array($fld, $this->_required_fields)) {
unset($domain_data[$fld]);
}
}
// get the newly inserted domain-id // save iplist
$domain_id = Database::lastInsertId(); $iplist = $domain_data['ips'];
$iplist_arr = array_unique(explode(",", $iplist));
$knownIPsCheck = array_unique($this->_knownIpPortChk);
// check whether we actually have at least one of the used IP's in our system
$result_iplist = array_intersect($iplist_arr, $knownIPsCheck);
// write back iplist
$iplist = implode(",", $result_iplist);
// insert domain <-> ip/port reference // dont need that for the domain-insert-statement
if (empty($iplist)) { unset($domain_data['ips']);
$iplist = Settings::Get('system.ipaddress');
}
// split ip-list and remove duplicates // remember use_ssl value
$iplist_arr = array_unique(explode(",", $iplist)); $use_ssl = (bool)$domain_data['use_ssl'];
foreach ($iplist_arr as $ip) { // dont need that for the domain-insert-statement
// if we know the ip, at all variants (different ports, ssl and non-ssl) of it! unset($domain_data['use_ssl']);
if (isset($this->_knownIpPort[$ip])) {
foreach ($this->_knownIpPort[$ip] as $ipdata) {
// add domain->ip reference
Database::pexecute($this->_ipp_ins_stmt, array (
'domid' => $domain_id,
'ipid' => $ipdata['id']
));
}
}
}
return $domain_id; // finally ADD the domain to panel_domains
Database::pexecute($this->_ins_stmt, $domain_data);
} // get the newly inserted domain-id
$domain_id = Database::lastInsertId();
/** // insert domain <-> ip/port reference
* reads in the csv import file and returns an array with if (empty($iplist)) {
* all the domains to be imported $iplist = Settings::Get('system.ipaddress');
* }
* @param string $separator
*
* @return array
*/
private function _parseImportFile($separator = ";") {
if (empty($this->_impFile)) { // split ip-list and remove duplicates
throw new Exception("No file was given for import"); $iplist_arr = array_unique(explode(",", $iplist));
} foreach ($iplist_arr as $ip) {
// if we know the ip, at all variants (different ports, ssl and non-ssl) of it!
if (isset($this->_knownIpPort[$ip])) {
foreach ($this->_knownIpPort[$ip] as $ipdata) {
// no ssl ip/ports should be used for this domain
if ($use_ssl == false && $ipdata['ssl'] == 1) {
continue;
}
// add domain->ip reference
Database::pexecute($this->_ipp_ins_stmt, array(
'domid' => $domain_id,
'ipid' => $ipdata['id']
));
}
}
}
if (! file_exists($this->_impFile)) { return $domain_id;
throw new Exception("The file '" . $this->_impFile . "' could not be found"); }
}
if (! is_readable($this->_impFile)) { /**
throw new Exception("Unable to read file '" . $this->_impFile . "'"); * reads in the csv import file and returns an array with
} * all the domains to be imported
*
* @param string $separator
*
* @return array
*/
private function _parseImportFile($separator = ";")
{
if (empty($this->_impFile)) {
throw new Exception("No file was given for import");
}
$file_data = array (); if (! file_exists($this->_impFile)) {
throw new Exception("The file '" . $this->_impFile . "' could not be found");
}
$fh = @fopen($this->_impFile, "r"); if (! is_readable($this->_impFile)) {
if ($fh) { throw new Exception("Unable to read file '" . $this->_impFile . "'");
while (($line = fgets($fh)) !== false) { }
$tmp_arr = explode($separator, $line);
$data_arr = array ();
foreach ($tmp_arr as $idx => $data) {
// dont include more fields that the 13 we use
if ($idx > 12)
break;
$data_arr[$this->_required_fields[$idx]] = $data;
}
$file_data[] = array_map("trim", $data_arr);
}
} else {
throw new Exception("Unable to open file '" . $this->_impFile . "'");
}
fclose($fh);
return $file_data; $file_data = array();
} $fh = @fopen($this->_impFile, "r");
if ($fh) {
while (($line = fgets($fh)) !== false) {
$tmp_arr = explode($separator, $line);
$data_arr = array();
foreach ($tmp_arr as $idx => $data) {
// dont include more fields that the ones we use
if ($idx > (count($this->_required_fields) - 4)) // off-by-one + 3 auto-values
break;
$data_arr[$this->_required_fields[$idx]] = $data;
}
$file_data[] = array_map("trim", $data_arr);
}
} else {
throw new Exception("Unable to open file '" . $this->_impFile . "'");
}
fclose($fh);
/** return $file_data;
* reads customer data from panel_customer by $_custId }
*
* @return bool
*/
private function _readCustomerData() {
$cust_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE `customerid` = :cid"); /**
$this->_custData = Database::pexecute_first($cust_stmt, array ( * reads customer data from panel_customer by $_custId
'cid' => $this->_custId *
)); * @return bool
if (is_array($this->_custData) && isset($this->_custData['customerid']) && $this->_custData['customerid'] == $this->_custId) { */
return true; private function _readCustomerData()
} {
$this->_custData = null; $cust_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE `customerid` = :cid");
return false; $this->_custData = Database::pexecute_first($cust_stmt, array(
'cid' => $this->_custId
));
if (is_array($this->_custData) && isset($this->_custData['customerid']) && $this->_custData['customerid'] == $this->_custId) {
return true;
}
$this->_custData = null;
return false;
}
} /**
* reads domain data from panel_domain
/** *
* reads domain data from panel_domain * @return void
* */
* @return void private function _readDomainData()
*/ {
private function _readDomainData() { $knowndom_stmt = Database::prepare("SELECT `domain` FROM `" . TABLE_PANEL_DOMAINS . "` ORDER BY `domain` ASC");
Database::pexecute($knowndom_stmt);
$knowndom_stmt = Database::prepare("SELECT `domain` FROM `" . TABLE_PANEL_DOMAINS . "` ORDER BY `domain` ASC"); $this->_knownDomains = array();
Database::pexecute($knowndom_stmt); while ($dom = $knowndom_stmt->fetch()) {
$this->_knownDomains = array (); $this->_knownDomains[] = $dom['domain'];
while ($dom = $knowndom_stmt->fetch()) { }
$this->_knownDomains[] = $dom['domain']; }
}
}
/**
* reads ip/port data from panel_ipsandports
*
* @return void
*/
private function _readIpPortData() {
$knownip_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "`");
Database::pexecute($knownip_stmt);
$this->_knownIpPort = array ();
while ($ipp = $knownip_stmt->fetch()) {
$this->_knownIpPort[$ipp['ip']][] = $ipp;
$this->_knownIpPortChk[] = $ipp['ip'];
}
}
/**
* reads ip/port data from panel_ipsandports
*
* @return void
*/
private function _readIpPortData()
{
$knownip_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_IPSANDPORTS . "`");
Database::pexecute($knownip_stmt);
$this->_knownIpPort = array();
while ($ipp = $knownip_stmt->fetch()) {
$this->_knownIpPort[$ipp['ip']][] = $ipp;
$this->_knownIpPortChk[] = $ipp['ip'];
}
}
} }