/*
 * Copyright (C), 2000-2007 by the monit project group.
 * All Rights Reserved.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * 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 General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */


#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 "monitor.h"
#include "socket.h"
#include "event.h"


/**
 *  Connect to a data collector server and send event or status message.
 *
 *  @author Martin Pala, <martinp@tildeslash.com>
 *
 *  @version \$Id: collector.c,v 1.15 2007/07/25 12:54:28 hauk Exp $
 *
 *  @file
 */


/* -------------------------------------------------------------- Prototypes */


static int data_send(Collector_T, char *);
static int data_check(Collector_T);


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


/**
 * Post event or status data message to the collector
 * @param E An event object or NULL for status data
 * @return If failed, return HANDLER_COLLECTOR flag or HANDLER_PASSED flag if passed
 */
int handle_collector(Event_T E) {

  char        *D = NULL;
  Collector_T  C = Run.collectors;
  sigset_t     ns;
  sigset_t     os;
  int          rv = HANDLER_PASSED;

  /* The event is send to the collector just once - only in the case
   * that the state changed/matched */
  if(!C || (E && !E->state_changed && (E->id != EVENT_CHANGED || E->id != EVENT_MATCH)))
  {
    return rv;
  }

  set_signal_block(&ns, &os);

  while(!(C->socket=
    socket_create_t(C->url->hostname, C->url->port, SOCKET_TCP, C->ssl, C->timeout)
  ))
  {
    LogError("Collector: cannot open a connection to %s -- %s\n",
      C->url->url, STRERROR);

    if((C= C->next))
    {
      LogInfo("Collector: trying next server %s\n", C->url->url);
      continue;
    }
    else
    {
      LogError("Collector: no server available\n");
      rv = HANDLER_COLLECTOR;
      goto exit2;
    }
  }

  D = status_xml(E, LEVEL_FULL);

  if(!data_send(C, D))
  {
    LogError("Collector: communication failure\n");
    rv = HANDLER_COLLECTOR;
    goto exit1;
  }
  if(!data_check(C))
  {
    LogError("Collector: communication failure\n");
    rv = HANDLER_COLLECTOR;
    goto exit1;
  }
  DEBUG("Collector: %s message send to %s\n", E?"event":"status", C->url->url);

exit1:
  FREE(D);
  if(C->socket)
  {
    socket_free(&C->socket);
  }
exit2:
  unset_signal_block(&os);
  return rv;
}


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


/**
 * Send message to the server
 * @param C An collector object
 * @param D Data to send
 * @return TRUE if the message sending succeeded otherwise FALSE
 */
static int data_send(Collector_T C, char *D) {

  int   rv;
  char *auth;

  auth = Util_getBasicAuthHeader();
  rv = socket_print(C->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"
         "Connection: close\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("Collector: 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 collector object
 * @return TRUE if the response is valid otherwise FALSE
 */
static int data_check(Collector_T C) {

  int  n;
  int  status;
  char buf[STRLEN];

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

