/*
 *  transact.c - processes all transactions to the server.
 *  certain transactions such as sending & receiving a file and sending a 
 *  long nlist, are always made into light weight processes.
 */

#include <stdio.h>
#include <sys/file.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/wait.h>
#include "lwp/lwp.h"
#include "event.h"

static int      cur_sock;
static int      next_id;
static char     out_buff[BUFSIZ];
static char     in_buff[BUFSIZ];
int             PROV = NOBODY;
static int      full_format = FALSE;

extern int      debug;
extern int      menu;
extern int      disptype;
extern int      errno;
extern char    *msglist[]; /* the list of error messages */

extern int type_size;
extern int sponsor_size;
extern int source_size;

static char    *uid; /* the user id who sent the current transaction */
static char    *type;
static char    *trans_data; /* everything after the 1st ":" */  
static char     trans_type;
static int      cont;
static int      web_changed;	/* the web has changed since last written */

#define	strnode(str)	web_node(atol(str))
static EVENT * event_list[1024];
static int list_size;
int
hdl_transact(conn)
	CONN           *conn;
{
	char           *machtype;
	char            mode[10];
	char            log_line[256];
	int             length;
	int             quit, red;
	time_t          secs;

	printf("\nHandling transaction to %s, port %d, socket #%d.\n",
	       conn->c_hostname,
	       conn->c_portnum,
	       conn->c_socket);
	time(&conn->c_last);
	cont = TRUE;
	cur_sock = conn->c_socket;
	uid = conn->c_uid;
	full_format = (conn->c_flags & C_FULLFMT);
	bzero(in_buff, BUFSIZ);
	length = read(cur_sock, in_buff, BUFSIZ);
	if (length <= 0) {
		if (length)
			perror("socket read");
                if (errno == EWOULDBLOCK)
		  return 0;
                else
                  return 1;
	}
	if (in_buff[length - 1] == LF)
		length--;
	if (in_buff[length - 1] == CR)
		length--;
	in_buff[length] = '\0';

	if (!parse_trans() || length == 0)
		return 1;	/* quit and close connection */

	sprintf(log_line, "%s:%d:%c:%s", uid, cur_sock, trans_type, trans_data);
	log_trans(log_line);	/* log the transaction */
	proc_trans(trans_data);
	deliver();
	return 0;		/* ok */
}

parse_trans()
{
	char           *c;

	trans_type = *in_buff;
	c = (char *) index(in_buff, DLM);
	if (c)
		trans_data = c + 1;
	else if (in_buff[1] && trans_type)
		trans_data = in_buff + 1;
	else
		trans_data = "";
	printf("Transaction(type=`%c' data=[%s])\n", trans_type, trans_data);
	if (trans_type == 'q')
		return 0;
	return 1;
}

proc_trans(char *trans_data)
{
int x;
EVENT *event;
char *cp;
  switch (trans_type) {

  case T_ADD_EVENT:
    add_event(trans_data);
    break; 
  case T_MODIFY_EVENT:
    modify_event(trans_data);
    break; 
  case T_QUERY_EVENT:
    parse_query(trans_data);
    send_ok();
    break; 
  case T_LOAD:
    load_events();
    send_ok();
    break;
  case T_FIND:
    event = find_event(atol(trans_data));
    send_event(event);
    send_eom();
    break;
  case T_SEND_TABLE:
    send_table(*trans_data);
    break;
  case T_SEND_ALL:
    show_all_events();
    break;
  case T_SAVE:
    save_events();
    send_ok();
    break;
  case T_DEL_REC:
    event = find_event(atol(trans_data));    
    del_rec_from_list(event);
    send_ok();
    break;
  default:
    send_huh();
  }
}

test_owner(char *source)
{				/* test to see if a certain uid is the owner
				 * of a node */
	char            provline[PROV_LN_SZ];
	char            allline[PROV_LN_SZ];
	char            testline[PROV_LN_SZ], *cp;
	FILE           *fopen(), *provfile;

	provfile = fopen(PROV_FILE, "r");
	if (!provfile) {
		perror(PROV_FILE);
		return;
	}
	sprintf(testline, "%s:%s", source, uid);
	sprintf(allline, "All:%s", uid);
	while (fgets(provline, PROV_LN_SZ, provfile) != '\0') {
		cp = (char *) rindex(provline, ':');
		if (cp) {
			*cp = '\0';
			if ((strcasecmp(testline, provline) == 0)
			    || (strcasecmp(allline, provline) == 0)) {
				fclose(provfile);
				return 1;
			}
		}
	}
	fclose(provfile);
	return 0;

}
valid_source(char *srce)
{				/* test to see if a source has been entered
				 * in the provider table. The provider table 
structure is <source>:<user_id>:<password> */

	FILE           *fopen(), *provfile;
	char            provline[PROV_LN_SZ], *cp;
	int             i;

	provfile = fopen(PROV_FILE, "r");
	if (!provfile) {
		perror(PROV_FILE);
		return 0;
	}
	while (fgets(provline, PROV_LN_SZ, provfile) != '\0') {
		cp = (char *) index(provline, ':');
		if (cp) {
			*cp = '\0';
			i = strlen(provline);
			if (strcasecmp(srce, provline, i) == 0) {
				fclose(provfile);
				return 1;
			}
		}
	}
	fclose(provfile);
	return 0;		/* close file */

}
static char     src[20];

char           *
test_prov()
{				/* test to see if a uid is a provider */

	FILE           *fopen(), *provfile;
	char            provline[PROV_LN_SZ], *id_pswd;
	int             i;

	provfile = fopen(PROV_FILE, "r");
	if (!provfile) {
		perror(PROV_FILE);
		return 0;
	}
	while (fgets(provline, PROV_LN_SZ, provfile) != '\0') {
		id_pswd = (char *)index(provline, DLM);
		if (id_pswd) {
			*id_pswd = '\0';
			strcpy(src, provline);
			id_pswd++;
			i = strlen(id_pswd);
			*(id_pswd + i - 1) = '\0';
			if (strcasecmp(trans_data, id_pswd) == 0) {
				fclose(provfile);
				return src;
			}
		}
	}
	fclose(provfile);
	return NULL;		/* close file */

}

test_admin(passwd)
	char           *passwd;
{
	/* test to see if a uid is an admin */

	FILE           *fopen(), *adminfile;
	char            adminline[ADMIN_LN_SZ];

	adminfile = fopen(ADMIN_FILE, "r");
	if (!adminfile) {
		perror(ADMIN_FILE);
		return 0;
	}
	while (fgets(adminline, ADMIN_LN_SZ, adminfile) != '\0') {
		adminline[strlen(adminline) - 1] = '\0';
		if (strcasecmp(passwd, adminline) == 0) {
			fclose(adminfile);
			return 1;
		}
	}
	fclose(adminfile);
	return 0;		/* close file */

}

static int dfd;
inc_nextid()
{				/* increments next id in mem + on disk */
	char            line[20];

	if ((dfd = open(NEXTID_FILE, O_WRONLY | O_TRUNC | O_CREAT)) == 0)
		printf("error incrementing next id\n");
	next_id++;
	sprintf(line, "%d\n", next_id);
	write(dfd, line, strlen(line));
	close(dfd);
	return;
}

get_nextid()
{				/* reads the next id from disk */
	char            line[20];
	int             red;

	if ((dfd = open(NEXTID_FILE, O_RDONLY)) == 0)
		printf("error getting next id\n");
	red = read(dfd, line, 20);
	line[red] = '\0';
	next_id = atoi(line);
	close(dfd);
	return;
}


log_trans(line)			/* write the transaction to the log file */
	char           *line;
{
	char            logline[300];
	time_t          secs;

	time(&secs);
	sprintf(logline, "%s:%s", line, ctime(&secs));
	if ((dfd = open(TRANS_LOG, O_APPEND | O_CREAT | O_WRONLY, 0644)) == 0)
		printf("error logging transaction\n");
	write(dfd, logline, strlen(logline));
	close(dfd);
	return;
}

find_ver() /* find the version number of the client associated with a 
              certain machine */
{
	FILE           *fopen(), *verfile;
	char            verline[ADMIN_LN_SZ];
	int             len;

	verfile = fopen(VER_FILE, "r");
	if (!verfile)
		return;
	len = strlen(trans_data);
	while (fgets(verline, ADMIN_LN_SZ, verfile) != '\0') {
		verline[strlen(verline) - 1] = '\0';
		if (strncmp(trans_data, verline, len) == 0) {
			fclose(verfile);
			send_msg(verline);
			return 0;
		}
	}
	fclose(verfile);
	send_err();

	return 0;		/* close file */
}

void
parse_event_text(char *cp,EVENT *event_rec)
{
int next_id,more = TRUE;
char *fld_type,*fld_value,*fld,*title;

fld_type = cp;
while (more) {
  more = (int) fld_value = cp = next_field(&cp);
 fld = fld_type;
 if (more)
    more = (int) fld_type = cp = next_field(&cp);
printf("type = %s, value = <%s>\n",fld,fld_value);
  switch (*fld) { /* assign value to event records */

  case F_ID:
    event_rec->id = atol(fld_value);
    printf("field id -%s-\n",fld_value);
    break;
  case F_TITLE:
    printf("field title -%s-\n",fld_value);
    title = (char *) malloc(strlen(fld_value));
    strcpy(title,fld_value);
    event_rec->title = title;
    break;
  case F_SOURCE:
    event_rec->source = get_idnum_from_table(fld_value,source_table,source_size);
    printf("field source -%s-\n",fld_value);
    break;
  case F_SPONSOR:
    event_rec->sponsor = get_idnum_from_table(fld_value,sponsor_table,sponsor_size);
    printf("field sponsor -%s-\n",fld_value);
    break;
  case F_TYPE:
    printf("field type -%s-\n",fld_value);
    event_rec->type = get_idnum_from_table(fld_value,type_table,type_size);
    printf("type num = %d\n",event_rec->type);
    break;
  case F_DATE:
    printf("field date -%s-\n",fld_value);
    break;
  default:
    break;
    /* send err message + get out */
  } /* end case */
}
  return;
}

add_event(char *trans_data)
{
EVENT  *event_rec;
ADD_EVENT_INFO add_event_info;

event_rec =  (EVENT *) malloc(sizeof(EVENT));
parse_event_text(trans_data,event_rec);
send_ok();
/* test auth */
/* assign id, chg date etc */
/* add to linked list */
add_rec_to_list(event_rec);
}

modify_event(char *trans_data)
{
EVENT  *event_rec;
event_rec = find_event(atol(trans_data));
trans_data = next_field(&trans_data);
parse_event_text(trans_data,event_rec);
send_ok();
}
/* pointers to first + last records in linked list */
static EVENT *first_rec = 0; 
static EVENT *last_rec = 0;

int
add_rec_to_list(EVENT *event_rec) { /* add to the linked list */

if (!first_rec)
  first_rec = event_rec;
else
  last_rec->next = event_rec;
event_rec->prev = last_rec;
event_rec->next = 0;
last_rec = event_rec;
}

int
del_rec_from_list(EVENT *event_rec) { /* remove from the linked list */

EVENT *prev_rec,*next_rec;

if (first_rec == event_rec) {
  first_rec = event_rec->next;
  if (last_rec != event_rec)
    first_rec->prev = 0;
  else
    last_rec = 0;
}
else
  {
    prev_rec = event_rec->prev;
    next_rec = event_rec->next;
    prev_rec->next = event_rec->next;
  if (last_rec != event_rec)
    next_rec->prev = event_rec->prev;
  else
    last_rec = 0;
  }
}

void
show_all_events()
{
EVENT *event;

for (event = first_rec; event; event = event->next)
    send_event_abr(event);
send_eom();
}

EVENT *
find_event(long id)
{
EVENT *event;

for (event = first_rec; event; event = event->next)
    if (event->id == id)
      return event;
return 0;
}

void
send_event(EVENT *event)
{
char buf[1024];
  sprintf(buf,"id:%d\ntitle:%s\n",event->id,event->title);
  sprintf(buf,"%stype:%s\n",buf,get_value_from_table(event->type,type_table,type_size));
  sprintf(buf,"%ssource:%s\n",buf,get_value_from_table(event->source,source_table,source_size));
  sprintf(buf,"%ssponsor:%s\n",buf,get_value_from_table(event->sponsor,sponsor_table,sponsor_size));
  send(buf);
}

void
send_event_abr(EVENT *event)
{
char buf[1024];
  sprintf(buf,"%d:%s",event->id,event->title);
  sprintf(buf,"%s:%s",buf,get_value_from_table(event->type,type_table,type_size));
  sprintf(buf,"%s:%s",buf,get_value_from_table(event->source,source_table,source_size));
  sprintf(buf,"%s:%s\n",buf,get_value_from_table(event->sponsor,sponsor_table,sponsor_size));
  send(buf);
}

void
save_events()
{
EVENT *event;
FILE *fopen(), *eventfile;

eventfile = fopen(EVENT_FILE,"w+");
if (!eventfile) {
  perror(eventfile);
  return;
}
for (event = first_rec; event; event = event->next)
  {
    fprintf(eventfile,"%d:%d:%d:%d:%s\n",event->id,event->type,event->sponsor,
	    event->source,event->title);
}
fclose(eventfile);
}
void
load_events()
{
FILE *fopen(), *eventfile;
EVENT *event_rec;
char line[500],*cp,*title;

eventfile = fopen(EVENT_FILE,"r+");
if (!eventfile) {
  perror(eventfile);
  return;
}
while (fgets(line,500,eventfile) != '\0') {
  cp = line;
  event_rec =  (EVENT *) malloc(sizeof(EVENT)); 
  event_rec->id = atol(line);
  event_rec->type = atoi(next_field(&cp)); 
  event_rec->sponsor = atoi(next_field(&cp)); 
  event_rec->source = atoi(next_field(&cp));
  title = (char *) malloc(strlen(next_field(&cp)));
  strcpy(title,cp);
  title[strlen(title)-1] = '\0';
  event_rec->title = title; 
  add_rec_to_list(event_rec);
}
fclose(eventfile);
}

/*
 * Return pointer to start of next field (after it's starting colon) 
 */
char    *
next_field(char **ptr)
{
	register char  *cp = *ptr;

	while (*cp && *cp != DLM)
		cp++;
	if (*cp == '\0')
	  return *cp;
	*cp = '\0';
	return *ptr = ++cp;
}

int
load_table(TABLE_ENTRY *table, char *filename)
{
FILE  *fopen(),*fn;
char  line[50],*temp;
int   line_size = 50,pos = 0,i;
TABLE_ENTRY *tbl_rec;

fn = fopen(filename,"r");
if (!fn) {
  perror(filename);
  return 0;
}
tbl_rec = table;
while (fgets(line,line_size,fn) != '\0') {
  tbl_rec->num = atoi(get_token(line,1));
  temp = get_token(line,2);
  temp[strlen(temp)-1] = '\0';
  strcpy(tbl_rec->string,temp);
  tbl_rec++;
  pos++;
}
printf("\n number of entries = %d\n",pos);
for (i=0;i < pos;i++) {
printf("%s -- %d\n",table[i].string,table[i].num);
}
fclose(fn);
return pos;
}

int
get_idnum_from_table(char *str,TABLE_ENTRY *table,int table_size)
{
int i;
for (i = 0; i < table_size; i++) {
  if (strcasecmp(table[i].string,str) == 0)
    return table[i].num;
}
return 0;
}

char *
get_value_from_table(int num,TABLE_ENTRY *table,int table_size)
{
int i;
for (i=0; i < table_size; i++) {
  if (table[i].num == num)
    return table[i].string;
}
return 0;
}

/* SEND_TABLE - send a validation table to client */
send_table(char fld)
{
char line[50];
int i,table_size = 0;
TABLE_ENTRY *atable;

printf("fld = %c\n",fld);
	  switch (fld) {
	    case F_TYPE:
	      table_size = type_size;
	      atable = type_table;
	      break;
	    case F_SPONSOR:
	      table_size = sponsor_size;
	      atable = sponsor_table;
	      break;
	    case F_SOURCE:
	      table_size = source_size;
	      atable = source_table;
	      break;
	    }
  
sprintf(line,"%d\n",table_size);
send(line);
 for (i = 0; i < table_size; i++) {
   sprintf(line,"%d:%s\n",atable[i].num,atable[i].string);
   send(line);
} 
send_eom();
}

int 
parse_query(char *cp)
{
int next_id,value,more = TRUE;
int i,put_ptr;
char *fld_type,*fld_value,*fld,*title,*operator,op;

list_size = load_list();
fld_type = cp;
while (more) {
  more = (int) operator = cp = next_field(&cp);
  more = (int) fld_value = cp = next_field(&cp);
 fld = fld_type;
  op = *operator;
 if (more)
    more = (int) fld_type = cp = next_field(&cp); 

printf("type = %s, op = %c value = <%s>\n",fld,op,fld_value);
  put_ptr = 0;
  switch (*fld) {
    
  case F_TITLE: /* string compare */
    printf("field title -%s-\n",fld_value);
    title = (char *) malloc(strlen(fld_value));
    strcpy(title,fld_value);
    break;

  case F_TYPE: /* integer compare */
    value = get_idnum_from_table(fld_value,type_table,type_size);
    for (i = 0; i < list_size; i++) {
      if (op == EQUALS) {
	if (value == event_list[i]->type) {
	  event_list[put_ptr] = event_list[i];
	  put_ptr++;
	}}
      else if (op == NOT_EQUAL)
	  if (value != event_list[i]->type) {
	    event_list[put_ptr] = event_list[i];
	    put_ptr++;
	  }
    }
    list_size = put_ptr;
    break;

  case F_SPONSOR: /* integer compare */
    value = get_idnum_from_table(fld_value,sponsor_table,sponsor_size);
    for (i = 0; i < list_size; i++) {
      if (op == EQUALS) {
	if (value == event_list[i]->sponsor) {
	  event_list[put_ptr] = event_list[i];
	  put_ptr++;
	}}
      else if (op == NOT_EQUAL)
	  if (value != event_list[i]->sponsor) {
	    event_list[put_ptr] = event_list[i];
	    put_ptr++;
	  }
    }
    list_size = put_ptr;
    break;

  case F_SOURCE: /* integer compare */
    value = get_idnum_from_table(fld_value,source_table,source_size);
    for (i = 0; i < list_size; i++) {
      if (op == EQUALS){ 
	if (value == event_list[i]->source) {
	  event_list[put_ptr] = event_list[i];
	  put_ptr++;
	}}
      else if (op == NOT_EQUAL)
	  if (value != event_list[i]->source) {
	    event_list[put_ptr] = event_list[i];
	    put_ptr++;
	  }
    }
    list_size = put_ptr;
    break;

  default:
    break;
    /* send err message + get out */
  } /* end case */
}
printf("about to send list #%d\n",list_size);
send_list();
  return;
}

int
load_list()
{
EVENT *event;
int cnt;

for (event = first_rec, cnt = 0; event; event = event->next,cnt++)
    event_list[cnt] = event;
return cnt;
}

int
send_list()
{
EVENT *event;
int i;
char line[30];

sprintf(line,"%d events found\n",list_size);
send(line);
for ( i = 0; i < list_size; i++)
  send_event_abr(event_list[i]);
send_eom();
}
