%PDF- %PDF-
| Direktori : /usr/local/prospamfilter/library/SpamFilter/PanelSupport/ |
| Current File : //usr/local/prospamfilter/library/SpamFilter/PanelSupport/Cpanel.php |
<?php
/**
*************************************************************************
* *
* ProSpamFilter *
* Bridge between Webhosting panels & SpamExperts filtering *
* *
* Copyright (c) 2010-2011 SpamExperts B.V. All Rights Reserved, *
* *
*************************************************************************
* *
* Email: support@spamexperts.com *
* Website: htttp://www.spamexperts.com *
* *
*************************************************************************
* *
* This software is furnished under a license and may be used and copied *
* only in accordance with the terms of such license and with the *
* inclusion of the above copyright notice. No title to and ownership *
* of the software is hereby transferred. *
* *
* You may not reverse engineer, decompile or disassemble this software *
* product or software product license. *
* *
* SpamExperts may terminate this license if you don't comply with any *
* of the terms and conditions set forth in our end user *
* license agreement (EULA). In such event, licensee agrees to return *
* licensor or destroy all copies of software upon termination of the *
* license. *
* *
* Please see the EULA file for the full End User License Agreement. *
* *
*************************************************************************
*
* @category SpamExperts
* @package ProSpamFilter
* @author $Author$
* @copyright Copyright (c) 2011, SpamExperts B.V., All rights Reserved. (http://www.spamexperts.com)
* @license Closed Source
* @version 3.0
* @link https://my.spamexperts.com/kb/34/Addons
* @since 3.0
*/
class SpamFilter_PanelSupport_Cpanel
{
const PANEL_FILESYSTEM_LOCATION = "/usr/local/cpanel/";
/**
* @access public
* @var Cpanel_PublicAPI $_api
*/
public $_api;
/**
* @access protected
* @var SpamFilter_Logger $_logger object
*/
protected $_logger;
/**
* @param $_config object
*/
public $_config;
/**
* @param $_options object
*/
var $_options;
public static function getHooksList()
{
return array(
array(
'category' => 'Whostmgr',
'event' => 'Accounts::Create',
'stage' => 'post',
'action' => ''
),
array(
'category' => 'Whostmgr',
'event' => 'Accounts::Remove',
'stage' => 'pre',
'action' => ''
),
array(
'category' => 'Whostmgr',
'event' => 'Accounts::Modify',
'stage' => 'post',
'action' => ''
),
array(
'category' => 'Whostmgr',
'event' => 'Domain::park',
'stage' => 'post',
'blocking' => 1
),
array(
'category' => 'Whostmgr',
'event' => 'Domain::unpark',
'stage' => 'pre',
'blocking' => 1
),
array('category' => 'PkgAcct',
'event' => 'Restore',
'stage' => 'post',
'action' => ''
),
array(
'category' => 'Cpanel',
'event' => 'Api2::AddonDomain::addaddondomain',
'stage' => 'post',
'action' => ''
),
array(
'category' => 'Cpanel',
'event' => 'Api2::AddonDomain::deladdondomain',
'stage' => 'pre',
'action' => ''
),
array(
'category' => 'Cpanel',
'event' => 'Api2::SubDomain::addsubdomain',
'stage' => 'post',
'action' => '',
'escalateprivs' => 1
),
array(
'category' => 'Cpanel',
'event' => 'Api2::SubDomain::delsubdomain',
'stage' => 'pre',
'action' => '',
'escalateprivs' => 1
),
array(
'category' => 'Cpanel',
'event' => 'Api2::CustInfo::savecontactinfo',
'stage' => 'post',
'action' => ''
),
array(
'category' => 'Cpanel',
'event' => 'Api2::Email::setmxcheck',
'stage' => 'post',
'action' => ''
)
);
}
/**
* Verifies whether is possible to use this class on this server and sets up API communication.
*
* @param array $options
*
* @return SpamFilter_PanelSupport_Cpanel
* @throws Exception in case it is not possible
*
* @see SpamFilter_Configuration
*
* @access public
*/
public function __construct($options = array())
{
$this->_logger = Zend_Registry::get('logger');
if (!file_exists(self::PANEL_FILESYSTEM_LOCATION)) {
$this->_logger->crit("Wrong Panelsupport library loaded. This is not cPanel.");
throw new Exception("Wrong Panelsupport library loaded");
}
$this->_options = $options;
if (isset($options['altconfig'])) {
$this->_logger->info("Loading alternative configuration.");
$configurator = new SpamFilter_Configuration($this->_options['altconfig']);
} else {
$this->_logger->info("Loading default configuration.");
$configurator = new SpamFilter_Configuration(CFG_PATH . '/settings.conf');
}
if (!Zend_Registry::isRegistered('general_config')) {
$this->_logger->crit("Config not loaded, please check the configuration file");
$this->_options['skipapi'] = true; // Skip api at this point
}
if (isset($this->_options['skipapi']) && ($this->_options['skipapi'])) {
$this->_logger->info("Skipping API initialization.");
$this->_api = null;
return true;
}
$this->_config = Zend_Registry::get('general_config');
if (is_readable('/root/.accesstoken')) {
$this->_logger->debug("Using file to obtain access token");
$hash = trim(file_get_contents('/root/.accesstoken'));
} else {
$this->_logger->debug("Using binary to obtain access hash");
$hash = $configurator->getPassword();
}
if ((!$hash) || (!isset($hash)) || (empty($hash)) || (strlen($hash) < 5)) {
$this->_logger->crit("Unable to authenticate to the API with a missing password: please check if your token exists under 'Manage API Tokens' and in /root/.accesstoken file");
return false;
}
// Include the autoloader
// require_once('Cpanel/Util/Autoload.php');
$this->_logger->debug("Initializing cPanel PublicAPI..");
// Make a configuration data array
$whmconfig = array(
'service' => array(
'whm' => array(
'config' => array(
'user' => 'root',
'hash' => $hash,
),
),
),
);
// Instantiate the PublicAPI client
try {
$this->_logger->debug("Creating instance...");
$this->_api = ((isset($options['cpanel_api_instance'])
&& ($options['cpanel_api_instance'] instanceof Cpanel_PublicAPI))
? $options['cpanel_api_instance'] : Cpanel_PublicAPI::getInstance($whmconfig));
} catch (exception $e) {
$this->_logger->crit("cPanel API failure: " . $e->getMessage());
return false;
}
$this->_logger->debug("API has been initialized succesfully.");
return true;
}
/**
* Override the API with a new Cpanel_PublicAPI instance
*
* @param Cpanel_PublicAPI $api API to use
*
* @return bool status
*
* @access public
* @unused
* @see Cpanel_PublicAPI::getInstance()
*/
public function setApi(Cpanel_PublicAPI $api)
{
$this->_api = $api;
return $this;
}
/**
* Retrieve the API object (instanceof Cpanel_PublicAPI)
*
* @return Cpanel_PublicAPI
*
* @access public
* @unused
* @see Cpanel_PublicAPI::getInstance()
*/
public function getApi()
{
return $this->_api;
}
/**
* Checks whether the API is available
*
*
* @return bool Status
*
* @access public
*/
public function apiAvailable()
{
$this->_logger->debug("Checking if API is available..");
// Check if the API is available
if (!$this->_api || !$this->testApi()) {
$this->_logger->crit("API Unavailable. ");
return false;
}
$v = $this->getVersion();
if ($v === false || (!isset($v))) {
$this->_logger->crit("API unavailable. ");
return false;
}
$this->_logger->debug("API Available. ");
return true;
}
/**
* Get the version of the used control panel
*
* @return string|bool Version|Status failed
*
* @access public
* @requires Cpanel_PublicAPI
*/
public function getVersion()
{
$version = $this->testApi();
if (!$version) {
// Fallback method required, API not available?
$string = trim(shell_exec("/usr/local/cpanel/cpanel -V | awk {'print $1'}"));
$this->_logger->debug("Fallback versioncheck received versionstring: '{$string}'.");
if (!empty($string)) {
$this->_logger->debug("Fallback versioncheck: '{$string}'.");
return $string;
}
$this->_logger->err("Version retrieval failed.");
}
return $version;
}
public function testApi()
{
if (isset($this->_api)) {
// Make a Whostmgr query
/** @var $response stdClass */
try {
$response = $this->_api->whm_api('version');
if (isset($response) && $response->version) {
// Print result string
$this->_logger->debug("Returning API received version: '{$response->version}'.");
return $response->version;
}
} catch (Exception $e) {
$this->_logger->crit("Exception caught in " . __METHOD__ . " method : " . $e->getMessage());
}
}
return false;
}
/**
* Check whether the minimum version is met
*
* @return bool True/False
*
* @access public
* @see getVersion()
*/
public function minVerCheck()
{
$this->_logger->debug("Doing version check.");
$ver = $this->getVersion();
if (!empty($ver)) {
$this->_logger->debug("Checking if '$ver' matches...");
if (version_compare($ver, '11.28', '>=') == 1) {
$this->_logger->debug("Version OK ($ver)!.");
return true;
}
} else {
$this->_logger->debug("Current version number is empty.");
return false;
}
$this->_logger->err("Version not OK! (is: '{$ver}').");
return false;
}
/**
* Setup the DNS for the provided domain
*
* @param $data array containing data to use
*
* @return bool Status
*
* @access public
* @see removeMXRecords()
* @see AddRecord()
*/
public function SetupDNS($data)
{
$domain = $data['domain'];
if (!isset($domain)) {
$this->_logger->err("Request to provision DNS but no domain supplied");
return false;
}
$this->_logger->debug("DNS setup requested for '{$domain}'");
$records = $data['records'];
if (!isset($records)) {
$this->_logger->err("Request to provision DNS for '{$domain}' but no records supplied");
return false;
}
// Remove all current MX records
$this->removeMXRecords($domain);
// Setup DNS for $domain using array $records (key = priority);
foreach ($records as $prio => $value) {
if (!empty($value)) {
$this->addMxRecord($domain, $prio, $value);
} else {
$this->_logger->debug("Skipping one DNS record because of empty value.");
}
unset($zoneRecord);
}
/**
* The domain must be "local" to avoid email processing, but only when domain is protected
*
* @see https://trac.spamexperts.com/ticket/18108
*/
if (empty($data['unprotect'])) {
$config = Zend_Registry::get('general_config');
if (0 < $config->bulk_change_routing) {
$this->SwitchMXmode(
array(
'domain' => $domain,
'mode' => 'local',
)
);
}
}
return true;
}
/**
* Get SPF Record for specified domain
*
* @param array $params
*
* @return array $spfrecord
*
* @access public
*/
private function getSPFRecord($params)
{
$result = $this->_api->getWhm()->makeQuery('dumpzone', $params);
$array = $result->getResponse('array');
$spfrecord = [];
if ($array['metadata']['result']) {
foreach ($array['data']['zone'] as $zone) {
foreach ($zone['record'] as $record) {
if (isset($record['txtdata'])
&& substr($record['txtdata'], 0, 5) == 'v=spf'
&& $record['name'] === ($params['domain'] . '.')
) {
$spfrecord = $record;
}
}
}
}
return $spfrecord;
}
/**
* Setup SPF Record for specified domain
*
* @param array $params
*
* @return bool Status
*
* @access public
*/
public function SetupSPF($params)
{
if (empty($this->_config->spf_record)) {
$this->_logger->debug("Cannot add SPF Record. SPF Record is not set in settings.");
return false;
}
$idn = new IDNA_Convert();
$args['domain'] = $idn->encode($params['domain']);
$args['api.version'] = '1';
$spfrecord = $this->getSPFRecord($args);
$response = array();
// SPF Record found in DNS
if (isset($spfrecord['txtdata']) && !empty($spfrecord['txtdata'])) {
$args['line'] = $spfrecord['Line'];
$args['name'] = $spfrecord['name'];
$args['class'] = $spfrecord['class'];
$args['ttl'] = $spfrecord['ttl'];
$args['type'] = $spfrecord['type'];
$args['txtdata'] = $this->_config->spf_record;
$response = $this->_api->getWhm()->makeQuery('editzonerecord', $args)->getResponse('array'); //check status
} else {
//No existing spf so we create new one
$args['name'] = $idn->encode($args['domain']) . ".";
$args['class'] = 'IN';
$args['ttl'] = '14400';
$args['type'] = 'TXT';
$args['txtdata'] = $this->_config->spf_record;
$response = $this->_api->getWhm()->makeQuery('addzonerecord', $args)->getResponse('array');//check status
}
return ($response['metadata']['result'] == 1) ? true : false;
}
/**
* Remove SPF Record for specified domain
*
* @param string $domain - domain name
*
* @return bool Status
*
* @access public
*/
public function RemoveSPF($domain)
{
$idn = new IDNA_Convert();
$args['domain'] = $idn->encode($domain);
$args['api.version'] = '1';
$spfrecord = $this->getSPFRecord($args);
if (isset($spfrecord['txtdata']) && $spfrecord['txtdata'] == $this->_config->spf_record) {
$args['zone'] = $domain;
$args['line'] = $spfrecord['Line'];
$response = $this->_api->getWhm()->makeQuery('removezonerecord', $args)->getResponse('array');//check status
return ($response['metadata']['result'] == 1) ? true : false;
}
return false;
}
/**
* Switches MX mode to a different mode.
*
* @param array $params
*
* @return bool Status
*
* @access private
* @bug The result content is not returning actual status due to cPanel publicAPI limitation.
*/
public function SwitchMXmode($params)
{
if (empty($params['domain'])) {
$this->_logger->debug("Domain can't be empty.");
return false;
}
$domain = $params['domain'];
$mode = (empty($params['mode'])) ? 'local' : $params['mode'];
if (empty($params['user'])) {
// If the user is not given, we have to look it up ourselves.
// Please note: The user *must* be the one assigned to the domain else the call will not provide data.
$user = $this->getDomainUser($domain);
} else {
$user = $params['user'];
}
$idn = new IDNA_Convert();
$encodedDomain = $idn->encode($domain);
if ($user !== false) {
$this->_logger->debug("Switching MX mode for '{$domain}' to '{$mode}'..");
try {
$response = $this->_api->getWhm()
->api2_query($user, 'Email', 'setalwaysaccept', array('domain' => $encodedDomain, 'mxcheck' => $mode));
$arr = $response->getResponse('array');
} catch (Exception $e) {
$this->_logger->crit("Exception caught in " . __METHOD__ . " method : " . $e->getMessage());
}
$this->_logger->debug("Switching MX mode for '{$domain}' to '{$mode}' resulted in " . serialize($arr));
// @TODO: Awaiting response on this potential bug (not getting return content)
// lets just return 'true' for now, hoping it went successfully
return true;
}
$this->_logger->debug("Unable to switch MX mode.");
return false;
}
/**
* Retrieves current MX mode
*
* @param $params [domain, user] Domain to retrieve info from
*
* @return string MX Mode
*
* @access public
*/
public function GetMXmode($params)
{
if (empty($params['domain'])) {
$this->_logger->debug("Domain can't be empty.");
return false;
}
$idn = new IDNA_Convert();
$domain = $params['domain'];
$encodedDomain = $idn->encode($domain);
if (empty($params['user'])) {
// If the user is not given, we have to look it up ourselves.
// Please note: The user *must* be the one assigned to the domain else the call will not provide data.
$user = $this->getDomainUser($encodedDomain);
} else {
$user = $params['user'];
}
if ($user !== false) {
$this->_logger->debug("Retrieving MX mode for '{$domain}'..");
try {
/** @see https://trac.spamexperts.com/ticket/21273#comment:11 */
if (in_array(strtolower($user), array('root', 'admin'))) {
/** @var $response Cpanel_Query_Object */
$response = $this->_api->whm_api('domainuserdata', array('domain' => $encodedDomain));
$responseData = $response->getResponse('array');
if (!empty($responseData['userdata']['user'])) {
$user = $responseData['userdata']['user'];
}
}
$response = $this->_api->getWhm()->api2_query($user, 'Email', 'listmxs', array('domain' => $encodedDomain));
$arr = $response->getResponse('array');
$this->_logger->debug("GetMXmode returned: " . serialize($arr));
if (!isset($arr['cpanelresult']['data']['0']['detected'])) {
$this->_logger->err("The API did not report back with required data. Not sure whether this is remote!");
return false;
} else {
return strtolower($arr['cpanelresult']['data'][0]['detected']);
}
} catch (Exception $e) {
$this->_logger->crit("Exception caught in " . __METHOD__ . " method : " . $e->getMessage());
}
}
$this->_logger->debug("Unable to get MX mode.");
return false;
}
/**
* Retrieves all domains capable of sending/receiving email.
*
* @param string $user The username to retrieve domains for.
*
* @return array Value of destination host
*
* @access public
*/
public function getAllMailDomains($user)
{
// Get all domains owned by $user
try {
$response = $this->_api->getWhm()->api2_query($user, 'Email', 'listmaildomains');
$arr = $response->getResponse('array');
} catch (Exception $e) {
$this->_logger->crit("Exception caught in " . __METHOD__ . " method : " . $e->getMessage());
}
$domains = array();
foreach ($arr['cpanelresult']['data'] as $data) {
$domains[] = $data['domain'];
}
return $domains;
}
/**
* Retrieves the destination for the provided domain
*
* @param string $domain Domain to lookup destination for
* @param string $source Source used in logging.
*
* @return string Value of destination host
*
* @access public
* @see getMXRecordContent()
*/
public function getDestination($domain, $source = '')
{
if (!empty($source)) {
$this->_logger->info("GetDestination: Domaintype = '{$source}'");
}
if (empty($domain)) {
$this->_logger->debug("GetDestination: Empty domain provided to lookup.");
return null;
}
$this->_logger->info("Requesting current set destinations for '{$domain}'");
$config = Zend_Registry::get('general_config');
if ($config->use_existing_mx) {
$this->_logger->debug("Requested to use existing MX records from '{$domain}' as route. Retrieving them");
// Users want to use existing MX records as destinations (routes). Retrieve them!
$mxr = $this->GetMXRecordContent($domain);
if (empty($mxr)) {
$this->_logger->debug("Destination retrieval for domain '{$domain}' has FAILED.");
$destination = null;
return $destination;
}
$this->_logger->debug("Current MX records for '{$domain}' have been retrieved.");
// Build me an array with MX records!
$my_rr[] = $config->mx1;
if (!empty($config->mx2)) {
$my_rr[] = $config->mx2;
}
if (!empty($config->mx3)) {
$my_rr[] = $config->mx3;
}
if (!empty($config->mx4)) {
$my_rr[] = $config->mx4;
}
$myRRCount = count($my_rr);
$foundRR = 0;
// Check if they aren't already pointing to the filter cluster.
foreach ($mxr as $r) {
// $r = record
$this->_logger->debug("MX record value for '{$domain}': '{$r}'");
if (in_array($r, $my_rr)) {
$foundRR++;
// Record $r exists in array $my_rr
}
}
$c1 = count($mxr);
if ($c1 > $myRRCount) {
$this->_logger->debug("More records found ({$c1}) than configured ({$myRRCount}).");
}
if ($foundRR == $myRRCount) {
// We have found the same records as stated in "my records".
$this->_logger->debug(
"Current set MX records for '{$domain}' seems to be pointing to the filtering cluster already. Falling back. ({$foundRR} vs {$myRRCount})"
);
$destination = null;
return $destination;
} else {
$this->_logger->debug(
"I found {$foundRR} current records but the ones to set are {$myRRCount}, falling back to server hostname to deliver email on."
);
}
$this->_logger->debug("Merging current MX records for '{$domain}'");
$destination = implode(',', $mxr); // Glue them with a ,
} else {
$this->_logger->debug("GetDestination: Default destination (this server)");
// Use the default destination (aka: server hostname)
$destination = null;
}
$this->_logger->debug("Returning destination: '{$destination}' for domain '{$domain}'");
return $destination;
}
public function createBulkProtectResponse($domain, $reason, $reasonStatus = "error", $rawResult)
{
return array(
"domain" => $domain,
"counts" => array(
"ok" => 0,
"failed" => 0,
"normal" => 0,
"parked" => 0,
"addon" => 0,
"subdomain" => 0,
"skipped" => 1,
"updated" => 0,
),
"reason" => $reason,
"reason_status" => $reasonStatus,
'rawresult' => $rawResult,
"time_start" => $_SERVER['REQUEST_TIME'],
"time_execute" => time() - $_SERVER['REQUEST_TIME'],
);
}
/**
* Protect all domains on the server according to the configuration
*
* @param array $params
*
* @return array containing result data
*
* @access public
* @see getLocalAccounts()
* @see IsRemoteDomain()
* @see _setupProgressBar()
* @see SpamFilter_Panel_Account
* @see SpamFilter_Hooks
* @see SpamFilter_Panel_ProtectWhm
* @see getParkedDomains()
* @see getAddonDomains()
*/
public function bulkProtect($params)
{
//set current user
$params['user'] = $this->_config->apiuser;
$params['password'] = $this->_config->apipass;
$domain = $params['domain'];
try {
$hook = new SpamFilter_Hooks;
$protect = new SpamFilter_Panel_ProtectWhm($hook, $this->_api);
$protect->setDomain($domain);
$accountInstance = new SpamFilter_Panel_Account($params,
!$this->_config->handle_only_localdomains, $this->_api);
$protect->setAccount($accountInstance);
if (0 < $this->_config->handle_only_localdomains
&& SpamFilter_Hooks::SKIP_REMOTE == $accountInstance->getErrorCode()) {
return $this->createBulkProtectResponse($domain, "Skipped: Domain is remote", "error", SpamFilter_Hooks::SKIP_REMOTE);
}
switch ($params['type']) {
case 'account':
case 'domain':
$protect->domainProtectHandler($this->getDestination($domain, 'normal domain'), $this);
break;
case 'parked':
$protect->parkedDomainProtectHandler($domain, $this->getDestination($domain, 'parked alias'));
break;
case 'addon':
$protect->addonDomainProtectHandler($domain, $this->getDestination($domain, 'parked addon'));
break;
case 'subdomain':
$protect->subDomainProtectHandler($domain, $this->getDestination($params['owner_domain'], 'subdomain'));
break;
default:
break;
}
} catch (Exception $e) {
$this->_logger->crit("Exception caught in " . __METHOD__ . " method : " . $e->getMessage());
}
return $protect->getResult();
}
/**
* Retrieve all users on this system
*
*
* @param array $params
*
* @access public
* @return array of users
*
* @see listaccts()
*/
public function getUsers($params = null)
{
$this->_logger->debug("getUsers");
$output = array();
$accounts = $this->listaccts($params);
if (isset($accounts) && (!empty($accounts)) && is_array($accounts) && (count($accounts) > 0)) {
foreach ($accounts as $account) {
if (isset($account['user'])) {
// User
$this->_logger->debug("User is '{$account['user']}'");
$output[] = $account['user'];
}
if (isset($account['owner'])) {
// Reseller
$this->_logger->debug(
"The user '{$account['user']}' is owned by reseller '{$account['owner']}', adding to the list"
);
$output[] = $account['user'];
}
}
$this->_logger->debug("Sorting results");
return array_unique($output);
}
return array();
}
/**
* Retrieve all users and their primary domain on this system
*
*
* @param array $params
*
* @return array Raw array of users
*
* @access public
* @see listaccts()
*/
public function getPrimaryUsers($params = null)
{
$this->_logger->debug("getPrimaryUsers");
$accounts = $this->listaccts($params);
if (!empty($accounts) && is_array($accounts)) {
return $accounts['acct'];
}
return array();
}
/**
* Retrieve all domains for given username
*
* @param $data Array containing username entry
*
* @return array of domains for the provided user
*
* @access public
* @see listaccts()
*/
public function getUsersDomains($data)
{
$username = $data['username'];
$username = trim($username);
$this->_logger->debug("Show all domains of reseller '{$username}'");
$domains_user = $this->getDomains(array('username' => $username, 'level' => 'user'));
$domains_owner = $this->getDomains(array('username' => $username, 'level' => 'owner'));
$localDomains = array_merge($domains_user, $domains_owner);
// Return a unique array, since the OWNER= can also contain USER=
$unique = $this->getUniqueDomains($localDomains);
$c = is_array($unique) ? count($unique) : 0;
$this->_logger->debug("Returning {$c} domains");
return $unique;
}
/**
* Check whether we the user is allowed
*
*
* @return bool status
*
* @access public
* @see permitted()
* @deprecated Replaced by SpamFilter_ACL
*/
public function isAllowed()
{
$this->_logger->debug("Security Check");
return $this->permitted('all');
}
/**
* Retrieve email address linked to domain
*
* @param $data array with domain value
*
* @return string|bool Domain email address or 'false' if not set.
*
* @access public
*/
public function getDomainContact($data)
{
$domain = $data['domain'];
$this->_logger->debug("Requesting domain contact for '{$domain}'");
$data = $this->listaccts(array('username' => $domain, 'level' => 'domain'));
$email = (isset($data['acct']['0']['email'])) ? trim($data['acct']['0']['email']) : '';
if (!empty($email)) {
if ($email != "*unknown*") {
// WHM can return "*unknown*" as a contact. Rather had a FALSE... ah well.
$this->_logger->debug("Email is set, so returning it.");
return $email;
}
}
$this->_logger->debug("Email is not set / is unknown {$email}.");
return false;
}
/**
* Lists all local accounts
*
* @param array $params 'search' Optional: Apply a search, 'searchby' Optional: Search based on (e.g. user/owner/domain)
*
* @return array List of domains
* @access private
*/
private function listaccts($params = array())
{
$searchby = (!empty($params['username'])) ? $params['username'] : null;
$search = (!empty($params['level'])) ? $params['level'] : 'owner';
// Escape values since we want literal results, not partial.
if ($searchby !== null) {
$searchby = '^' . preg_quote($searchby) . '$';
}
$this->_logger->debug("ListAccts. Search: '{$search}' with value '{$searchby}'.");
$arr = array();
try {
/** @var $response Cpanel_Query_Object */
if ('^root$' == $searchby && $this->permitted('all')) {
$response = $this->_api->whm_api('listaccts');
$arr = $response->getResponse('array');
} else {
$response = $this->_api->whm_api('listaccts', array('search' => $search, 'searchby' => $searchby));
$arr = $response->getResponse('array');
// look for owner domains too.
if ($search == 'owner') {
$this->_logger->debug("Search for owner '{$searchby}' account data.");
$resellerResponse = $this->_api->whm_api('listaccts', array('search' => 'user', 'searchby' => $searchby));
$resellerArr = $resellerResponse->getResponse('array');
$arr['acct'] = array_merge($arr['acct'], $resellerArr['acct']);
}
}
$this->_logger->info("Data returned: " . serialize($arr));
if (isset($arr['data']) && isset($arr['data']['reason']) && isset($arr['data']['result'])
&& (!$arr['data']['result'])
) {
$this->_logger->err("Retrieving accounts failed ({$arr['data']['reason']})");
return false;
}
} catch (Exception $e) {
$this->_logger->crit("Exception caught in " . __METHOD__ . " method : " . $e->getMessage());
}
$this->_logger->debug("Retrieval of accounts has been completed");
// Check if there is just ONE account, since the cPanel return array is as flat as a dime then.
return (array)$arr;
}
/**
* Retrieve all MX records for the given domain
*
* @param string $domain domain to lookup MX records for
*
* @return array|boolean
*
* @access public
*/
public function getMxRecords($domain)
{
$idn = new IDNA_Convert();
$args = array(
'api.version' => 1,
'domain' => $idn->encode($domain),
);
try {
// Use the makeQuery method, it will simply assume you KNOW that
// the function is valid on the cPanel/WHM side
$response = $this->_api->getWhm()->makeQuery('listmxs', $args);
// Then deal with your response as normal
$arr = $response->getResponse('array');
return (!empty($arr['data']['record'])) ? $arr['data']['record'] : false;
} catch (Exception $e) {
// Something went awry :(
$this->_logger->err("Retrieval of MX records for '{$domain}' has failed (" . $e->getMessage() . ")");
}
return false;
}
/**
* @param $domain
* @param $line
*
* @return mixed
*/
public function removeDNSRecord($domain, $line)
{
$this->_logger->debug("Removing DNS line '{$line}' for domain '{$domain}' ... ");
$idn = new IDNA_Convert();
try {
/** @var Cpanel_Query_Object $response */
$response = $this->_api->whm_api('removezonerecord', array('zone' => $idn->encode($domain), 'Line' => $line));
$arr = $response->getResponse('array');
$this->_logger->debug(
"Removing DNS line '{$line}' for domain '{$domain}' resulted into " . print_r($arr, true)
);
if (0 < $arr['result']['0']['status']) {
// Wait for BIND reloaded ("smart delay")
// @see https://trac.spamexperts.com/software/ticket/14566
$nowMicrotime = microtime(true);
usleep(ceil(1000000 * (ceil($nowMicrotime) - $nowMicrotime)));
}
} catch (Exception $e) {
$this->_logger->crit("Exception caught in " . __METHOD__ . " method : " . $e->getMessage());
}
return $arr['result']['0']['status'];
}
/**
* Remove all MX records for the given domain
*
* @param string $domain Domain to remove MX records for
*
* @return bool Status
*
* @access private
* @see GetMXRecordContent()
*/
private function removeMXRecords($domain)
{
$this->_logger->debug("Removing MX records for '{$domain}'");
$records = $this->getMxRecords($domain);
if (is_array($records)) {
// Reorder list so the highest line is handled first, because line numbers *will* change
$this->_logger->debug("MX sort before:" . serialize($records));
$records = array_sort($records, 'Line', SORT_DESC); // Sort by highest number first
$this->_logger->debug("MX sort after:" . serialize($records));
// Remove all found MX records.
foreach ($records as $record) {
$this->_logger->debug("Removing record for {$domain} (Line: {$record['Line']})");
$this->removeDNSRecord($domain, $record['Line']);
}
}
return true;
}
/**
* Retrieve MX record content for a given domain
*
* @param string $domain Domain to retrieve MX content from
*
* @return array of MX record content
*
* @access private
* @see getMxRecords()
*/
public function GetMXRecordContent($domain)
{
$this->_logger->debug("Get MX record contents for '{$domain}'");
$mxrecords = $this->getMxRecords($domain);
$records = array();
if (is_array($mxrecords)) {
foreach ($mxrecords as $mxrecord) {
// Append the record to the list
$records[] = $mxrecord['exchange'];
}
}
return $records;
}
/**
* Create DNS record
*
* @param string $domain Domain to create record for
* @param $zoneRecord Array of values to set
*
* @return bool Status
*
* @access private
* @deprecated replaced by AddMxRecord
*/
private function AddRecord($domain, $zoneRecord)
{
$serialized = serialize($zoneRecord);
$this->_logger->debug("Add Record to domain '{$domain}' (values: {$serialized})");
try {
$response = $this->_api->addzonerecord($domain, $zoneRecord);
if (!isset($response)) {
$this->_logger->err("Adding record to '{$domain}' (values: {$serialized}) has failed!");
return false;
}
} catch (Exception $e) {
$this->_logger->crit("Exception caught in " . __METHOD__ . " method : " . $e->getMessage());
}
$this->_logger->debug("Adding record to '{$domain}' (values: {$serialized}) has been completed succesfully!");
return true;
}
/**
* AddMXRecord
* Create MX record
*
* @param string $domain Domain to create record for
* @param int $priority Which priority to add a record for
* @param string $server Which server to set as MX record destination.
*
* @return bool Status
*
* @access public
*/
public function addMxRecord($domain, $priority, $server)
{
$idn = new IDNA_Convert();
$encodedDomain = $idn->encode($domain);
$args = array(
//'api.version' => 1, // Only when doing savemxs
//'domain' => $domain, // Only when doing savemxs
'name' => $encodedDomain . '.',
// Don't forget the trailing dot, else it will be a subdomain
'exchange' => $server,
// Destination server as normal
'preference' => (int)$priority,
// Convert to integer. Strings will become '0' instead then.
'class' => 'IN',
// Just to be sure
'type' => 'MX',
// Only when doing addzonerecord
'ttl' => (isset($this->_config->default_ttl)) ? $this->_config->default_ttl : 3600,
// Use set TTL or default in case it is not set.
);
try {
/** @var Cpanel_Query_Object $response */
$response = $this->_api->whm_api('addzonerecord', array('zone' => $encodedDomain, 'args' => $args));
$arr = $response->getResponse('array');
$this->_logger->debug(
"Creation of MX record ({$priority} - {$server}) for '{$domain}' resulted into " . print_r($arr, true) . ""
);
// Wait for BIND reloaded ("smart delay")
// @see https://trac.spamexperts.com/software/ticket/14566
if (0 < $arr['result']['0']['status']) {
$nowMicrotime = microtime(true);
usleep(ceil(1000000 * (ceil($nowMicrotime) - $nowMicrotime)));
}
#return $arr['metadata']['result']; // only for savemxs
return $arr['result']['0']['status']; // only for addzonerecord
} catch (Exception $e) {
// Something went awry :(
$this->_logger->err("Creation of MX records for '{$domain}' has failed (" . $e->getMessage() . ")");
}
return false;
}
/**
* getLocalAccounts
* Retrieve all local accounts
*
*
* @return array|bool of accounts|status failed
*
* @access private
*/
private function getLocalAccounts()
{
$this->_logger->debug("Get local accounts");
$accounts = $this->listaccts();
if (isset($accounts['acct']) && is_array($accounts['acct'])) {
$c = count($accounts['acct']);
$this->_logger->debug("Returning {$c} local accounts");
return $accounts['acct'];
}
$this->_logger->debug("No accounts to return");
return false;
}
/**
* getAddonDomains
* Retrieve all addon domains
*
* @param string $username Username to retrieve addons for
* @param string $filter Filter based on specific data
*
* @return array of addon domains
*
* @access public
*/
public function getAddonDomains($username, $filter = null)
{
$result = array();
$filename = "/var/cpanel/userdata/{$username}/main";
if (!file_exists($filename) || !is_readable($filename)) {
$this->_logger->warn("Failed to retrieve parked domains for '$username': unable to read '{$filename}'. Retrieving addon domains via API.");
$params = array(
'cpanel_jsonapi_func' => 'listaddondomains',
'cpanel_jsonapi_apiversion' => 2,
'cpanel_jsonapi_module' => 'Park',
'user' => $username,
);
$response = $this->_api->getWhm()->makeQuery('cpanel', $params);
$domains = $response->getResponse('array');
foreach ($domains['cpanelresult']['data'] as $domain) {
$result[] = array(
'domain' => $domain['rootdomain'],
'alias' => $domain['domain']
);
}
} else {
$lines = file($filename, FILE_IGNORE_NEW_LINES + FILE_SKIP_EMPTY_LINES + FILE_TEXT);
$mainDomain = '';
foreach ($lines as $line) {
if (preg_match('~^main_domain: (.+)$~i', $line, $m)) {
$mainDomain = trim($m[1]);
break;
}
}
$sectionHasStarted = false;
foreach ($lines as $line) {
if ($sectionHasStarted) {
if (preg_match('~^\s+([^:]+)~i', $line, $m)) {
$result[] = array(
'domain' => $mainDomain,
'alias' => trim($m[1]),
);
} else {
break;
}
continue;
}
$sectionHasStarted = (0 === strpos($line, 'addon_domains:'));
}
}
return (!empty($result) ? $result : false);
}
/**
* getParkedDomains
* Retrieve all parked domains
*
* @param string $username Username to retrieve parked domains for
* @param string $filter Filter based on specific data
*
* @return array of parked domains
*
* @access public
*/
public function getParkedDomains($username)
{
$result = array();
$filename = "/var/cpanel/userdata/{$username}/main";
if (!file_exists($filename) || !is_readable($filename)) {
$this->_logger->warn("Failed to retrieve parked domains for '$username': unable to read '{$filename}'. Retrieving parked domains via API.");
$params = array(
'cpanel_jsonapi_func' => 'listparkeddomains',
'cpanel_jsonapi_apiversion' => 2,
'cpanel_jsonapi_module' => 'Park',
'user' => $username,
);
$response = $this->_api->getWhm()->makeQuery('cpanel', $params);
$domains = $response->getResponse('array');
foreach ($domains['cpanelresult']['data'] as $domain) {
$result[] = array('alias' => $domain['domain']);
}
} else {
$lines = file($filename, FILE_IGNORE_NEW_LINES + FILE_SKIP_EMPTY_LINES + FILE_TEXT);
$sectionHasStarted = false;
foreach ($lines as $line) {
if ($sectionHasStarted) {
if (preg_match('~^\s+-\s+(.+)$~i', $line, $m)) {
$result[] = array(
'alias' => trim($m[1]),
);
} else {
break;
}
continue;
}
$sectionHasStarted = (0 === strpos($line, 'parked_domains:'));
}
}
return (!empty($result) ? $result : false);
}
public function getSubDomains($username)
{
$result = array();
$filename = "/var/cpanel/userdata/{$username}/main";
if (!file_exists($filename) || !is_readable($filename)) {
$this->_logger->warn("Failed to retrieve parked domains for '$username': unable to read '{$filename}'. Retrieving parked domains via API.");
$params = array(
'cpanel_jsonapi_func' => 'listsubdomains',
'cpanel_jsonapi_apiversion' => 2,
'cpanel_jsonapi_module' => 'SubDomain',
'user' => $username,
);
$response = $this->_api->getWhm()->makeQuery('cpanel', $params);
$domains = $response->getResponse('array');
foreach ($domains['cpanelresult']['data'] as $domain) {
$result[] = array('alias' => $domain['domain']);
}
} else {
$lines = file($filename, FILE_IGNORE_NEW_LINES + FILE_SKIP_EMPTY_LINES + FILE_TEXT);
$sectionHasStarted = false;
foreach ($lines as $line) {
if ($sectionHasStarted) {
if (preg_match('~^\s+-\s+(.+)$~i', $line, $m)) {
$result[] = array(
'alias' => trim($m[1]),
);
} else {
break;
}
continue;
}
$sectionHasStarted = (0 === strpos($line, 'sub_domains:'));
}
}
return (!empty($result) ? $result : false);
}
/**
* permitted
* Check whether the current user is permitted to to something
*
* @param string $acl Which ACL should be checked
*
* @return bool true/false
* @see SpamFilter_Core
*
* @access private
*/
private function permitted($acl)
{
// Check if ($user) is $acl allowed
$user = SpamFilter_Core::getUsername();
if (empty($user)) {
$this->_logger->debug("Rejecting access to not-logged-in user '{$user}'");
return false;
}
if ($user == "root") {
$this->_logger->debug("Granting access to '{$user}'");
return true;
}
if (!empty($acl)) {
if (!file_exists("/var/cpanel/resellers")) {
$this->_logger->debug("Unable to verify '{$user}', there are no resellers in the system. [FILE NOT EXISTS]");
return false; // If we return false, this will fail.
}
$lines = file("/var/cpanel/resellers");
if ((count($lines)) == 0) {
$this->_logger->debug("Unable to verify '{$user}', there are no resellers in the system. [FILE IS EMPTY]");
return false; // If we return false, this will fail.
}
foreach ($lines as $line) {
if (preg_match("/^$user:/", $line)) {
$line = preg_replace("/^$user:/", "", $line);
$perms = explode(",", $line);
foreach ($perms as $perm) {
if ($perm == "all" || $perm == $acl) {
$this->_logger->info("[WHM] Granting access to '{$user}' because of perm: '{$perm}'");
return true;
}
}
}
}
}
$this->_logger->info("Rejecting access to '{$acl}' for {$user}");
return false;
}
/**
* IsLocalDomain
* Check whether the domain is a local domain
*
* @param string $domain Domain to check
*
* @return bool true/false
*
* @access public
* @deprecated This is not used, so therefor should be removed.
*/
public function IsLocalDomain($domain)
{
$domain = trim($domain);
$this->_logger->debug("Checking if {$domain} is listed in /etc/localdomains");
$domains = array_map('trim', (array)file('/etc/localdomains', FILE_IGNORE_NEW_LINES));
if (is_array($domains) && in_array($domain, $domains)) {
$this->_logger->debug("Domain {$domain} is listed in /etc/localdomains");
return true;
}
$this->_logger->debug("Domain {$domain} is NOT listed in /etc/localdomains");
return false;
}
/**
* Internal factory method for progressbar setup
*
* @access protected
*
* @param int $total
*
* @return Zend_ProgressBar|null
* @see is_cli()
* @see Zend_ProgressBar
* @see Zend_ProgressBar_Adapter_Console
* @see SpamFilter_ProgressBar_Adapter_JsPush
* @deprecated We do not use it at this point
*/
protected function _setupProgressBar($total)
{
/** @var $logger SpamFilter_Logger */
$logger = Zend_Registry::get('logger');
try {
// Check for CLI vs HTTP mode.
if (function_exists('is_cli') && is_cli()) {
$adapter = new Zend_ProgressBar_Adapter_Console();
} else {
$adapter = new SpamFilter_ProgressBar_Adapter_JsPush(array(
'updateMethodName' => 'Zend_ProgressBar_Update',
'finishMethodName' => 'Zend_ProgressBar_Finish'
)
);
}
$progressBar = new Zend_ProgressBar($adapter, 0, $total);
$logger->err("Progressbar set for a total of {$total} items.");
return $progressBar;
} catch (Exception $e) {
$logger->err("Initializing progressbar failed:" . $e->getMessage());
}
return null;
}
/**
* Retrieve all resellers
*
* @access public
*
* @param bool $raw
*
* @return array
*/
public function getResellers($raw = false)
{
$this->_logger->debug("Retrieving resellers..");
try {
/** @var $response stdClass */
$response = $this->_api->whm_api('listresellers');
if ($raw) {
return $response->reseller; //return the raw content without pre-processing.
}
$output = array();
if (isset($response)) {
$resellers = $response->reseller;
foreach ($resellers as $reseller) {
$this->_logger->debug("Adding '{$reseller}' to the output.");
$output[]['username'] = $reseller;
}
}
} catch (Exception $e) {
$this->_logger->crit("Exception caught in " . __METHOD__ . " method : " . $e->getMessage());
}
$this->_logger->debug("Returning array of resellers: " . serialize($output));
return $output;
}
/**
* Retrieve userlevel
*
* @access public
*
* @param string $user
*
* @return string User type
*
* @throws Zend_Controller_Plugin_Exception
* @throws Zend_Exception
*
* @see permitted
*/
public function getUserLevel($user = null)
{
// @TODO: Detect whether this is cPanel or WHM, because it (obviously) makes a difference in what we do/show.
/** @var $logger SpamFilter_Logger */
$logger = Zend_Registry::get('logger');
$logger->debug("Retrieving userlevel");
// Returns the userlevel from the current user.
// For WHM, this can either be reseller or admin.
if (empty($user)) {
$user = strtolower(trim(SpamFilter_Core::getUsername()));
}
// Easy check!
if ($this->permitted('all') || $user == 'root') {
// Since we are either 'root' or have 'all' permissions (which equals root) we are admin.
$logger->debug("Logged in as either 'admin' or has permission to 'all'");
return "role_admin";
}
//if not root it must be reseller or user
$response = $this->_api->whm_api('acctcounts', array('reseller' => $user));
$data = $response->getResponse('array');
if ($data['result']['0']['status'] == 1) {
$logger->debug("WHM API returned that specified user is reseller. Returning role_reseller.");
return 'role_reseller';
}
// In any other case we are a reseller, it is WHM remember?
$logger->debug("No privileges, client port used. Determined as enduser.");
return "role_enduser";
//@TODO: Might also be an emailuser, we need some checks to determine that.
}
/**
* Retrieve all domains belonging to a specific user (owner)
*
* @static
*
* @param array $params should contain 'username' username to retrieve domains for, 'level' by default 'owner'
*
* @return array Domains
* @access public
*/
public function getAccountDomains($params)
{
$accounts = $this->listaccts($params);
$domains = array();
if (isset($accounts) && (!empty($accounts)) && is_array($accounts) && (count($accounts) > 0)) {
$this->_logger->debug("Search resulted in > 0 entries: " . serialize($accounts));
foreach ($accounts['acct'] as $key => $data) {
$domains[$key]['domain'] = $data['domain'];
$domains[$key]['username'] = $data['user'];
}
return $domains;
}
return false;
}
/**
* Retrieve all domains belonging to a specific user
*
* @static
*
* @param array $params should contain 'username' username to retrieve domains for, 'level' by default 'owner'
*
* @return array Domains
* @access public
* @see getParkedDomains()
* @see getAddonDomains()
* @see
*/
public function getDomains($params)
{
if (!empty($params['username'])) {
$username = $params['username'];
$level = (!empty($params['level'])) ? $params['level'] : 'owner';
$order = (!empty($params['order'])) ? $params['order'] : 'asc';
$this->_logger->debug("Show all domains of user '{$username}' (level: {$level})");
$accounts = $this->getAccountDomains($params);
$domains = array();
if (isset($accounts) && (!empty($accounts))) {
$config = Zend_Registry::get('general_config');
foreach ($accounts as $user) {
$domains[] = array(
'domain' => $user['domain'],
'type' => 'account',
'user' => $user['username'],
);
if ($config->handle_extra_domains) {
$parked = $this->getParkedDomains($user['username']);
if (!empty($parked) && is_array($parked)) {
foreach ($parked as $alias) {
$domains[] = array(
'domain' => $alias['alias'],
'type' => 'parked',
'user' => $user['username'],
'owner_domain' => $user['domain'],
);
}
}
$addon = $this->getAddonDomains($user['username']);
if (!empty($addon) && is_array($addon)) {
foreach ($addon as $alias) {
$domains[] = array(
'domain' => $alias['alias'],
'type' => 'addon',
'user' => $user['username'],
'owner_domain' => $user['domain'],
);
}
}
$subdomain = $this->getSubDomains($user['username']);
if (!empty($subdomain) && is_array($subdomain)) {
foreach ($subdomain as $alias) {
$domains[] = array(
'domain' => $alias['alias'],
'type' => 'subdomain',
'user' => $user['username'],
'owner_domain' => $user['domain'],
);
}
}
}
}
}
return $this->getSortedDomains(array('domains' => $domains, 'order' => $order));
} else {
$this->_logger->debug("Username should not be empty");
return array();
}
}
/**
* validateOwnership
* Check whether we are allowed to operate on this domain
*
* @static
*
* @param string $domain Domain to check
*
* @return bool true/false
* @access public
* @see SpamFilter_Core::getUsername()
*/
public function validateOwnership($domain)
{
$user = SpamFilter_Core::getUsername();
$this->_logger->debug("Checking access to '{$domain}' for '{$user}'");
if (empty($user)) {
$this->_logger->debug("Rejecting domain access to unset user '{$user}'");
return false;
}
if ($user == "root") {
$this->_logger->debug("Granting domain access to '{$user}', as this is the main admin.");
return true;
}
$response = $this->_api->getWhm()->makeQuery('domainuserdata', array('domain' => $domain));
$data = $response->getResponse('array');
if ($data['userdata']['user'] == $user) {
$this->_logger->debug("Granting domain access to '{$user}', as this is the user of domain: '$domain'.");
return true;
}
$response = $this->_api->getWhm()->makeQuery('accountsummary', array('user' => $data['userdata']['user']));
$resData = $response->getResponse('array');
if ($resData['acct'][0]['owner'] == $user) {
$this->_logger->debug("Granting domain access to '{$user}', as this is the owner of domain: '$domain'.");
return true;
}
// User / Owner is not match
return false;
}
/**
* IsRemoteDomain
* Check whether the domain is a remote domain
*
* @param array $params Domain to check
*
* @return bool true/false
* @access public
*
* @throws RuntimeException
* @see getDomainUser()
*
*/
public function IsRemoteDomain($params)
{
// use the API to determine MX status, as we might not have permissions to read /etc/remotedomains
// We use API2 call Email::listmxs to see if the "detected" option is not 'remote' or 'secondary'
if (empty($params['domain'])) {
$this->_logger->debug("Domain should not be empty");
return false;
} else {
$domain = $params['domain'];
}
if (empty($params['user'])) {
// If the user is not given, we have to look it up ourselves.
// Please note: The user *must* be the one assigned to the domain else the call will not provide data.
$user = $this->getDomainUser($domain);
} else {
$user = $params['user'];
}
$is_remote = array();
if ($this->_config->add_extra_alias) {
$domain_to_check = (!empty($params['owner_domain'])) ? $params['owner_domain'] : $domain;
} else {
$domain_to_check = $domain;
}
if ($user !== false) {
$typeMx = $this->GetMXmode(array('domain' => $domain_to_check, 'user' => $user));
$this->_logger->debug("{$domain}'s MX mode is detected as '{$typeMx}'");
$is_remote[] = (in_array($typeMx, array("remote", "secondary")) ? 1 : 0);
} else {
$this->_logger->debug("{$domain}'s MX mode guessing: the check is skipped due to empty user");
}
$is_remote_flag = (array_sum($is_remote) > 0 || count($is_remote) === 0) ? true : false;
$this->_logger->debug("{$domain} is " . ((!$is_remote_flag) ? "NOT " : "") . "a remote domain");
return $is_remote_flag;
}
/**
* isInFilter
* Check whether the domain added to the filter.
*
* @param string $domain Domain to check
*
* @return bool true/false
* @access public
*/
public function isInFilter($domain, $ownerDomain = '')
{
if (SpamFilter_Domain::exists($domain)) {
$this->_logger->debug("[isInFilter] The domain '{$domain}' is in the filter.");
return true;
}
if (!empty($ownerDomain) && SpamFilter_Domain::exists($ownerDomain)) {
$this->_logger->debug("[isInFilter] Checking for alias of domain '{$ownerDomain}'");
$SEAPI = new SpamFilter_ResellerAPI;
$apiResponse = $SEAPI->domainalias()->list(array('domain' => $ownerDomain));
foreach ($apiResponse as $alias) {
if ($alias == $domain) {
$this->_logger->debug("[isInFilter] The domain '{$domain}' is alias of domain $ownerDomain.");
return true;
}
}
}
$this->_logger->debug("[isInFilter] The domain '{$domain}' is NOT in the filter, or request failed.");
return false;
}
/**
* Retrieve the username associated with the domainname
*
* @param string $domain Domain to check
*
* @return string Username
* @access public
*/
public function getDomainUser($domain)
{
/** A request-level cache for domain -> user relations */
if (!function_exists('mb_strtolower')) { // if zend.multibyte extensions is off, script will crash when we want to use mb_strtolower
$toLower = 'strtolower';
} else {
$toLower = 'mb_strtolower';
mb_internal_encoding('UTF-8');
}
$domainUsersCache = array();
if (empty($domainUsersCache)) {
$data = $this->listaccts();
if (isset($data['acct']) && is_array($data['acct'])) {
foreach ($data['acct'] as $entry) {
if (isset($entry['domain'], $entry['user'])) {
$domainUsersCache[strtolower($entry['domain'])] = $entry['user'];
}
}
}
}
$idn = new IDNA_Convert();
$domain = strtolower($idn->encode($domain));
/**
* Perhaps it isn't an account domain?
*
* @see https://trac.spamexperts.com/ticket/17588
*/
if (!isset($domainUsersCache[$domain])) {
/** @noinspection PhpUndefinedClassInspection */
$cacheKey = SpamFilter_Core::getDomainsCacheId();
/** @noinspection PhpUndefinedClassInspection */
$domains = SpamFilter_Panel_Cache::get($cacheKey);
// No cache set, proceed with retrieval
if (!$domains) {
/** @noinspection PhpUndefinedClassInspection */
$domains = $this->getDomains(array('username' => SpamFilter_Core::getUsername(), 'level' => 'owner'));
// Cache miss, save the data
/** @noinspection PhpUndefinedClassInspection */
SpamFilter_Panel_Cache::set($cacheKey, $domains);
}
if (is_array($domains)) {
foreach ($domains as $d) {
if ($d['domain'] == $domain && isset($d['owner_domain'])) {
$domain = $toLower($d['owner_domain']);
break;
}
}
}
}
return (isset($domainUsersCache[$domain]) ? $domainUsersCache[$domain] : false);
}
/**
* Retrieve the reseller associated with the domainname, used for reseller-centric actions
*
* @param string $domain Domain to check
*
* @return string Username
* @access public
* @see listaccts()
*/
public function getDomainOwner($domain)
{
$data = $this->listaccts(
array('username' => $domain, 'level' => 'domain')
); // An alternative solution is using "domainuserdata"
$owner = (isset($data['acct']['0']['owner'])) ? trim($data['acct']['0']['owner']) : '';
if (!empty($owner)) {
// The username is apparnatly set :-)
$this->_logger->debug("Username is set, so returning it.");
return $owner;
}
$this->_logger->debug("Username is not set / is unknown.");
return false;
}
/**
* Sets the configured brand in the panel
*
* @param array $aBrand Array of brand data
*
* @return bool Status
*
* @see generatePluginConfig()
*
* @access public
*/
public function setBrand($aBrand)
{
$this->_logger->info("[Cpanel] Pushing setBrand to plugin config generator.");
$this->_logger->debug("[Cpanel] setBrand for brandname: {$aBrand['brandname']}");
$this->_logger->debug("[Cpanel] setBrand for brandicon: {$aBrand['brandicon']}");
/**
* Update plugin icon
*
* @see https://trac.spamexperts.com/ticket/17790#comment:2
*/
if (!empty($aBrand['brandicon']) && function_exists('imagecreatefromstring') && function_exists('imagegif')) {
$png = imagecreatefromstring(base64_decode($aBrand['brandicon']));
if (false !== $png) {
imagegif($png, '/usr/local/cpanel/whostmgr/docroot/themes/x/icons/prospamfilter.gif');
imagedestroy($png);
}
}
return $this->generatePluginConfig($aBrand['brandname'], $aBrand['brandicon']);
}
/**
* generatePluginConfig
* Generates the plugin file used by cPanel
*
* @param string $brandname Brandname to use (optional)
* @param string $brandicon Brand icon to use (optional)
*
* @return bool Status
*
* @access public
* @see SpamFilter_Brand
*
*/
public function generatePluginConfig($brandname = null, $brandicon = null)
{
//find old brandincons
$oldIcons = array_merge(
glob("/usr/local/cpanel/base/frontend/x3/branding/prospamfilter*"),
glob("/usr/local/cpanel/base/frontend/paper_lantern/branding/psf*")
);
$this->_logger->info("[Cpanel] Generating cpanelplugin");
$pluginfile = "/usr/local/prospamfilter/frontend/cpanel/cpanel11/prospamfilter.cpanelplugin";
$branding = new SpamFilter_Brand();
if (empty($brandname)) {
// No name provided, generate it.
$brandname = $branding->getBrandUsed();
} else {
$this->_logger->info("[Cpanel] Generating for '{$brandname}'");
}
// Process
if ((isset($brandname)) && (!empty($brandname))) {
$this->_logger->info("[Cpanel] Generating for '{$brandname}'");
if ((isset($brandicon)) && (!empty($brandicon))) {
$this->_logger->info("[Cpanel] Using custom icon");
$icon_content = $brandicon;
} else {
$this->_logger->info("[Cpanel] Using configured/default icon");
$icon_content = $branding->getBrandIcon();
}
$file_content = "#cpanel plugin file 2.0 (for use with /usr/local/cpanel/bin/register_cpanelplugin)\n";
$file_content .= "version: 2.0\n";
$file_content .= "name:prospamfilter\n";
$file_content .= "description:{$brandname}\n";
$file_content .= "featuremanager:1\n";
$file_content .= "url:prospamfilter/index.html\n";
$file_content .= "acontent:\n";
$file_content .= "onclick:\n";
$file_content .= "if:\n";
$file_content .= "itemorder:999\n";
$file_content .= "itemdesc:{$brandname}\n";
$file_content .= "group:mail\n";
// paper_lantern config update
$paper_brand = array(
'icon' => "se-logo.png",
'group_id' => "email",
'order' => 999,
'name' => $brandname,
'type' => "link",
'id' => "psf",
'uri' => "prospamfilter/index.html",
'feature' => "prospamfilter",
);
//update icon for the IE
if (!empty($icon_content) && is_link("/usr/local/prospamfilter/frontend/cpanel/psf")) {
file_put_contents(
"/usr/local/prospamfilter/frontend/cpanel/psf/brandicon.png",
base64_decode($icon_content)
);
}
$icon_content = chunk_split($icon_content, 76, "\n");
$file_content .= "image:{$icon_content}";
// Write file content
$this->_logger->debug("[Cpanel] Writing cpanelplugin content to plugin file.");
$written = file_put_contents($pluginfile, $file_content);
$this->_logger->debug("[Cpanel] Writing paper_lantern new branding config.");
// Write plugin file for paper_lantern
if (!empty($icon_content)) {
file_put_contents(
"/usr/local/prospamfilter/bin/cpanel/paper_lantern/psf_button/" . $paper_brand['icon'],
base64_decode($icon_content)
);
}
file_put_contents(
"/usr/local/prospamfilter/bin/cpanel/paper_lantern/psf_button/install.json",
'[' . json_encode($paper_brand, 128) . ']'
);
//creating new archive containing the new config
shell_exec("cd /usr/local/prospamfilter/bin/cpanel/paper_lantern/ && tar cfj psf.tar.bz2 psf_button");
if (!$written) {
// Failed writing to the file.
} else {
// Cleanup old left overs
$this->_logger->debug("[Cpanel] Checking for leftover images blocking icon generation..");
foreach ($oldIcons as $filename) {
$this->_logger->debug("[Cpanel] Removing leftover: {$filename}.");
unlink($filename);
}
// Re-register plugin
$this->_logger->debug("[Cpanel] Re-registering plugin for paper_lantern + jupiter & generating sprites.");
$buttonConfig = '/usr/local/prospamfilter/bin/cpanel/paper_lantern/psf.tar.bz2';
if (is_dir('/usr/local/cpanel/base/frontend/paper_lantern')) {
shell_exec("/usr/local/cpanel/scripts/install_plugin {$buttonConfig} --theme paper_lantern");
}
if (is_dir('/usr/local/cpanel/base/frontend/jupiter')) {
shell_exec("/usr/local/cpanel/scripts/install_plugin {$buttonConfig} --theme jupiter");
}
$this->_logger->debug("[Cpanel] Re-registering plugin for other themes & generating sprites.");
// Creating dynamicui directory
// @see https://trac.spamexperts.com/ticket/22354
if (is_dir('/usr/local/cpanel/base/frontend/x3')
&& !is_dir('/usr/local/cpanel/base/frontend/x3/dynamicui')
) {
mkdir('/usr/local/cpanel/base/frontend/x3/dynamicui');
}
$status = shell_exec("/usr/local/cpanel/bin/register_cpanelplugin {$pluginfile}");
//@see https://trac.spamexperts.com/ticket/17098
$this->_logger->debug("[Cpanel] Setting up brand name for the side-menu");
$cgi = '/usr/local/prospamfilter/frontend/whm/prospamfilter.php';
if (file_exists($cgi)) {
shell_exec("sed -e 's/#WHMADDON:prospamfilter:.*/#WHMADDON:prospamfilter:{$brandname}/' -i {$cgi}");
$appConfigFile = '/usr/local/prospamfilter/bin/cpanel/appconfig/prospamfilter_whm.conf';
shell_exec("/usr/local/cpanel/bin/register_appconfig $appConfigFile");
} else {
$this->_logger->err("[Cpanel] Could not set brand name to sidemenu. {$cgi} doesn't exist");
}
if (strstr($status, 'Register Complete')) {
$this->_logger->info("[Cpanel] Regenerating cpanelplugin has been completed.");
return true;
}
}
}
$this->_logger->err("[Cpanel] Regenerating cpanelplugin has FAILED");
return false;
}
/**
* Collection domains getter
*
*
* @param bool $informer
*
* @return array of domais
*
* @access public
*
* @todo Check if this still works with the new paneldriver
*/
public function getCollectionDomains($informer = false)
{
$collectionDomains = SpamFilter_Panel_Cache::get('collectiondomains');
if ($informer) {
/** @var $sessionManager stdClass */
$sessionManager = new SpamFilter_Session_Namespace;
$sessionManager->bulkprotectinformer = 'Getting list of domains...';
$sessionManager->bulkprotectinformerstatus = 'run';
}
if (!$collectionDomains) {
// Get all domains
$local_accounts = $this->getLocalAccounts();
if (is_array($local_accounts) && 0 < sizeof($local_accounts)) {
foreach ($local_accounts as $account) {
$accountInstance = new SpamFilter_Panel_Account($account,
!$this->_config->handle_only_localdomains, $this->_api);
$user = $accountInstance->getUser();
$collectionDomains[] = array(
'name' => $accountInstance->getDomain(),
'type' => 'domain',
'user' => $user
);
// Additional domains:
// Check if we have to handle additional domains
if ($this->_config->handle_extra_domains) {
// Check if there is a username, we need this to look for addons.
if (!empty($user)) {
$this->_logger->debug("Handling additional domains for '{$account['user']}'.");
/*
Add Parked Domains.
*/
if ($informer && isset($sessionManager)) {
$sessionManager->bulkprotectinformer = 'Getting list of parked domains...';
}
$parkedDomains = $this->getParkedDomains($accountInstance->getUser());
if (empty($parkedDomains)) {
// No results.
$this->_logger->debug(
"No parked domains domains to add for '{$account['user']}' ('{$account['domain']}')."
);
} else {
$this->_logger->debug(
"Adding parked domains for '{$account['user']}' ('{$account['domain']}')."
);
foreach ($parkedDomains as $parked) {
$collectionDomains[] = array(
'name' => $parked['alias'],
'owner_domain' => $account['domain'],
'type' => 'parked',
'user' => $user,
);
}
}
/*
Add Addon Domains.
*/
if ($informer && isset($sessionManager)) {
$sessionManager->bulkprotectinformer = 'Getting list of addon domains...';
}
$addonDomains = $this->getAddonDomains($account['user']);
if (empty($addonDomains)) {
// No results.
$this->_logger->debug("No additional domains to add for '{$account['user']}'.");
} else {
$this->_logger->debug("Adding additional domains for '{$account['user']}'.");
foreach ($addonDomains as $addon) {
$collectionDomains[] = array(
'name' => $addon['alias'],
'owner_domain' => $account['domain'],
'type' => 'addon',
'user' => $user
);
}
}
/*
Add Subomains.
*/
if ($informer && isset($sessionManager)) {
$sessionManager->bulkprotectinformer = 'Getting list of subdomains...';
}
$subDomains = $this->getsubDomains($account['user']);
if (empty($subDomains)) {
// No results.
$this->_logger->debug("No additional subdomains to add for '{$account['user']}'.");
} else {
$this->_logger->debug("Adding additional subdomains for '{$account['user']}'.");
foreach ($subDomains as $sub) {
$collectionDomains[] = array(
'name' => $sub['alias'],
'owner_domain' => $account['domain'],
'type' => 'subdomain',
'user' => $user
);
}
}
} else {
$this->_logger->debug("Cannot add subdomains because we don't have a username");
}
}
}
$this->_logger->info("Collection has completed.");
} else {
$this->_logger->info("Nothing to protect.");
if ($informer && isset($sessionManager)) {
$sessionManager->bulkprotectinformer = 'Nothing to protect.';
}
}
if (!$collectionDomains) {
$collectionDomains = array();
}
// Filter duplicates from the result
// @see https://trac.spamexperts.com/software/ticket/14697
$collectionDomains = self::multidimArrayUnique($collectionDomains, 'name');
// Refresh domains cache
SpamFilter_Panel_Cache::set('collectiondomains', $collectionDomains);
}
if ($informer && isset($sessionManager)) {
$sessionManager->bulkprotectinformerstatus = 'protecting';
if (is_array($collectionDomains) && 0 < count($collectionDomains)) {
$sessionManager->bulkprotectinformer
= 'List of domains has completed. Starting the process of protecting...';
}
}
$order = SpamFilter_Panel_Cache::get('domains_sort_order');
$order = (!empty($order)) ? $order : 'asc';
return $this->getSortedDomains(
array('domains' => $collectionDomains, 'order' => $order, 'forbulkprotect' => true)
);
}
/**
* Retrieve all server IP's
*
* @return array of IP addresses
*
* @access public
*/
public function getIpAddresses()
{
$args = array(
'api.version' => 1,
);
try {
$response = $this->_api->getWhm()->makeQuery('listips', $args);
$arr = $response->getResponse('array');
$ips = array();
if (isset($arr) && is_array($arr) && isset($arr['data']) && isset($arr['data']['ip'])) {
foreach ($arr['data']['ip'] as $ipset) {
$this->_logger->debug("Adding IP {$ipset['ip']} to the list of IP's to return");
$ips[] = $ipset['ip'];
}
}
$this->_logger->debug("Returning ips:" . serialize($ips));
return $ips;
} catch (Exception $e) {
// Something went awry :(
$this->_logger->err("Retrieval of IP addresses has failed (" . $e->getMessage() . ")");
}
return false;
}
/**
* Migrate all domains to a diffent user
*
* @param $params Array of parameters
*
* @return bool status
*
* @access public
*/
public function migrateDomainsTo($params)
{
if (!is_array($params)) {
$this->_logger->err("Unable to migrate due to incorrect request.");
return false;
}
if (!isset($params['username'])) {
$this->_logger->err("Unable to migrate due to missing username.");
return false;
}
if (!isset($params['password'])) {
$this->_logger->err("Unable to migrate due to missing password.");
return false;
}
$username = $params['username'];
$password = $params['password'];
$this->_logger->info("Going to migrate all domains to {$username}.");
// Retrieve all domains
$collectionDomains = $this->getCollectionDomains();
$domains = array();
foreach ($collectionDomains as $collectionDomain) {
$domains[] = $collectionDomain['name'];
}
$countDomains = count($domains);
if ($countDomains > 0) {
$this->_logger->info("Starting migration.");
$hook = new SpamFilter_Hooks;
$freelimit = $hook->GetFreeLimit($username, $password);
$is_success = false;
if ('unlimit' == $freelimit || $freelimit - $countDomains >= 0) {
$limitdomain = null;
$notmoved = array();
$moved = array();
$rejected = array();
while (!empty($domains)) {
$domain_chunk = array_splice($domains, 0, 25);
$response = $hook->MigrateDomain($domain_chunk, $username, $password);
$limitdomain = (!empty($response['result']['limit']) && is_null($limitdomain))
? $response['result']['limit'] : 0;
if (!empty($response['result']['notmoved'])) {
$notmoved = array_merge($notmoved, (array)$response['result']['notmoved']);
}
if (!empty($response['result']['rejected'])) {
$rejected = array_merge($rejected, (array)$response['result']['rejected']);
}
if (!empty($response['result']['moved'])) {
$moved = array_merge($moved, (array)$response['result']['moved']);
}
}
$this->_logger->info("Migration completed.");
$messages = array();
if (count($notmoved) > 0 || count($rejected) > 0) {
if (count($notmoved) > 0) {
$messages[] = array(
'message' => 'Domains have not been migrated: ' . join(', ', $notmoved),
'status' => 'error'
);
}
if (count($rejected) > 0) {
$messages[] = array(
'message' => "You don't own some of the listed domains so they aren't moved: " . join(
', ',
$rejected
),
'status' => 'error'
);
}
if (count($moved) > 0) {
$messages[] = array(
'message' => "Domains have been migrated: " . join(', ', $moved),
'status' => 'success'
);
$is_success = true;
}
} else {
$messages[] = array('message' => 'Domains have been migrated to new user.', 'status' => 'success');
$is_success = true;
}
return array('is_success' => $is_success, 'messages' => $messages);
} else {
$errMsg = "Migration failed. The new user's domains limit is lower than required for the migration.";
$this->_logger->err($errMsg);
$messages[] = array('message' => $errMsg, 'status' => 'error');
return array('is_success' => $is_success, 'messages' => $messages);
}
}
$this->_logger->err("Migration failed.");
return false;
}
/**
* Retrieve all unique domains
*
* @param array $domains Array containing domain entry
*
* @return bool|array Array of unique domains
*
* @access public
*/
public function getUniqueDomains($domains)
{
// Return a unique array, since the OWNER= can also contain USER=
if (is_array($domains)) {
$unique = $domain_names = array();
foreach ($domains as $domain) {
if (!empty($domain['domain']) && !in_array($domain['domain'], $domain_names)) {
$unique[] = $domain;
$domain_names[] = $domain['domain'];
}
}
return $unique;
}
return false;
}
/**
* Retrieve username for the domain from the collection domains list
*
* @param $domain string domain name
*
* @return string
*
* @access public
*/
public function getUsernameByDomain($domain)
{
$username = null;
$usernamesCacheId = 'domain_users';
$domains = SpamFilter_Panel_Cache::get($usernamesCacheId);
if (empty($domains)) {
$domains = $this->getDomains(
array(
'username' => SpamFilter_Core::getUsername(),
'level' => 'owner',
)
);
SpamFilter_Panel_Cache::set($usernamesCacheId, $domains, 1800);
}
if (is_array($domains)) {
foreach ($domains as $d) {
if ($domain == $d['domain']) {
$username = (!empty($d['user']) ? $d['user'] : null);
break;
}
}
}
if (is_null($username)) {
$owners = SpamFilter_Panel_Cache::get('owners');
if (is_array($owners) && array_key_exists($domain, $owners)) {
$username = $owners[$domain];
}
}
return $username;
}
/**
* Retrieve sorted domains list
*
* @param $params [domains array, order string ('asc', 'desc'), forbulkprotect boolean]
*
* @return array
*
* @access public
*/
public function getSortedDomains($params)
{
$domains = (!empty($params['domains'])) ? $params['domains'] : null;
$order = (!empty($params['order'])) ? $params['order'] : 'asc';
$forbulkprotect = (!empty($params['forbulkprotect'])) ? $params['forbulkprotect'] : false;
$domain_name = ($forbulkprotect) ? 'name' : 'domain';
if (is_array($domains)) {
$unique = $domain_names = $domains_sorted = array();
foreach ($domains as $key => $domain) {
if (!empty($domain[$domain_name]) && !in_array($domain[$domain_name], $domain_names)) {
$unique[$domain[$domain_name]] = $key;
$domain_names[] = $domain[$domain_name];
}
}
if ('asc' == $order) {
ksort($unique);
} else {
krsort($unique);
}
foreach ($unique as $key) {
$domains_sorted[] = $domains[$key];
}
return $domains_sorted;
}
return false;
}
/**
* Returns domains matched with filter
*
* @param $params [domains array, filter string]
*
* @return array
*
* @access public
*/
public function filterDomains($params)
{
$filter = $params['filter'];
$filteredDomains = array();
foreach ($params['domains'] as $domain) {
if (strpos($domain['domain'], $filter) !== FALSE) {
$filteredDomains[] = $domain;
}
}
return $filteredDomains;
}
/**
* Method for array uniqualization. Can be applied for multidimensional
* arrays only. Does comparison based on string representation on entry
*
* @static
* @access public
*
* @param array $array
* @param string $uniqueField
*
* @return array
*/
final public static function multidimArrayUnique(array $array, $uniqueField)
{
$addedValues = $result = array();
foreach ($array as $entry) {
if (!empty($entry[$uniqueField]) && empty($addedValues[$entry[$uniqueField]])) {
$addedValues[$entry[$uniqueField]] = 1;
$result[] = $entry;
}
}
return $result;
}
/**
* Returns package name assigned to domain
*
* @param $domain - domain
*
* @return string - package name
*
* @access public
*/
public function getDomainPackage($domain)
{
$idn = new IDNA_Convert();
$args['domain'] = $idn->encode($domain);
try {
$response = $this->_api->getWhm()->makeQuery('accountsummary', $args);
$arr = $response->getResponse('array');
} catch (Exception $e) {
$this->_logger->crit("Exception caught in " . __METHOD__ . " method : " . $e->getMessage());
}
return $arr['acct'][0]['plan'];
}
/**
* Returns package name assigned to domain
*
* @param $domain - domain
*
* @return string - package name
*
* @access public
*/
public function getUserPackages($user)
{
$args['user'] = $user;
try {
$response = $this->_api->getWhm()->makeQuery('accountsummary', $args);
$arr = $response->getResponse('array');
} catch (Exception $e) {
$this->_logger->crit("Exception caught in " . __METHOD__ . " method : " . $e->getMessage());
}
return $arr['acct'][0]['plan'];
}
/**
* Returns name of feature list assigned to package
*
* @param $package - package
*
* @return string - feature list name
*
* @access public
*/
public function getFeatureList($package)
{
$args['api.version'] = '1';
$args['pkg'] = $package;
try {
$response = $this->_api->getWhm()->makeQuery('getpkginfo', $args);
$arr = $response->getResponse('array');
} catch (Exception $e) {
$this->_logger->crit("Exception caught in " . __METHOD__ . " method : " . $e->getMessage());
}
return $arr['data']['pkg']['FEATURELIST'];
}
/**
* Check if domain has enabled feature
*
* @param $domain - domain
*
* @return boolean
*
* @access public
*/
public function hasFeatureEnabled($domain)
{
$this->_logger->debug(__METHOD__ . " checking for domain '{$domain}'");
$package = $this->getDomainPackage($domain);
$args['featurelist'] = $this->getFeatureList($package);
$args['api.version'] = '1';
$this->_logger->debug(__METHOD__ . " package for domain '{$domain}' is '{$package}'");
$this->_logger->debug(__METHOD__ . " feature list for package '{$package}' is '{$args['featurelist']}'");
$hasFeatureEnabled = false;
try {
$response = $this->_api->getWhm()->makeQuery('get_featurelist_data', $args);
$arr = $response->getResponse('array');
if ($arr['metadata']['result'] === 1) {
foreach ($arr['data']['features'] as $feature) {
if ($feature['id'] === 'prospamfilter') {
if ($feature['value'] === "1" && $feature['is_disabled'] === "0") {
$hasFeatureEnabled = true;
} else {
if ($feature['value'] === "0") {
$this->_logger->debug(__METHOD__ . " not included in feature list '{$args['featurelist']}'");
}
if ($feature['is_disabled'] === "1") {
$this->_logger->debug(__METHOD__ . " included in feature list 'disabled'");
}
}
break;
}
}
}
} catch (Exception $e) {
$this->_logger->crit("Exception caught in " . __METHOD__ . " method : " . $e->getMessage());
}
$this->_logger->debug(__METHOD__ . " result for domain '{$domain}': " . var_export($hasFeatureEnabled, true));
return $hasFeatureEnabled;
}
/**
* Check if reseller has enabled feature
*
* @param $user
*
* @return boolean
*
* @access public
*/
public function resellerHasFeatureEnabled($user)
{
$this->_logger->debug("Checking if Prospamfilter feature is available for " . $user);
$args = array(
'api.version' => '1',
'user' => $user,
'feature' => 'prospamfilter'
);
$hasFeature = false;
try {
$response = $this->_api->getWhm()->makeQuery('verify_user_has_feature', $args);
$arr = $response->getResponse('array');
$hasFeature = $arr['data']['has_feature'];
} catch (Exception $e) {
$this->_logger->crit("Exception caught in " . __METHOD__ . " method : " . $e->getMessage());
}
$this->_logger->debug("Prospamfilter feature is available result: " . $hasFeature);
return $hasFeature;
}
/**
* Gather all Installed Hooks
*
* @return array
*
* @access public
*/
public function listHooks()
{
$json = shell_exec("/usr/local/cpanel/bin/manage_hooks list --output JSON");
$array = json_decode($json, TRUE);
if (is_array($array)) {
return $array;
} else {
die("Cannot gather hook list! Aborted!");
}
}
/**
* Check if hook is already added into panel
*
* @hooks - array with data of hooks
* @file - script file name
*
* @return bool
*
* @access public
*/
public function isHookExists(array $hooks, $category, $event, $stage, $file)
{
if (!empty($hooks[$category][$event])) {
foreach ($hooks[$category][$event] as $record) {
if (strpos($record['hook'], $file) !== FALSE && $record['stage'] == $stage) {
return true;
}
}
}
return false;
}
/**
* Adding or delete hook using manage_hook functionality
*
* @params - array with data of hooks
* @see guide to standarized hooks at : https://documentation.cpanel.net/display/SDK/Guide+to+Standardized+Hooks+-+Hookable+Events
*
* @return array
*
* @access public
*/
public function manageHooks($params)
{
$registeredHooks = $this->listHooks();
$file = $params['file'];
$do = $params['do'];
if (!is_executable($file)) {
shell_exec('chmod +x ' . $file);
}
foreach ($params['hooks'] as $hook) {
if ($do == 'add' && $this->isHookExists($registeredHooks, $hook['category'], $hook['event'], $hook['stage'], $file)) {
echo 'Skipped hook ' . $hook['event'] . ' as it already exists.' . PHP_EOL;
continue;
} else {
$commandStr = "/usr/local/cpanel/bin/manage_hooks " . $do . " script " . $file . " --manual --category " . $hook['category'] . " --event '" . $hook['event'] . "' --stage " . $hook['stage'] . " --action='" . $hook['action'] . "'";
if (isset($hook['escalateprivs']) && $hook['escalateprivs']) {
$commandStr .= " --escalateprivs";
}
system($commandStr);
}
}
return "Done." . PHP_EOL;
}
/**
* Returns main domain of user.
*
* @param $user - user name
*
* @return string - main domain
*
* @access public
*/
public function getMainDomain($user)
{
$args['user'] = $user;
$response = $this->_api->getWhm()->makeQuery('accountsummary', $args);
$arr = $response->getResponse('array');
return $arr['acct'][0]['domain'];
}
/**
*
*
*/
public function isDisabledForResellers()
{
return $this->_config->disable_reseller_access ? true : false;
}
}