/* @(#) $Id: create_db.c,v 1.12 2006/10/09 04:13:54 dcid Exp $ */

/* Copyright (C) 2005,2006 Daniel B. Cid <dcid@ossec.net>
 * All right reserved.
 *
 * This program is a free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public
 * License (version 2) as published by the FSF - Free Software
 * Foundation
 */

#include "shared.h"
#include "syscheck.h"
#include "os_crypto/md5/md5_op.h"
#include "os_crypto/sha1/sha1_op.h"

/* flags for read_dir and read_file */
#define CREATE_DB   1
#define CHECK_DB    2    
int __counter = 0;


/** Prototypes **/
int read_dir(char *dir_name, int opts, int flag);

/* int check_file(char *file_name)
 * Checks if the file is already in the database.
 */
int check_file(char *file_name)
{
    char buf[MAX_LINE +2];
    buf[MAX_LINE +1] = '\0';
    
    while(fgets(buf, MAX_LINE, syscheck.fp) != NULL)
    {
        if((buf[0] != '#') && (buf[0] != ' ') && (buf[0] != '\n'))
        {
            char *n_buf;

            /* Removing the new line */
            n_buf = strchr(buf,'\n');
            if(n_buf == NULL)
                continue;

            *n_buf = '\0';


            /* First 6 characters are for internal use */
            n_buf = buf;
            n_buf+=6;

            n_buf = strchr(n_buf, ' ');
            if(n_buf)
            {
                n_buf++;

                /* Checking if name matches */
                if(strcmp(n_buf, file_name) == 0)
                    return(1);
            }
        }
    }

    /* New file */
    #ifdef WIN32
    sleep(1);
    #endif
    
    debug2("%s: DEBUG: new file '%s'.", ARGV0, file_name);
    return(0);
}



/* int read_file(char *file_name, int opts, int flag)
 * Reads and generates the integrity data of a file.
 */
int read_file(char *file_name, int opts, int flag)
{
    struct stat statbuf;
    
    /* Checking if file is to be ignored */
    if(syscheck.ignore)
    {
        int i = 0;
        while(syscheck.ignore[i] != NULL)
        {
            if(strncasecmp(syscheck.ignore[i], file_name, 
                                           strlen(syscheck.ignore[i])) == 0)
            {
                return(0);            
            }

            i++;
        }
    }


    /* If check_db, we just need to verify that the file is
     * already present. If not, we add it.
     */
    if(flag == CHECK_DB)
    {
        /* File in the database already */
        fseek(syscheck.fp, 0, SEEK_SET);
        if(check_file(file_name))
        {
            /* Sleeping in here too */
            if(__counter >= (3 * syscheck.sleep_after))
            {
                sleep(syscheck.tsleep);
                __counter = 0;
            }
            __counter++;

            return(0);
        }
        fseek(syscheck.fp, 0, SEEK_END);
    }

    
    /* Win32 does not have lstat */
    #ifdef WIN32
    if(stat(file_name, &statbuf) < 0)
    #else
    if(lstat(file_name, &statbuf) < 0)
    #endif
    {
        merror("%s: Error accessing '%s'.",ARGV0, file_name);
        return(-1);
    }
    
    if(S_ISDIR(statbuf.st_mode))
    {
        #ifdef DEBUG
        verbose("%s: Reading dir: %s\n",ARGV0, file_name);
        #endif

        return(read_dir(file_name, opts, flag));
    }
    
    
    /* No S_ISLNK on windows */
    #ifdef WIN32
    else if(S_ISREG(statbuf.st_mode))
    #else
    else if(S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode))
    #endif    
    {
        os_md5 mf_sum;
        os_sha1 sf_sum;

        /* Cleaning sums */
        strncpy(mf_sum, "xxx", 4);
        strncpy(sf_sum, "xxx", 4);


        /* getting md5sum */
        if(opts & CHECK_MD5SUM)
        {
            /* generating md5 of the file */
            if(OS_MD5_File(file_name, mf_sum) < 0)
            {
                strncpy(mf_sum, "xxx", 4);
            }
        }

        /* getting sha1sum */
        if(opts & CHECK_SHA1SUM)
        {
            /* generating md5 of the file */
            if(OS_SHA1_File(file_name, sf_sum) < 0)
            {
                strncpy(sf_sum, "xxx", 4);
            }
        }

        
        /* Adding file */
        fprintf(syscheck.fp,"%c%c%c%c%c%c%d:%d:%d:%d:%s:%s %s\n",
                opts & CHECK_SIZE?'+':'-',
                opts & CHECK_PERM?'+':'-',
                opts & CHECK_OWNER?'+':'-',
                opts & CHECK_GROUP?'+':'-',
                opts & CHECK_MD5SUM?'+':'-',
                opts & CHECK_SHA1SUM?'+':'-',
                opts & CHECK_SIZE?(int)statbuf.st_size:0,
                opts & CHECK_PERM?(int)statbuf.st_mode:0,
                opts & CHECK_OWNER?(int)statbuf.st_uid:0,
                opts & CHECK_GROUP?(int)statbuf.st_gid:0,
                opts & CHECK_MD5SUM?mf_sum:"xxx",
                opts & CHECK_SHA1SUM?sf_sum:"xxx",
                file_name);


        /* Sleeping in here too */
        if(__counter >= (3 * syscheck.sleep_after))
        {
            sleep(syscheck.tsleep);
            __counter = 0;
        }
        __counter++;

        
        #ifdef DEBUG 
        verbose("%s: file '%s %s'",ARGV0, file_name, mf_sum);
        #endif
    }
    else
    {
        #ifdef DEBUG
        verbose("%s: *** IRREG file: '%s'\n",ARGV0,file_name);
        #endif
    }

    return(0);
}


/* read_dir v0.1
 *
 */
int read_dir(char *dir_name, int opts, int flag)
{
    int dir_size;
   
    char f_name[PATH_MAX +2]; 
    DIR *dp;
    
	struct dirent *entry;

    f_name[PATH_MAX +1] = '\0';
	
    /* Directory should be valid */
    if((dir_name == NULL)||((dir_size = strlen(dir_name)) > PATH_MAX))
    {
        if(flag == CREATE_DB)
            merror(NULL_ERROR, ARGV0);
        
        return(-1);
    }
    
    
    /* Opening the directory given */
    dp = opendir(dir_name);
	if(!dp)
    {
        if(errno == ENOTDIR)
        {
            if(read_file(dir_name, opts, flag) == 0)
                return(0);
        }
        
        if(flag == CREATE_DB)
        {
            merror("%s: Error opening directory: '%s': %s ",
                                              ARGV0,
                                              dir_name,
                                              strerror(errno));
        }
        
        return(-1);
    }
    

    while((entry = readdir(dp)) != NULL)
    {
        char *s_name;
        
        /* Just ignore . and ..  */
        if((strcmp(entry->d_name,".") == 0) ||
           (strcmp(entry->d_name,"..") == 0))  
            continue;
            
        strncpy(f_name, dir_name, PATH_MAX);
       
        s_name = f_name;
        
        s_name += dir_size;


        /* checking if the file name is already null terminated */
        if(*(s_name-1) != '/')
            *s_name++ = '/';
            
        *s_name = '\0';
        
        strncpy(s_name, entry->d_name, PATH_MAX - dir_size -2);
        read_file(f_name, opts, flag);
    }

    closedir(dp);
    return(0);
}


/* int check_db()
 * Checks database for new files.
 */
int check_db()
{
    int i = 0;

    /* Read all available directories */
    __counter = 0;
    do
    {
        read_dir(syscheck.dir[i], syscheck.opts[i], CHECK_DB);
        i++;
    }while(syscheck.dir[i] != NULL);

    return(0);
}


/* int create_db
 * Creates the file database.
 */
int create_db()
{
    int i = 0;
    
    syscheck.fp = fopen(syscheck.db,"w+"); /* Read and write */
    if(!syscheck.fp)
    {
        ErrorExit("%s: Unable to create syscheck database "
                  "at '%s'. Exiting..",ARGV0,syscheck.db);
        return(0);    
    }


    /* Creating an local fp only */
    unlink(syscheck.db);

    
    /* dir_name can't be null */
    if((syscheck.dir == NULL) || (syscheck.dir[0] == NULL))
    {
        merror("%s: No directories to check.",ARGV0);
        return(-1);
    }
    

    /* Read all available directories */
    __counter = 0;
    do
    {
        read_dir(syscheck.dir[i], syscheck.opts[i], CREATE_DB);
        i++;
    }while(syscheck.dir[i] != NULL);

    return(0);

}

/* EOF */
