/* swevents.c  - Event creation and reading.
 
   Copyright (C) 2003-2004 James H. Lowe, Jr.  <jhlowe@acm.org>
   All Rights Reserved.

   COPYING TERMS AND CONDITIONS:
   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 2, 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, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include "swuser_config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include "swutilname.h"
#include "swlib.h"
#include "strob.h"
#include "swevents.h"

static char g_redir1[] = "";
static char g_redir2[] = "1>&2";
static char g_redirnull[] = "1>/dev/null";

extern struct swEvents eventsArray[];
static struct swEvents * g_evnt = eventsArray;

static
struct swEvents *
get_struct_by_message(char * line, struct swEvents * evnt)
{
	char * ws;
	char * event_code;	
	static char buf[100];
	struct swEvents * eop;

	strncpy(buf, line, sizeof(buf)-1);
	buf[sizeof(buf)-1] = '\0';

	event_code=strstr(buf, "SW");
	if (event_code == NULL) return NULL;
	if ((ws=strpbrk(event_code, ": \r\n")) == NULL)
		return NULL;
	else
		*ws = '\0';	

	eop = evnt;
	while (eop->valueM != -1) {
		if (strcmp(eop->codeM, event_code) == 0)  {
			return eop;
		}
		eop++;
	}
	return NULL;
}

static
struct swEvents *
get_struct_by_value(int value, struct swEvents * evnt)
{
	struct swEvents * eop;
	eop = evnt;
	while (eop->valueM != -1) {
		if (eop->valueM == value)  {
			return eop;
		}
		eop++;
	}
	return NULL;
}

static
char *
swevent_code(struct swEvents * evnt, int value)
{
	struct swEvents * eop;
	eop = evnt;
	while (eop->valueM != -1) {
		if (eop->valueM == value)  {
			return eop->codeM;
		}
		eop++;
	}
	return  eop->codeM;
}

static
int
get_event_status(char * line, int * statusp, char ** message)
{
	char * s;
	char * s1;
	*message = (char*)NULL;
	*statusp = -1;
	/*
	 * Example:
	 * swcopy: SW_SESSION_ENDS on source host low08-11 at line 56: status=0
	 */

	if (
		strstr(line, ": SW_") ||
		strstr(line, ": SWI_") ||
		strstr(line, ": SWBIS_")
	) {
		if (strstr(line, "status=")) {
			/*
			* The event has a status
			*/
			if (swevent_is_error(line, statusp) < 0 &&
				*statusp == 0) 
			{
				/*
				 * Make sure the status is non zero 
				 * for internal errors
				 */
				*statusp = -1;
			}
		} else {
			/*
			 * No explicit status.
			 * Set the default status.
			 */
			if (swevent_is_error(line, statusp) < 0) {
				fprintf(stderr, "error from swevent_is_error\n");
				*statusp = -1;
			}
		}

		/*
		 * Set the message.
		 * Find the message by finding the 2nd ':'
		 * of (less reliably) the last ':'
		 */
		s = strrchr(line, ':');
		s1 = strchr(line, ':');
		if (s1) s1 = strchr(s1+1, ':');
		if (s1 && s != s1) {
			/*
			* This may happen if the message has a ':'
			s = s1;
			*/
		}
		if (s) {
			s++;
			while(s && *s == ' ') s++; 
			*message = s;
		}
		return 0;
	}
	return -1;
}

static
char *
swevent_message(struct swEvents * evnt, STROB * buf, int value, char * host, 
				char * loc, char * msg, int verbose_level)
{
	if (verbose_level >= 4)
		strob_sprintf(buf, 0, 
				"%s: %s on %s host `hostname` at line $LINENO: %s",
			 		swlib_utilname_get(), 
					swevent_code(evnt, value), 
					loc, msg); 
	else
		strob_sprintf(buf, 0, "%s: %s on %s host `hostname`: %s", 
				swlib_utilname_get(), 
				swevent_code(evnt, value),
				 loc, msg); 
	return strob_str(buf);
}

char *
swevent_shell_echo(struct swEvents * evnt, int to_fd, int verbose_level, 
			char * loc, int value, char * host, char * msg)
{
	char * ret;
	STROB * buf = strob_open(10);
	STROB * sbuf = strob_open(10);
	struct swEvents * eop;
	char * redir = g_redirnull;

	eop = get_struct_by_value(value, evnt);
	if (!eop) {
		return strdup("echo \"\" 1>/dev/null");
	}

	if (eop->is_swi_eventM || /* need to always echo this internal event */
		verbose_level >= eop->verbose_threshholdM || 
					verbose_level < 0) {
		if (to_fd == 2 || eop->is_swi_eventM) {
			/*
			 * Internal swi protocol events are always on
			 * stderr.
			 */
			redir = g_redir2;
		}
		if (to_fd == 1) redir = g_redir1;
		if (to_fd == 0) redir = g_redirnull;
	} else {
		redir = g_redirnull;
	}

	strob_sprintf(buf, 0, "echo \"");
	strob_sprintf(buf, 1, "%s", 
			swevent_message(evnt, sbuf, value, host, loc, 
						msg, verbose_level));
	strob_sprintf(buf, 1, "\" %s", redir);
	strob_close(sbuf);
	/*
	 * Don't FIXME ??
	 * New memory is allocated on each call, thus forming a memory leak.
	 */
	ret = strdup(strob_str(buf));
	strob_close(buf);
	return ret;
}

int
swevent_get_value(struct swEvents * evnt, char * msg)
{
	struct swEvents * eop;
	eop = evnt;
	while (eop->valueM != -1) {
		if (strlen(eop->codeM) && strstr(msg, eop->codeM)) {
			return eop->valueM;
		}
		eop++;
	}
	return  eop->valueM;
}

int
swevent_is_error(char * line, int * statusp)
{
	int n = 0;
	int does_have_status = 0;
	struct swEvents * evnts = eventsArray;
	struct swEvents * ev;
	char * s = (char*)NULL;
	if ((s=strstr(line, "status="))) {
		s += strlen("status=");
		n = /* swlib_status_ */ atoi(s);
		if (n == INT_MAX || n == INT_MIN) {
			/*
			 FIXME this is dead code.
			 * error
			 */
			does_have_status = 0;
		} else {
			/*
			 * read a valid status
			 */
			does_have_status = 1;
		}
	}
	*statusp = n;

	ev = get_struct_by_message(line, evnts);

	if (!ev) {
		fprintf(stderr, "get_struct_by_message returned null\n");
		*statusp = n;
		return -1;
	}
	if (
		*statusp == SW_ERROR ||
		(ev->default_statusM == SW_ERROR && !does_have_status) ||
		(ev->default_statusM == -(SW_ERROR)) /* -SW_ERROR means always an error */
	) {
		/*
		* The event and/or message indicates an error.
		*/
		return 1;
	}
	
	/*
	* The event is not an error.
	*/
	return 0;
}

ssize_t
swevent_write_rpsh_event(int fd, char * event_line, int len)
{
	char * msg;
	struct swEvents * evnt = eventsArray;
	struct swEvents * this_evnt;
	int ret = 0;
	int status;
	int event_value;
	STROB * tmp;

	tmp = strob_open(100);

	event_value = swevent_get_value(evnt, event_line);
	if (event_value < 0) {
		fprintf(stderr, "swevent_write_rpsh_event: event line not found [%s]\n", event_line);
		return -10;
	} 
	this_evnt = get_struct_by_value(event_value, evnt);
	if (!this_evnt) return -20;
	if (this_evnt->is_swi_eventM == 0) {
		/*
		 * this event is not monitored by the
		 * swicol_ routines therefore don't
		 * send it.
		 */
		return 0;
	}
	get_event_status(event_line, &status, &msg);
	strob_strcpy(tmp, "");
	/*
	fprintf(stderr, "JL msg=[%s]\n", msg); 
	*/

	/*
	 * Form a simplified message in the form of
	 *   <number>:<status>\n
	 * where number is the event number
	 */

	if (msg && strstr(msg, "status=") != msg) {
		/*
		 * The message is not "status=X"
		 */
		if (msg)
			strob_sprintf(tmp, 0, "%d:%s", event_value, msg);
		else
			strob_sprintf(tmp, 0, "%d:", event_value);
		swlib_squash_trailing_vnewline(strob_str(tmp));
		strob_sprintf(tmp, 1, "\n");
	} else {
		/*
		 * The message is a simple status
		 */
		strob_sprintf(tmp, 0, "%d:%d\n", event_value, status);
	}
	/*
	fprintf(stderr, "JL writing [%s]\n", strob_str(tmp)); 
	*/
	ret = write(fd, strob_str(tmp), strob_strlen(tmp));
	if (ret < 0 || ret != (int)strob_strlen(tmp)) {
		fprintf(stderr, "swevent_write_rpsh_event: error: write: ret=%d fd=%d %s\n",
				ret, fd, strerror(errno));
		ret = -1;
	}
	strob_close(tmp);
	return ret;
}
