/*
 * 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_STDLIB_H
#include <stdlib.h>
#endif

#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif

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

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

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

#include "monitor.h"
#include "event.h"
#include "net.h"
#include "alert.h"


/**
 *  Implementation of the alert module
 *
 *  @author Jan-Henrik Haukeland, <hauk@tildeslash.com>
 *  @author Martin Pala <martinp@tildeslash.com>
 *  @author Christian Hopp <chopp@iei.tu-clausthal.de>
 *
 *  @version \$Id: alert.c,v 1.55 2007/07/25 12:54:28 hauk Exp $
 *  @file
 */


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


static void copy_mail(Mail_T, Mail_T);
static void replace_bare_linefeed(Mail_T *);
static void substitute(Mail_T *, Event_T);


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


/**
 * Notify registred users about the event
 * @param E An Event object
 * @return If failed, return HANDLER_ALERT flag or HANDLER_PASSED if passed
 */
int handle_alert(Event_T E) {

  Service_T s;
  int rv = HANDLER_PASSED;

  ASSERT(E);

  s= Event_get_source(E);
  if(!s) {
    LogError("Aborting alert\n");
    return rv;
  }

  if(s->maillist || Run.maillist) {
    Mail_T m;
    Mail_T n;
    sigset_t ns, os;
    Mail_T list= NULL;
    set_signal_block(&ns, &os);
    /*
     * Build a mail-list with local recipients that has registered interest
     * for this event.
     */
    for(m= s->maillist; m; m= m->next) {
      
      if(
        /* particular event notification type is allowed for given recipient */
        IS_EVENT_SET(m->events, Event_get_id(E)) &&
        (
          /* 'changed/matched' event types are sent always */
          E->id == EVENT_CHANGED || E->id == EVENT_MATCH ||
          /* state change notification is sent always */
          E->state_changed       ||
          /* in the case that the state is failed for more cycles we check
           * whether we should send the reminder */
          (
            E->state && m->reminder && E->count % m->reminder == 0
          )
        )
      )
      {

        Mail_T tmp= NULL;

        NEW(tmp);
        copy_mail(tmp, m);
        substitute(&tmp, E);
        replace_bare_linefeed(&tmp);
        tmp->next= list;
        list= tmp;

        DEBUG("%s notification is sent to %s\n", Event_get_description(E), m->to);

      }

    }

    /*
     * Build a mail-list with global recipients that has registered interest
     * for this event. Recipients which are defined in the service localy
     * overrides the same recipient events which are registered globaly.
     */
    for(m= Run.maillist; m; m= m->next) {

      int skip= FALSE;

      for(n= s->maillist; n; n= n->next) {
        if(IS(m->to, n->to)) {
          skip= TRUE;
          break;
        }
      }

      if(
        /* the local service alert definition has not overrided the global one */
        !skip &&
        /* particular event notification type is allowed for given recipient */
        IS_EVENT_SET(m->events, Event_get_id(E)) &&
        (
          /* 'changed' event type is sent always */
          E->id == EVENT_CHANGED || E->id == EVENT_MATCH ||
          /* state change notification is sent always */
          E->state_changed       ||
          /* in the case that the state is failed for more cycles we check
           * whether we should send the reminder */
          (
            E->state && m->reminder && E->count % m->reminder == 0
          )
        )
      )
      {

        Mail_T tmp= NULL;

        NEW(tmp);
        copy_mail(tmp, m);
        substitute(&tmp, E);
        replace_bare_linefeed(&tmp);
        tmp->next= list;
        list= tmp;

        DEBUG("%s notification is sent to %s\n", Event_get_description(E), m->to);

      }

    }

    if(list) {

      if(!sendmail(list)) {
        rv = HANDLER_ALERT;
      }
      gc_mail_list(&list);

    }

    unset_signal_block(&os);

  }

  return rv;

}


static void substitute(Mail_T *m, Event_T e) {

  char timestamp[STRLEN];

  ASSERT(m && e);

  Util_replaceString(&(*m)->from,    "$HOST", Run.localhostname);
  Util_replaceString(&(*m)->subject, "$HOST", Run.localhostname);
  Util_replaceString(&(*m)->message, "$HOST", Run.localhostname);
  
  Util_getRFC822Date(&e->collected, timestamp, STRLEN);
  Util_replaceString(&(*m)->subject, "$DATE", timestamp);
  Util_replaceString(&(*m)->message, "$DATE", timestamp);

  Util_replaceString(&(*m)->subject, "$SERVICE", Event_get_source_name(e));
  Util_replaceString(&(*m)->message, "$SERVICE", Event_get_source_name(e));

  Util_replaceString(&(*m)->subject, "$EVENT", Event_get_description(e));
  Util_replaceString(&(*m)->message, "$EVENT", Event_get_description(e));

  Util_replaceString(&(*m)->subject, "$DESCRIPTION", NVLSTR(Event_get_message(e)));
  Util_replaceString(&(*m)->message, "$DESCRIPTION", NVLSTR(Event_get_message(e)));

  Util_replaceString(&(*m)->subject, "$ACTION", Event_get_action_description(e));
  Util_replaceString(&(*m)->message, "$ACTION", Event_get_action_description(e));
}


static void copy_mail(Mail_T n, Mail_T o) {

  ASSERT(n && o);
  
  n->to= xstrdup(o->to);
  n->from=
      o->from?
      xstrdup(o->from):
      Run.MailFormat.from?
      xstrdup(Run.MailFormat.from):
      xstrdup(ALERT_FROM);
  n->subject=
      o->subject?
      xstrdup(o->subject):
      Run.MailFormat.subject?
      xstrdup(Run.MailFormat.subject):
      xstrdup(ALERT_SUBJECT);
  n->message=
      o->message?
      xstrdup(o->message):
      Run.MailFormat.message?
      xstrdup(Run.MailFormat.message):
      xstrdup(ALERT_MESSAGE);
}


static void replace_bare_linefeed(Mail_T *m) {
  
  Util_replaceString(&(*m)->message, "\r\n", "\n");
  Util_replaceString(&(*m)->message, "\n", "\r\n");
}
