<commit_before><?php

/**
 * If you are using autoloader (like you should!) remove this include
 */

include './Exception.php';

/**
 * PHP Cloud Server implementation for RackSpace (tm)
 *
 * THIS SOFTWARE IS PROVIDED "AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @author <NAME> <<EMAIL>>
 * @link http://github.com/AlekseyKorzun/php-cloudservers/
 * @link http://www.schematic.com
 * @version 0.1
 * @license bsd
 */

class Cloud_Server {
    const METHOD_AUTH = 1;
    const METHOD_POST = 2;
    const METHOD_DELETE = 3;
    const METHOD_PUT = 4;

    private $_apiUser;
    private $_apiKey;
    private $_apiToken;

    protected $_apiServerUri;
    protected $_apiAuthUri = 'https://auth.api.rackspacecloud.com/v1.0';

    protected $_apiBackup = array(
        'weekly' => array(
                'DISABLED',
                'SUNDAY',
                'MONDAY',
                'TUESDAY',
                'WEDNESDAY',
                'THURSDAY',
                'FRIDAY',
                'SATURDAY'),
        'daily' => array(
                'DISABLED',
                'H_0000_0200',
                'H_0200_0400',
                'H_0400_0600',
                'H_0600_0800',
                'H_0800_1000',
                'H_1000_1200',
                'H_1400_1600',
                'H_1600_1800',
                'H_1800_2000',
                'H_2000_2200',
                'H_2200_0000'));

    protected $_apiResource;
    protected $_apiAgent = 'PHP Cloud Server client';
    protected $_apiJson;
    protected $_apiResponse;
    protected $_apiResponseCode;
    protected $_apiServers = array();

    protected $_enableDebug = false;

    /**
     * Class constructor
     *
     * @param string $apiId user id that will be used for API
     * @param string $apiKey key that was generated by Rackspace
     * @return null
     */
    function __construct($apiId, $apiKey)
    {
        if (!$apiId || !$apiKey) {
            throw new Cloud_Exception('Please provide valid API credentials');
        }

        $this->_apiUser = $apiId;
        $this->_apiKey = $apiKey;
    }

    /**
     * Get authentication token
     *
     * @return mixed return authentication token or false on failure
     */
    public function getToken()
    {
        if (!empty($this->_apiToken)) {
           return $this->_apiToken;
        }

        return false;
    }

    /**
     * Set authentication token
     *
     * @param string $tokenId token you wish to set
     * @return null
     */
    public function setToken($tokenId)
    {
        $this->_apiToken = $tokenId;
    }

    /**
     * Perform authentication
     *
     * @return string returns recieved token
     */
    public function authenticate () {
        $this->_doRequest(self::METHOD_AUTH);
        return $this->_apiToken;
    }

    /**
     * Performs CURL requests (POST,PUT,DELETE,GET) required by API.
     *
     * @param string $method HTTP method that will be used for current request
     * @throws Cloud_Exception
     * @return null
     */
    private function _doRequest($method = null)
    {
        if (!$this->_apiToken && $method != self::METHOD_AUTH) {
            $this->_doRequest(self::METHOD_AUTH);
        }

        $curl = curl_init();

        $headers = array(
            sprintf("%s: %s", 'X-Auth-Token', $this->_apiToken),
            sprintf("%s: %s", 'Content-Type', 'application/json'));

        curl_setopt($curl, CURLOPT_URL, $this->_apiServerUri.$this->_apiResource);

        switch ($method) {
            case self::METHOD_POST:
                curl_setopt($curl, CURLOPT_POST, true);
                curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($this->_apiJson));
            break;
            case self::METHOD_PUT:
                curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "PUT");
                array_push($headers, json_encode($this->_apiJson));
            break;
            case self::METHOD_DELETE:
                curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "DELETE");
            break;
            case self::METHOD_AUTH:
                $headers = array(
                    sprintf("%s: %s", 'X-Auth-User', $this->_apiUser),
                    sprintf("%s: %s", 'X-Auth-Key', $this->_apiKey));
                curl_setopt($curl, CURLOPT_URL, $this->_apiAuthUri);
                curl_setopt($curl, CURLOPT_HEADERFUNCTION, array(&$this,'_requestAuth'));
            break;
            default:
                // By default we request data using GET method
                $headers = array(
                    sprintf("%s: %s", 'X-Auth-Token', $this->_apiToken));
            break;
        }

        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($curl, CURLOPT_HEADER, 0);
        curl_setopt($curl, CURLOPT_USERAGENT, $this->_apiAgent);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);

        // If debug is enabled we will output CURL data to screen
        if ($this->_enableDebug) {
            curl_setopt($curl, CURLOPT_VERBOSE, 1);
        }

        $this->_apiResponse = curl_exec($curl);

        // Also for debugging purposes output response we got
        if ($this->_enableDebug) {
            var_dump($this->_apiResponse);
        }

        if (curl_errno($curl) > 0) {
            throw new Cloud_Exception('Unable to process this request');
        }

        // Retrieve returned HTTP code and throw exceptions when possible
        // error occurs
        $curlInfo = curl_getinfo($curl);
        if (!empty($curlInfo['http_code'])) {
            $this->_apiResponseCode = (int) $curlInfo['http_code'];
            switch ($this->_apiResponseCode) {
                case '401':
                    // User is no longer authorized, re-authenicate with API
                    $this->_doRequest(self::METHOD_AUTH);
                    $this->_doRequest($method);
                break;
                case '400':
                    throw new Cloud_Exception('Access is denied for the given request. Check your X-Auth-Token header. The token may have expired.');
                break;
                case '404':
                    throw new Cloud_Exception('The server has not found anything matching the Request URI.');
                break;
                case '403':
                    throw new Cloud_Exception('Access is denied for the given request.');
                break;
                case '413':
                    throw new Cloud_Exception('The server is refusing to process a request because the request entity is larger than the server is willing or able to process.');
                break;
                case '500':
                    throw new Cloud_Exception('The server encountered an unexpected condition which prevented it from fulfilling the request.');
                break;
            }
        }
        curl_close($curl);
    }

    /**
     * This method is used for processing authentication response.
     *
     * Basically we retrieve authentication token and server management
     * URI from returned headers.
     *
     * @param mixed $ch instance of curl
     * @param string $header
     * @return int leight of header
     */
    private function _requestAuth($ch, $header)
    {
        if (stripos($header, 'X-Auth-Token') === 0) {
            $this->_apiToken = trim(substr($header, strlen('X-Auth-Token')+1));
        }
        if (stripos($header, 'X-Server-Management-Url') === 0) {
            $this->_apiServerUri = trim(substr($header, strlen('X-Server-Management-Url')+1));
        }

        return strlen($header);
    }

    /**
     * Enables debugging output
     *
     * @return null
     */
    public function enableDebug()
    {
        $this->_enableDebug = true;
    }

    /**
     * Disable debugging output
     *
     * @return null
     */
    public function disableDebug()
    {
        $this->_enableDebug = false;
    }

    /**
     * Retrieves details regarding specific server flavor
     *
     * @param int $flavorId id of a flavor you wish to retrieve details for
     * @return mixed returns json string containing details for requested flavor or
     * false on failure
     */
    public function getFlavor ($flavorId)
    {
        $this->_apiResource = '/flavors/'. (int) $flavorId;
        $this->_doRequest();

        if ($this->_apiResponseCode && ($this->_apiResponseCode == '200'
           	    || $this->_apiResponseCode == '203')) {
        	return $this->_apiResponse;
        }

        return false;
    }

    /**
     * Retrieves all of the available server flavors
     *
     * @return mixed returns json string containing available server configurations or
     * false on failure
     */
    public function getFlavors ()
    {
        $this->_apiResource = '/flavors';
        $this->_doRequest();

        if ($this->_apiResponseCode && ($this->_apiResponseCode == '200'
           	    || $this->_apiResponseCode == '203')) {
        	return $this->_apiResponse;
        }

        return false;
    }

    /**
     * Creates a new image of server
     *
     * @param string $name name of new image
     * @param int $serverId server id for which you wish to base this image on
     * @return mixed returns json string containing details of created image or false on failure
     */
    public function createImage ($name, $serverId)
    {
        $this->_apiResource = '/images';
        $this->_apiJson = array ('image' => array(
                                    'serverId' => (int) $serverId,
                                    'name' => (string) $name));
        $this->_doRequest(self::METHOD_POST);

        if ($this->_apiResponseCode && $this->_apiResponseCode == '200') {
        	return $this->_apiResponse;
        }

        return false;
    }

    /**
     * Retrieves details of specific image
     *
     * @param int $imageId id of image you wish to retrieve details for
     * @return json string containing details of requested image
     */
    public function getImage ($imageId)
    {
        $this->_apiResource = '/images/'. (int) $imageId;
        $this->_doRequest();

        if ($this->_apiResponseCode && ($this->_apiResponseCode == '200'
                || $this->_apiResponseCode == '203')) {
            return $this->_apiResponse;
        }
    }

    /**
     * Retrieves all of the available images
     *
     * @return mixed returns json string of available images or false on failure
     */
    public function getImages ()
    {
        $this->_apiResource = '/images';
        $this->_doRequest();

        if ($this->_apiResponseCode && ($this->_apiResponseCode == '200'
                || $this->_apiResponseCode == '203')) {
            return $this->_apiResponse;
        }

        return false;
    }

    /**
     * Retrieves configuration details for specific server
     *
     * @return mixed json string containing server details or false on failure
     */
    public function getServer ($serverId)
    {
        $this->_apiResource = '/servers/'. (int) $serverId;
        $this->_doRequest();

        if ($this->_apiResponseCode && $this->_apiResponseCode == '200') {
            // Save server names to avoid creating dublicate servers
            if (property_exists($this->_apiResponse, 'server')) {
                $this->_apiServers[(int) $this->_apiResponse->server->id] =
                    array('id' => (int) $this->_apiResponse->server->id,
                            'name' => (string) $this->_apiResponse->server->name);
            }

            return $this->_apiResponse;
        }

        return false;
    }

    /**
     * Retrieves currently available servers
     *
     * @return mixed json string containing current servers or false on failure
     */
    public function getServers ()
    {
        $this->_apiResource = '/servers';
        $this->_doRequest();

        if ($this->_apiResponseCode && $this->_apiResponseCode == '200') {
            if (property_exists($this->_apiResponse, 'servers')) {
                // Reset internal server array
                $this->_apiServers = array();
                foreach ($this->_apiResponse->servers as $server) {
                    $this->_apiServers[(int) $server->id]['name'] = (string) $server->name;
                }
            }

            return $this->_apiResponse;
        }

        return false;
    }

    /**
     * Retrieves current API limits
     *
     * @return mixed json string containing current limits or false on failure
     */
    public function getLimits ()
    {
        $this->_apiResource = '/limits';
        $this->_doRequest();

        if ($this->_apiResponseCode && ($this->_apiResponseCode == '200'
                || $this->_apiResponseCode == '203')) {
            return $this->_apiResponse;
        }

        return false;
    }

    public function shareServerIp ($serverId, $serverIp, $groupId, $doConfigure = false)
    {
        $this->_apiResource = '/servers/'. (int) $serverId .'/ips/public/'. $serverIp;
        $this->_apiJson = array ('shareIp' => array(
                                    'sharedIpGroupId' => (int) $groupId,
                                    'configureServer' => (bool) $doConfigure));
        $this->_doRequest(self::METHOD_PUT);

        if ($this->_apiResponseCode && $this->_apiResponseCode == '201') {
            return true;
        }

        return false;
    }

    /**
     * Removes a shared server IP from server
     * @param int $serverId id of server this action is peformed for
     * @param string $serverIp IP you wish to unshare
     * @return bool returns true on success or false on failure
     */
    public function unshareServerIp ($serverId, $serverIp)
    {
        $this->_apiResource = '/servers/'. (int) $serverId .'/ips/public/'. (string) $serverIp;
        $this->_doRequest(self::METHOD_DELETE);

        if ($this->_apiResponseCode && $this->_apiResponseCode == '202') {
            return true;
        }

        return false;
    }

    /**
     * Get IP's assigned to server
     *
     * @param int $serverId id of server you wish to retrieve ips for
     * @param string $type type of addresses to retrieve could be private/public or
     * false for both types.
     * @return mixed returns json string of server addresses or false of failure
     */
    public function getServerIp ($serverId, $type = false)
    {
       $this->_apiResource = '/servers/'. (int) $serverId .'/ips'. ($type ? '/'. $type : '');
       $this->_doRequest();

        if ($this->_apiResponseCode && ($this->_apiResponseCode == '200'
                || $this->_apiResponseCode == '203')) {
            return $this->_apiResponse;
        }

        return false;
    }

    /**
     * Add a server to shared ip group
     *
     * @param string $name name of shared ip group you are creating
     * @param int $serverId id of server you wish to add to this group
     * @return mixed returns json string containing id of created shared ip group or false on failure
     */
    public function addSharedIpGroup ($name, $serverId)
    {
        $this->_apiResource = '/shared_ip_groups';
        $this->_apiJson = array ('sharedIpGroup' => array(
                                    'name' => (string) $name,
                                    'server' => (int) $serverId));
        $this->_doRequest(self::METHOD_POST);

        if ($this->_apiResponseCode && $this->_apiResponseCode == '201') {
            return $this->_apiResponse;
        }

        return false;
    }

    /**
     * Delete shared IP group
     *
     * @param int $groupId id of group you wish to delete
     * @return bool returns true on success and false on failure
     */
    public function deleteSharedIpGroup ($groupId)
    {
        $this->_apiResource = '/shared_ip_groups/'. (int) $groupId;
        $this->_doRequest(self::METHOD_DELETE);

        if ($this->_apiResponseCode && $this->_apiResponseCode == '204') {
            return true;
        }

        return false;
    }

    /**
     * Retrieve details for specific IP group
     *
     * @param int $groupId id of specific shared group you wish to retrieve details
     * for
     * @return mixed returns json string containing details about requested group
     * or false on failure
     */
    public function getSharedIpGroup ($groupId)
    {
        $this->_apiResource = '/shared_ip_groups/'. (int) $groupId;
        $this->_doRequest();

        if ($this->_apiResponseCode && ($this->_apiResponseCode == '200'
                || $this->_apiResponseCode == '203')) {
            return $this->_apiResponse;
        }

        return false;
    }

    /**
     * Retrieve all the available shared IP groups
     *
     * @param bool $isDetailed should response contain an array of servers group has
     * @return mixed returns json string of groups or false on failure
     */
    public function getSharedIpGroups ($isDetailed = false)
    {
        $this->_apiResource = '/shared_ip_groups'. ($isDetailed ? '/detail' : '');
        $this->_doRequest();

        if ($this->_apiResponseCode && ($this->_apiResponseCode == '200'
                || $this->_apiResponseCode == '203')) {
        	return $this->_apiResponse;
        }

        return false;
    }

    /**
     * Retrieve back-up schedule for a specific server
     *
     * @param int $serverId id of server you wish to retrieve back-up schedule for
     * @return mixed returns json string of current back-up schedule or false on failure
     */
    public function getBackupSchedule ($serverId)
    {
        $this->_apiResource = '/servers/'. (int) $serverId .'/backup_schedule';
        $this->_doRequest();

	    if ($this->_apiResponseCode && ($this->_apiResponseCode == '200'
                || $this->_apiResponseCode == '203')) {
            return $this->_apiResponse;
	    }

        return false;
    }

    /**
     * Create a new back-up schedule for a server
     *
     * @param int $serverId id of a server this back-up schedule is intended for
     * @param string $weekly day of the week this back-up should run, please
     * $_apiBackup array and/or documentation for valid parameters.
     * @param string $daily time of the day this back-up should run, please
     * $_apiBackup array and/or documentation for valid parameters.
     * @param bool $isEnabled should this scheduled back-up be enabled or disabled,
     * default is set to enabled.
     * @throws Cloud_Exception
     * @return bool true on success and false on failure
     */
    public function addBackupSchedule ($serverId, $weekly, $daily, $isEnabled = true)
    {
        if (!in_array((string) strtoupper($weekly), $this->_apiBackup['weekly'])) {
            throw new Cloud_Exception ('Passed weekly back-up parameter is not supported');
        }

        if (!in_array((string) strtoupper($daily), $this->_apiBackup['daily'])) {
            throw new Cloud_Exception ('Passed weekly back-up parameter is not supported');
        }

        $this->_apiResource = '/servers/'. (int) $serverId .'/backup_schedule';
        $this->_apiJson = array ('backupSchedule' => array(
                                    'enabled' => (bool) $isEnabled,
                                    'weekly' => (string) strtoupper($weekly),
                                    'daily' => (string) strtoupper($daily)));
        $this->_doRequest(self::METHOD_POST);

	    if ($this->_apiResponseCode && $this->_apiResponseCode == '204') {
            return true;
	    }

        return false;
    }

    /**
     * Deletes scheduled back-up for specific server
     *
     * @param int $serverId id of server you wish to delete all scheduled back-ups
     * for<commit_msg>Proper logic updates and minor bug fixes<commit_after><?php

/**
 * If you are using autoloader (like you should!) remove this include
 */

include './Exception.php';

/**
 * PHP Cloud Server implementation for RackSpace (tm)
 *
 * THIS SOFTWARE IS PROVIDED "AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @author <NAME> <<EMAIL>>
 * @link http://github.com/AlekseyKorzun/php-cloudservers/
 * @link http://www.schematic.com
 * @version 0.1
 * @license bsd
 */

class Cloud_Server {
    const METHOD_AUTH = 1;
    const METHOD_POST = 2;
    const METHOD_DELETE = 3;
    const METHOD_PUT = 4;

    private $_apiUser;
    private $_apiKey;
    private $_apiToken;

    protected $_apiServerUri;
    protected $_apiAuthUri = 'https://auth.api.rackspacecloud.com/v1.0';

    protected $_apiBackup = array(
        'weekly' => array(
                'DISABLED',
                'SUNDAY',
                'MONDAY',
                'TUESDAY',
                'WEDNESDAY',
                'THURSDAY',
                'FRIDAY',
                'SATURDAY'),
        'daily' => array(
                'DISABLED',
                'H_0000_0200',
                'H_0200_0400',
                'H_0400_0600',
                'H_0600_0800',
                'H_0800_1000',
                'H_1000_1200',
                'H_1400_1600',
                'H_1600_1800',
                'H_1800_2000',
                'H_2000_2200',
                'H_2200_0000'));

    protected $_apiResource;
    protected $_apiAgent = 'PHP Cloud Server client';
    protected $_apiJson;
    protected $_apiResponse;
    protected $_apiResponseCode;
    protected $_apiServers = array();

    protected $_enableDebug = false;

    /**
     * Class constructor
     *
     * @param string $apiId user id that will be used for API
     * @param string $apiKey key that was generated by Rackspace
     * @return null
     */
    function __construct($apiId, $apiKey)
    {
        if (!$apiId || !$apiKey) {
            throw new Cloud_Exception('Please provide valid API credentials');
        }

        $this->_apiUser = $apiId;
        $this->_apiKey = $apiKey;
    }

    /**
     * Get authentication token
     *
     * @return mixed return authentication token or false on failure
     */
    public function getToken()
    {
        if (!empty($this->_apiToken)) {
           return $this->_apiToken;
        }

        return false;
    }

    /**
     * Set authentication token
     *
     * @param string $tokenId token you wish to set
     * @return null
     */
    public function setToken($tokenId)
    {
        $this->_apiToken = $tokenId;
    }

    /**
     * Perform authentication
     *
     * @return string returns recieved token
     */
    public function authenticate () {
        $this->_doRequest(self::METHOD_AUTH);
        return $this->_apiToken;
    }

    /**
     * Performs CURL requests (POST,PUT,DELETE,GET) required by API.
     *
     * @param string $method HTTP method that will be used for current request
     * @throws Cloud_Exception
     * @return null
     */
    private function _doRequest($method = null)
    {
        if (!$this->_apiToken && $method != self::METHOD_AUTH) {
            $this->_doRequest(self::METHOD_AUTH);
        }

        $curl = curl_init();

        $headers = array(
            sprintf("%s: %s", 'X-Auth-Token', $this->_apiToken),
            sprintf("%s: %s", 'Content-Type', 'application/json'));

        curl_setopt($curl, CURLOPT_URL, $this->_apiServerUri.$this->_apiResource);

        switch ($method) {
            case self::METHOD_POST:
                curl_setopt($curl, CURLOPT_POST, true);
                curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($this->_apiJson));
            break;
            case self::METHOD_PUT:
                curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "PUT");
                array_push($headers, json_encode($this->_apiJson));
            break;
            case self::METHOD_DELETE:
                curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "DELETE");
            break;
            case self::METHOD_AUTH:
                $headers = array(
                    sprintf("%s: %s", 'X-Auth-User', $this->_apiUser),
                    sprintf("%s: %s", 'X-Auth-Key', $this->_apiKey));
                curl_setopt($curl, CURLOPT_URL, $this->_apiAuthUri);
                curl_setopt($curl, CURLOPT_HEADERFUNCTION, array(&$this,'_requestAuth'));
            break;
            default:
                // By default we request data using GET method
                $headers = array(
                    sprintf("%s: %s", 'X-Auth-Token', $this->_apiToken));
            break;
        }

        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($curl, CURLOPT_HEADER, 0);
        curl_setopt($curl, CURLOPT_USERAGENT, $this->_apiAgent);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);

        // If debug is enabled we will output CURL data to screen
        if ($this->_enableDebug) {
            curl_setopt($curl, CURLOPT_VERBOSE, 1);
        }

        $this->_apiResponse = curl_exec($curl);

        // Also for debugging purposes output response we got
        if ($this->_enableDebug) {
            var_dump($this->_apiResponse);
        }

        if (curl_errno($curl) > 0) {
            throw new Cloud_Exception('Unable to process this request');
        }

        // Retrieve returned HTTP code and throw exceptions when possible
        // error occurs
        $curlInfo = curl_getinfo($curl);
        if (!empty($curlInfo['http_code'])) {
            $this->_apiResponseCode = (int) $curlInfo['http_code'];
            switch ($this->_apiResponseCode) {
                case '401':
                    // User is no longer authorized, re-authenicate with API
                    $this->_doRequest(self::METHOD_AUTH);
                    $this->_doRequest($method);
                break;
                case '400':
                    throw new Cloud_Exception('Access is denied for the given request. Check your X-Auth-Token header. The token may have expired.');
                break;
                case '404':
                    throw new Cloud_Exception('The server has not found anything matching the Request URI.');
                break;
                case '403':
                    throw new Cloud_Exception('Access is denied for the given request.');
                break;
                case '413':
                    throw new Cloud_Exception('The server is refusing to process a request because the request entity is larger than the server is willing or able to process.');
                break;
                case '500':
                    throw new Cloud_Exception('The server encountered an unexpected condition which prevented it from fulfilling the request.');
                break;
            }
        }
        curl_close($curl);
    }

    /**
     * This method is used for processing authentication response.
     *
     * Basically we retrieve authentication token and server management
     * URI from returned headers.
     *
     * @param mixed $ch instance of curl
     * @param string $header
     * @return int leight of header
     */
    private function _requestAuth($ch, $header)
    {
        if (stripos($header, 'X-Auth-Token') === 0) {
            $this->_apiToken = trim(substr($header, strlen('X-Auth-Token')+1));
        }
        if (stripos($header, 'X-Server-Management-Url') === 0) {
            $this->_apiServerUri = trim(substr($header, strlen('X-Server-Management-Url')+1));
        }

        return strlen($header);
    }

    /**
     * Enables debugging output
     *
     * @return null
     */
    public function enableDebug()
    {
        $this->_enableDebug = true;
    }

    /**
     * Disable debugging output
     *
     * @return null
     */
    public function disableDebug()
    {
        $this->_enableDebug = false;
    }

    /**
     * Retrieves details regarding specific server flavor
     *
     * @param int $flavorId id of a flavor you wish to retrieve details for
     * @return mixed returns json string containing details for requested flavor or
     * false on failure
     */
    public function getFlavor ($flavorId)
    {
        $this->_apiResource = '/flavors/'. (int) $flavorId;
        $this->_doRequest();

        if ($this->_apiResponseCode && ($this->_apiResponseCode == '200'
           	    || $this->_apiResponseCode == '203')) {
        	return $this->_apiResponse;
        }

        return false;
    }

    /**
     * Retrieves all of the available server flavors
     *
     * @return mixed returns json string containing available server configurations or
     * false on failure
     */
    public function getFlavors ()
    {
        $this->_apiResource = '/flavors';
        $this->_doRequest();

        if ($this->_apiResponseCode && ($this->_apiResponseCode == '200'
           	    || $this->_apiResponseCode == '203')) {
        	return $this->_apiResponse;
        }

        return false;
    }

    /**
     * Creates a new image of server
     *
     * @param string $name name of new image
     * @param int $serverId server id for which you wish to base this image on
     * @return mixed returns json string containing details of created image or false on failure
     */
    public function createImage ($name, $serverId)
    {
        $this->_apiResource = '/images';
        $this->_apiJson = array ('image' => array(
                                    'serverId' => (int) $serverId,
                                    'name' => (string) $name));
        $this->_doRequest(self::METHOD_POST);

        if ($this->_apiResponseCode && $this->_apiResponseCode == '200') {
        	return $this->_apiResponse;
        }

        return false;
    }

    /**
     * Retrieves details of specific image
     *
     * @param int $imageId id of image you wish to retrieve details for
     * @return json string containing details of requested image
     */
    public function getImage ($imageId)
    {
        $this->_apiResource = '/images/'. (int) $imageId;
        $this->_doRequest();

        if ($this->_apiResponseCode && ($this->_apiResponseCode == '200'
                || $this->_apiResponseCode == '203')) {
            return $this->_apiResponse;
        }
    }

    /**
     * Retrieves all of the available images
     *
     * @return mixed returns json string of available images or false on failure
     */
    public function getImages ()
    {
        $this->_apiResource = '/images';
        $this->_doRequest();

        if ($this->_apiResponseCode && ($this->_apiResponseCode == '200'
                || $this->_apiResponseCode == '203')) {
            return $this->_apiResponse;
        }

        return false;
    }

    /**
     * Retrieves configuration details for specific server
     *
     * @return mixed json string containing server details or false on failure
     */
    public function getServer ($serverId)
    {
        $this->_apiResource = '/servers/'. (int) $serverId;
        $this->_doRequest();

        if ($this->_apiResponseCode && $this->_apiResponseCode == '200') {
            // Save server names to avoid creating dublicate servers
            if (property_exists($this->_apiResponse, 'server')) {
                $this->_apiServers[(int) $this->_apiResponse->server->id] =
                    array('id' => (int) $this->_apiResponse->server->id,
                            'name' => (string) $this->_apiResponse->server->name);
            }

            return $this->_apiResponse;
        }

        return false;
    }

    /**
     * Retrieves currently available servers
     *
     * @return mixed json string containing current servers or false on failure
     */
    public function getServers ()
    {
        $this->_apiResource = '/servers';
        $this->_doRequest();

        if ($this->_apiResponseCode && $this->_apiResponseCode == '200') {
            if (property_exists($this->_apiResponse, 'servers')) {
                // Reset internal server array
                $this->_apiServers = array();
                foreach ($this->_apiResponse->servers as $server) {
                    $this->_apiServers[(int) $server->id]['name'] = (string) $server->name;
                }
            }

            return $this->_apiResponse;
        }

        return false;
    }

    /**
     * Retrieves current API limits
     *
     * @return mixed json string containing current limits or false on failure
     */
    public function getLimits ()
    {
        $this->_apiResource = '/limits';
        $this->_doRequest();

        if ($this->_apiResponseCode && ($this->_apiResponseCode == '200'
                || $this->_apiResponseCode == '203')) {
            return $this->_apiResponse;
        }

        return false;
    }

    public function shareServerIp ($serverId, $serverIp, $groupId, $doConfigure = false)
    {
        $this->_apiResource = '/servers/'. (int) $serverId .'/ips/public/'. $serverIp;
        $this->_apiJson = array ('shareIp' => array(
                                    'sharedIpGroupId' => (int) $groupId,
                                    'configureServer' => (bool) $doConfigure));
        $this->_doRequest(self::METHOD_PUT);

        if ($this->_apiResponseCode && $this->_apiResponseCode == '201') {
            return true;
        }

        return false;
    }

    /**
     * Removes a shared server IP from server
     * @param int $serverId id of server this action is peformed for
     * @param string $serverIp IP you wish to unshare
     * @return bool returns true on success or false on failure
     */
    public function unshareServerIp ($serverId, $serverIp)
    {
        $this->_apiResource = '/servers/'. (int) $serverId .'/ips/public/'. (string) $serverIp;
        $this->_doRequest(self::METHOD_DELETE);

        if ($this->_apiResponseCode && $this->_apiResponseCode == '202') {
            return true;
        }

        return false;
    }

    /**
     * Get IP's assigned to server
     *
     * @param int $serverId id of server you wish to retrieve ips for
     * @param string $type type of addresses to retrieve could be private/public or
     * false for both types.
     * @return mixed returns json string of server addresses or false of failure
     */
    public function getServerIp ($serverId, $type = false)
    {
       $this->_apiResource = '/servers/'. (int) $serverId .'/ips'. ($type ? '/'. $type : '');
       $this->_doRequest();

        if ($this->_apiResponseCode && ($this->_apiResponseCode == '200'
                || $this->_apiResponseCode == '203')) {
            return $this->_apiResponse;
        }

        return false;
    }

    /**
     * Add a server to shared ip group
     *
     * @param string $name name of shared ip group you are creating
     * @param int $serverId id of server you wish to add to this group
     * @return mixed returns json string containing id of created shared ip group or false on failure
     */
    public function addSharedIpGroup ($name, $serverId)
    {
        $this->_apiResource = '/shared_ip_groups';
        $this->_apiJson = array ('sharedIpGroup' => array(
                                    'name' => (string) $name,
                                    'server' => (int) $serverId));
        $this->_doRequest(self::METHOD_POST);

        if ($this->_apiResponseCode && $this->_apiResponseCode == '201') {
            return $this->_apiResponse;
        }

        return false;
    }

    /**
     * Delete shared IP group
     *
     * @param int $groupId id of group you wish to delete
     * @return bool returns true on success and false on failure
     */
    public function deleteSharedIpGroup ($groupId)
    {
        $this->_apiResource = '/shared_ip_groups/'. (int) $groupId;
        $this->_doRequest(self::METHOD_DELETE);

        if ($this->_apiResponseCode && $this->_apiResponseCode == '204') {
            return true;
        }

        return false;
    }

    /**
     * Retrieve details for specific IP group
     *
     * @param int $groupId id of specific shared group you wish to retrieve details
     * for
     * @return mixed returns json string containing details about requested group
     * or false on failure
     */
    public function getSharedIpGroup ($groupId)
    {
        $this->_apiResource = '/shared_ip_groups/'. (int) $groupId;
        $this->_doRequest();

        if ($this->_apiResponseCode && ($this->_apiResponseCode == '200'
                || $this->_apiResponseCode == '203')) {
            return $this->_apiResponse;
        }

        return false;
    }

    /**
     * Retrieve all the available shared IP groups
     *
     * @param bool $isDetailed should response contain an array of servers group has
     * @return mixed returns json string of groups or false on failure
     */
    public function getSharedIpGroups ($isDetailed = false)
    {
        $this->_apiResource = '/shared_ip_groups'. ($isDetailed ? '/detail' : '');
        $this->_doRequest();

        if ($this->_apiResponseCode && ($this->_apiResponseCode == '200'
                || $this->_apiResponseCode == '203')) {
        	return $this->_apiResponse;
        }

        return false;
    }

    /**
     * Retrieve back-up schedule for a specific server
     *
     * @param int $serverId id of server you wish to retrieve back-up schedule for
     * @return mixed returns json string of current back-up schedule or false on failure
     */
    public function getBackupSchedule ($serverId)
    {
        $this->_apiResource = '/servers/'. (int) $serverId .'/backup_schedule';
        $this->_doRequest();

	    if ($this->_apiResponseCode && ($this->_apiResponseCode == '200'
                || $this->_apiResponseCode == '203')) {
            return $this->_apiResponse;
	    }

        return false;
    }

    /**
     * Create a new back-up schedule for a server
     *
     * @param int $serverId id of a server this back-up schedule is intended for
     * @param string $weekly day of the week this back-up should run, please
     * $_apiBackup array and/or documentation for valid parameters.
     * @param string $daily time of the day this back-up should run, please
     * $_apiBackup array and/or documentation for valid parameters.
     * @param bool $isEnabled should this scheduled back-up be enabled or disabled,
     * default is set to enabled.
     * @throws Cloud_Exception
     * @return bool true on success and false on failure
     */
    public function addBackupSchedule ($serverId, $weekly, $daily, $isEnabled = true)
    {
        if (!in_array((string) strtoupper($weekly), $this->_apiBackup['weekly'])) {
            throw new Cloud_Exception ('Passed weekly back-up parameter is not supported');
        }

        if (!in_array((string) strtoupper($daily), $this->_apiBackup['daily'])) {
            throw new Cloud_Exception ('Passed daily back-up parameter is not supported');
        }

        $this->_apiResource = '/servers/'. (int) $serverId .'/backup_schedule';
        $this->_apiJson = array ('backupSchedule' => array(
                                    'enabled' => (bool) $isEnabled,
                                    'weekly' => (string) strtoupper($weekly),
                                    'daily' => (string) strtoupper($daily)));
        $this->_doRequest(self::METHOD_POST);

	    if ($this->_apiResponseCode && $this->_apiResponseCode == '204') {
            return true;
	    }

        return false;
    }

    /**
     * Deletes scheduled back-up for specific server
     *
     * @param int $serverId id of server you wish to delete all scheduled back-ups
     * for
{'fraction_non_alphanumeric': 0.1161318810715337, 'fraction_numerical': 0.009763516828574232, 'mean_word_length': 1.9906096397916513, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 335, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 18, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4775621', 'n_tokens_mistral': 11519, 'n_tokens_neox': 10743, 'n_words': 3251}