/*
 * Copyright (C) Tildeslash Ltd. All rights reserved.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License version 3.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * In addition, as a special exception, the copyright holders give
 * permission to link the code of portions of this program with the
 * OpenSSL library under certain conditions as described in each
 * individual source file, and distribute linked combinations
 * including the two.
 *
 * You must obey the GNU Affero General Public License in all respects
 * for all of the code used other than OpenSSL.  
 */


#include "config.h"

#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif

#ifdef HAVE_STRING_H
#include <string.h>
#endif 

#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif

#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif 

#include "monit.h"
#include "socket.h"
#include "event.h"


/**
 *  Connect to a data collector servlet and send the event or status message.
 *
 *  @file
 */


/* ----------------------------------------------------------------- Private */


/**
 * Send message to the server
 * @param C An mmonit object
 * @param D Data to send
 * @return TRUE if the message sending succeeded otherwise FALSE
 */
static int data_send(Socket_T socket, Mmonit_T C, const char *D) {
        char *auth = Util_getBasicAuthHeader(C->url->user, C->url->password);
        int rv = socket_print(socket,
                          "POST %s HTTP/1.1\r\n"
                          "Host: %s:%d\r\n"
                          "Content-Type: text/xml\r\n"
                          "Content-Length: %d\r\n"
                          "Pragma: no-cache\r\n"
                          "Accept: */*\r\n"
                          "User-Agent: %s/%s\r\n"
                          "%s"
                          "\r\n"
                          "%s",
                          C->url->path,
                          C->url->hostname, C->url->port,
                          strlen(D),
                          prog, VERSION,
                          auth?auth:"",
                          D);
        FREE(auth);
        if (rv <0) {
                LogError("M/Monit: error sending data to %s -- %s\n", C->url->url, STRERROR);
                return FALSE;
        }
        return TRUE;
}


/**
 * Check that the server returns a valid HTTP response
 * @param C An mmonit object
 * @return TRUE if the response is valid otherwise FALSE
 */
static int data_check(Socket_T socket, Mmonit_T C) {
        int  status;
        char buf[STRLEN];

        if (! socket_readln(socket, buf, sizeof(buf))) {
                LogError("M/Monit: error receiving data from %s -- %s\n", C->url->url, STRERROR);
                return FALSE;
        }
        Str_chomp(buf);
        int n = sscanf(buf, "%*s %d", &status);
        if (n != 1 || (status >= 400)) {
                LogError("M/Monit: message sending failed to %s -- %s\n", C->url->url, buf);
                return FALSE;
        }
        return TRUE;
}


/* ------------------------------------------------------------------ Public */


/**
 * Post event or status data message to mmonit
 * @param E An event object or NULL for status data
 * @return If failed, return HANDLER_MMONIT flag or HANDLER_SUCCEEDED flag if succeeded
 */
int handle_mmonit(Event_T E) {
        int       rv = HANDLER_MMONIT;
        Socket_T  socket = NULL;
        Mmonit_T  C = Run.mmonits;

        /* The event is sent to mmonit just once - only in the case that the state changed */
        if (! C || (E && ! E->state_changed))
                return HANDLER_SUCCEEDED;

        StringBuffer_T sb = StringBuffer_create(256);
        for (; C; C = C->next) {
                if (! (socket = socket_create_t(C->url->hostname, C->url->port, SOCKET_TCP, C->ssl, C->timeout))) {
                        LogError("M/Monit: cannot open a connection to %s -- %s\n", C->url->url, STRERROR);
                        goto error;
                }
                status_xml(sb, E, E ? LEVEL_SUMMARY : LEVEL_FULL, 2, socket_get_local_host(socket));
                if (! data_send(socket, C, StringBuffer_toString(sb))) {
                        LogError("M/Monit: cannot send %s message to %s\n", E ? "event" : "status", C->url->url);
                        goto error;
                }
                StringBuffer_clear(sb);
                socket_shutdown_write(socket);
                if (! data_check(socket, C)) {
                        LogError("M/Monit: %s message to %s failed\n", E ? "event" : "status", C->url->url);
                        goto error;
                }
                rv = HANDLER_SUCCEEDED; // Return success if at least one M/Monit succeeded
                DEBUG("M/Monit: %s message sent to %s\n", E ? "event" : "status", C->url->url);
error:
                if (socket)
                        socket_free(&socket);
        }
        StringBuffer_free(&sb);
        return rv;
}

