<?php

/*
 * PHP server library for EnvayaSMS
 *
 * For example usage see example/www/index.php
 */

class EnvayaSMS
{
    const ACTION_INCOMING = 'incoming';
    const ACTION_OUTGOING = 'outgoing';
    const ACTION_SEND_STATUS = 'send_status';
    const ACTION_TEST = 'test';

    const STATUS_QUEUED = 'queued';
    const STATUS_FAILED = 'failed';
    const STATUS_SENT = 'sent';
    
    const MESSAGE_TYPE_SMS = 'sms';
    const MESSAGE_TYPE_MMS = 'mms';    
    
    static function escape($val)
    {
        return htmlspecialchars($val, ENT_QUOTES, 'UTF-8');
    }    
    
    private static $request;
    
    static function get_request()
    {
        if (!isset(static::$request))
        {
            $version = @$_POST['version'];     

            // If API version changes, could return
            // different EnvayaSMS_Request instance
            // to support multiple phone versions

            static::$request = new EnvayaSMS_Request();
        }
        return static::$request;
    }                    
}

class EnvayaSMS_Request
{   
    private $request_action;
    
    public $version;
    public $phone_number;

    function __construct()
    {
        $this->version = $_POST['version'];
        $this->phone_number = $_POST['phone_number'];
    }
    
    function get_action()
    {
        if (!$this->request_action)
        {
            $this->request_action = $this->_get_action();
        }
        return $this->request_action;
    }
    
    private function _get_action()
    {
        switch (@$_POST['action'])
        {
            case EnvayaSMS::ACTION_INCOMING:
                return new EnvayaSMS_Action_Incoming($this);
            case EnvayaSMS::ACTION_OUTGOING:
                return new EnvayaSMS_Action_Outgoing($this);                
            case EnvayaSMS::ACTION_SEND_STATUS:
                return new EnvayaSMS_Action_SendStatus($this);
            case EnvayaSMS::ACTION_TEST:
                return new EnvayaSMS_Action_Test($this);
            default:
                return new EnvayaSMS_Action($this);
        }
    }            
    
    function is_validated($correct_password)
    {
        $signature = @$_SERVER['HTTP_X_REQUEST_SIGNATURE'];        
        if (!$signature)
        {
            return false;
        }
        
        $is_secure = (!empty($_SERVER['HTTPS']) AND filter_var($_SERVER['HTTPS'], FILTER_VALIDATE_BOOLEAN));
        $protocol = $is_secure ? 'https' : 'http';
        $full_url = $protocol . "://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];    
        
        $correct_signature = $this->compute_signature($full_url, $_POST, $correct_password);           
        
        //error_log("Correct signature: '$correct_signature'");
        
        return $signature === $correct_signature;
    }

    function compute_signature($url, $data, $password)
    {
        ksort($data);
        
        $input = $url;
        foreach($data as $key => $value)
            $input .= ",$key=$value";

        $input .= ",$password";
        
        //error_log("Signed data: '$input'");
        
        return base64_encode(sha1($input, true));            
    }
    
    static function get_messages_xml($messages)
    {
        ob_start();
        echo "<?xml version='1.0' encoding='UTF-8'?>\n";
        echo "<messages>";
        foreach ($messages as $message)
        {   
            $id = isset($message->id) ? " id='".EnvayaSMS::escape($message->id)."'" : "";
            $to = isset($message->to) ? " to='".EnvayaSMS::escape($message->to)."'" : "";        
            echo "<sms$id$to>".EnvayaSMS::escape($message->message)."</sms>";
        }
        echo "</messages>";        
        return ob_get_clean();    
    }           
}

class EnvayaSMS_OutgoingMessage
{
    public $id;             // ID generated by server
    public $to;             // destination phone number
    public $message;        // content of SMS message
}

class EnvayaSMS_Action
{
    public $type;    
    public $request;
    
    function __construct($request)
    {
        $this->request = $request;
    }
}

class EnvayaSMS_MMS_Part
{
    public $form_name;  // name of form field with MMS part content
    public $cid;        // MMS Content-ID
    public $type;       // Content type
    public $filename;   // Original filename of MMS part on sender phone
    public $tmp_name;   // Temporary file where MMS part content is stored
    public $size;       // Content length
    public $error;      // see http://www.php.net/manual/en/features.file-upload.errors.php

    function __construct($args)
    {
        $this->form_name = $args['name'];
        $this->cid = $args['cid'];
        $this->type = $args['type'];
        $this->filename = $args['filename'];
        
        $file = $_FILES[$this->form_name];
        
        $this->tmp_name = $file['tmp_name'];
        $this->size = $file['size'];
        $this->error = $file['error'];
    }
}

class EnvayaSMS_Action_Incoming extends EnvayaSMS_Action
{    
    public $from;           // Sender phone number
    public $message;        // The message body of the SMS, or the content of the text/plain part of the MMS.
    public $message_type;   // EnvayaSMS::MESSAGE_TYPE_MMS or EnvayaSMS::MESSAGE_TYPE_SMS
    public $mms_parts;      // array of EnvayaSMS_MMS_Part instances

    function __construct($request)
    {
        parent::__construct($request);
        $this->type = EnvayaSMS::ACTION_INCOMING;
        $this->from = $_POST['from'];
        $this->message = $_POST['message'];
        $this->message_type = $_POST['message_type'];
        
        if ($this->message_type == EnvayaSMS::MESSAGE_TYPE_MMS)
        {
            $this->mms_parts = array();
            foreach (json_decode($_POST['mms_parts'], true) as $mms_part)
            {
                $this->mms_parts[] = new EnvayaSMS_MMS_Part($mms_part);
            }
        }               
    }    
    
    function get_response_xml($messages)
    {
        return $this->request->get_messages_xml($messages);
    }    
}

class EnvayaSMS_Action_Outgoing extends EnvayaSMS_Action
{    
    function __construct($request)
    {
        parent::__construct($request);
        $this->type = EnvayaSMS::ACTION_OUTGOING;
    }    
    
    function get_response_xml($messages)
    {
        return $this->request->get_messages_xml($messages);
    }    
}

class EnvayaSMS_Action_Test extends EnvayaSMS_Action
{    
    function __construct($request)
    {
        parent::__construct($request);
        $this->type = EnvayaSMS::ACTION_TEST;
    }
}

class EnvayaSMS_Action_SendStatus extends EnvayaSMS_Action
{    
    public $status;     // EnvayaSMS::STATUS_* values
    public $id;         // server ID previously used in EnvayaSMS_OutgoingMessage
    public $error;      // textual descrption of error (if applicable)
    
    function __construct($request)
    {
        parent::__construct($request);   
        $this->type = EnvayaSMS::ACTION_SEND_STATUS;        
        $this->status = $_POST['status'];
        $this->id = $_POST['id'];
        $this->error = $_POST['error'];
    } 
}