sima/autoconf/
sima/hosts/i386/
sima/mudlib/
sima/mudlib/kernel/
sima/mudlib/obj/
sima/mudlib/sys/
sima/synhash/mips/
/* Copyright 1995 J"orn Rennecke */

#include "config.h"

#ifdef ACCESS_CONTROL

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>

#include "common.h"
#include "comm.h"

#define MAX_MESSAGE_LENGTH 256

static struct access_address {
    int32 addr, mask;
    int32 hour_mask;
    int32 wday_mask;
    struct access_class *class;
    struct access_address *next;
} *all_access_addresses = 0;

static struct access_class {
    long id;
    mp_int max_usage, usage;
    struct access_class *next;
    char message[8];
} *all_access_classes = 0;

static time_t last_read_time = 0;

extern void refresh_access_data(void (*)(struct in_addr*, long *));

static struct access_class *find_access_class(struct in_addr *full_addr) {
    int32 addr;
    struct access_address *aap;
    time_t seconds;
    struct tm *tm_p;

    addr = full_addr->s_addr;
    tm_p = 0;
    for (aap = all_access_addresses; aap; aap = aap->next) {
#if DEBUG_ACCESS_CHECK
    fprintf(stderr, "'%s', %ld %ld\n",
	inet_ntoa(*(struct in_addr*)&aap->addr),
	(long)aap->class->max_usage, (long)aap->class->usage);
#endif
	if ((aap->addr ^ addr) & aap->mask)
	    continue;
	if (aap->wday_mask >= 0) {
	    if (!tm_p) {
		time(&seconds);
		tm_p = localtime(&seconds);
#if DEBUG_ACCESS_CHECK
		fprintf(stderr, "h:%d w:%d\n", tm_p->tm_hour, tm_p->tm_wday);
#endif
	    }
	    if ( !((1 << tm_p->tm_hour) & aap->hour_mask) )
		continue;
	    if ( !((1 << tm_p->tm_wday) & aap->wday_mask) )
		continue;
	}
	return aap->class;
    }
    return 0;
}

static void add_access_entry(struct in_addr *full_addr, long *idp) {
    struct access_class *acp;

    acp = find_access_class(full_addr);
    if (acp) {
	acp->usage++;
	*idp = acp->id;
    }
}

static void read_access_file() {
    FILE *infp;
    struct access_address *aap, *next_aap, **last;
    struct access_class *acp, *next_acp;
    char message[MAX_MESSAGE_LENGTH];
    int i;
    int32 addr, mask;

    for (aap = all_access_addresses; aap; aap = next_aap) {
	next_aap = aap->next;
	free((char *)aap);
    }
    for (acp = all_access_classes; acp; acp = next_acp) {
	next_acp = acp->next;
	free((char *)acp);
    }
    all_access_classes = 0;
    infp = fopen(ACCESS_FILE, "r");
    last = &all_access_addresses;
    if (infp) for(addr = mask = 0;;) {
	long max_usage, class_id;
	int first_hour, last_hour;

	addr <<= 8;
	mask <<= 8;
	if (fscanf(infp, "%9[^.:\n]%[.:]", message, message+12) != 2 ||
	    *message == '#')
	{
	    do {
		i = fgetc(infp);
		if (i == EOF)
		    goto file_end;
	    } while(i != '\n');
	    addr = mask = 0;
	    continue;
	}
	if (*message != '*') {
	    int j;
	    j = atoi(message);
	    if ((unsigned)j > 0xff)
		break;
	    addr += j;
	    mask += 0xff;
	}
	if (message[12] == '.')
	    continue;
	max_usage = 0;
	message[0] = '\0';
	i = fscanf(infp, "%ld:%ld:%d:%d:",
	  &class_id, &max_usage, &first_hour, &last_hour);
	if (!i)
	    break;
	aap = malloc(sizeof *aap);
	if (!aap)
	    break;
	*last = aap;
	aap->addr = htonl(addr);
	aap->mask = htonl(mask);
	aap->wday_mask = -1;
	if (i == 4) {
	    if (first_hour || last_hour) {
		aap->wday_mask = 0x7f;
		if (first_hour <= last_hour) {
		    aap->hour_mask = (2 << last_hour) - (1 << first_hour);
		} else {
		    aap->hour_mask = -(1 << first_hour) + (2 << last_hour) - 1;
		}
	    }
	} else if (i == 2) {
	    char c, c2[2];

	    for (;;) {
		c = 'm';
		fscanf(infp, "%c %1[=]", &c, c2);
		switch(c) {
		  case 'w':
		  {
		    int32 *maskp;

		    maskp = &aap->wday_mask;
		    goto get_mask;
		  case 'h':
		    maskp = &aap->hour_mask;
		  get_mask:
		    mask = 0;
		    do {
			int j, k;

			*c2 = '\0';
			if (!fscanf(infp, "%d %1[-,:] ", &j, c2))
			    break;
			if (*c2 == '-') {
			    k = 24;
			    fscanf(infp, "%d %1[,:] ", &k, c2);
			    if (j <= k) {
				mask |= (2 << k) - (1 << j);
			    } else {
				mask |= -(1 << j) + (2 << k) - 1;
			    }
			} else {
			    mask |= 1 << j;
			}
		    } while (*c2 == ',');
		    *maskp = mask;
		    aap->wday_mask &= 0x7f; /* make sure it's not negative */
		    continue;
		  }
		  default:
		    ungetc(c, infp);
		  case 'm':
		    break;
		}
		break;
	    }
	}
	fgets(message, sizeof message, infp);
	for (acp = all_access_classes; acp; acp = acp->next) {
	    if (acp->id == class_id)
		break;
	}
	i = strlen(message);
	if (message[i-1] == '\n')
	    message[--i] = '\0';
	if (!acp) {
	    acp =
		malloc(sizeof *acp - sizeof acp->message + 1 + i);
	    if (!acp) {
		free((char *)aap);
		break;
	    }
	    acp->id = class_id;
	    acp->max_usage = max_usage == -1 ? MAXINT : max_usage;
	    acp->usage = 0;
	    strcpy(acp->message, message);
	    acp->next = all_access_classes;
	    all_access_classes = acp;
	}
	aap->class = acp;
	last = &aap->next;
	addr = mask = 0;
    }
file_end:
    *last = 0;
    refresh_access_data(add_access_entry);
}


char *allow_host_access(full_addr, idp)
    struct in_addr *full_addr;
    long *idp;
{
    struct stat statbuf;
    struct access_class *acp;

    if (!stat(ACCESS_FILE, &statbuf) && statbuf.st_mtime > last_read_time) {
	last_read_time = statbuf.st_mtime;
	read_access_file();
    }
    acp = find_access_class(full_addr);
    if (acp) {
	if (acp->usage >= acp->max_usage)
	    return acp->message;
	acp->usage++;
	*idp = acp->id;
	return 0;
    }
    return "No matching entry";
}

void release_host_access(num)
    long num;
{
    struct access_class *acp;

#if DEBUG_ACCESS_CHECK
    fprintf(stderr, "release_host_access %ld called.\n", num);
#endif
    for (acp = all_access_classes; acp; acp = acp->next) {
	if (acp->id != num)
	    continue;
	acp->usage--;
	break;
    }
}

#endif /* ACCESS_RESTRICTED */