/*
 *  Copyright (C) 2002 - 2004 Tomasz Kojm <tkojm@clamav.net>
 *
 *  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 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, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif

#ifdef CLAMUKO

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <pthread.h>
#include <clamav.h>

#include "server.h"
#include "others.h"
#include "cfgparser.h"
#include "dazukoio.h"
#include "clamuko.h"
#include "defaults.h"
#include "output.h"

#include <search.h>
#include <fcntl.h>

int compare(const void *pa, const void *pb) {
	if(*(int *)pa < *(int *)pb) return -1;
	if(*(int *)pa > *(int *)pb) return 1;
	return 0;
}

void clamuko_exit(int sig)
{

    printf("*Clamuko: clamuko_exit(), signal %d\n", sig);

//     if(clamuko_scanning) {
// 	printf("*Clamuko: stopped while scanning %s\n", acc->filename);
// 	acc->deny = 0;
// 	dazukoReturnAccess_TS(dazuko_id,&acc); /* is it needed ? */
//     }

//     if(dazukoUnregister_TS(&dazuko_id))
// 	printf("!Can't unregister with Dazuko\n");

    printf("Clamuko stopped.\n");

    pthread_exit(NULL);
}

void *clamukoth(void *arg)
{
	struct thrarg *tharg = (struct thrarg *) arg;
	sigset_t sigset;
	const char *virname;
        struct sigaction act;
	unsigned long mask = 0;
	const struct cfgstruct *pt;
	short int scan;
	int sizelimit = 0;
	int scanmodified = 0;
	struct stat sb;
	struct dazuko_access *acc;
	void *root=NULL;
	void *val;
	clamuko_scanning = 0;
	dazuko_id_t		*dazuko_id;



    /* ignore all signals except SIGUSR1 */
    sigfillset(&sigset);
    sigdelset(&sigset, SIGUSR1);
    sigdelset(&sigset, SIGSEGV);
    pthread_sigmask(SIG_SETMASK, &sigset, NULL);
    act.sa_handler = clamuko_exit;
    sigfillset(&(act.sa_mask));
    sigaction(SIGUSR1, &act, NULL);
    sigaction(SIGSEGV, &act, NULL);

    /* register */
    if(dazukoRegister_TS(&dazuko_id,"ClamAV", "r+")) {
	printf("!Clamuko: Can't register with Dazuko\n");
	return NULL;
    } else
	printf("Clamuko: Correctly registered with Dazuko.\n");

    /* access mask */
    if(cfgopt(tharg->copt, "ClamukoScanOnOpen")) {
	printf("Clamuko: Scan-on-open mode activated.\n");
	mask |= DAZUKO_ON_OPEN;
    }
    if(cfgopt(tharg->copt, "ClamukoScanOnClose")) {
	printf("Clamuko: Scan-on-close mode activated.\n");
	mask |= DAZUKO_ON_CLOSE;
    }
    if(cfgopt(tharg->copt, "ClamukoScanOnExec")) {
	printf("Clamuko: Scan-on-exec mode activated.\n");
	mask |= DAZUKO_ON_EXEC;
    }

    if(cfgopt(tharg->copt, "ClamukoScanOnCloseModified")) {
	printf("Clamuko: Scan-on-close-modified mode activated.\n");
	mask |= DAZUKO_ON_OPEN;
	mask |= DAZUKO_ON_CLOSE;
	scanmodified = 1;
    }

    if(!mask) {
	printf("!Access mask is not configured properly.\n");
	dazukoUnregister_TS(&dazuko_id);
	return NULL;
    }

    if(dazukoSetAccessMask_TS(dazuko_id,mask)) {
	printf("!Clamuko: Can't set access mask in Dazuko.\n");
	dazukoUnregister_TS(&dazuko_id);
	return NULL;
    }

    if((pt = cfgopt(tharg->copt, "ClamukoIncludePath"))) {
	while(pt) {
	    if((dazukoAddIncludePath_TS(dazuko_id,pt->strarg))) {
		printf("!Clamuko: Dazuko -> Can't include path %s\n", pt->strarg);
		dazukoUnregister_TS(&dazuko_id);
		return NULL;
	    } else
		printf("Clamuko: Included path %s\n", pt->strarg);

	    pt = (struct cfgstruct *) pt->nextarg;
	}
    } else {
	printf("!Clamuko: please include at least one path.\n");
	dazukoUnregister_TS(&dazuko_id);
	return NULL;
    }

    if((pt = cfgopt(tharg->copt, "ClamukoExcludePath"))) {
	while(pt) {
	    if((dazukoAddExcludePath_TS(dazuko_id,pt->strarg))) {
		printf("!Clamuko: Dazuko -> Can't exclude path %s\n", pt->strarg);
		dazukoUnregister_TS(&dazuko_id);
		return NULL;
	    } else
		printf("Clamuko: Excluded path %s\n", pt->strarg);

	    pt = (struct cfgstruct *) pt->nextarg;
	}
    }

    if((pt = cfgopt(tharg->copt, "ClamukoMaxFileSize"))) {
	sizelimit = pt->numarg;
    } else
	sizelimit = CL_DEFAULT_CLAMUKOMAXFILESIZE;

    if(sizelimit)
	printf("Clamuko: Max file size limited to %d bytes.\n", sizelimit);
    else
	printf("Clamuko: File size limit disabled.\n");

    while(1) {

	if(dazukoGetAccess_TS(dazuko_id,&acc) == 0) {
	    clamuko_scanning = 1;
	    scan = 1;
	    
	    
	    
	    //printf("Examining: %s id: %i\n",acc->filename,tharg->pid);
	    
	    /* If opened for writing, cache filename for scanning when closed */
	    if (scanmodified){
	        scan = 0;
		switch (acc->event)
		{
			case DAZUKO_ON_OPEN:
				if (acc->set_flags){
					if 	((acc->flags & O_RDWR) 
						|| (acc->flags & O_WRONLY) 
						|| (acc->flags & O_APPEND) 
						|| (acc->flags & O_SYNC) 
						|| (acc->flags & O_CREAT) 
						|| (acc->flags & O_TRUNC) 
						|| (acc->flags & O_EXCL)){
						val = tsearch((void *)acc->filename, &root, compare);
						if(val == NULL) break;
						printf("Adding file to tree: %s id: %i\n",acc->filename,tharg->pid);
					}else{
                        val = tfind((void *)acc->filename, &root, compare);
                        if (val != NULL){
							scan = 1;
						}
					}	
				}else{
					scan = 1;
				}
				break;
			case DAZUKO_ON_CLOSE:
                val = tfind((void *)acc->filename, &root, compare);
				if (val != NULL){
					scan = 1;
					tdelete((void *)acc->filename, &root, compare);
					printf("Deleting File from tree: %s id: %i\n",acc->filename,tharg->pid);
				}
				break;
			default:
				scan = 1;
				break;

		}
	    }

	    if(sizelimit) {
		stat(acc->filename, &sb);
		if(sb.st_size > sizelimit) {
		    scan = 0;
		    printf("*Clamuko: %s skipped (too big)\n", acc->filename);
		}
	    }

// 	    if (scan)
// 	    	printf("Going to scan: %s id: %i\n",acc->filename,tharg->pid);

	    if(scan && cl_scanfile(acc->filename, &virname, NULL, tharg->root, tharg->limits, tharg->options) == CL_VIRUS) {
		printf("Clamuko: %s: %s FOUND\n", acc->filename, virname);
		//virusaction(virname, tharg->copt);
		acc->deny = 1;
	    } else
		acc->deny = 0;

	    if(dazukoReturnAccess_TS(dazuko_id,&acc)) {
		printf("Can't return access to Dazuko.\n");
		printf("Clamuko stopped.\n");
		dazukoUnregister_TS(&dazuko_id);
		clamuko_scanning = 0;
		return NULL;
	    }

	    clamuko_scanning = 0;
	}
    }

    /* can't be ;) */
    return NULL;
}


#endif
