#!/usr/bin/php -n
<?php
dl('curl.so');
dl('json.so');

define('OK', 0);
define('CRITICAL', 2);
define('VERSION', '2.0');

function logMe($message, $priority = LOG_INFO)
{
    openlog('OpMon-' . basename(__FILE__), LOG_PID, LOG_LOCAL0);
    syslog($priority, $message);
    closelog();
}

function printArray(Array &$arr)
{
    foreach ($arr as $key => $value) {
        if (is_array($value)) {
            return printArray($value);
        }

        $msg = (is_numeric($key)) ? "\t" : "\t" . $key . ': ';
        print $msg . preg_replace('/\n */', ' ', $value) . PHP_EOL;
    }
}

function sendSms($username, $password, $aggregateId, $from, $to, $message, $verbose = false)
{
    $authorizationKey = base64_encode($username . ':' . $password);
    $response = array();
    $url = 'https://api-rest.zenvia.com/services/send-sms';

    $message = str_replace(".", "_", $message);

    if (!$aggregateId) {
	$aggregateId = 0;
    }

    $postData = sprintf(
        '{"sendSmsRequest":{"from":"%s","to":"%s","msg": "%s","aggregateId":"%s"}}',
        $from,
        $to,
        $message,
        $aggregateId
    );

    $httpHeader = array(
        'Content-Type: application/json',
        'Authorization: Basic ' . $authorizationKey,
        'Accept: application/json'
    );

    if ($verbose) {
        print 'Request:' . PHP_EOL;
        print 'Headers' . PHP_EOL;
        printArray($httpHeader);
        print PHP_EOL;
        print 'Post data ' . PHP_EOL;
        print "\t" . $postData . PHP_EOL;
    }

    $curl = curl_init();
    curl_setopt($curl, CURLOPT_HEADER, false);
    curl_setopt($curl, CURLOPT_FOLLOWLOCATION, false);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_FAILONERROR, true);
    curl_setopt($curl, CURLOPT_HTTPHEADER, $httpHeader);
    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "POST");
    curl_setopt($curl, CURLOPT_POSTFIELDS, $postData);

    $response['data']  = curl_exec($curl);
    $response['error'] = array(
        'code'    => curl_errno($curl),
        'message' => curl_error($curl)
    );

    return $response;
}

function version()
{
    print basename(__FILE__) . " version " . VERSION . PHP_EOL;
}

function usage()
{
    print "Usage: " . basename(__FILE__) . " [OPTION]..." . PHP_EOL;

}

function help()
{
    usage();

    print <<<EOF
This plugin sends SMS' notifications using Zenvia's API, for hosts and services.

General Options:
    -f, --from              Sender's information. It can be a contact name,
                          enterprise name or a phone number, for example. The
                          number of characters used in the "from" field is
                          deducted from the total of available characters for
                          the message (150).

        --hostaddress         Address of a monitored host. Required for a host
                          notification.

        --hostname            Name of a monitored host. Required for all
                          notifications.

        --hoststate           Current host state. Required for host
                          notifications.

        --hostoutput          Host check output. Required for host
                          notifications.

    -m, --message             Message that will be sent in the SMS. This message
                          must have 150 characters at most including "from"
                          field data size. If this options isn't defined, the
                          message will be built from the other options.

        --notificationtype    Type of a notification. Required for all
                          notifications.

    -p, --password            Zenvia password.

    -I, --aggregateId         Zenvia aggregateId.

        --servicedesc         Name of a service. Required for service
                          notifications.

        --servicestate        Current service state. Required for a service
                          notification.

        --serviceoutput       Service check output. Required for a service
                          notification.

        --shortdatetime       Notification date/time. Required for all
                          notifications.

    -t, --to                  Recipient number using format (DDI)(DDD)(number).
                          Example: 555199998888

    -u, --username            Zenvia account.

Extra Options:

    -h, --help                Display this help.
    -V, --version             Show plugin's version.
    -v, --verbose             Be verbose.

EOF;
}

function validateOptions(Array &$opts)
{
    $opts['help'] = ((isset($opts['h'])) || (isset($opts['help'])));
    $opts['version'] = ((isset($opts['V'])) || (isset($opts['version'])));

    if (($opts['help']) || ($opts['version'])) {
        return OK;
    }

    $opts['verbose']  = ((isset($opts['v'])) || (isset($opts['verbose'])));
    $opts['username'] = (isset($opts['u'])) ? $opts['u'] : $opts['username'];
    $opts['password'] = (isset($opts['p'])) ? $opts['p'] : $opts['password'];
    $opts['aggregateId'] = (isset($opts['I'])) ? $opts['I'] : 0;
    $opts['message']  = (isset($opts['m'])) ? $opts['m'] : $opts['message'];
    $opts['from']     = (isset($opts['f'])) ? $opts['f'] : $opts['from'];
    $opts['to']       = (isset($opts['t'])) ? $opts['t'] : $opts['to'];

    $validNotification = true;
    if (empty($opts['message'])) {
        $validNotification = ((! empty($opts['shortdatetime']))
            && (! empty($opts['notificationtype'])));

        $validHost = ((! empty($opts['hostname']))
            && (! empty($opts['hostaddress']))
            && (! empty($opts['hoststate']))
            && (! empty($opts['hostoutput']))
        );

        $validService = ((! empty($opts['hostname']))
            && (! empty($opts['servicedesc']))
            && (! empty($opts['servicestate']))
            && (! empty($opts['serviceoutput'])));

        $validNotification = (($validNotification)
            && (($validHost) || ($validService)));
    }

    $validNotification = (($validNotification)
        && (! empty($opts['from']))
        && (! empty($opts['to'])));

    return ($validNotification) ? OK : CRITICAL;
}

function buildMessage(Array &$opts)
{
    if (! empty($opts['message'])) {
        return $opts['message'];
    }

    $host     = "Host: " . $opts['hostname'] . '\n';
    $hostAddr = (empty($opts['hostaddress']))
        ? ""
        : "Address: " . $opts['hostaddress'] . '\n';
    $service  = (empty($opts['servicedesc']))
        ? ""
        : "Service: " . $opts['servicedesc'] . '\n';
    $detail   = (empty($opts['servicedesc']))
        ? "Detail: " . $opts['hostoutput'] . '\n'
        : "Detail: " . $opts['serviceoutput'] . '\n';
    $state    = (empty($opts['servicedesc']))
        ? $opts['hoststate'] . ' - ' . $opts['shortdatetime'] . '\n'
        : $opts['servicestate'] . ' - ' . $opts['shortdatetime'] . '\n';

    $message = sprintf(
        "%s%s%s%s%s",
        $opts['notificationtype'] . '\n',
        $host,
        (empty($service)) ? $hostAddr : $service,
        $state,
        $detail
    );

    return $message;
}

/**
 * When the OpMon doesn't know translate a macro it keeps the macro's name as
 * a notification command argument. Therefore must be removed the macro's names
 * from the arguments to avoid a host notification with the text
 * "Service: $SERVICEDESC$", for example.
 */
function removeUnprocessedMacros(Array $opts)
{
    foreach ($opts as $key => $value) {
        $opts[$key] = preg_replace('/^\$_?([A-Z]|[0-9])+\$$/', '', $value);
    }

    return $opts;
}

function main()
{
    $options = getopt(
        'u:p:I:f:t:m:hvV',
        array(
            'username:',
            'password:',
            'aggregateId',
            'from:',
            'to:',
            'message:',
            'help',
            'verbose',
            'version',
            'shortdatetime:',
            'notificationtype:',
            'hostname:',
            'hostaddress:',
            'hoststate:',
            'hostoutput:',
            'servicedesc:',
            'servicestate:',
            'serviceoutput:'
        )
    );

    $options = removeUnprocessedMacros($options);

    if (validateOptions($options) != OK) {
        usage();
        print "Try '" . basename(__FILE__) . " --help' for more information." . PHP_EOL;
        exit(CRITICAL);
    }

    if ($options['help']) {
        help();
        exit(OK);
    }

    if ($options['version']) {
        version();
        exit(OK);
    }

    /**
     * Maximum length to a message according with Zenvia's API documentation is
     * 142 characters for Nextel clients and 160 to the others. This size
     * includes "msg" and "from" fields.
     *
     * Error 012: Message content overflow
     *
     * Conteudo do SMS muito longo. Verifique se o tamanho dos parametros "msg"
     * e "from" juntos nao ultrapassam 150 caracteres.
     *
     *
     * Reference:
     *
     * http://www.zenvia.com.br/desenvolvedores/documentacao/conceitos-chave-e-dicas/
     * http://www.zenvia.com.br/desenvolvedores/documentacao/
     * documentos-antigos/api-legada-1-0/envio-de-sms-unico-http-webservice/
     */
    $sizeLimit = 142 - strlen($options['from']) - 1;
    $options['message'] = substr(buildMessage($options), 0, $sizeLimit);

    $response = sendSms(
        $options['username'],
        $options['password'],
        $options['aggregateId'],
        $options['from'],
        $options['to'],
        $options['message'],
        $options['verbose']
    );

    if ($response['error']['code'] == 22) {
        logMe($response['error']['message'], LOG_ERR);
    }

    $responseStatus = json_decode($response['data']);
    $responseStatus = $responseStatus->sendSmsResponse;

    if ($responseStatus->statusDescription == "Error") {
        $msg  = "Status Code: " . $responseStatus->statusCode;
        $msg .= " Status Description: " . $responseStatus->statusDescription;
        $msg .= " Detailed Code: " . $responseStatus->detailCode;
        $msg .= " Detailed Description: " . $responseStatus->detailDescription;

        logMe($msg, LOG_CRIT);
    }

    if ($options['verbose']) {
        print PHP_EOL . PHP_EOL;
        print 'Response: ' . PHP_EOL ;
        printArray($response);
        print PHP_EOL;
    }

    return ($response['data']) ? OK : CRITICAL;
}

exit(main());
