/* lscomp.c - written by Winston Dang */
/* not used at all by IMM */
/******************************************************************************
  IMM version 3.0
  Written by Winston Dang <wkd@hawaii.edu>
  
  * Copyright (c) University of Hawaii 1993. All rights reserved.
  *  
  * License is granted to copy, to use, and to make and to use derivative
  * works for research, educational, government or evaluation purposes, provided
  * that the University of Hawaii is acknowledged in all documentation pertaining
  * to any such copy or derivative work. University of Hawaii grants no other 
  * licenses expressed or implied. The University of Hawaii trade name should 
  * not be used in any advertising without its written  permission.
  *  
  * UNIVERSITY OF HAWAII MAKES NO REPRESENTATIONS CONCERNING EITHER THE
  * MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
  * FOR ANY PARTICULAR PURPOSE.  The software is provided "as is" without
  * express or implied warranty of any kind.
  *  
  * These notices must be retained in any copies of any part of this software.
  ******************************************************************************/

#define MAIN
#include "protocol.h"
#include "immserv.h"    
#include <sys/stat.h>
#include <dirent.h>
#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#ifdef __NETBSD__
#include <stdlib.h>
#endif

#define TRUE 1
#define FALSE 0
    
char lsltr[20] = "lsltr";
char directory[100];
char filename[80];
FILE *logfp;
extern char *mailprog;
int deleteflag = FALSE;
int seed;

/* socket stuff */
struct sockaddr_in addr_dist;
char portnum[6];
int len_addr_dist;

/******************************************************************************/
main(argc,argv)
int argc;
char **argv;
{
    int counter = 0;
    initialize(argc,argv);
    OpentransFD(portnum,group);
    for (;;) {
	if (checkdate(lsltr)) {
	    drawline("BEGIN");
	    diffcomp(lsltr);
	    drawline("END");
	    counter = 0;
	} else if (counter++ < 6) {
	    drawline("No new lsltr file received");
	    counter = 0;
	    mailmessage("No new lsltr file received\n");
	}
	putchar('.');
	fflush(stdout);
	sleep(600);
    }
}

/******************************************************************************/
/* check the date of the file */
checkdate(name)
char *name;
{
    static unsigned long lasttime = 0;
    struct stat info;
    if (stat(name, &info) != 0) {
	fprintf(logfp,"Can't stat %s\n",name);
	exit(1);
    }
    if (lasttime != info.st_ctime) {
	lasttime = info.st_ctime;
	return TRUE;
    }
    return FALSE;
}

/******************************************************************************/
int drawline(title)
char *title;
{
    time_t currtime;
    int i;
    currtime = time(NULL);
    fprintf(logfp,"\n");
    for (i=0; i < 20; i++) fputc('-',logfp);
    fprintf(logfp," %s %s ",title, ctime(&currtime));
}

/******************************************************************************/
/* compare the file 'declfile' to the contents in the current directory */
diffcomp(declfile)
char *declfile;
{
    FILE *infile;
    int transfer,day;
    char hourmin[6];
    unsigned long size;
    char month[4];
    int wordsize;
    char buffer[100];
    struct stat info;
    
    /* open the directory contents file */
    if ((infile = fopen(declfile,"r")) == NULL) {
	perror("lsltr fopen failed\n");
	exit(0);
    }
    strcpy(directory,".");
    checkoutdir(RECOV_INIT,directory);
    
    /* read through the directory contents file - comparing it to existing */
    while (fgets(buffer,100, infile) != NULL) {
	if (debug) fputs(buffer,stdout);
	switch (*buffer) {
	case 't':		/* total value */
	    if (strncmp(buffer,"total",5) == 0)
		continue;
	case 'l':		/* link list indicator */
	case 'd':		/* directory indicator */
	case '-':		/* file */
	    transfer = sscanf(buffer,"%*s %*d %*s %*d %lu %s %d %s %s",
			      &size, month, &day, hourmin, filename);
	    /* make sure we read 5 items on the line */
	    if (transfer == 5) {
		if (*buffer == '-') {
		    wordsize = strlen(filename) - 1;
		    if (filename[wordsize] == '*')
			filename[wordsize] = NULL;
		    sprintf(buffer,"%s/%s",directory,filename);
		    if (stat(buffer, &info) != 0) {
			fprintf(logfp,"01 Dont have %s\n",buffer);
			sendrequest(RECOV_PUT,filename);
			continue;
		    }
		    /* verify the size of the file */
		    if (size != info.st_size && strcmp(filename,lsltr) != 0) {
			fprintf(logfp,"02 detect size diff %s/%s %10lu %s %2d %s %lu\n",
			       directory,filename,size,month,day,hourmin,
			       info.st_size);
			sendrequest(RECOV_PUT,filename);
		    }
		    /* verify that it is the next item up on the current directory */
		    if (debug) fprintf(logfp,"FILE %20s %10lu %s %2d %s %lu\n",
				      filename,size,month,day,hourmin,
				      info.st_size);			   
		    checkoutdir(RECOV_GET,filename);
		}
		continue;
	    }
	default:
	    checkoutdir(RECOV_FREE,directory);
	    transfer = sscanf(buffer,"%s",directory);
	    /* verify that we are looking at a directory */
	    if (transfer == 1 ) {
		wordsize = strlen(directory) - 1;
		if (directory[wordsize] != ':') continue;
		directory[wordsize] = NULL;
		if (debug) fprintf(logfp,"directory %s\n",
				  directory);
		checkoutdir(RECOV_INIT,directory);
		continue;
	    }
	}
    }
    fclose(infile);
    sendrequest(RECOV_FREE,NULL);
}

/******************************************************************************/
/* scan the directory and look at each entry */
int checkoutdir(cmd, name)
int cmd;
char *name;
{
    static int num_entries = 0;
    static int item = 0;
    
    DIR *dirp;
    struct dirent *dp;
    static struct listdef *filelist = NULL;
    
    switch (cmd) {
    case RECOV_INIT:
	/* scan new directory */
	num_entries = 0;
	item = 0;
	sendrequest(RECOV_INIT, directory);
	dirp = opendir(name);
	if (dirp == NULL) {
	    fprintf(logfp,"03 directory %s inacessible\n",name);
	    return FALSE;
	}
	while ((dp = readdir(dirp)) != NULL) {
	    if (dp->d_name[0] != '.') {
		alphalist(RECOV_PUT, &filelist, dp->d_name);
		num_entries++;
	    }
	}
	(void) closedir(dirp);
	if (debug > 2) alphalist(RECOV_TEST, &filelist,NULL);
	return TRUE;
    case RECOV_GET:
	/* check directory entry */
	while (filelist != NULL) {
	    if (strcmp(name, filelist->name) > 0) {
		/* found a file not in list - remove it */
		if (checkfile(filelist->name,directory) == TRUE) {
		    fprintf(logfp,"04 need to unlink %s/%s\n",
			   directory,filelist->name);
		    if (deleteflag) removeit(filelist->name,directory);
		}
	    } else {
		if (debug) fprintf(logfp,"SCANDIR %s\n",filelist->name);
		alphalist(RECOV_GET, &filelist, NULL);
		return TRUE;
	    }
	    alphalist(RECOV_GET, &filelist, NULL);
	}
	return FALSE;
    case RECOV_FREE:
    	/* clean up previous items */
	if (num_entries) {
	    sendrequest(RECOV_FREE,NULL);
	    while ( filelist != NULL) {
		if (checkfile(filelist->name,directory) == TRUE) {
		    fprintf(logfp,"05 Extraneous file %s/%s\n",
			   directory, filelist->name);
		    if (deleteflag) removeit(filelist->name,directory);
		}
	    	alphalist(RECOV_GET, &filelist, NULL);
	    }
	}
	return TRUE;
    }
}

/******************************************************************************/
/* unlink the named file */
int removeit(name, direct)
char *name;
char *direct;
{
    char retval;
    char *buf;
    buf = (char *) liststrdup(direct, name);
    retval = unlink(buf);
    free(buf);
    return retval;
}
/******************************************************************************/
int sendrequest(cmd, name)
int cmd;
char *name;
{
    int i;
    static char dirname[128];
    static struct bufdef request;
    static int timedur_miss = 10;
    if (debug && (name != NULL)) printf("request for %d %s\n",cmd,name);
    switch (cmd) {
    case RECOV_INIT:
	/* initialize the send buffer */
	request.bytepos = 0;
	request.head.flags = 0;
	strcpy( (char *) request.data, name);
	strcpy( dirname,name);
	strcat( (char *) request.data, " ");
	return TRUE;
    case RECOV_PUT:
	if ((strlen((char *) request.data) + strlen(name)) > T_MAX) {
	    if (debug) printf("fragment\n");
	    sendrequest(RECOV_FREE, name);
	    sendrequest(RECOV_INIT, dirname);
	}

	request.bytepos++;
	strcat( (char *) request.data, name);
	strcat( (char *) request.data, " ");
	return TRUE;
	
    case RECOV_FREE:
	if (request.bytepos == 0) return FALSE;

#ifdef SOLARIS
	i =  timedur_miss * (rand_r(&seed) >> 5) / 1024;
	sleep(i);
#else
#ifdef hpux
       i =  timedur_miss * (rand() >> 5) / 1024;
	sleep(i);
#else
	i =  timedur_miss * (random() % 1024) / 1024;
	sleep(i);
#endif
#endif	
	request.head.fid = 0;
	request.head.type = BUF_TYPE_REQUEST;
	i = BUFDEF_SIZE + strlen((char *) request.data);
	if (!debug) fprintf(logfp,"99 request packet %d %s\n",
			   request.bytepos,request.data);

	while (sendto(xmitsock, (char *) &request, i ,0,
		      (struct sockaddr *) &addr_dist, len_addr_dist) < 0) {
	    fprintf(logfp,"cannot send request report\n");
	    sleep(1);
	}
	request.bytepos = 0;
	sleep(2);
	return TRUE;
    }
}

/******************************************************************************/
/* test if the named file is a regular file */
int checkfile(name, direct)
char *name;
char *direct;
{
    char retval = FALSE;
    struct stat info;
    char *buf;
    if (*name == '.') return retval;
	buf = (char *) liststrdup(direct,name);    
    if (stat(buf, &info) == 0) {
	if (S_ISREG(info.st_mode)) {
	    if ((info.st_ctime + 86400) < time(NULL)) {
		retval = TRUE;
	    } else
		fprintf(logfp,"06 new inserted file %s found\n",buf);
	}
    }
    free(buf);
    return retval;
}

/******************************************************************************/
int OpentransFD(portgrp,hostname)
char *portgrp;
char *hostname;
{
    char MC_GROUP[16];
    unsigned int i1, i2, i3, i4;
    struct hostent *hp;

    /* set up the response socket to the server */
    if (hostname != NULL) {
	if(sscanf(hostname, "%u.%u.%u.%u", &i4, &i3, &i2, &i1) != 4){
	    if((hp=gethostbyname(hostname)) == 0){
		fprintf(logfp,"%d-Unknown host %s\n", getpid(), hostname);
		exit(-1);
	    }
	    memcpy((char *)&addr_dist.sin_addr,hp->h_addr, 4);
	    addr_dist.sin_family = AF_INET;
	    strcpy(MC_GROUP, inet_ntoa(addr_dist.sin_addr));
	}else{
	    strcpy(MC_GROUP, hostname);
	    addr_dist.sin_family = AF_INET;
	    addr_dist.sin_addr.s_addr = (u_long)inet_addr(hostname);
	}
    }
    /* Initialize the send socket */
    xmitsock=CreateSendSocket(&addr_dist,&len_addr_dist,portgrp,MC_GROUP,&ttl);
    if (debug) printf("host %s port %s\n",MC_GROUP,portgrp);
    
}
/******************************************************************************/
void abortprog(int nop)
{
    time_t currtime;
    currtime = time(NULL);
    fprintf(logfp,"BYE BYE %s\n",ctime(&currtime));
    fclose(logfp);
    exit(0);
}
/******************************************************************************/
int initialize(argc, argv)
int argc;
char **argv;
{
    int i=1;
    char *direct;
    char logname[200];
    logfp = stdout;
    while (i < argc) {
	if (*argv[i] == '-')
	    switch (*(argv[i] + 1)) {
	    case 'd':
		i++;
		debug = atoi(argv[i]);
		break;
	    case 'p':
		/* take the port number */
		i++;
		strcpy(portnum,argv[i]);
		break;
	    case 'i':
		/* change the MULTICAST address */
		i++;
		strcpy(group,argv[i]);
		break;
	    case 'l':
		/* log file */
		i++;
		if (debug) fprintf(logfp,"log file %s\n",argv[i]);
		sprintf(logname,"%s.log",argv[i]);
		logfp = fopen(logname,"w");
		if (logfp == NULL) fatal("cannot open  log: %s\n",argv[i]);
		break;
	    case 'm':
		/* mail program */
		i++;
		mailprog = argv[i];
		break;
	    case 't':
		/* alter the time to live */
		i++;
		ttl = atoi(argv[i]);
		break;
	    case 'h':
		fprintf(logfp,"lscomp [-d] [-p <port>] [-I <ip>] [-t -<ttl>] [-h]\n");
		fprintf(logfp,"ver 1.0 Written by Winston Dang @copyright 1993\n");
		exit(0);
	    }
	i++;
    }
    /* get the environment */
    direct = (char *) getenv("IMM_IMAGE_DIR");
    if (direct == NULL) {
	fatal("You need to define the environment var IMM_IMAGE_DIR\n");
	exit(1);
    }
    if (chdir(direct))
	warn("failed to chdir to %s\n",direct);
    signal(SIGTERM, abortprog);
    signal(SIGINT,  abortprog);
    signal(SIGQUIT, abortprog);
    seed = time(NULL) % 65536;
#if defined SOLARIS || defined(hpux)
	srand(seed);
#else 
	srandom(seed);
#endif	

}


