/*------------------------------------------------
 *
 * BLOCKS.C
 *
 * Used as part of the avacl (Avacl pronounced as Debable)
 */
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <ctype.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <time.h>

#include "strlower.h"
#include "blocks.h"



#define _MAXLINELEN 1024
#define _MAXFILES 1000
#define _MAXDIRDEPTH 5
#define _MAXNAMESBLOCK 1000
#define _MAXTEXTBLOCK 1000
#define _MAXTYPEBLOCK 1000
#define _MAXTESTBLOCK 1000

	/* System Logging */
#define _SL (LOG_MAIL|LOG_INFO)


/* a record for storing the status of our scannings, per file */
struct _filerec {
	char filename[513];
	char filenamelc[513];
	char filetype[513];
	int sizetest;
	int typetest;
	int nametest;
	int virustest;
	int texttest;
	};
	
	
	
/* a record for holding the filename-blocking database records */	
struct _nameblockrec {
	char filename[513];
	char comment[513];
	};
	

/* a record for holding the text-search-blocking database records */
struct _textblockrec {
	char searchfor[513];
	char comment[513];
	};

/* a record for holding the filename-blocking database records */	
struct _typeblockrec {
	char filetype[513];
	char comment[513];
	};
	


/* Globals */
int maxtestoffset;
int nameblock_count;
int textblock_count;
int typeblock_count;
int testblock_count;
int filelist_count;
int type_highestkey;


/* the list of filenames to block... */
struct _nameblockrec *nameblock[_MAXNAMESBLOCK];

/* the list of text strings to block... */
struct _textblockrec *textblock[_MAXTEXTBLOCK];

/* the list of filetypes to block... */
struct _typeblockrec *typeblock[_MAXTYPEBLOCK];



/* Semi-Constants */
char _NULL_BLOCK[]="";


/*-----------------------------------------------------------------
 * LOAD_nameblock
 *
 * Load in the text file with the filename matching blocks
 *
 */
int LOAD_nameblock( char *fname ){
#ifdef DEBUG
	int i;
#endif
	char line[_MAXLINELEN+1];
	FILE *f;

	f = fopen(fname,"r");
	
	nameblock_count = 0;
	
	if (!f){
		syslog(_SL,"Error: Cannot open filename block file '%s'",fname);
		}
	else {
		while ( fgets(line,_MAXLINELEN,f) ){
		
			/* allocate memory for the new name block */
			nameblock[nameblock_count] = malloc(sizeof(struct _nameblockrec));			
			
			/* scan in the details from the file */
			sscanf(line,"%[^:]:%[^\n\r]%*[\n\r]"\
				,nameblock[nameblock_count]->filename\
				,nameblock[nameblock_count]->comment\
				);
			
			/* convert the filename spec to lower case...*/
			strlower(nameblock[nameblock_count]->filename);
			/* increment the counter */
			nameblock_count++;
			
			} /* while */
			
		fclose(f);
		
		} /* if (f) */

#ifdef DEBUG
	fprintf(stderr,"\n------NAMEBLOCK LISTING-------\n");
	for (i = 0; i < nameblock_count; i++){
		fprintf(stderr,"NameBlock %d: %s, %s\n",i,nameblock[i]->filename, nameblock[i]->comment);
		}
	fprintf(stderr,"------------------------------\n\n");
#endif			
			
	return 0;
	}




/*-----------------------------------------------------------------
 * REMOVE_nameblock
 *
 * Removes a single nameblock entry that matches the param
 *
 */
int REMOVE_nameblock( char *removeme ){
	
	int i;
	
	for (i = 0; i < nameblock_count; i++){
#ifdef DEBUG
		fprintf(stderr,"RemoveNameBlock: comparing %s to %s\n",nameblock[i]->filename, removeme);
#endif
		
		if (strcmp(removeme, nameblock[i]->filename)==0) {
			nameblock[i]->filename[0] = '\0';
			i = nameblock_count;
			}
			
		} /* for */
		
	return 0;
	}
	


int ADD_nameblock( char *addme, char *comment ){

	/* convert nameblock to lower case */
	strlower(addme);
				
	/* allocate memory for the new name block */
	nameblock[nameblock_count] = malloc(sizeof(struct _nameblockrec));			

	if (nameblock[nameblock_count]) {			
		/* scan in the details from the file */
		strcpy(nameblock[nameblock_count]->filename, addme);
		strcpy(nameblock[nameblock_count]->comment, comment);
			
		/* increment the counter */
		nameblock_count++;
		
		}
	
	return 0;
	}		
		


/*-----------------------------------------------------------------
 * TEST_nameblock
 *
 * tests each file name to see if it's supposed to be blocked
 */
int TEST_nameblock( char *filename ){

	int i;
	int result=0;
		
#ifdef DEBUG
fprintf(stderr,"\n---------------FILENAME TEST-----------");
#endif


	/* convert to lowercase */
	strlower(filename);
	
	/* for every filename we're wanting to try and block, test against
	 * the filenames we have listed in the unpack directory */	
	for (i = 0; i < nameblock_count; i++){

		if (nameblock[i]->filename[0] != '\0') {
#ifdef DEBUG
			fprintf(stderr,"\nNameBlock: Comparing %s to %s...",filename, nameblock[i]->filename);
#endif			
			/* test for filename match */
			if (strstr(filename, nameblock[i]->filename)){
				result++;
				i = nameblock_count;
#ifdef DEBUG
				fprintf(stderr,"HIT");
#endif
				} /* if there was a filename match */
			} /* if nameblock filename is set */
		} /* for each filename to block */		

#ifdef DEBUG
printf("\n--------------------------\n");	
#endif
	return result;
	}
	
	













	









	
	
/*-----------------------------------------------------------------
 * LOAD_typeblock
 *
 * Load in the file types which we're wanting to block
 *
 */
int LOAD_typeblock( char *fname ){

#ifdef DEBUG
	int i;
#endif
	char line[_MAXLINELEN+1];
	FILE *f;

	f = fopen(fname,"r");
	
	typeblock_count = 0;
	
	if (!f){
		syslog(_SL,"Error: Could not open typeblock file '%s'",fname);
		}
	else {
		while ( fgets(line,_MAXLINELEN,f) ){
		
			/* allocate memory for the new name block */
			typeblock[typeblock_count] = malloc(sizeof(struct _typeblockrec));			
			
			/* scan in the details from the file */
			sscanf(line,"%[^:]:%[^\n\r]%*[\n\r]"\
				,typeblock[typeblock_count]->filetype\
				,typeblock[typeblock_count]->comment\
				);
						
			/* increment the counter */
			typeblock_count++;
			
			} /* while */
			
		fclose(f);
		
		} /* if (f) */

#ifdef DEBUG
	fprintf(stderr,"\n------TYPEBLOCK LISTING-------\n");
	for (i = 0; i < typeblock_count; i++){
		fprintf(stderr,"TypeBlock %d: %s, %s\n",i,typeblock[i]->filetype, typeblock[i]->comment);
		}
	fprintf(stderr,"------------------------------\n\n");
#endif			
	return 0;
	}





/*-----------------------------------------------------------------
 * REMOVE_typeblock
 *
 * Removes a single nameblock entry that matches the param
 *
 */
int REMOVE_typeblock( char *removeme ){
	
	int i;
	
	for (i = 0; i < typeblock_count; i++){

#ifdef DEBUG
		fprintf(stderr,"RemoveTypeBlock: comparing %s to %s\n",typeblock[i]->filetype, removeme);
#endif
		if ( strcmp(typeblock[i]->filetype, removeme) == 0 ) {
			typeblock[i]->filetype[0] = '\0';
			}
			
		} /* for */
		
	return 0;
	}









/*-----------------------------------------------------------------
 * TEST_typeblock
 *
 * tests each file type to see if it's supposed to be blocked
 */
int TEST_typeblock( char *filetype ){

	int i;
	int result = 0;
		

#ifdef DEBUG
fprintf(stderr,"\n---------------FILETYPE TEST-----------");
#endif

	/* for every filename we're wanting to try and block, test against
	 * the filenames we have listed in the unpack directory */	
	for (i = 0; i < typeblock_count; i++){
	
		/* test for filename match */
		if (typeblock[i]->filetype[0] != '\0'){
#ifdef DEBUG
			fprintf(stderr,"\nTypeBlock: comparing %s to %s...",filetype,typeblock[i]->filetype);
#endif
			if (strstr(filetype, typeblock[i]->filetype)){
				result++;
				i = typeblock_count;
#ifdef DEBUG
				fprintf(stderr,"HIT");
#endif
				} /* if there's a match */
			} /* if typeblock is set */
		} /* for each filetype to block */		

#ifdef DEBUG
printf("\n---------------------------------\n");	
#endif
	return result;
	}
	
	







/*-----------------------------------------------------------------
 * LOAD_textblock
 *
 * Load in the text strings we require to block on
 *
 */
int LOAD_textblock( char *fname ){

	char line[_MAXLINELEN+1];
	FILE *f;

	f = fopen(fname,"r");
	
	textblock_count = 0;
	
	if (!f){
		syslog(_SL,"Error: Could not open textblock file '%s'",fname);
		}
	else {
		while ( fgets(line,_MAXLINELEN,f) ){
		
			/* allocate memory for the new name block */
			textblock[textblock_count] = malloc(sizeof(struct _textblockrec));			
			
			/* scan in the details from the file */
			sscanf(line,"%[^:]:%[^\n\r]%*[\n\r]"\
				,textblock[textblock_count]->searchfor
				,textblock[textblock_count]->comment\
				);
			
			/* convert the filename spec to lower case...*/
			strlower(textblock[textblock_count]->searchfor);
			
			/* increment the counter */
			textblock_count++;
			
			} /* while */
			
		fclose(f);
		
		} /* if (f) */
			
	return 0;
	}











/*-----------------------------------------------------------------
 * search_file
 *
 * searches through a file for "searchfor"
 * Given that I did algorithms & complexity at University, I think 
 * it's disgusting that I've used this as a kludge for now
 *
 * Just dont' tell my professor!
 *
 */

int search_file( char *searchfor, char *fname ){

	char *swindow;  /* the searchwindow pointer */
	char swc = *searchfor;  /* [S]earch[W]indow[C]har */
	int slen = strlen(searchfor); /* [S]earch(string)[LEN]gth */
	int i; /* traditional loop counter */
	int found = 0; /* search status */
	FILE *f=fopen(fname,"r");   /* file we're searching from */
	
	/* allocate the memory required to hold the search window */
	swindow = malloc((slen+1) *sizeof(char));
	
	/* clear out the window of any junk */
	memset(swindow,0,slen+1);
	
	/* if our file didn't open okay, then we've got a problem, hence report */
	if (!f){
		syslog(_SL,"Error: Cannot read/open %s for text search",fname);
		}
	else {
	
		/* while more data to read in */
		while ((found == 0) && ( (swindow[slen-1] = (char)tolower(fgetc(f)) ) != EOF)){
			
			/* do the string compare */
			/* NOTE - we do the 1st char compare 1st, as it's QUICKER for
			 * tossing away non-matches than using strcmp to do it
			 */
			if ((swc == swindow[0]) && (strcmp(swindow,searchfor)==0)) {
				found = 1;
				}
			else {
				/* shuffle our chars down */
				for (i = 0; i < (slen-1); i++){
					swindow[i] = (char)swindow[i+1];
					}
				} /* elseif */
				
			} /* while */
			
		} /* if */		
	
	return found;
   }


		 
	
	
/*-----------------------------------------------------------------
 * TEST_textblock
 *
 * reads through each files contents and checks for a textstring
 * on which to block
 */
int TEST_textblock( char *filename ){

	int i;
	int result = 0;		
#ifdef DEBUG
printf("Testing text...\n");
#endif

	/* for every string of text we want to test for ... */
	for (i = 0; i < textblock_count; i++){
		
		/* search for the string of text in the file */
		if (search_file(textblock[i]->searchfor,filename) != 0) {
			result++;					
			} /* if test */
			
		} /* for each filename to block */		

#ifdef DEBUG
printf("End of text test\n");
#endif
	
	return result;
	}


	 
		
/*-----------------------------------------------------------------
 * TEST_filesize()
 *
 * Compares filesizes
 *
 * NOTE - this test in KiloBytes (1024)
 */
int TEST_sizeblock( char *filename, int maxsize ){
	
	struct stat buf;
	int result = 0;
	
	/* get the file stats */
	stat(filename,&buf);
	
	if ( (maxsize > 0) && ((buf.st_size/1024) > maxsize) ) result++;
	
	return result;
	}
	
	
	



	
int CLEAR_typeblock( void ) {	/* clear out the type list */
	int i;
	for (i = 0; i < typeblock_count; i++){
		free(typeblock[i]); typeblock[i] = NULL;
		}
		
	typeblock_count = 0;
	return 0;
	}

int CLEAR_nameblock( void ) {
	int i;		
	/* clear out the name list */
	for (i = 0; i < nameblock_count; i++){
		free(nameblock[i]); nameblock[i] = NULL;
		}
	
	nameblock_count = 0;
	return 0;
	}
		
int CLEAR_textblock( void ) {
	int i;
	/* clear out the text list */
	for (i = 0; i < textblock_count; i++){
		free(textblock[i]); textblock[i] = NULL;
		}

	textblock_count = 0;
	return 0;
	}

	


/*------------------------------------------------
 * SHUTDOWN_cleanup()
 *
 * cleans out malloc'd memory for the holding of 
 * the filelists etc
 */
int SHUTDOWN_cleanup( void ){
	
		
	CLEAR_nameblock();
	CLEAR_typeblock();
	CLEAR_textblock();
	
	
	return 0;
	}


