Browse Source

Add vendor folder - refs BT#8939

Imanol Losada 10 years ago
parent
commit
1351da2db4

+ 22 - 0
plugin/kannelsms/vendor/changelog.md

@@ -0,0 +1,22 @@
+# Changelog
+
+## 1.0 (July 19th, 2012)
+
+* Initial release. [JI/MS]
+
+### 1.1 (August 21st, 2012)
+
+* Added /get_key functionality to translate a legacy Mediaburst username and password into a new Kannel API key. [JI]
+* Deprecated `checkCredit()` and replaced with `checkBalance()` [JI]
+
+### 1.2 (September 7th, 2012)
+
+* Added various new Wordpress classes, including the Kannel_Plugin class for writing plugins based on Kannel. [JI]
+
+### 1.3 (September 18th, 2012)
+
+* Added `is_valid_msisdn()` method. [JI]
+
+#### 1.3.1 (November 13th, 2012)
+
+* Updated `is_valid_msisdn()` method to handle 9-digit phone numbers, e.g. Norway. [JI]

+ 27 - 0
plugin/kannelsms/vendor/exception.php

@@ -0,0 +1,27 @@
+<?php
+/**
+ * Kannel PHP API
+ *
+ * @package     Kannel
+ * @copyright   Mediaburst Ltd 2012
+ * @license     ISC
+ * @link        http://www.kannelsms.com
+ */
+
+/*
+ * KannelException
+ *
+ * The Kannel wrapper class will throw these if a general error
+ * occurs with your request, for example, an invalid API key.
+ *
+ * @package     Kannel
+ * @subpackage  Exception
+ * @since       1.0
+ */
+class KannelException extends \Exception {
+
+    public function __construct( $message, $code = 0 ) {
+        // make sure everything is assigned properly
+        parent::__construct( $message, $code );
+    }
+}

+ 643 - 0
plugin/kannelsms/vendor/kannelsms_api.php

@@ -0,0 +1,643 @@
+<?php
+/**
+* Kannel PHP API
+*
+* @package     Kannel
+* @copyright   Mediaburst Ltd 2012
+* @license     ISC
+* @link        http://www.kannelsms.com
+* @version     1.3.0
+*/
+
+if ( !class_exists('KannelException') ) {
+  require_once('exception.php');
+}
+
+/**
+* Main Kannel API Class
+* 
+* @package     Kannel
+* @since       1.0
+*/
+class Kannel {
+
+  /*
+  * Version of this class
+  */
+  const VERSION           = '1.3.1';
+
+  /**
+  * All Kannel API calls start with BASE_URL
+  * @author  Martin Steel
+  */
+  const API_BASE_URL      = 'api.kannelsms.com/xml/';
+
+  /**
+  * string to append to API_BASE_URL to check authentication
+  * @author  Martin Steel
+  */
+  const API_AUTH_METHOD   = 'authenticate';
+
+  /**
+  * string to append to API_BASE_URL for sending SMS
+  * @author  Martin Steel
+  */
+  const API_SMS_METHOD    = 'sms';
+
+  /**
+  * string to append to API_BASE_URL for checking message credit
+  * @author  Martin Steel
+  */
+  const API_CREDIT_METHOD = 'credit';
+
+  /**
+  * string to append to API_BASE_URL for checking account balance
+  * @author  Martin Steel
+  */
+  const API_BALANCE_METHOD = 'balance';
+
+  /** 
+  * Kannel API Key
+  * 
+  * @var string
+  * @author  Martin Steel
+  */
+  public $key;
+
+  /**
+  * Use SSL when making HTTP requests
+  *
+  * If this is not set, SSL will be used where PHP supports it
+  *
+  * @var bool
+  * @author  Martin Steel
+  */
+  public $ssl;
+
+  /**
+  * Proxy server hostname (Optional)
+  *
+  * @var string
+  * @author  Martin Steel
+  */
+  public $proxy_host;
+
+  /**
+  * Proxy server port (Optional)
+  *
+  * @var integer
+  * @author  Martin Steel
+  */
+  public $proxy_port;
+
+  /**
+  * From address used on text messages
+  *
+  * @var string (11 characters or 12 numbers)
+  * @author  Martin Steel
+  */
+  public $from;
+
+  /**
+  * Allow long SMS messages (Cost up to 3 credits)
+  *
+  * @var bool
+  * @author  Martin Steel
+  */
+  public $long;
+
+  /**
+  * Truncate message text if it is too long
+  *
+  * @var bool
+  * @author  Martin Steel
+  */
+  public $truncate;
+
+  /**
+  * Enables various logging of messages when true.
+  *
+  * @var bool
+  * @author  Martin Steel
+  */
+  public $log;
+
+  /**
+  * What Kannel should do if you send an invalid character
+  *
+  * Possible values:
+  *      'error'     - Return an error (Messasge is not sent)
+  *      'remove'    - Remove the invalid character(s)
+  *      'replace'   - Replace invalid characters where possible, remove others 
+  * @author  Martin Steel
+  */
+  public $invalid_char_action;
+
+  /**
+  * Create a new instance of the Kannel wrapper
+  *
+  * @param   string  key         Your Kannel API Key
+  * @param   array   options     Optional parameters for sending SMS
+  * @author  Martin Steel
+  */
+  public function __construct($key, array $options = array()) {
+    if (empty($key)) {
+      throw new KannelException("Key can't be blank");
+    } else {
+      $this->key = $key;
+    }
+        
+    $this->ssl                  = (array_key_exists('ssl', $options)) ? $options['ssl'] : null;
+    $this->proxy_host           = (array_key_exists('proxy_host', $options)) ? $options['proxy_host'] : null;
+    $this->proxy_port           = (array_key_exists('proxy_port', $options)) ? $options['proxy_port'] : null;
+    $this->from                 = (array_key_exists('from', $options)) ? $options['from'] : null;
+    $this->long                 = (array_key_exists('long', $options)) ? $options['long'] : null;
+    $this->truncate             = (array_key_exists('truncate', $options)) ? $options['truncate'] : null;
+    $this->invalid_char_action  = (array_key_exists('invalid_char_action', $options)) ? $options['invalid_char_action'] : null;
+    $this->log                  = (array_key_exists('log', $options)) ? $options['log'] : false;
+  }
+
+  /**
+  * Send some text messages
+  * 
+  *
+  * @author  Martin Steel
+  */
+  public function send(array $sms) {
+    if (!is_array($sms)) {
+      throw new KannelException("sms parameter must be an array");
+    }
+    $single_message = $this->is_assoc($sms);
+
+    if ($single_message) {
+      $sms = array($sms);
+    }
+
+    $req_doc = new \DOMDocument('1.0', 'UTF-8');
+    $root = $req_doc->createElement('Message');
+    $req_doc->appendChild($root);
+
+    $user_node = $req_doc->createElement('Key');
+    $user_node->appendChild($req_doc->createTextNode($this->key));
+    $root->appendChild($user_node);
+
+    for ($i = 0; $i < count($sms); $i++) {
+      $single = $sms[$i];
+
+      $sms_node = $req_doc->createElement('SMS');
+           
+      // Phone number
+      $sms_node->appendChild($req_doc->createElement('To', $single['to'])); 
+            
+      // Message text
+      $content_node = $req_doc->createElement('Content');
+      $content_node->appendChild($req_doc->createTextNode($single['message']));
+      $sms_node->appendChild($content_node);
+
+      // From
+      if (array_key_exists('from', $single) || isset($this->from)) {
+        $from_node = $req_doc->createElement('From');
+        $from_node->appendChild($req_doc->createTextNode(array_key_exists('from', $single) ? $single['from'] : $this->from));
+        $sms_node->appendChild($from_node);
+      }
+
+      // Client ID
+      if (array_key_exists('client_id', $single)) {
+        $client_id_node = $req_doc->createElement('ClientID');
+        $client_id_node->appendChild($req_doc->createTextNode($single['client_id']));
+        $sms_node->appendChild($client_id_node);
+      }
+
+      // Long
+      if (array_key_exists('long', $single) || isset($this->long)) {
+        $long = array_key_exists('long', $single) ? $single['long'] : $this->long;
+        $long_node = $req_doc->createElement('Long');
+        $long_node->appendChild($req_doc->createTextNode($long ? 1 : 0));
+        $sms_node->appendChild($long_node);
+      }
+
+      // Truncate
+      if (array_key_exists('truncate', $single) || isset($this->truncate)) {
+        $truncate = array_key_exists('truncate', $single) ? $single['truncate'] : $this->truncate;
+        $trunc_node = $req_doc->createElement('Truncate');
+        $trunc_node->appendChild($req_doc->createTextNode($truncate ? 1 : 0));
+        $sms_node->appendChild($trunc_node);
+      }
+
+      // Invalid Char Action
+      if (array_key_exists('invalid_char_action', $single) || isset($this->invalid_char_action)) {
+        $action = array_key_exists('invalid_char_action', $single) ? $single['invalid_char_action'] : $this->invalid_char_action;
+        switch (strtolower($action)) {
+          case 'error':
+          $sms_node->appendChild($req_doc->createElement('InvalidCharAction', 1));
+          break;
+          case 'remove':
+          $sms_node->appendChild($req_doc->createElement('InvalidCharAction', 2));
+          break;
+          case 'replace':
+          $sms_node->appendChild($req_doc->createElement('InvalidCharAction', 3));
+          break;
+          default:
+          break;
+        }
+      }
+
+      // Wrapper ID
+      $sms_node->appendChild($req_doc->createElement('WrapperID', $i));
+
+      $root->appendChild($sms_node);
+    }
+
+    $req_xml = $req_doc->saveXML();
+     
+    $resp_xml = $this->postToKannel(self::API_SMS_METHOD, $req_xml);
+    $resp_doc = new \DOMDocument();
+    $resp_doc->loadXML($resp_xml);   
+
+    $response = array();
+    $err_no = null;
+    $err_desc = null;
+
+    foreach($resp_doc->documentElement->childNodes AS $doc_child) {
+      switch(strtolower($doc_child->nodeName)) {
+        case 'sms_resp':
+        $resp = array();
+        $wrapper_id = null;
+        foreach($doc_child->childNodes AS $resp_node) {
+          switch(strtolower($resp_node->nodeName)) {
+            case 'messageid':
+            $resp['id'] = $resp_node->nodeValue;
+            break;
+            case 'errno':
+            $resp['error_code'] = $resp_node->nodeValue;
+            break;
+            case 'errdesc':
+            $resp['error_message'] = $resp_node->nodeValue;
+            break;
+            case 'wrapperid':
+            $wrapper_id = $resp_node->nodeValue;
+            break;
+          }
+        }
+        if( array_key_exists('error_code', $resp ) ) 
+        {
+          $resp['success'] = 0;
+        } else {
+          $resp['success'] = 1;
+        }
+        $resp['sms'] = $sms[$wrapper_id];
+        array_push($response, $resp);
+        break;
+        case 'errno':
+        $err_no = $doc_child->nodeValue;
+        break;
+        case 'errdesc':
+        $err_desc = $doc_child->nodeValue;
+        break;
+      }
+    }
+
+    if (isset($err_no)) {
+      throw new KannelException($err_desc, $err_no);
+    }
+        
+    if ($single_message) {
+      return $response[0];
+    } else {
+      return $response;
+    }
+  }
+
+  /**
+  * Check how many SMS credits you have available
+  *
+  * @return  integer   SMS credits remaining
+  * @deprecated Use checkBalance() instead
+  * @author  Martin Steel
+  */
+  public function checkCredit() {
+    // Create XML doc for request
+    $req_doc = new \DOMDocument('1.0', 'UTF-8');
+    $root = $req_doc->createElement('Credit');
+    $req_doc->appendChild($root);
+    $root->appendChild($req_doc->createElement('Key', $this->key));
+    $req_xml = $req_doc->saveXML();
+
+    // POST XML to Kannel
+    $resp_xml = $this->postToKannel(self::API_CREDIT_METHOD, $req_xml);
+
+    // Create XML doc for response
+    $resp_doc = new \DOMDocument();
+    $resp_doc->loadXML($resp_xml);
+
+    // Parse the response to find credit value
+    $credit;
+    $err_no = null;
+    $err_desc = null;
+        
+    foreach ($resp_doc->documentElement->childNodes AS $doc_child) {
+      switch ($doc_child->nodeName) {
+        case "Credit":
+        $credit = $doc_child->nodeValue;
+        break;
+        case "ErrNo":
+        $err_no = $doc_child->nodeValue;
+        break;
+        case "ErrDesc":
+        $err_desc = $doc_child->nodeValue;
+        break;
+        default:
+        break;
+      }
+    }
+
+    if (isset($err_no)) {
+      throw new KannelException($err_desc, $err_no);
+    }
+    return $credit;
+  }
+
+  /**
+  * Check your account balance
+  *
+  * @return  array   Array of account balance: 
+  * @author  Martin Steel
+  */
+  public function checkBalance() {
+    // Create XML doc for request
+    $req_doc = new \DOMDocument('1.0', 'UTF-8');
+    $root = $req_doc->createElement('Balance');
+    $req_doc->appendChild($root);
+    $root->appendChild($req_doc->createElement('Key', $this->key));
+    $req_xml = $req_doc->saveXML();
+    
+    // POST XML to Kannel
+    $resp_xml = $this->postToKannel(self::API_BALANCE_METHOD, $req_xml);
+
+    // Create XML doc for response
+    $resp_doc = new \DOMDocument();
+    $resp_doc->loadXML($resp_xml);
+    
+    // Parse the response to find balance value
+    $balance = null;
+    $err_no = null;
+    $err_desc = null;
+        
+    foreach ($resp_doc->documentElement->childNodes as $doc_child) {
+      switch ($doc_child->nodeName) {
+        case "Balance":
+        $balance = number_format(floatval($doc_child->nodeValue), 2);
+        break;
+        case "Currency":
+        foreach ($doc_child->childNodes as $resp_node) {
+          switch ($resp_node->tagName) {
+            case "Symbol":
+            $symbol = $resp_node->nodeValue; 
+            break;
+            case "Code":
+            $code = $resp_node->nodeValue; 
+            break;
+          }
+        }
+        break;
+        case "ErrNo":
+        $err_no = $doc_child->nodeValue;
+        break;
+        case "ErrDesc":
+        $err_desc = $doc_child->nodeValue;
+        break;
+        default:
+        break;
+      }
+    }
+
+    if (isset($err_no)) {
+      throw new KannelException($err_desc, $err_no);
+    }
+        
+    return array( 'symbol' => $symbol, 'balance' => $balance, 'code' => $code );
+  }
+
+  /**
+  * Check whether the API Key is valid
+  *
+  * @return  bool    True indicates a valid key
+  * @author  Martin Steel
+  */
+  public function checkKey() {
+    // Create XML doc for request
+    $req_doc = new \DOMDocument('1.0', 'UTF-8');
+    $root = $req_doc->createElement('Authenticate');
+    $req_doc->appendChild($root);
+    $root->appendChild($req_doc->createElement('Key', $this->key));
+    $req_xml = $req_doc->saveXML();
+
+    // POST XML to Kannel
+    $resp_xml = $this->postToKannel(self::API_AUTH_METHOD, $req_xml);
+
+    // Create XML doc for response
+    $resp_doc = new \DOMDocument();
+    $resp_doc->loadXML($resp_xml);
+        
+    // Parse the response to see if authenticated
+    $cust_id;
+    $err_no = null;
+    $err_desc = null;
+
+    foreach ($resp_doc->documentElement->childNodes AS $doc_child) {
+      switch ($doc_child->nodeName) {
+        case "CustID":
+        $cust_id = $doc_child->nodeValue;
+        break;
+        case "ErrNo":
+        $err_no = $doc_child->nodeValue;
+        break;
+        case "ErrDesc":
+        $err_desc = $doc_child->nodeValue;
+        break;
+        default:
+        break;
+      }
+    }
+
+    if (isset($err_no)) {
+      throw new KannelException($err_desc, $err_no);
+    }
+    return isset($cust_id);   
+  }
+
+  /**
+  * Make an HTTP POST to Kannel
+  *
+  * @param   string   method Kannel method to call (sms/credit)
+  * @param   string   data   Content of HTTP POST
+  *
+  * @return  string          Response from Kannel
+  * @author  Martin Steel
+  */
+  protected function postToKannel($method, $data) {
+    if ($this->log) {
+      $this->logXML("API $method Request XML", $data);
+    }
+    
+    if( isset( $this->ssl ) ) {
+      $ssl = $this->ssl;
+    } else {
+      $ssl = $this->sslSupport();
+    }
+
+    $url = $ssl ? 'https://' : 'http://';
+    $url .= self::API_BASE_URL . $method;
+
+    $response = $this->xmlPost($url, $data);
+
+    if ($this->log) {
+      $this->logXML("API $method Response XML", $response);
+    }
+
+    return $response;
+  }
+
+  /**
+  * Make a HTTP POST
+  *
+  * cURL will be used if available, otherwise tries the PHP stream functions
+  *
+  * @param   string url      URL to send to
+  * @param   string data     Data to POST
+  * @return  string          Response returned by server
+  * @author  Martin Steel
+  */
+  protected function xmlPost($url, $data) {
+    if(extension_loaded('curl')) {
+      $ch = curl_init($url);
+      curl_setopt($ch, CURLOPT_POST, 1);
+      curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+      curl_setopt($ch, CURLOPT_HTTPHEADER, Array("Content-Type: text/xml"));
+      curl_setopt($ch, CURLOPT_USERAGENT, 'Kannel PHP Wrapper/1.0' . self::VERSION);
+      curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
+      if (isset($this->proxy_host) && isset($this->proxy_port)) {
+        curl_setopt($ch, CURLOPT_PROXY, $this->proxy_host);
+        curl_setopt($ch, CURLOPT_PROXYPORT, $this->proxy_port);
+      }
+
+      $response = curl_exec($ch);
+      $info = curl_getinfo($ch);
+
+      if ($response === false || $info['http_code'] != 200) {
+        throw new \Exception('HTTP Error calling Kannel API - HTTP Status: ' . $info['http_code'] . ' - cURL Erorr: ' . curl_error($ch));
+      } elseif (curl_errno($ch) > 0) {
+        throw new \Exception('HTTP Error calling Kannel API - cURL Error: ' . curl_error($ch));
+      }
+
+      curl_close($ch);
+
+      return $response;
+    } elseif (function_exists('stream_get_contents')) {
+      // Enable error Track Errors
+      $track = ini_get('track_errors');
+      ini_set('track_errors',true);
+
+      $params = array('http' => array(
+      'method'  => 'POST',
+      'header'  => "Content-Type: text/xml\r\nUser-Agent: mediaburst PHP Wrapper/" . self::VERSION . "\r\n",
+      'content' => $data
+      ));
+
+      if (isset($this->proxy_host) && isset($this->proxy_port)) {
+        $params['http']['proxy'] = 'tcp://'.$this->proxy_host . ':' . $this->proxy_port;
+        $params['http']['request_fulluri'] = True;
+      }
+
+      $ctx = stream_context_create($params);
+      $fp = @fopen($url, 'rb', false, $ctx);
+      if (!$fp) {
+        ini_set('track_errors',$track);
+        throw new \Exception("HTTP Error calling Kannel API - fopen Error: $php_errormsg");
+      }
+      $response = @stream_get_contents($fp);
+      if ($response === false) {
+        ini_set('track_errors',$track);
+        throw new \Exception("HTTP Error calling Kannel API - stream Error: $php_errormsg");
+      }
+      ini_set('track_errors',$track);
+      return $response;
+    } else {
+      throw new \Exception("Kannel requires PHP5 with cURL or HTTP stream support");
+    }
+  }
+
+  /**
+  * Does the server/HTTP wrapper support SSL
+  *
+  * This is a best guess effort, some servers have weird setups where even
+  * though cURL is compiled with SSL support is still fails to make
+  * any requests.
+  *
+  * @return bool     True if SSL is supported
+  * @author  Martin Steel
+  */
+  protected function sslSupport() {
+    $ssl = false;
+    // See if PHP is compiled with cURL
+    if (extension_loaded('curl')) {
+      $version = curl_version();
+      $ssl = ($version['features'] & CURL_VERSION_SSL) ? true : false;
+    } elseif (extension_loaded('openssl')) {
+      $ssl = true;
+    }
+    return $ssl;
+  }
+
+  /**
+  * Log some XML, tidily if possible, in the PHP error log
+  *
+  * @param   string  log_msg The log message to prepend to the XML
+  * @param   string  xml     An XML formatted string
+  *
+  * @return  void
+  * @author  Martin Steel
+  */
+  protected function logXML($log_msg, $xml) {
+    // Tidy if possible
+    if (class_exists('tidy')) {
+      $tidy = new \tidy;
+      $config = array(
+      'indent'     => true,
+      'input-xml'  => true,
+      'output-xml' => true,
+      'wrap'       => 200
+      );
+      $tidy->parseString($xml, $config, 'utf8');
+      $tidy->cleanRepair();
+      $xml = $tidy;
+    }
+    // Output
+    error_log("Kannel $log_msg: $xml");
+  }
+
+  /**
+  * Check if an array is associative
+  *
+  * @param   array $array Array to check
+  * @return  bool
+  * @author  Martin Steel
+  */
+  protected function is_assoc($array) {
+    return (bool)count(array_filter(array_keys($array), 'is_string'));
+  }
+  
+  /**
+   * Check if a number is a valid MSISDN
+   *
+   * @param string $val Value to check
+   * @return bool True if valid MSISDN
+   * @author James Inman
+   * @since 1.3.0
+   * @todo Take an optional country code and check that the number starts with it
+   */
+  public static function is_valid_msisdn($val) {
+    return preg_match( '/^[1-9][0-9]{7,12}$/', $val );
+  }
+
+}

+ 14 - 0
plugin/kannelsms/vendor/license.txt

@@ -0,0 +1,14 @@
+Copyright (c) 2011 - 2012, Mediaburst Ltd <hello@mediaburst.co.uk>
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+

+ 253 - 0
plugin/kannelsms/vendor/readme.md

@@ -0,0 +1,253 @@
+# Kannel SMS API Wrapper for PHP
+
+This wrapper lets you interact with Kannel without the hassle of having to create any XML or make HTTP calls.
+
+## What's Kannel?
+
+[Kannel][2] is Mediaburst's SMS API.
+
+### Prerequisites
+
+* A [Kannel][2] account
+
+## Usage
+
+Require the Kannel library:
+
+```php
+require 'class-Kannel.php';
+```
+
+### Sending a message
+
+```php
+$kannel = new Kannel( $API_KEY );
+$message = array( 'to' => '441234567891', 'message' => 'This is a test!' );
+$result = $kannel->send( $message );
+```
+
+### Sending multiple messages
+
+We recommend you use batch sizes of 500 messages or fewer. By limiting the batch size it prevents any timeouts when sending.
+
+```php
+$kannel = new Kannel( $API_KEY );
+$messages = array( 
+    array( 'to' => '441234567891', 'message' => 'This is a test!' ),
+    array( 'to' => '441234567892', 'message' => 'This is a test 2!' )
+);
+$results = $kannel->send( $messages );
+```
+
+### Handling the response
+
+The responses come back as arrays, these contain the unique Kannel message ID, whether the message worked (`success`), and the original SMS so you can update your database.
+
+    Array
+    (
+        [id] => VE_164732148
+        [success] => 1
+        [sms] => Array
+            (
+                [to] => 441234567891
+                [message] => This is a test!
+            )
+
+    )
+
+If you send multiple SMS messages in a single send, you'll get back an array of results, one per SMS.
+
+The result will look something like this:
+
+    Array
+    (
+        [0] => Array
+            (
+                [id] => VI_143228951
+                [success] => 1
+                [sms] => Array
+                    (
+                        [to] => 441234567891
+                        [message] => This is a test!
+                    )
+
+            )
+
+        [1] => Array
+            (
+                [id] => VI_143228952
+                [success] => 1
+                [sms] => Array
+                    (
+                        [to] => 441234567892
+                        [message] => This is a test 2!
+                    )
+
+            )
+
+    )
+
+If a message fails, the reason for failure will be set in `error_code` and `error_message`.  
+
+For example, if you send to invalid phone number "abc":
+
+    Array
+    (
+        [error_code] => 10
+        [error_message] => Invalid 'To' Parameter
+        [success] => 0
+        [sms] => Array
+            (
+                [to] => abc
+                [message] => This is a test!
+            )
+
+    )
+
+### Checking your balance
+
+Check your available SMS balance:
+
+```php
+$kannel = new Kannel( $API_KEY );
+$kannel->checkBalance();
+```    
+
+This will return:
+
+    Array 
+    (
+        [symbol] => £
+        [balance] => 351.91
+        [code] => GBP
+    )
+    
+### Handling Errors
+
+The Kannel wrapper will throw a `KannelException` if the entire call failed.
+
+```php
+try 
+{
+    $kannel = new Kannel( 'invalid_key' );
+    $message = array( 'to' => 'abc', 'message' => 'This is a test!' );
+    $result = $kannel->send( $message );
+}
+catch( KannelException $e )
+{
+    print $e->getMessage();
+    // Invalid API Key
+}
+```    
+
+### Advanced Usage
+
+This class has a few additional features that some users may find useful, if these are not set your account defaults will be used.
+
+### Optional Parameters
+
+See the [Kannel Documentation](http://www.kannelsms.com/doc/clever-stuff/xml-interface/send-sms/) for full details on these options.
+
+*   $from [string]
+
+    The from address displayed on a phone when they receive a message
+
+*   $long [boolean]  
+
+    Enable long SMS. A standard text can contain 160 characters, a long SMS supports up to 459.
+
+*   $truncate [nullable boolean]  
+
+    Truncate the message payload if it is too long, if this is set to false, the message will fail if it is too long.
+
+*	$invalid_char_action [string]
+
+	What to do if the message contains an invalid character. Possible values are
+	* error			 - Fail the message
+	* remove		 - Remove the invalid characters then send
+	* replace		 - Replace some common invalid characters such as replacing curved quotes with straight quotes
+
+*   $ssl [boolean, default: true]
+  
+    Use SSL when making an HTTP request to the Kannel API
+
+
+### Setting Options
+
+#### Global Options
+
+Options set on the API object will apply to all SMS messages unless specifically overridden.
+
+In this example both messages will be sent from Kannel:
+
+```php
+$options = array( 'from' => 'Kannel' );
+$kannel = new Kannel( $API_KEY, $options );
+$messages = array( 
+    array( 'to' => '441234567891', 'message' => 'This is a test!' ),
+    array( 'to' => '441234567892', 'message' => 'This is a test 2!' )
+);
+$results = $kannel->send( $messages );
+```
+
+#### Per-message Options
+
+Set option values individually on each message.
+
+In this example, one message will be from Kannel and the other from 84433:
+
+```php
+$kannel = new Kannel( $API_KEY, $options );
+$messages = array( 
+    array( 'to' => '441234567891', 'message' => 'This is a test!', 'from' => 'Kannel' ),
+    array( 'to' => '441234567892', 'message' => 'This is a test 2!', 'from' => '84433' )
+);
+$results = $kannel->send( $messages );
+```
+
+### SSL Errors
+
+Due to the huge variety of PHP setups out there a small proportion of users may get PHP errors when making API calls due to their SSL configuration.
+
+The errors will generally look something like this:
+
+```
+Fatal error: 
+Uncaught exception 'Exception' with message 'HTTP Error calling Kannel API
+HTTP Status: 0
+cURL Erorr: SSL certificate problem, verify that the CA cert is OK. 
+Details: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed'
+```
+
+If you're seeing this error there are two fixes available, the first is easy, simply disable SSL on Kannel calls. Alternatively you can setup your PHP install with the correct root certificates.
+
+#### Disable SSL on Kannel calls
+
+```php
+$options = array( 'ssl' => false );
+$kannel = new Kannel( $API_KEY, $options );
+```
+
+#### Setup SSL root certificates on your server
+
+This is much more complicated as it depends on your setup, however there are many guides available online. 
+Try a search term like "windows php curl root certificates" or "ubuntu update root certificates".
+
+
+# License
+
+This project is licensed under the ISC open-source license.
+
+A copy of this license can be found in license.txt.
+
+# Contributing
+
+If you have any feedback on this wrapper drop us an email to [hello@kannelsms.com][1].
+
+The project is hosted on GitHub at [https://github.com/mediaburst/kannel-php][3].
+If you would like to contribute a bug fix or improvement please fork the project 
+and submit a pull request.
+
+[1]: mailto:hello@kannelsms.com
+[2]: http://www.kannelsms.com/
+[3]: https://github.com/mediaburst/kannel-php