#include "../defs.h"
#include <sys/types.h>
#include <sys/time.h>

#include "../defaults.h"
#include "asn1.h"
#include "snmp.h"
#include "snmp_impl.h"
#include "snmp_vars.h"

#include "acl.h"

#define OIDCMP(l1, l2, o1, o2) (((l1) == (l2)) \
				&& !bcmp((char *)(o1), (char *)(o2), \
					 (l1)*sizeof(oid)))

#define ACLTARGET_MASK		0x01
#define ACLSUBJECT_MASK	        0x02
#define ACLRESOURCES_MASK	0x04
#define ACLPRIVILEGES_MASK	0x08
#define ACLSTORAGETYPE_MASK	0x10
#define ACLSTATUS_MASK		0x20

#define ACLCOMPLETE_MASK	0x3F /* all columns */


struct aclEntry *
acl_rowCreate(target, subject, resources)
    int target, subject, resources;
{
    struct aclEntry *ap;

    ap = acl_createEntry(target, subject, resources);
    ap->aclBitMask = 0;
    ap->reserved->aclStatus = ACLNONEXISTENT;

    ap->aclBitMask = ap->reserved->aclBitMask =
	ACLTARGET_MASK | ACLSUBJECT_MASK;
    /* Watch out for this becoming permanent by accident:
     * If during FREE stage below we discover row didn't exist before,
     * free row.
     */
    return ap;
}

acl_rowDelete(target, subject, resources)
    int target, subject, resources;
{
    acl_destroyEntry(target, subject, resources);
}

#if 0
/* The write_acl() function was ifdef'ed out 12/29/95 by dgt
 * The arguments being passed to get_aclEntry() were bad anyway so it
 * couldn't have worked in the first place.
 */

/*
 * If statP is non-NULL, the referenced object is at that location.
 * If statP is NULL and ap is non-NULL, the instance exists, but not this variable.
 * If statP is NULL and ap is NULL, then neither this instance nor the variable exists.
 */
int
write_acl(action, var_val, var_val_type, var_val_len, statP, name, length)
   int      action;
   u_char   *var_val;
   u_char   var_val_type;
   int      var_val_len;
   u_char   *statP;
   oid      *name;
   int      length;
{
    struct aclEntry *ap, *rp;
    int var, targetlen, subjectlen;
    oid *target, *subject;
    int32 val;
    int bigsize = 1000;

/*
 * This routine handles requests for variables of the form:
 * .iso.org.dod.internet.snmpSecrets.partyAccess.aclTable.aclEntry.X.oidlen.oid.oidlen.oid
 * or .1.3.6.1.2.1.21.2.1.1.X.oidlen.oid.oidlen.oid, where the oid suffixes are
 * variable length.
 * Therefore, the length of the first index is name[11] and the index starts
 * at name[12].  The length of the second index starts at name[12 + name[11]],
 * and the second index starts at name[13 + name[1]].
 */
    if (length < 16)
	return SNMP_ERR_NOCREATION;
    var = name[10];
    targetlen = name[11];
    target = name + 12;
    if (length <= 12 + targetlen)
	return SNMP_ERR_NOCREATION;
    subjectlen = name[12 + targetlen];
    subject = name + 13 + targetlen;
    if (length != 13 + targetlen + subjectlen)
	return SNMP_ERR_NOCREATION;
    /* XXX are these length checks necessary?  If not, take them out of
       here and party_vars.c */

    ap = acl_getEntry(target, targetlen, subject, subjectlen);
    if (ap)
	rp = ap->reserved;
    if (action == RESERVE1 && !ap){
	if ((ap = acl_rowCreate(target, targetlen,
				subject, subjectlen)) == NULL)
	    return SNMP_ERR_RESOURCEUNAVAILABLE;
	rp = ap->reserved;
	/* create default vals here in reserve area */
	rp->aclPriveleges = ACLPRIVELEGESGET | ACLPRIVELEGESGETNEXT;
	rp->aclStatus = ACLACTIVE;
	rp->aclBitMask = ACLCOMPLETE_MASK;
    } else if (action == COMMIT){
	if (ap->aclStatus == ACLNONEXISTENT){
	    /* commit the default vals */
	    /* This haapens at most once per entry because the status is set to
	       valid after the first pass.  After that, this commit code
	       does not get executed.  It is also important to note that this
	       gets executed before any of the commits below (and never after
	       them), so they overlay their data on top of these defaults.
	       This commit code should allow for the object specific code
	       to have overlayed data after the code above has executed.
	      */
	    ap->aclPriveleges = rp->aclPriveleges;
	    ap->aclStatus = rp->aclStatus;
	    ap->aclBitMask = rp->aclBitMask;
	}
    } else if (action == FREE){
	if (ap && ap->aclStatus == ACLNONEXISTENT){
	    acl_rowDelete(target, targetlen, subject, subjectlen);
	    ap = rp = NULL;
	}
	if (ap)	/* satisfy postcondition for bitMask */
	    rp->aclBitMask = ap->aclBitMask;
    }

/* XXX !!! check return values from the asn_parse_* routines */
    switch(var){
      case ACLPRIVELEGES:
	if (action == RESERVE1){
	    if (var_val_type != ASN_INTEGER)
		return SNMP_ERR_WRONGTYPE;
	    asn_parse_int(var_val, &bigsize, &var_val_type, &val, sizeof(val));
	    if (val < 0 || val > 31)
		return SNMP_ERR_WRONGVALUE;
	    rp->aclPriveleges = val;
	    rp->aclBitMask |= ACLPRIVILEGES_MASK;
	} else if (action == COMMIT){
	    ap->aclPriveleges = rp->aclPriveleges;
	}
	break;
      case ACLSTATUS:
	if (action == RESERVE1){
	    if (var_val_type != ASN_INTEGER)
		return SNMP_ERR_WRONGTYPE;
	    asn_parse_int(var_val, &bigsize, &var_val_type, &val, sizeof(val));
	    if (val < 1 || val > 2)
		return SNMP_ERR_WRONGVALUE;
	    rp->aclStatus = val;
	    rp->aclBitMask |= ACLSTATUS_MASK;
	} else if (action == RESERVE2){
	    if ((rp->aclStatus == ACLACTIVE)
		&& (rp->aclBitMask != ACLCOMPLETE_MASK))
		return SNMP_ERR_INCONSISTENTVALUE;
	    /* tried to set incomplete row valid */
	} else if (action == COMMIT){
	    ap->aclStatus = rp->aclStatus;
	} else if (action == ACTION && ap->aclStatus == ACLDESTROY){
		acl_rowDelete(ap->aclTarget, ap->aclSubject, ap->aclResources);
	}
	break;
      case ACLTARGET:
      case ACLSUBJECT:
      default:
	    return SNMP_ERR_NOCREATION;
    }
    if (action == COMMIT)	/* make any new columns aapear */
	ap->aclBitMask = rp->aclBitMask;

    return SNMP_ERR_NOERROR;
}
#endif

u_char *
var_acl(vp, name, length, exact, var_len, write_method)
    register struct variable *vp;   /* IN - pointer to variable entry that points here */
    register oid *name;      /* IN/OUT - input name requested, output name found */
    register int *length;    /* IN/OUT - length of input and output oid's */
    int          exact;      /* IN - TRUE if an exact match was requested. */
    int          *var_len;   /* OUT - length of variable or 0 if function returned. */
    int          (**write_method)(); /* OUT - pointer to function to set variable, otherwise 0 */
{
    oid newname[MAX_NAME_LEN], lowname[MAX_NAME_LEN], *np;
    int newnamelen, lownamelen;
    struct aclEntry *ap, *lowap = NULL;
    u_int32 mask;
    int target, subject, resources;

/*
 * This routine handles requests for variables of the form:
 *
 * .iso.org.dod.internet.snmpV2.snmpModules.partyMIB.partyMIBObjects
 * .snmpAccess.aclTable.aclEntry.X.tgt.sub.res
 * or .1.3.6.1.6.3.3.2.3.1.1.X.sub.tgt.res
 * Therefore, the target is at name[12], the subject is at name[13], and
 * the resources is at name[14].
 */

    mask = 1 << (vp->magic - 1);
    bcopy((char *)vp->name, (char *)newname, (int)vp->namelen * sizeof(oid));
    if (exact){
        if (*length != 15 ||
	    bcmp((char *)name, (char *)vp->name, 12 * sizeof(oid)))
	    return NULL;
	target = name[12];
	subject = name[13];
	resources = name[14];
#if 0
    	*write_method = write_acl;
#endif
        ap = acl_getEntry(target, subject, resources);
	if (ap == NULL)
	    return NULL;
	if (!(ap->aclBitMask & mask))
	    return NULL;
    } else {
      /* find "next" control entry */
      acl_scanInit();
      for(ap = acl_scanNext(); ap; ap = acl_scanNext()){
	if (!(ap->aclBitMask & mask))
	    continue;
	np = newname + 12;
	*np++ = ap->aclTarget;
	*np++ = ap->aclSubject;
	*np = ap->aclResources;
	newnamelen = 15;
	if ((compare(newname, newnamelen, name, *length) > 0) &&
	    (!lowap || compare(newname, newnamelen,
			       lowname, lownamelen) < 0)){
	    /*
	     * if new one is greater than input and closer to input than
	     * previous lowest, save this one as the "next" one.
	     */
	    bcopy((char *)newname, (char *)lowname, newnamelen * sizeof(oid));
	    lownamelen = newnamelen;
	    lowap = ap;
	}
      }
      if (lowap == NULL)
	  return NULL;
      ap = lowap;
      bcopy((char *)lowname, (char *)name, lownamelen * sizeof(oid));
      *length = lownamelen;
    }

    *var_len = sizeof(int32);
    long_return = 0;

    switch (vp->magic){
      case ACLPRIVELEGES:
	return (u_char *)&ap->aclPriveleges;
      case ACLSTORAGETYPE:
	return (u_char *)&ap->aclStorageType;
      case ACLSTATUS:
	return (u_char *)&ap->aclStatus;
      default:
            ERROR("");
    }
    return NULL;
}

