<?php

//change the following path if you have moved the Accounting.php.inc file into your home dir path
$accountingPath = "/usr/local/cpanel/Cpanel/Accounting.php.inc";

//do not change anything below here unless you know what you are doing
if (@is_file($accountingPath)) {
    require_once $accountingPath;
} else {
    require_once dirname(__FILE__).'/Accounting.php.inc';
}

require_once 'newedge/classes/NE_MailGateway.php';
require_once 'modules/admin/models/ServerPlugin.php';
require_once dirname(__FILE__).'/CpanelApi.php';

/**
 * cPanel Plugin for ClientExec
 * @package Plugins
 * @version August.07.2010
 * @lastAuthor Jamie Chung
 * @email jamie@clientexec.com
 */
class PluginCpanel extends ServerPlugin
{
	var $api;

	/**
	 * Sets up the CpanelApi object in order to make requests to the server.
	 * @param <type> $args Standard set of arguments in order to make API request.
	 */
	public function setup ( $args )
	{
		$this->api = new CpanelApi ($args['ServerHostName'],
                            $args['plugin_cpanel_Username'],
                            $args['plugin_cpanel_Access_Hash'],
                            $args['plugin_cpanel_Use_SSL']);

		$this->api->setupLogger ( $this->logger, 4);
	}

	/**
	 * Emails cPanel server errors.
	 * @param String $name
	 * @param String $message
	 * @param Array $args
	 * @return string
	 */
	function email_error ( $name, $message, $args )
	{
		$error = "Cpanel Account ".$name." Failed. ";
		$error .= "A email with the Details was sent to ". $args['plugin_cpanel_Failure_E-mail'].'<br /><br />';

		if ( is_array($message) )
		{
			$message = implode ( "\n", trim($message) );
		}

		$this->logger->log(2, 'cPanel Error: '.print_r(array('type' => $name, 'error' => $error, 'message' => $message, 'params' => $args), true));

		if ( !empty($args['plugin_cpanel_Failure_E-mail']) )
		{
			$mailGateway = new NE_MailGateway();
			$mailGateway->mailMessageEmail( $message,
							   $args['plugin_cpanel_Failure_E-mail'],
							   "Cpanel Plugin",
							   $args['plugin_cpanel_Failure_E-mail'],
							   "",
							   "Cpanel Account ".$name." Failure");
		}

		return $error.nl2br($message);
	}

	function getVariables()
	{
	   /* Specification
		    itemkey     - used to identify variable in your other functions
		    type        - text,textarea,yesno,password,hidden ( type hidden are variables used by CE and are required )
		    description - description of the variable, displayed in ClientExec
		    encryptable - used to indicate the variable's value must be encrypted in the database
	   */

        $variables = array (
                   /*T*/"Name"/*/T*/ => array (
                                        "type"=>"hidden",
                                        "description"=>"Used By CE to show plugin - must match how you call the action function names",
                                        "value"=>"CPanel"
                                       ),
                   /*T*/"Description"/*/T*/ => array (
                                        "type"=>"hidden",
                                        "description"=>/*T*/"Description viewable by admin in server settings"/*/T*/,
                                        "value"=>/*T*/"CPanel control panel integration"/*/T*/
                                       ),
                   /*T*/"Username"/*/T*/ => array (
                                        "type"=>"text",
                                        "description"=>/*T*/"Username used to connect to server"/*/T*/,
                                        "value"=>""
                                       ),
                   /*T*/"Access Hash"/*/T*/ => array (
                                        "type"=>"textarea",
                                        "description"=>/*T*/"Password used to connect to server"/*/T*/,
                                        "value"=>"",
                                        "encryptable"=>true
                                       ),
                   /*T*/"Use SSL"/*/T*/ => array (
                                        "type"=>"yesno",
                                        "description"=>/*T*/"Set NO if you do not have PHP compiled with cURL.  YES if your PHP is compiled with cURL<br><b>NOTE:</b>It is suggested that you keep this as YES"/*/T*/,
                                        "value"=>"1"
                                       ),
                   /*T*/"Failure E-mail"/*/T*/ => array (
                                        "type"=>"text",
                                        "description"=>/*T*/"E-mail address Cpanel error messages will be sent to"/*/T*/,
                                        "value"=>""
                                        ),
                   /*T*/"Actions"/*/T*/ => array (
                                        "type"=>"hidden",
                                        "description"=>/*T*/"Current actions that are active for this plugin per server"/*/T*/,
                                        "value"=>"Create,Delete,Update,Suspend,UnSuspend"
                                       ),
                    /*T*/'reseller'/*/T*/  => array(
                                        'type'          => 'hidden',
                                        'description'   => /*T*/'Whether this server plugin can set reseller accounts'/*/T*/,
                                        'value'         => '1',
                                       ),
                    /*T*/'reseller_acl_fields'/*/T*/ => array(
                                        'type'          => 'hidden',
                                        'description'   => /*T*/'ACL field for reseller account'/*/T*/,
                                        'value'         => array(
                                                            'acl-rslimit-disk'      => /*T*/'Disk space in MB<br />(If you wish to set Disk space AND Bandwitdh as unlimited, leave this field empty.<br />if you wish to limit Bandwidth but not Disk Space, enter a very large number here)'/*/T*/,
                                                            'acl-rsolimit-disk'     => /*T*/'Disk space overselling allowed'/*/T*/,
                                                            'acl-rslimit-bw'        => /*T*/'Bandwidth in MB<br />(If you wish to set Disk space AND Bandwitdh as unlimited, leave this field empty.<br />if you wish to limit Disk Space but not Bandwidth, enter a very large number here)'/*/T*/,
                                                            'acl-rsolimit-bw'       => /*T*/'Bandwidth overselling allowed'/*/T*/,
                                                            'acl-list-accts'        => /*T*/'List Accounts'/*/T*/,
                                                            'acl-show-bandwidth'    => /*T*/'View Account Bandwidth Usage'/*/T*/,
                                                            'acl-create-acct'       => /*T*/'Account Creation'/*/T*/,
                                                            'acl-edit-account'      => /*T*/'Account Modification'/*/T*/,
                                                            'acl-suspend-acct'      => /*T*/'Account Suspension'/*/T*/,
                                                            'acl-kill-acct'         => /*T*/'Acccount Termination'/*/T*/,
                                                            'acl-upgrade-account'   => /*T*/'Account Upgrades'/*/T*/,
                                                            'acl-limit-bandwidth'   => /*T*/'Bandwidth Limiting Modification'/*/T*/,
                                                            'acl-edit-mx'           => /*T*/'Edit MX Entries'/*/T*/,
                                                            'acl-frontpage'         => /*T*/'Enabling/Disabling FrontPage Extensions'/*/T*/,
                                                            'acl-mod-subdomains'    => /*T*/'Enabling/Disabling SubDomains'/*/T*/,
                                                            'acl-passwd'            => /*T*/'Password Modification'/*/T*/,
                                                            'acl-quota'             => /*T*/'Quota Modification'/*/T*/,
                                                            'acl-res-cart'          => /*T*/'Reset Shopping Cart'/*/T*/,
                                                            'acl-ssl-gencrt'        => /*T*/'SSL CSR/CRT Generator'/*/T*/,
                                                            'acl-ssl'               => /*T*/'SSL Site Management'/*/T*/,
                                                            'acl-demo-setup'        => /*T*/'Turn an account into a demo account'/*/T*/,
                                                            'acl-rearrange-accts'   => /*T*/'Rearrange Accounts'/*/T*/,
                                                            'acl-clustering'        => /*T*/'Clustering'/*/T*/,
                                                            'acl-create-dns'        => /*T*/'Add DNS'/*/T*/,
                                                            'acl-edit-dns'          => /*T*/'Edit DNS'/*/T*/,
                                                            'acl-park-dns'          => /*T*/'Park DNS'/*/T*/,
                                                            'acl-kill-dns'          => /*T*/'Remove DNS'/*/T*/,
                                                            'acl-add-pkg'           => /*T*/'Add/Remove Packages'/*/T*/,
                                                            'acl-edit-pkg'          => /*T*/'Edit Packages'/*/T*/,
                                                            'acl-add-pkg-shell'     => /*T*/'Allow Creation of Packages With Shell Access'/*/T*/,
                                                            'acl-allow-unlimited-disk-pkgs' => /*T*/'Allow Creation of Packages with Unlimited Diskspace'/*/T*/,
                                                            'acl-allow-unlimited-pkgs' => /*T*/'Allow Creation of Packages with Unlimited Features'/*/T*/,
                                                            'acl-add-pkg-ip'        => /*T*/'Allow Creation of Packages With a Dedicated IP'/*/T*/,
                                                            'acl-allow-addoncreate' => /*T*/'Allow Creation of Packages with Addon Domains'/*/T*/,
                                                            'acl-allow-parkedcreate'=> /*T*/'Allow Creation of Packages With Parked Domains'/*/T*/,
                                                            'acl-onlyselfandglobalpkgs' => /*T*/'Allow creation of accounts with packages that are global or owned by this user'/*/T*/,
                                                            'acl-disallow-shell'    => /*T*/'Never allow creation of accounts with shell access'/*/T*/,
                                                            'acl-stats'             => /*T*/'View Account Statistics'/*/T*/,
                                                            'acl-status'            => /*T*/'View Server Status'/*/T*/,
                                                            'acl-restart'           => /*T*/'Restart Services'/*/T*/,
                                                            'acl-mailcheck'         => /*T*/'Mail Trouble Shooter'/*/T*/,
                                                            'acl-restftp'           => /*T*/'Resync Ftp Passwords'/*/T*/,
                                                            'acl-news'              => /*T*/'News Modification'/*/T*/,
                                                            // keep this one last because it's too powerful, and better think about users security
                                                            'acl-all'               => /*T*/'All Features (root access)'/*/T*/,
                                                        ),
                                       ),
            /*T*/'package_addons'/*/T*/ => array(
                                        'type'          => 'hidden',
                                        'description'   => /*T*/'Supported signup addons variables'/*/T*/,
                                        'value'         => array(
                                                                'DISKSPACE', 'BANDWIDTH', 'SSL'
                                                                ),
                                       ),
	   );
	   return $variables;
	}

    /**
     * Checks if a plan exists.
     * @param String $plan to check against.
     * @param <type> $args
     * @return boolean
     */
    function CheckCpanelPlan($plan, $args)
    {
	    $this->setup($args);
	    $packages = $this->api->packages();
	    if ( is_array($packages) && isset($packages[$plan]) )
	    {
		    return true;
	    }
	    return false;
    }

    /**
     * Preps for account creation or update.
     * @param <type> $args
     * @return NE_Error
     */
    function validateCredentials($args)
    {
		$this->setup($args);
		$args['DomainUsername'] = trim(strtolower($args['DomainUsername']));

		$errors = array();

        // Ensure that the username is not test and doesn't contain test
        if (strpos(strtolower($args['DomainUsername']), 'test') !== false) {
            if (strtolower($args['DomainUsername']) != 'test') {
                $args['DomainUsername'] = str_replace('test', '', $args['DomainUsername']);
            } else {
                $errors[] = 'Domain username can\'t contain \'test\'';
            }
        }

        // Username cannot start with a number
        if (is_numeric(mb_substr(trim($args['DomainUsername']), 0, 1))) {
            $args['DomainUsername'] = preg_replace("/^\d*/", '', $args['DomainUsername']);

            if (is_numeric(mb_substr(trim($args['DomainUsername']), 0, 1)) || strlen(trim($args['DomainUsername'])) == 0) {
                $errors[] = 'Domain username can\'t start with a number';
            }
        }

        // Username cannot contain a dash (-)
        if (strpos($args['DomainUsername'], "-") !== false) {
            $args['DomainUsername'] = str_replace("-", "", $args['DomainUsername']);
            $errors[] = 'Domain username can\'t contain dashes';
        }

        // Username cannot be greater than 8 characters
        if (strlen($args['DomainUsername']) > 8) {
            $args['DomainUsername'] = mb_substr($args['DomainUsername'], 0, 8);
        }
	else if ( strlen(trim($args['DomainUsername'])) <= 0 )
	{
		$errors[] = 'The cPanel username is blank.';
	}
	else if ( strlen(Trim($args['DomainPassword'])) <= 0 )
	{
		$errors[] = 'The cPanel password is blank';
	}


	// Only make the request if there have been no errors so far.
	if ( count($errors) == 0 )
	{
		// if it's an update, skip username existance check
		// Only perform the request when we are not updating and we are not signinig up.
		/*if ((!isset($args['isUpdate']) || !$args['isUpdate']) && !NE_SIGNUP) {
			$accts = $this->api->accounts();

			$i = 1;
			while (isset($accts[$args['DomainUsername']])) {
				$args['DomainUsername'] = mb_substr($args['DomainUsername'], 0, 7).$i++;
			}
		}*/

		if (strpos($args['DomainPassword'], $args['DomainUsername']) !== false) {
			$errors[] = 'Domain password can\'t contain domain username';
		}
	}

	if ( count($errors) > 0 )
		{
		    $this->logger->log(4, "plugin_cpanel::validate::error: ".print_r($errors,true));
			throw new Exception($errors[0]);
	}

	return $args['DomainUsername'];
    }

    //plugin function called after new account is activated ( approved )
    /**
     * @link http://docs.cpanel.net/twiki/bin/view/AllDocumentation/AutomationIntegration/CreateAccount
     * @param <type> $args
     * @return NE_Error
     */
    function create($args)
    {
	    $this->setup($args);

		$errors = array();

        if (!$args['plugin_cpanel_Create'] == 1) return;
        if ($args['PackageNameOnServer'] == null) {
            throw new Exception("Cpanel create method: This package is not tied to a Cpanel account");
        }

        // package add-ons handling
        if ( isset($args['is_reseller']) && isset($args['package_addons']['DISKSPACE'])) {
            $args['acl-rslimit-disk'] += ((int)$args['package_addons']['DISKSPACE']);
        }
        if ( isset($args['is_reseller']) && isset($args['package_addons']['BANDWIDTH'])) {
            $args['acl-rslimit-bw'] += ((int)$args['package_addons']['BANDWIDTH']) * 1024; // Convert from Gigs to MB
        }
        if ( isset($args['is_reseller']) && isset($args['package_addons']['SSL']) && $args['package_addons']['SSL'] == 1) {
            $args['acl-ssl'] = 1;
        }

	   /*
	    * Checks if the plan exists, otherwise produce an error.
	    */
        if ( !$this->CheckCpanelPlan($args['PackageNameOnServer'], $args) )
		{
			$error = "The package was not found on the server";
			$errors[] = $this->email_error('Creation', $error, $args );
        }

		if ( $args['DomainSharedIP']==0 )
		{
			$error = "At this time we can not create Accounts that do not use the Shared IP";
			$errors[] = $this->email_error('Creation', $error, $args);
		}

	   /*
	    * Let's start the account parameter preperation.
	    */

	   $params = array();

	   // If it is a reseller account, set the proper properties.
	   if ( isset($args['is_reseller']) )
	   {
		   $params['reseller'] = 1;
	   }

	   $params['username'] = $args['DomainUsername'];
	   $params['domain'] = $args['DomainName'];
	   $params['plan'] = urlencode($args['PackageNameOnServer']);
	   $params['password'] = $args['DomainPassword'];
           $params['quota'] = $args['acl-rslimit-disk'];
           $params['bwlimit'] = $args['acl-rslimit-bw'];
           $params['contactemail'] = $args['UserEmail'];
           
	   $request = $this->api->call('createacct', $params);

	   if ( $request->result[0]->status != 1 )
	   {
		   $errors[] = $this->email_error('Creation', $request->result[0]->statusmsg, $args);
	   }
	   else if ( $request->result[0]->status == 1 )
	   {
			//$request = $this->api->call('modifyacct', array('user' => $args['DomainUsername'], 'email'))

            // setup the reseller permissions if necessary
			if ( isset($params['reseller']) && $params['reseller'] == 1 )
			{
				$this->_setResellerACLs($args);
			}
        }
		else
		{
			$errors[] = "Error connecting to cpanel server";
        }

        // Rather than returning an error object every time, just return it here.
        // Need to check if an actual error exists to avoid returning an error object on a success message
        // As the domain.php class now checks for an error object and stops execution if one exists
	    if ( count($errors) > 0 )
	    {
		    $this->logger->log(4, "plugin_cpanel::create::error: ".print_r($errors,true));
		    throw new Exception ( $errors[0] );
	    }

		return;
    }

    function update($args)
    {
	    $this->setup($args);
		$this->logger->log(4, $args);
		$errors = array();

        if(!$args['plugin_cpanel_Update']==1)exit;

		foreach ( array_keys($args) as $keys )
		{
		   // Make sure the changing value is not blank.
		   if ( empty($args[$keys]) )
		   {
			   continue;
		   }

		   switch ( $keys )
		   {

			/**
			* Change Password
			* @link http://docs.cpanel.net/twiki/bin/view/AllDocumentation/AutomationIntegration/ChangePassword
			*/
			   case 'CHANGE_PASSWORD':

					$request = $this->api->call('passwd', array('user' => $args['DomainUsername'], 'pass' => urlencode($args['CHANGE_PASSWORD'])));

					if ( $request->passwd[0]->status != 1 )
					{
						$errors[] = $this->email_error('Password Change', $request->passwd[0]->statusmsg, $args);
					}

				   break;

			/**
			 * Change Package
			 * @link http://docs.cpanel.net/twiki/bin/view/AllDocumentation/AutomationIntegration/ChangePackage
			 */
			   case 'CHANGE_PACKAGE':

					if (!$this->CheckCpanelPlan($args['PackageNameOnServer'], $args)) exit;

					$request = $this->api->call('changepackage', array('user' => $args['DomainUsername'], 'pkg' => urlencode($args['PackageNameOnServer'])));

					if ( $request->result[0]->status != 1 )
					{
						$errors[] = $this->email_error('Plan Change', $request->result[0]->statusmsg, $args);
					}
					else
					{
						// setup or delete the reseller permissions if necessary
						if (isset($args['is_reseller']) && $args['is_reseller'] == 1) {
						   $this->_addReseller($args);
						   $this->_setResellerACLs($args);
						} else {
						   $this->_removeReseller($args);
						}
					}
					break;


			/**
			 * Change Username
			 * @link http://docs.cpanel.net/twiki/bin/view/AllDocumentation/AutomationIntegration/ModifyAccount
			 */
			   case 'CHANGE_USERNAME':

					$request = $this->api->call('modifyacct', array('user' => $args['DomainUsername'], 'newuser' => $args['CHANGE_USERNAME']));

					if ( $request->result[0]->status != 1 )
					{
						$errors[] = $this->email_error('Username Change', $request->result[0]->statusmsg, $args);
					}
					// Internal fix. Just incase the CHANGE_DOMAIN case is called.
					$args['DomainUsername'] = $args['CHANGE_USERNAME'];
				   break;


			/**
			 * Change IP Address
			 * @link http://docs.cpanel.net/twiki/bin/view/AllDocumentation/AutomationIntegration/SetSiteIp
			 */
			   case 'CHANGE_IP':

					$request = $this->api->call('setsiteip', array('user' => $args['DomainUsername'], 'ip' => $args['CHANGE_IP']));

					// setsiteip produces a different JSON structure. weird.
					if ( $request->status != 1 )
					{
						$errors[] = $this->email_error('IP Change', $request->statusmsg, $args);
					}

				   break;

			/**
			 * Change Domain Name
			 * @link http://docs.cpanel.net/twiki/bin/view/AllDocumentation/AutomationIntegration/ModifyAccount
			 */
			   case 'CHANGE_DOMAIN':

					$request = $this->api->call('modifyacct', array('user' => $args['DomainUsername'], 'domain' => $args['CHANGE_DOMAIN']));

					if ( $request->result[0]->status != 1 )
					{
						$errors[] = $this->email_error('Domain Change', $request->result[0]->statusmsg, $args);
					}
				   break;


			   /**
				* Legacy code... trying to find alternative in new API.
				*/
			   case 'CHANGE_EMAIL':

					$whmReq = "/scripts2/dochangeemail?user=".$args['DomainUsername']."&email=".urlencode($args['CHANGE_EMAIL']);
					$changeEmail = $this->_callFunction('whmreq', array($whmReq,
							$args['ServerHostName'],
							$args['plugin_cpanel_Username'],
							$args['plugin_cpanel_Access_Hash'],
							$args['plugin_cpanel_Use_SSL']));

					if (strpos($changeEmail, "Error") == true)
					{
						$changeEmail .= " There was an error changing the Email on Cpanel ";

						$errors[] = $this->email_error('Email', $changeEmail, $args);
					}
					break;
		   }
		}

        // Rather than returning an error object every time, just return it here.
        // Need to check if an actual error exists to avoid returning an error object on a success message
        // As the domain.php class now checks for an error object and stops execution if one exists
	    if ( count($errors) > 0 )
	    {
		    $this->logger->log(4, "plugin_cpanel::update::error: ".print_r($errors,true));
			throw new Exception ( $errors[0] );
	    }
	    else
	    {
		    return;
	    }
	}


    /**
     * Removes an account from the cPanel server.
     * @link http://docs.cpanel.net/twiki/bin/view/AllDocumentation/AutomationIntegration/TerminateAccount
     * @param <type> $args
     * @return NE_Error
     */
    function delete($args)
    {
	    $this->setup($args);
	    $request = $this->api->call('removeacct', array('user' => $args['DomainUsername']));

	    if ( $request->result[0]->status != 1 )
	    {
		    $error = $this->email_error ( 'Deletion', $request->result[0]->statusmsg, $args );
	    }

        // Rather than returning an error object every time, just return it here.
        // Need to check if an actual error exists to avoid returning an error object on a success message
        // As the domain.php class now checks for an error object and stops execution if one exists
	    if ( isset($error) )
	    {
		    $this->logger->log(4, "plugin_cpanel::delete::error: ".$error);
			throw new Exception ( $error );
	    }
	    else
	    {
		    return;
	    }
    }

    /**
     * Suspends an account.
     * @link http://docs.cpanel.net/twiki/bin/view/AllDocumentation/AutomationIntegration/SuspendAccount
     * @link http://docs.cpanel.net/twiki/bin/view/AllDocumentation/AutomationIntegration/SuspendReseller
     * @param <type> $args
     * @return NE_Error
     */
    function suspend($args)
    {
	    $this->setup($args);
	    $action = ( isset($args['is_reseller']) ) ? 'suspendreseller' : 'suspendacct';
	    $request = $this->api->call($action, array('user' => $args['DomainUsername']));

	    if ( $request->result[0]->status != 1 )
	    {
		    $error = $this->email_error( 'Suspension', $request->result[0]->statusmsg, $args );
	    }

        // Rather than returning an error object every time, just return it here.
        // Need to check if an actual error exists to avoid returning an error object on a success message
        // As the domain.php class now checks for an error object and stops execution if one exists
	    if ( isset($error) )
	    {
		    $this->logger->log(4, "plugin_cpanel::suspend::error: ".$error);
			throw new Exception ( $error );
	    }

		return;
    }

    /**
     * Unsuspends an account
     * @link http://docs.cpanel.net/twiki/bin/view/AllDocumentation/AutomationIntegration/UnsuspendAcount
     * @link http://docs.cpanel.net/twiki/bin/view/AllDocumentation/AutomationIntegration/UnsuspendReseller
     * @return NE_Error
     */
    function unsuspend($args)
    {
		$this->setup($args);

		$action = ( isset($args['is_reseller']) ) ? 'unsuspendreseller' : 'unsuspendacct';
		$request = $this->api->call($action, array('user' => $args['DomainUsername']));

		if ( $request->result[0]->status != 1 )
		{
			$error = $this->email_error ( 'Unsuspension', $request->result[0]->statusmsg, $args );
		}

		// Rather than returning an error object every time, just return it here.
		// Need to check if an actual error exists to avoid returning an error object on a success message
		// As the domain.php class now checks for an error object and stops execution if one exists
		if ( isset($error) )
		{
		    $this->logger->log(4, "plugin_cpanel::unsuspend::error: ".$error);
			throw new Exception ( $error );
		}

		return;
    }

    /**
     * Tests that the account credentials are working.
     * @link http://docs.cpanel.net/twiki/bin/view/AllDocumentation/AutomationIntegration/DisplaycPanelWHMVersion
     * @param <type> $args
     * @return NE_Error
     */
    function testLogin($args)
    {
	    $this->setup($args);
	    $version = $this->api->version();

	    $this->logger->log(4, 'Cpanel Version: '.$version);

	    if ( strlen(trim($version)) == 0 )
	    {
		    return new Exception("Connection to server failed.");
	    }
    }


    /**
     * Setup Reseller Account Creatin Limits
     * @link http://docs.cpanel.net/twiki/bin/view/AllDocumentation/AutomationIntegration/SetResellersACLList
     * @param <type> $args
     */
    function _setResellerACLs($args)
    {
	    $this->setup($args);
		// TODO: Verify that these are correct ACLs and what they do.
		$resourceLimits = array('acl-rslimit-disk', 'acl-rsolimit-disk', 'acl-rslimit-bw', 'acl-rsolimit-bw');

		$acls = array();
		foreach ( $args as $key => $value )
		{
			if ( mb_substr ($key, 0, 4) == 'acl-' )
			{
			   if ( in_array($key, $resourceLimits) )
			   {
				   $key = mb_substr($key, 4);
			   }
			   $acls[$key] = $value;
			}
		}

		if ((isset($args['acl-rslimit-disk']) && $args['acl-rslimit-disk'])
		  || (isset($args['acl-rslimit-bw']) && $args['acl-rslimit-bw']))
		{
			$acls['resreslimit'] = 1;
		}

		$request = $this->api->call('setacls', array_merge(array('reseller' => $args['DomainUsername']), $acls));

		if ( $request->result[0]->status != 1 )
		{
			$error = $request->result[0]->statusmsg;
			$this->email_error ( 'Setup Reseller', $error, $args );
		}

		// Rather than returning an error object every time, just return it here.
		// Need to check if an actual error exists to avoid returning an error object on a success message
		// As the domain.php class now checks for an error object and stops execution if one exists
		if ( isset($error) )
		{
		    $this->logger->log(4, "plugin_cpanel::setupreselleracls::error: ".$error);
			throw new Exception ( $error );
		}
    }

    /**
     * Add reseller privileges to an account.
     * @link http://docs.cpanel.net/twiki/bin/view/AllDocumentation/AutomationIntegration/AddResellerPrivileges
     * @param <type> $args
     */
    function _addReseller($args)
    {
		$this->setup($args);
		$request = $this->api->call('setupreseller', array('user' => $args['DomainUsername'], 'makeowner' => 1));

		if ( $request->result[0]->status != 1 )
		{
			$error = $this->email_error ( 'Setup Reseller', $request->result[0]->statusmsg, $args );
		}

		// Rather than returning an error object every time, just return it here.
		// Need to check if an actual error exists to avoid returning an error object on a success message
		// As the domain.php class now checks for an error object and stops execution if one exists
		if ( isset($error) )
		{
		    $this->logger->log(4, "plugin_cpanel::setupreseller::error: ".$error);
			throw new Exception ( $error );
		}
    }

    /**
     * Remove reseller privileges from an account.
     * @link http://docs.cpanel.net/twiki/bin/view/AllDocumentation/AutomationIntegration/RemoveResellerPrivileges
     * @param <type> $args
     */
    function _removeReseller($args)
    {
		$this->setup($args);
		$request = $this->api->call('unsetupreseller', array('user' => $args['DomainUsername'], 'makeowner' => 1));

		if ( $request->result[0]->status != 1 )
		{
			$error = $this->email_error ( 'Unsetup Reseller', $request->result[0]->statusmsg, $args );
		}

		// Rather than returning an error object every time, just return it here.
		// Need to check if an actual error exists to avoid returning an error object on a success message
		// As the domain.php class now checks for an error object and stops execution if one exists
		if ( isset($error) )
		{
		    $this->logger->log(4, "plugin_cpanel::unsetupreseller::error: ".$error);
			throw new Exception ( $error );
		}
    }

    // CPanel throws a lot of E_NOTICES...
    function _callFunction($functionName, $functionParameters)
    {
        $this->logger->log(4, "CPanel::$functionName -> ".$functionParameters[0]);
        $errorReporting = error_reporting();
        error_reporting(E_ALL & ~E_NOTICE);
        $result = call_user_func_array($functionName, $functionParameters);
        error_reporting($errorReporting);

        return $result;
    }

    function readPackageList($data)
    {
        $parser = xml_parser_create();
        xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
        xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
        xml_parse_into_struct($parser, $data, $values, $tags);
        xml_parser_free($parser);

        // loop through the structures
        foreach ($tags as $key=>$val) {
            if ($key == "package") {
                $packageranges = $val;
                // each contiguous pair of array entries are the
                // lower and upper range for each package definition
                for ($i=0; $i < count($packageranges); $i+=2) {
                    $offset = $packageranges[$i] + 1;
                    $len = $packageranges[$i + 1] - $offset;
                    $tdb[] = $this->parsePackage(array_slice($values, $offset, $len));
                }
            } else {
                continue;
            }
        }
        return $tdb;
    }

   function parsePackage($values)
   {
    for ($i=0; $i < count($values); $i++) {
        $pack[$values[$i]["tag"]] = $values[$i]["value"];
    }
    return $pack;
   }
}

?>