Rackspace is a great email hosting company, providing, amongst other things, a handy API for creating bespoke email solutions. The exercise of integrating that API into your application is of course left to the end user. I’ve spent some time working on a Rackspace API library for the PHP programming framework, CodeIgniter. This is not functionally complete – I have only implemented the interfaces that I needed – but it should provide a useful springboard for your own projects.
Configuration
In /system/application/config/RackspaceAPI.php:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); $config['user_key'] = 'your user key'; $config['secret_key'] = 'your secret key'; $config['user_agent'] = 'name of your app'; $config['api_version'] = 'v0'; // amend if necessary $config['rackspace_host'] = 'api.emailsrvr.com'; // amend if necessary /* End of file RackspaceAPI.php */ /* Location: ./system/application/config/RackspaceAPI.php */
Library
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); /** * Uses curl and pecl_http */ class Rackspace_API { /** * Store recent http_message * @var object */ protected $_http_message; /** * CI object * @var object */ protected $_ci; /** * Rackspace config items */ protected $_user_key; protected $_secret_key; protected $_user_agent; protected $_api_version; protected $_rackspace_host; function __construct() { $this->_ci =& get_instance(); $this->_ci->config->load('RackspaceAPI', TRUE); $this->_user_key = $this->_ci->config->item('user_key', 'RackspaceAPI'); $this->_secret_key = $this->_ci->config->item('secret_key', 'RackspaceAPI'); $this->_user_agent = $this->_ci->config->item('user_agent', 'RackspaceAPI'); $this->_api_version = $this->_ci->config->item('api_version', 'RackspaceAPI'); $this->_rackspace_host = $this->_ci->config->item('rackspace_host', 'RackspaceAPI'); } /** * Get info about a domain * @param string $domain * @return stdClass Object ( 'error' => bool, * 'result' => string (error message) | stdClass Object */ public function getDomainInfo($domain) { return $this->genericGet('/customers/me/domains/'.$domain); } /** * Get all domain names * @return stdClass Object ( 'error' => bool, * 'result' => string (error message) | array (domains) */ public function getDomains() { $obj = $this->genericGet('/customers/me/domains'); if(!$obj->error){ // Reformat into an array of domains foreach($obj->result->domains as $domain) { $domains[]=$domain->name; } $obj->result = $domains; } return $obj; } /** * Get info about a mailbox ($domain@$id) * @param string $domain * @param string $id * @return stdClass Object ( 'error' => bool, * 'result' => string (error message) | stdClass Object */ public function getMailboxInfo($domain, $id) { return $this->genericGet('/customers/me/domains/'.$domain.'/rs/mailboxes/'.$id); } /** * Used by Get functions above - generalised use case * @param string $url - see the API; constructed by the calling function * @return stdClass Object ( 'error' => bool, * 'result' => string (error message) | stdClass Object */ private function genericGet($url) { $this->get( $url, 'application/json'); if($this->_http_message->getResponseCode() == 200) { // Call worked. JSON is missing enclosing brackets, apparently needed by json_decode $json = '['.$this->_http_message->getBody().']'; if(is_string($json)) { $obj = json_decode($json); $result->error = false; $result->result = $obj[0]; } else { // JSON failure $result->error = true; $result->result = 'Failed to parse JSON'; } } else { // API call failed $result->error = true; $result->result = $this->_http_message->getHeader("x-error-message"); } return $result; } /** * Create a mailbox ($domain@$id) * @param string $domain * @param string $id * @param string $first: First name * @param string $last: Last name * @param string $name: Display as * @param string $office: Name of office/profit centre * @param string $locno: Office/profit centre number * @param string $password * @param string $fwd: comma-separated forwarding address(es) - max 4 off domain * @param string $save: save forwarded email - 'true' or 'false' * saveForwardedEmail * @return stdClass Object ( 'error' => bool, * 'result' => string (error message) | stdClass Object */ public function addMailbox($domain, $id, $first, $last, $name, $office, $locno, $password, $fwd, $save='true') { $fields = array( 'password' => $password, 'emailForwardingAddresses' => $fwd, 'firstName' => $first, 'lastName' => $last, 'displayName' => $name, 'saveForwardedEmail' => $save, 'organization' => $office, 'organizationUnit' => $locno); return $this->genericPost( '/customers/me/domains/'.$domain.'/rs/mailboxes/'.$id, $fields); } /** * Used by Post functions above - generalised use case * Note: Rackspace API suggests use POST to add, PUT to edit * @param string $url - see the API; constructed by the calling function * @param array $fields - data to be POSTed * @return stdClass Object ( 'error' => bool, * 'result' => string (error message) | stdClass Object */ private function genericPost($url, $fields) { $this->post( $url, $fields, 'application/json'); if($this->_http_message->getResponseCode() == 200) { $result->error = false; $result->result = $this->_http_message->getBody(); } else { // API call failed $result->error = true; $result->result = $this->_http_message->getHeader("x-error-message"); } return $result; } /** * Edit user's forwarding * @param string $domain * @param string $id * @param string $fwd: comma-separated forwarding address(es) - max 4 off domain * @return stdClass Object ( 'error' => bool, * 'result' => string (error message) | stdClass Object */ public function changeForwarding($domain, $id, $fwd) { $fields = array( 'emailForwardingAddresses' => $fwd ); return $this->genericPut( '/customers/me/domains/'.$domain.'/rs/mailboxes/'.$id, $fields); } /** * Edit user's location * @param string $domain * @param string $id * @param string $office: Name of office/profit centre * @param string $locno: Office/profit centre number * @return stdClass Object ( 'error' => bool, * 'result' => string (error message) | stdClass Object */ public function changeLocation($domain, $id, $office, $locno) { $fields = array( 'organization' => $office, 'organizationUnit' => $locno); return $this->genericPut( '/customers/me/domains/'.$domain.'/rs/mailboxes/'.$id, $fields); } /** * Edit user's name * @param string $domain * @param string $id * @param string $first: First name * @param string $last: Last name * @param string $name: Display as * @return stdClass Object ( 'error' => bool, * 'result' => string (error message) | stdClass Object */ public function changeName($domain, $id, $first, $last, $name) { $fields = array( 'firstName' => $first, 'lastName' => $last, 'displayName' => $name); return $this->genericPut( '/customers/me/domains/'.$domain.'/rs/mailboxes/'.$id, $fields); } /** * Edit user's password * @param string $domain * @param string $id * @param string $password * @return stdClass Object ( 'error' => bool, * 'result' => string (error message) | stdClass Object */ public function changePassword($domain, $id, $password) { $fields = array( 'password' => $password); return $this->genericPut( '/customers/me/domains/'.$domain.'/rs/mailboxes/'.$id, $fields); } /** * Used by Put functions above - generalised use case * Note: Rackspace API suggests use PUT to edit, POST to add * @param string $url - see the API; constructed by the calling function * @param array $fields - data to be PUT * @return stdClass Object ( 'error' => bool, * 'result' => string (error message) | stdClass Object */ private function genericPut($url, $fields) { $this->put( $url, $fields); if($this->_http_message->getResponseCode() == 200) { $result->error = false; $result->result = $this->_http_message->getBody(); } else { // API call failed $result->error = true; $result->result = $this->_http_message->getHeader("x-error-message"); } return $result; } /** * Delete a mailbox * @param string $domain * @param string $id * @return stdClass Object ( 'error' => bool, * 'result' => string (error message) | stdClass Object */ public function deleteMailbox($domain, $id) { return $this->genericDelete("/customers/me/domains/$domain/rs/mailboxes/$id"); } /** * Used by Get functions above - generalised use case * @param string $url - see the API; constructed by the calling function * @return stdClass Object ( 'error' => bool, * ['result' => string (error message)] */ private function genericDelete($url) { $this->delete($url); if($this->_http_message->getResponseCode() == 200) { // Call worked. $result->error = false; } else { if($this->_http_message->getResponseCode() == 500) { // Internal server error $result->error = true; $result->result = 'An internal server error occurred deleting object. Url: '.$url; } else { // API call failed $result->error = true; $result->result = $this->_http_message->getHeader("x-error-message"); } } return $result; } // The remainder of this file is mostly lifted from Rackspace's examples: http://api-wiki.apps.rackspace.com/api-wiki/index.php/PHP_Examples_(Rest_API) private function get($url_string, $format) { $headers = array("Accept: $format"); $curl_session = self::construct_session($url_string, $headers); $this->_http_message = self::send_request($curl_session); } private function post($url_string, $fields, $format) { $headers = array("Accept: $format"); $curl_session = self::construct_session($url_string, $headers); curl_setopt($curl_session, CURLOPT_POST, true); curl_setopt($curl_session, CURLOPT_POSTFIELDS, $fields); $this->_http_message = self::send_request($curl_session); } private function put($url_string, $fields) { $curl_session = self::construct_session($url_string, array()); curl_setopt($curl_session, CURLOPT_CUSTOMREQUEST, 'PUT'); curl_setopt($curl_session, CURLOPT_POSTFIELDS, $fields); $this->_http_message = self::send_request($curl_session); } private function delete($url_string) { $curl_session = self::construct_session($url_string, array()); curl_setopt($curl_session, CURLOPT_CUSTOMREQUEST, 'DELETE'); $this->_http_message = self::send_request($curl_session); } private function send_request($curl_session) { $response = curl_exec($curl_session); curl_close($curl_session); /* Reponse string may contain two HTTP sessions, if there was an initial "HTTP/1.1 100 Continue" response. So strip that first response out. Eg: HTTP/1.1 100 Continue Via: 1.1 [proxy] HTTP/1.1 400 Bad Request Via: 1.1 [proxy] Connection: Keep-Alive Proxy-Connection: Keep-Alive ... * */ $response = preg_replace('|HTTP/1.1 100.*HTTP/1.1|isU', 'HTTP/1.1', $response); return new HttpMessage($response); } private function construct_session($url_string, $existing_headers) { $headers = array_merge( self::authorization_headers(), $existing_headers); $url = self::construct_uri($url_string); $curl_session = curl_init($url); curl_setopt($curl_session, CURLOPT_HEADER, true); curl_setopt($curl_session, CURLOPT_HTTPHEADER, $headers); curl_setopt($curl_session, CURLOPT_RETURNTRANSFER, true); return $curl_session; } private function authorization_headers() { $time_stamp = date('YmdHis'); $data_to_sign = $this->_user_key . $this->_user_agent . $time_stamp. $this->_secret_key; $signature = base64_encode(sha1($data_to_sign, true)); $headers = array(); $headers[] = "User-Agent: " . $this->_user_agent; $headers[] = 'X-Api-Signature: ' . $this->_user_key . ":$time_stamp:$signature"; return $headers; } private function construct_uri($url_string) { $url = 'http://' . $this->_rackspace_host . '/' . $this->_api_version . $url_string; return $url; } } ?>
Example
Example usage:
function testRackspace() { $this->load->library('Rackspace_API'); $client = new Rackspace_API(); $obj = $client->getMailboxInfo('somedomain.com', 'test.user'); if($obj->error) { echo 'Error: '.$obj->result; } else { var_dump($obj); } }
Image copyright © Rackspace Ltd. All rights acknowledged.