/* ds_add.c - */

#ifndef lint
static char *rcsid = "$Header: /var/lib/cvsd/repos/CSRG/43BSDReno/contrib/isode-beta/quipu/ds_add.c,v 1.1.1.1 2018/04/24 16:12:56 root Exp $";
#endif

/*
 * $Header: /var/lib/cvsd/repos/CSRG/43BSDReno/contrib/isode-beta/quipu/ds_add.c,v 1.1.1.1 2018/04/24 16:12:56 root Exp $
 *
 *
 * $Log: ds_add.c,v $
 * Revision 1.1.1.1  2018/04/24 16:12:56  root
 * BSD 4.3reno
 *
 * Revision 7.2  90/07/09  14:45:37  mrose
 * sync
 * 
 * Revision 7.1  90/01/11  18:37:19  mrose
 * real-sync
 * 
 * Revision 7.0  89/11/23  22:17:03  mrose
 * Release 6.0
 * 
 */

/*
 *                                NOTICE
 *
 *    Acquisition, use, and distribution of this module and related
 *    materials are subject to the restrictions of a license agreement.
 *    Consult the Preface in the User's Manual for the full terms of
 *    this agreement.
 *
 */


#include "quipu/config.h"
#include "quipu/util.h"
#include "quipu/entry.h"
#include "quipu/add.h"
#include "quipu/malloc.h"

extern Entry database_root;
extern LLog * log_dsap;
extern int local_master_size;
extern int encode_DAS_AddEntryArgumentData();

do_ds_addentry (arg, error, binddn,target,di_p,dsp)
    struct ds_addentry_arg      *arg;
    struct DSError              *error;
    DN                          binddn;
    DN                          target;
    struct di_block		**di_p;
    char dsp;
{
Entry  entryptr,ptr;
register DN  dntop, dn = NULLDN;
DN  trail = NULLDN;
extern Entry database_root;
ContinuationRef cont_ref_parent ();
char * new_version ();
int retval;
extern int read_only;

	DLOG (log_dsap,LLOG_TRACE,("ds_add"));

	if (!dsp)
		target = arg->ada_object;

	/* stop aliases being dereferenced */
	arg->ada_common.ca_servicecontrol.svc_options |= SVC_OPT_DONTDEREFERENCEALIAS;

	error ->dse_type = DSE_NOERROR;
	/* first of all see if entry exists */

	if (target == NULLDN) {
		error->dse_type = DSE_NAMEERROR;
		error->ERR_NAME.DSE_na_problem = DSE_NA_NOSUCHOBJECT;
		error->ERR_NAME.DSE_na_matched = NULLDN;
		return (DS_ERROR_REMOTE);
	}

	switch (find_entry (target,&(arg->ada_common),binddn,NULLDNSEQ,FALSE,&entryptr, error, di_p)) 
	{
	case DS_OK:
		error->dse_type = DSE_UPDATEERROR;
		error->ERR_UPDATE.DSE_up_problem = DSE_UP_ALREADYEXISTS;
		return(DS_ERROR_REMOTE);
	case DS_CONTINUE:
	    /* Filled out di_p - what do we do with it ?? */
	    return(DS_CONTINUE);
	case DS_X500_ERROR:
	    /* Filled out error - what do we do with it ?? */
	    if ((error->dse_type != DSE_NAMEERROR) || (error->ERR_NAME.DSE_na_problem != DSE_NA_NOSUCHOBJECT)) {
		return(DS_X500_ERROR);
	    }
	    ds_error_free (error);  /* not interested - know it does not exist */
	    break;
	default:
	    /* SCREAM */
	    LLOG(log_dsap, LLOG_EXCEPTIONS, ("do_ds_read() - find_entry failed"));
	    return(DS_ERROR_LOCAL);
	}

	/* object does not exist, so create it */

	/* Strong authentication  */
	if ((retval = check_security_parms((caddr_t) arg, 
				encode_DAS_AddEntryArgumentData, 
				arg->ada_common.ca_security,
				arg->ada_common.ca_sig, &binddn)) != 0)
	{
		error->dse_type = DSE_SECURITYERROR;
		error->ERR_SECURITY.DSE_sc_problem = retval;
		return (DS_ERROR_REMOTE);
	}

	DLOG (log_dsap,LLOG_TRACE,("add - find parent"));

	if ((dntop = dn_cpy(target)) != NULLDN)
		for (dn=dntop; dn->dn_parent != NULLDN; dn=dn->dn_parent)
			trail = dn;

	if (trail == NULLDN) {
		dntop = NULLDN;
		entryptr = database_root;
		if (entryptr->e_data != E_DATA_MASTER) {
			error->dse_type = DSE_REFERRAL;
        		error->ERR_REFERRAL.DSE_ref_prefix = NULLDN;
			if ((error->ERR_REFERRAL.DSE_ref_candidates = cont_ref_parent (NULLDN)) == NULLCONTINUATIONREF) {
				error->dse_type = DSE_SERVICEERROR;
				error->ERR_SERVICE.DSE_sv_problem = DSE_SV_INVALIDREFERENCE;
			}
			return (DS_ERROR_CONNECT);
		}
	} else {
		trail->dn_parent = NULLDN;
		switch(find_child_entry(dntop,&(arg->ada_common),binddn,NULLDNSEQ,TRUE,&(entryptr), error, di_p))
		{
		case DS_OK:
		    /* Filled out entryptr - carry on */
		    break;
		case DS_CONTINUE:
		    /* Filled out di_p - what do we do with it ?? */
		    /* When add returns DS_CONTINUE the target must be changed */
		    return(DS_CONTINUE);

		case DS_X500_ERROR:
		    /* Filled out error - what do we do with it ?? */
		    return(DS_X500_ERROR);
		default:
		    /* SCREAM */
		    LLOG(log_dsap, LLOG_EXCEPTIONS, ("do_ds_add() - find_child_entry failed"));
		    return(DS_ERROR_LOCAL);
		}
	}

	if ( read_only || ((entryptr->e_parent != NULLENTRY) && (entryptr->e_parent->e_lock))) {
		error->dse_type = DSE_SERVICEERROR;
		error->ERR_SERVICE.DSE_sv_problem = DSE_SV_UNWILLINGTOPERFORM;
		dn_free (dntop);
		dn_free (dn);
		return (DS_ERROR_REMOTE);
	}

	/* not prepared to accept operation over DSP */
	if (dsp) {
		error->dse_type = DSE_SECURITYERROR;
		error->ERR_SECURITY.DSE_sc_problem = DSE_SC_AUTHENTICATION;
		dn_free (dntop);
		dn_free (dn);
		return (DS_ERROR_REMOTE);
	}

	DLOG (log_dsap,LLOG_TRACE,("add - acl"));
	if (check_acl (binddn,ACL_ADD,entryptr->e_acl->ac_child,dntop) == NOTOK) {
		error->dse_type = DSE_SECURITYERROR;
		error->ERR_SECURITY.DSE_sc_problem = DSE_SC_ACCESSRIGHTS;
		dn_free (dntop);
		dn_free (dn);
		return (DS_ERROR_REMOTE);
	}

	DLOG (log_dsap,LLOG_TRACE,("add - default"));

	DATABASE_HEAP;

	ptr = get_default_entry (entryptr);
	ptr->e_name = rdn_cpy (dn->dn_rdn);
	ptr->e_attributes = as_cpy (arg->ada_entry);
 
	modify_attr (ptr,binddn);

	DLOG (log_dsap,LLOG_TRACE,("add - unravel"));
	if (unravel_attribute (ptr,error) != OK) {
		dn_free (dntop);
		dn_free (dn);
		entry_free (ptr);
		GENERAL_HEAP;
		return (DS_ERROR_REMOTE);
	}

	DLOG (log_dsap,LLOG_TRACE,("add - schema"));
	if (check_schema (ptr,NULLATTR,error) != OK) {
		dn_free (dntop);
		dn_free (dn);
		entry_free (ptr);
		GENERAL_HEAP;
		return (DS_ERROR_REMOTE);
	}

	GENERAL_HEAP;

	dn_free (dn);
	dn_free (dntop);
		
	if ( entryptr->e_leaf) {
		Attr_Sequence newas;
		AttributeType newat;

		entryptr->e_child = ptr;
		/* add master and slave attributes */

		DATABASE_HEAP;

		if ((entryptr->e_parent->e_slave == NULLAV) && (entryptr->e_parent->e_master == NULLAV)) {
			extern char * mydsaname;
			newat = AttrT_new (MASTERDSA_OID);
			entryptr->e_master = str2avs (mydsaname,newat);
			newas = as_comp_new (newat,entryptr->e_master,NULLACL_INFO);
			entryptr->e_attributes = as_merge (entryptr->e_attributes,newas);
		} else {
			if ((entryptr->e_master = avs_cpy(entryptr->e_parent->e_master)) != NULLAV) {
				newat = AttrT_new (MASTERDSA_OID);
				newas = as_comp_new (newat,entryptr->e_master,NULLACL_INFO);
				entryptr->e_attributes = as_merge (entryptr->e_attributes,newas);
			}
			if ((entryptr->e_slave = avs_cpy (entryptr->e_parent->e_slave)) != NULLAV) {
				newat = AttrT_new (SLAVEDSA_OID);
				newas = as_comp_new (newat,entryptr->e_slave,NULLACL_INFO);
				entryptr->e_attributes = as_merge (entryptr->e_attributes,newas);
			}
		}
		/* add new QuipuNonLeaf objectclass */
		newat = AttrT_new (OBJECTCLASS_OID);
		newas = as_comp_new (newat,str2avs(NONLEAFOBJECT,newat),NULLACL_INFO);
		entryptr->e_attributes = as_merge (entryptr->e_attributes,newas);

		if (entryptr->e_parent != NULLENTRY) {
			if (entryptr->e_parent->e_edbversion)
				free (entryptr->e_parent->e_edbversion);
			entryptr->e_parent->e_edbversion = new_version();
		}
		if (entryptr->e_edbversion)
			free (entryptr->e_edbversion);
		entryptr->e_edbversion = new_version();
		ptr->e_edbversion = new_version();
		entryptr->e_allchildrenpresent = TRUE;

		modify_attr (entryptr,binddn);
		if (unravel_attribute (entryptr,error) != OK) 
			fatal (-31,"serious schema error");

#ifdef TURBO_DISK
		if (turbo_write(ptr) == NOTOK)
			fatal(-32,"add turbo_write(2) failure - check database");
#else
		if (journal (ptr) == NOTOK)
			fatal (-32,"add journal (2) failure - check database");
#endif

		entryptr->e_leaf = FALSE;
		/* rewrite the parent as well */
#ifdef TURBO_DISK
		if (turbo_write(ptr) == NOTOK)
			fatal(-31,"add parent turbo_write failed - check database");
#else
		if (journal (entryptr) != OK)
			fatal (-31,"add parent journal failed - check database");
#endif

		GENERAL_HEAP;

		local_master_size++;
		return (OK);

	} else {
		ptr->e_sibling = entryptr->e_child;
		entryptr->e_child = ptr;
	}

	if (ptr->e_parent != NULLENTRY) {
		if (ptr->e_parent->e_edbversion)
			free (ptr->e_parent->e_edbversion);
		ptr->e_parent->e_edbversion = new_version();
	}
#ifdef TURBO_DISK
	if (turbo_write(ptr) == NOTOK)
		fatal(-32,"add turbo_write failure - check database");
#else
	if (journal (ptr) == NOTOK)
		fatal (-32,"add journal failure - check database");
#endif

	local_master_size++;
	return (DS_OK);
}
