/*
    classes.c
    Copyright (C) 2000  Petr Vandrovec

    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


    Revision history:

	1.00  2000, May 4		Petr Vandrovec <vandrove@vc.cvut.cz>
		Initial version.

	1.01  2000, May 6		Petr Vandrovec <vandrove@vc.cvut.cz>
		Added NWDSGetClassItem, NWDSGetClassItemCount.

	1.02  2000, May 7		Petr Vandrovec <vandrove@vc.cvut.cz>
		Added NWDSBeginClassItem, NWDSDefineClass, NWDSPutClassItem,
			NWDSRemoveClassDef, NWDSListContainableClasses,
			NWDSModifyClassDef, NWDSReadAttrDef, NWDSGetAttrDef,
			NWDSDefineAttr, NWDSRemoveAttrDef.
*/

#include <ncp/nwcalls.h>
#include "nwnet_i.h"

#define CLASSDEF_ASN	0x0001
#define CLASSDEF_TIMES	0x0002
#define CLASSDEF_ITEMS	0x0004

static NWDSCCODE __NWDSReadClassDefV0(
		NWCONN_HANDLE		conn,
		nuint			infoType,
		nuint			allClasses,
		Buf_T*			classNames,
		nuint32*		iterHandle,
		Buf_T*			classDefs
) {
	static const nuint infoType2cmd[] = {
		0,
		CLASSDEF_ASN | CLASSDEF_ITEMS,
		CLASSDEF_ASN | CLASSDEF_ITEMS,
		CLASSDEF_ASN,
		CLASSDEF_ASN | CLASSDEF_ITEMS,
		CLASSDEF_ASN | CLASSDEF_TIMES | CLASSDEF_ITEMS
	};
	NW_FRAGMENT rq_frag[2];
	NW_FRAGMENT rp_frag[2];
	nuint8 rq_b[20];
	nuint8 rp_b[8];
	NWDSCCODE dserr;
	size_t rq_frags;
	
	if (!classDefs)
		return ERR_NULL_POINTER;
	DSET_LH(rq_b, 0, 0);
	DSET_LH(rq_b, 4, *iterHandle);
	DSET_LH(rq_b, 8, infoType);
	DSET_LH(rq_b, 12, allClasses);
	
	if (allClasses || !classNames) {
		DSET_LH(rq_b, 16, 0);
		rq_frag[0].fragSize = 20;
		rq_frags = 1;
	} else {
		if (classNames->operation != DSV_READ_CLASS_DEF)
			return ERR_BAD_VERB;
		rq_frag[0].fragSize = 16;
		rq_frag[1].fragAddr.ro = NWDSBufRetrieve(classNames, &rq_frag[1].fragSize);
		rq_frags = 2;
	}
	rq_frag[0].fragAddr.ro = rq_b;
	NWDSBufStartPut(classDefs, DSV_READ_CLASS_DEF);
	if (infoType > 5)
		infoType = 1;
	classDefs->cmdFlags = infoType2cmd[infoType];
	rp_frag[0].fragAddr.rw = rp_b;
	rp_frag[0].fragSize = 8;
	rp_frag[1].fragAddr.rw = NWDSBufPutPtrLen(classDefs, &rp_frag[1].fragSize);
	dserr = NWCFragmentRequest(conn, DSV_READ_CLASS_DEF, rq_frags, rq_frag, 
			2, rp_frag, NULL);
	if (dserr)
		return dserr;
	if (rp_frag[1].fragSize < 4)
		return ERR_INVALID_SERVER_RESPONSE;
	if (DVAL_LH(rp_b, 4) != infoType) {
		/* FIXME: __NWDSCloseIterationHandle(conn, DVAL_LH(rp_b, 0), DSV_READ_CLASS_DEF); */
		return ERR_INVALID_SERVER_RESPONSE;
	}
	*iterHandle = DVAL_LH(rp_b, 0);
	NWDSBufPutSkip(classDefs, rp_frag[1].fragSize);
	NWDSBufFinishPut(classDefs);
	return 0;
}

NWDSCCODE NWDSReadClassDef(
		NWDSContextHandle	ctx,
		nuint			infoType,
		nuint			allClasses,
		Buf_T*			classNames,
		nuint32*		iterHandle,
		Buf_T*			classDefs
) {
	NWCONN_HANDLE conn;
	nuint32 lh;
	NWDSCCODE dserr;
	struct wrappedIterationHandle* ih;
	
	if (*iterHandle == NO_MORE_ITERATIONS) {
		dserr = __NWDSGetConnection(ctx, &conn);
		if (dserr)
			return dserr;
		ih = NULL;
		lh = NO_MORE_ITERATIONS;	
	} else {
		ih = __NWDSIHLookup(*iterHandle, DSV_READ_CLASS_DEF);
		if (!ih)
			return ERR_INVALID_HANDLE;
		conn = ih->conn;
		lh = ih->iterHandle;
	}
	dserr = __NWDSReadClassDefV0(conn, infoType, allClasses, classNames, &lh, classDefs);
	if (ih)
		return __NWDSIHUpdate(ih, dserr, lh, iterHandle);
	return __NWDSIHCreate(dserr, conn, 0, lh, DSV_READ_CLASS_DEF, iterHandle);
}

NWDSCCODE NWDSGetClassDefCount(
	UNUSED(	NWDSContextHandle	ctx),
		Buf_T*			buf,
		NWObjectCount*		classDefCount
) {
	NWDSCCODE dserr;
	nuint32 tmp;
	
	if (!buf)
		return ERR_NULL_POINTER;
	if (buf->bufFlags & NWDSBUFT_INPUT)
		return ERR_BAD_VERB;
	switch (buf->operation) {
		case DSV_READ_CLASS_DEF:
			break;
		default:
			return ERR_BAD_VERB;
	}
	dserr = NWDSBufGetLE32(buf, &tmp);
	if (dserr)
		return dserr;
	if (classDefCount)
		*classDefCount = tmp;
	return 0;
}

NWDSCCODE NWDSGetClassDef(
		NWDSContextHandle	ctx,
		Buf_T*			buf,
		NWDSChar*		className,
		Class_Info_T*		classInfo
) {
	NWDSCCODE dserr;
	nuint32 tmp;
	
	if (!buf)
		return ERR_NULL_POINTER;
	if (buf->bufFlags & NWDSBUFT_INPUT)
		return ERR_BAD_VERB;
	switch (buf->operation) {
		case DSV_READ_CLASS_DEF:
			break;
		default:
			return ERR_BAD_VERB;
	}
	dserr = NWDSBufCtxString(ctx, buf, className, MAX_SCHEMA_NAME_BYTES, NULL);
	if (dserr)
		return dserr;
	if (buf->cmdFlags & CLASSDEF_ASN) {
		dserr = NWDSBufGetLE32(buf, &tmp);
		if (dserr)
			return dserr;
		if (classInfo) {
			classInfo->classFlags = tmp;

			dserr = NWDSBufGetLE32(buf, &tmp);
			if (dserr)
				return dserr;
			classInfo->asn1ID.length = tmp;
			if (tmp > MAX_ASN1_NAME)
				return NWE_BUFFER_OVERFLOW;
			dserr = NWDSBufGet(buf, classInfo->asn1ID.data, tmp);		
		} else {
			dserr = NWDSBufSkipBuffer(buf);
		}
		if (dserr)
			return dserr;
	}
	return dserr;
}

NWDSCCODE NWDSGetClassItemCount(
	UNUSED(	NWDSContextHandle	ctx),
		Buf_T*			buf,
		NWObjectCount*		itemCount
) {
	NWDSCCODE dserr;
	nuint32 tmp;
	
	if (!buf)
		return ERR_NULL_POINTER;
	if (buf->bufFlags & NWDSBUFT_INPUT)
		return ERR_BAD_VERB;
	switch (buf->operation) {
		case DSV_READ_CLASS_DEF:
		case DSV_LIST_CONTAINABLE_CLASSES:
			break;
		default:
			return ERR_BAD_VERB;
	}
	dserr = NWDSBufGetLE32(buf, &tmp);
	if (dserr)
		return dserr;
	if (itemCount)
		*itemCount = tmp;
	return 0;
}

NWDSCCODE NWDSGetClassItem(
		NWDSContextHandle	ctx,
		Buf_T*			buf,
		NWDSChar*		item
) {
	NWDSCCODE dserr;
	
	if (!buf)
		return ERR_NULL_POINTER;
	if (buf->bufFlags & NWDSBUFT_INPUT)
		return ERR_BAD_VERB;
	switch (buf->operation) {
		case DSV_READ_CLASS_DEF:
		case DSV_LIST_CONTAINABLE_CLASSES:
			break;
		default:
			return ERR_BAD_VERB;
	}
	dserr = NWDSBufCtxString(ctx, buf, item, MAX_SCHEMA_NAME_BYTES, NULL);
	if (dserr)
		return dserr;
	return 0;
}

NWDSCCODE NWDSPutClassItem(
		NWDSContextHandle	ctx,
		Buf_T*			buf,
		const NWDSChar*		item
) {
	NWDSCCODE dserr;

	if (!buf)
		return ERR_NULL_POINTER;
	if (!(buf->bufFlags & NWDSBUFT_INPUT))
		return ERR_BAD_VERB;
	switch (buf->operation) {
		case DSV_DEFINE_CLASS:
			if (!buf->attrCountPtr)
				return ERR_BAD_VERB;
			break;
		/* DSV_READ_ATTR_DEF should not be there... NWDSPutAttrName 
		   should be used with NWDSReadAttrDef */
		case DSV_READ_ATTR_DEF:
		case DSV_READ_CLASS_DEF:
		case DSV_MODIFY_CLASS_DEF:
			break;
		default:
			return ERR_BAD_VERB;
	}
	dserr = NWDSCtxBufString(ctx, buf, item);
	if (dserr)
		return dserr;
	DSET_LH(buf->attrCountPtr, 0, DVAL_LH(buf->attrCountPtr, 0) + 1);
	return 0;
}
	
NWDSCCODE NWDSBeginClassItem(
	UNUSED(	NWDSContextHandle	ctx),
		Buf_T*			buf
) {
	NWDSCCODE dserr;
	nuint8* p;
	
	if (!buf)
		return ERR_NULL_POINTER;
	if (!(buf->bufFlags & NWDSBUFT_INPUT))
		return ERR_BAD_VERB;
	switch (buf->operation) {
		case DSV_DEFINE_CLASS:
			break;
		default:
			return ERR_BAD_VERB;
	}
	p = buf->curPos;
	dserr = NWDSBufPutLE32(buf, 0);
	if (dserr)
		return dserr;
	buf->attrCountPtr = p;
	return 0;
}

static inline NWDSCCODE __NWDSFindSchemaServer(
		NWDSContextHandle	ctx,
		NWCONN_HANDLE*		conn
) {
	NWObjectID id;
	
	return __NWDSResolveName2w(ctx, L"[Root]", 
			DS_RESOLVE_WRITEABLE | DS_RESOLVE_V0, conn, &id);
}

static NWDSCCODE __NWDSDefineClassV0(
		NWCONN_HANDLE		conn,
		const Class_Info_T*	classInfo,
		Buf_T*			className,
		Buf_T*			classItems
) {
	NW_FRAGMENT rq_frag[4];
	nuint8 rq_b[8];
	nuint8 rq_b2[4 + MAX_ASN1_NAME];
	size_t asnlen;
	size_t asnlen2;
	
	if (!classInfo || !classItems)
		return ERR_NULL_POINTER;
	asnlen = classInfo->asn1ID.length;
	if (asnlen > MAX_ASN1_NAME)
		return NWE_BUFFER_OVERFLOW;
	DSET_LH(rq_b, 0, 0);	/* version */
	DSET_LH(rq_b, 4, classInfo->classFlags);
	DSET_LH(rq_b2, 0, asnlen);
	memcpy(rq_b2 + 4, classInfo->asn1ID.data, asnlen);
	asnlen2 = ROUNDPKT(asnlen);
	if (asnlen2 > asnlen)
		memset(rq_b2 + 4 + asnlen, 0, asnlen2 - asnlen);
	rq_frag[0].fragAddr.ro = rq_b;
	rq_frag[0].fragSize = 8;
	rq_frag[1].fragAddr.ro = NWDSBufRetrieve(className, &rq_frag[1].fragSize);
	rq_frag[2].fragAddr.ro = rq_b2;
	rq_frag[2].fragSize = 4 + asnlen2;
	rq_frag[3].fragAddr.ro = NWDSBufRetrieve(classItems, &rq_frag[3].fragSize);
	
	return NWCFragmentRequest(conn, DSV_DEFINE_CLASS, 4, rq_frag, 0, NULL, NULL);	
}

NWDSCCODE NWDSDefineClass(
		NWDSContextHandle	ctx,
		const NWDSChar*		className,
		const Class_Info_T*	classInfo,
		Buf_T*			classItems
) {
	NWDSCCODE dserr;
	NWCONN_HANDLE conn;
	nuint8 rq[4 + MAX_SCHEMA_NAME_BYTES];
	Buf_T rqb;
	
	NWDSSetupBuf(&rqb, rq, sizeof(rq));
	dserr = NWDSCtxBufString(ctx, &rqb, className);
	if (dserr)
		return dserr;
	dserr = __NWDSFindSchemaServer(ctx, &conn);
	if (dserr)
		return dserr;
	dserr = __NWDSDefineClassV0(conn, classInfo, &rqb, classItems);
	NWCCCloseConn(conn);
	return dserr;			
}

static NWDSCCODE __NWDSRemoveClassDefV0(
		NWCONN_HANDLE		conn,
		Buf_T*			className
) {
	NW_FRAGMENT rq_frag[2];
	nuint8 rq_b[4];
	
	DSET_LH(rq_b, 0, 0);	/* version */

	rq_frag[0].fragAddr.ro = rq_b;
	rq_frag[0].fragSize = 4;
	rq_frag[1].fragAddr.ro = NWDSBufRetrieve(className, &rq_frag[1].fragSize);
	
	return NWCFragmentRequest(conn, DSV_REMOVE_CLASS_DEF, 2, rq_frag, 0, NULL, NULL);	
}

NWDSCCODE NWDSRemoveClassDef(
		NWDSContextHandle	ctx,
		const NWDSChar*		className
) {
	NWDSCCODE dserr;
	NWCONN_HANDLE conn;
	nuint8 rq[4 + MAX_SCHEMA_NAME_BYTES];
	Buf_T rqb;
	
	NWDSSetupBuf(&rqb, rq, sizeof(rq));
	dserr = NWDSCtxBufString(ctx, &rqb, className);
	if (dserr)
		return dserr;
	dserr = __NWDSFindSchemaServer(ctx, &conn);
	if (dserr)
		return dserr;
	dserr = __NWDSRemoveClassDefV0(conn, &rqb);
	NWCCCloseConn(conn);
	return dserr;
}

static NWDSCCODE __NWDSModifyClassDefV0(
		NWCONN_HANDLE		conn,
		Buf_T*			className,
		Buf_T*			optionalAttrs
) {
	NW_FRAGMENT rq_frag[3];
	nuint8 rq_b[4];

	if (!optionalAttrs)
		return ERR_NULL_POINTER;	
	if (optionalAttrs->operation != DSV_MODIFY_CLASS_DEF)
		return ERR_BAD_VERB;
	DSET_LH(rq_b, 0, 0);	/* version */

	rq_frag[0].fragAddr.ro = rq_b;
	rq_frag[0].fragSize = 4;
	rq_frag[1].fragAddr.ro = NWDSBufRetrieve(className, &rq_frag[1].fragSize);
	rq_frag[2].fragAddr.ro = NWDSBufRetrieve(optionalAttrs, &rq_frag[2].fragSize);
	return NWCFragmentRequest(conn, DSV_MODIFY_CLASS_DEF, 3, rq_frag, 0, NULL, NULL);	
}

NWDSCCODE NWDSModifyClassDef(
		NWDSContextHandle	ctx,
		const NWDSChar*		className,
		Buf_T*			optionalAttrs
) {
	NWDSCCODE dserr;
	NWCONN_HANDLE conn;
	nuint8 rq[4 + MAX_SCHEMA_NAME_BYTES];
	Buf_T rqb;
	
	NWDSSetupBuf(&rqb, rq, sizeof(rq));
	dserr = NWDSCtxBufString(ctx, &rqb, className);
	if (dserr)
		return dserr;
	dserr = __NWDSFindSchemaServer(ctx, &conn);
	if (dserr)
		return dserr;
	dserr = __NWDSModifyClassDefV0(conn, &rqb, optionalAttrs);
	NWCCCloseConn(conn);
	return dserr;
}

static NWDSCCODE __NWDSListContainableClassesV0(
		NWCONN_HANDLE		conn,
		nuint32*		iterHandle,
		NWObjectID		objID,
		Buf_T*			containableClasses
) {
	NW_FRAGMENT rq_frag[1];
	NW_FRAGMENT rp_frag[2];
	nuint8 rq_b[12];
	nuint8 rp_b[4];
	NWDSCCODE dserr;
	
	if (!containableClasses)
		return ERR_NULL_POINTER;
	DSET_LH(rq_b, 0, 0);
	DSET_LH(rq_b, 4, *iterHandle);
	DSET_HL(rq_b, 8, objID);
	
	rq_frag[0].fragAddr.ro = rq_b;
	rq_frag[0].fragSize = 12;
	NWDSBufStartPut(containableClasses, DSV_LIST_CONTAINABLE_CLASSES);
	rp_frag[0].fragAddr.rw = rp_b;
	rp_frag[0].fragSize = 4;
	rp_frag[1].fragAddr.rw = NWDSBufPutPtrLen(containableClasses, &rp_frag[1].fragSize);
	dserr = NWCFragmentRequest(conn, DSV_LIST_CONTAINABLE_CLASSES, 
			1, rq_frag, 2, rp_frag, NULL);
	if (dserr)
		return dserr;
	if (rp_frag[1].fragSize < 4)
		return ERR_INVALID_SERVER_RESPONSE;
	*iterHandle = DVAL_LH(rp_b, 0);
	NWDSBufPutSkip(containableClasses, rp_frag[1].fragSize);
	NWDSBufFinishPut(containableClasses);
	return 0;
}

NWDSCCODE NWDSListContainableClasses(
		NWDSContextHandle	ctx,
		const NWDSChar*		parentName,
		nuint32*		iterHandle,
		Buf_T*			containableClasses
) {
	NWCONN_HANDLE conn;
	NWObjectID objID;
	nuint32 lh;
	NWDSCCODE dserr;
	struct wrappedIterationHandle* ih;
	
	if (*iterHandle == NO_MORE_ITERATIONS) {
		dserr = NWDSResolveName2(ctx, parentName, 
				DS_RESOLVE_READABLE | DS_RESOLVE_V0, &conn,
				&objID);
		if (dserr)
			return dserr;
		ih = NULL;
		lh = NO_MORE_ITERATIONS;	
	} else {
		ih = __NWDSIHLookup(*iterHandle, DSV_LIST_CONTAINABLE_CLASSES);
		if (!ih)
			return ERR_INVALID_HANDLE;
		conn = ih->conn;
		objID = ih->objectID;
		lh = ih->iterHandle;
	}
	dserr = __NWDSListContainableClassesV0(conn, &lh, objID, containableClasses);
	if (ih)
		return __NWDSIHUpdate(ih, dserr, lh, iterHandle);
	return __NWDSIHCreate(dserr, conn, objID, lh, DSV_LIST_CONTAINABLE_CLASSES, iterHandle);
}

#define ATTRDEF_SYNTAXID	0x0001
#define ATTRDEF_TIMES		0x0002
static NWDSCCODE __NWDSReadAttrDefV0(
		NWCONN_HANDLE		conn,
		nuint32*		iterHandle,
		nuint			infoType,
		nuint			allAttrs,
		Buf_T*			attrNames,
		Buf_T*			attrDefs
) {
	static const nuint it2cmd[] = {
		0,
		ATTRDEF_SYNTAXID,
		ATTRDEF_SYNTAXID | ATTRDEF_TIMES
	};
	NW_FRAGMENT rq_frag[2];
	NW_FRAGMENT rp_frag[2];
	nuint8 rq_b[20];
	nuint8 rp_b[8];
	NWDSCCODE dserr;
	size_t rq_frags;

	if (!attrDefs)
		return ERR_NULL_POINTER;
	DSET_LH(rq_b, 0, 0);
	DSET_LH(rq_b, 4, *iterHandle);
	DSET_LH(rq_b, 8, infoType);
	DSET_LH(rq_b, 12, allAttrs);
	
	rq_frag[0].fragAddr.ro = rq_b;
	
	if (allAttrs || !attrNames) {
		DSET_LH(rq_b, 16, 0);
		rq_frag[0].fragSize = 20;
		rq_frags = 1;
	} else {
		if (attrNames->operation != DSV_READ_ATTR_DEF)
			return ERR_BAD_VERB;
		rq_frag[0].fragSize = 16;
		rq_frags = 2;
		rq_frag[1].fragAddr.ro = NWDSBufRetrieve(attrNames, &rq_frag[1].fragSize);
	}
	NWDSBufStartPut(attrDefs, DSV_READ_ATTR_DEF);
	if (infoType > 2)
		attrDefs->cmdFlags = ATTRDEF_SYNTAXID;
	else
		attrDefs->cmdFlags = it2cmd[infoType];
	rp_frag[0].fragAddr.rw = rp_b;
	rp_frag[0].fragSize = 8;
	rp_frag[1].fragAddr.rw = NWDSBufPutPtrLen(attrDefs, &rp_frag[1].fragSize);
	dserr = NWCFragmentRequest(conn, DSV_READ_ATTR_DEF, rq_frags, rq_frag, 
			2, rp_frag, NULL);
	if (dserr)
		return dserr;
	if (rp_frag[1].fragSize < 4)
		return ERR_INVALID_SERVER_RESPONSE;
	if (infoType != DVAL_LH(rp_b, 4))
		return ERR_INVALID_SERVER_RESPONSE;
	*iterHandle = DVAL_LH(rp_b, 0);
	NWDSBufPutSkip(attrDefs, rp_frag[1].fragSize);
	NWDSBufFinishPut(attrDefs);
	return 0;
}

NWDSCCODE NWDSReadAttrDef(
		NWDSContextHandle	ctx,
		nuint			infoType,
		nuint			allAttrs,
		Buf_T*			attrNames,
		nuint32*		iterHandle,
		Buf_T*			attrDefs
) {
	NWCONN_HANDLE conn;
	nuint32 lh;
	NWDSCCODE dserr;
	struct wrappedIterationHandle* ih;
	
	if (*iterHandle == NO_MORE_ITERATIONS) {
		dserr = __NWDSGetConnection(ctx, &conn);
		if (dserr)
			return dserr;
		ih = NULL;
		lh = NO_MORE_ITERATIONS;	
	} else {
		ih = __NWDSIHLookup(*iterHandle, DSV_READ_ATTR_DEF);
		if (!ih)
			return ERR_INVALID_HANDLE;
		conn = ih->conn;
		lh = ih->iterHandle;
	}
	dserr = __NWDSReadAttrDefV0(conn, &lh, infoType, allAttrs, attrNames, attrDefs);
	if (ih)
		return __NWDSIHUpdate(ih, dserr, lh, iterHandle);
	return __NWDSIHCreate(dserr, conn, 0, lh, DSV_READ_ATTR_DEF, iterHandle);
}

NWDSCCODE NWDSGetAttrDef(
		NWDSContextHandle	ctx,
		Buf_T*			buf,
		NWDSChar*		attrName,
		Attr_Info_T*		attrInfo
) {
	NWDSCCODE dserr;
	nuint32 tmp;
	
	if (!buf)
		return ERR_NULL_POINTER;
	if (buf->bufFlags & NWDSBUFT_INPUT)
		return ERR_BAD_VERB;
	switch (buf->operation) {
		case DSV_READ_ATTR_DEF:
			break;
		default:
			return ERR_BAD_VERB;
	}
	dserr = NWDSBufCtxString(ctx, buf, attrName, MAX_SCHEMA_NAME_BYTES, NULL);
	if (dserr)
		return dserr;
	if (buf->cmdFlags & ATTRDEF_SYNTAXID) {
		dserr = NWDSBufGetLE32(buf, &tmp);
		if (dserr)
			return dserr;
		if (attrInfo)
			attrInfo->attrFlags = tmp;
		dserr = NWDSBufGetLE32(buf, &tmp);
		if (dserr)
			return dserr;
		if (attrInfo)
			attrInfo->attrSyntaxID = tmp;
		dserr = NWDSBufGetLE32(buf, &tmp);
		if (dserr)
			return dserr;
		if (attrInfo)
			attrInfo->attrLower = tmp;
		dserr = NWDSBufGetLE32(buf, &tmp);
		if (dserr)
			return dserr;
		if (attrInfo)
			attrInfo->attrUpper = tmp;
		if (attrInfo) {
			dserr = NWDSBufGetLE32(buf, &tmp);
			if (dserr)
				return dserr;
			attrInfo->asn1ID.length = tmp;
			if (tmp > MAX_ASN1_NAME)
				return NWE_BUFFER_OVERFLOW;
			dserr = NWDSBufGet(buf, attrInfo->asn1ID.data, tmp);		
		} else {
			dserr = NWDSBufSkipBuffer(buf);
		}
		if (dserr)
			return dserr;
	} else if (attrInfo) {
		attrInfo->attrFlags = 0;
		attrInfo->attrSyntaxID = SYN_UNKNOWN;
		attrInfo->attrLower = 0;
		attrInfo->attrUpper = 0;
		attrInfo->asn1ID.length = 0;
	}
	return dserr;
}

static NWDSCCODE __NWDSDefineAttrV0(
		NWCONN_HANDLE		conn,
		const Attr_Info_T*	attrInfo,
		Buf_T*			attrName
) {
	NW_FRAGMENT rq_frag[3];
	nuint8 rq_b[8];
	nuint8 rq_b2[16 + MAX_ASN1_NAME];
	size_t asnlen;
	size_t asnlen2;
	
	if (!attrInfo)
		return ERR_NULL_POINTER;
	asnlen = attrInfo->asn1ID.length;
	if (asnlen > MAX_ASN1_NAME)
		return NWE_BUFFER_OVERFLOW;
	DSET_LH(rq_b, 0, 0);	/* version */
	DSET_LH(rq_b, 4, attrInfo->attrFlags);
	DSET_LH(rq_b2, 0, attrInfo->attrSyntaxID);
	DSET_LH(rq_b2, 4, attrInfo->attrLower);
	DSET_LH(rq_b2, 8, attrInfo->attrUpper);
	DSET_LH(rq_b2, 12, asnlen);
	memcpy(rq_b2 + 16, attrInfo->asn1ID.data, asnlen);
	asnlen2 = ROUNDPKT(asnlen);
	if (asnlen2 > asnlen)
		memset(rq_b2 + 4 + asnlen, 0, asnlen2 - asnlen);
	rq_frag[0].fragAddr.ro = rq_b;
	rq_frag[0].fragSize = 8;
	rq_frag[1].fragAddr.ro = NWDSBufRetrieve(attrName, &rq_frag[1].fragSize);
	rq_frag[2].fragAddr.ro = rq_b2;
	rq_frag[2].fragSize = 16 + asnlen2;
	
	return NWCFragmentRequest(conn, DSV_DEFINE_ATTR, 3, rq_frag, 0, NULL, NULL);	
}

NWDSCCODE NWDSDefineAttr(
		NWDSContextHandle	ctx,
		const NWDSChar*		attrName,
		const Attr_Info_T*	attrInfo
) {
	NWDSCCODE dserr;
	NWCONN_HANDLE conn;
	nuint8 rq[4 + MAX_SCHEMA_NAME_BYTES];
	Buf_T rqb;
	
	NWDSSetupBuf(&rqb, rq, sizeof(rq));
	dserr = NWDSCtxBufString(ctx, &rqb, attrName);
	if (dserr)
		return dserr;
	dserr = __NWDSFindSchemaServer(ctx, &conn);
	if (dserr)
		return dserr;
	dserr = __NWDSDefineAttrV0(conn, attrInfo, &rqb);
	NWCCCloseConn(conn);
	return dserr;			
}

static NWDSCCODE __NWDSRemoveAttrDefV0(
		NWCONN_HANDLE		conn,
		Buf_T*			attrName
) {
	NW_FRAGMENT rq_frag[2];
	nuint8 rq_b[4];
	
	DSET_LH(rq_b, 0, 0);	/* version */

	rq_frag[0].fragAddr.ro = rq_b;
	rq_frag[0].fragSize = 4;
	rq_frag[1].fragAddr.ro = NWDSBufRetrieve(attrName, &rq_frag[1].fragSize);
	
	return NWCFragmentRequest(conn, DSV_REMOVE_ATTR_DEF, 2, rq_frag, 0, NULL, NULL);	
}

NWDSCCODE NWDSRemoveAttrDef(
		NWDSContextHandle	ctx,
		const NWDSChar*		attrName
) {
	NWDSCCODE dserr;
	NWCONN_HANDLE conn;
	nuint8 rq[4 + MAX_SCHEMA_NAME_BYTES];
	Buf_T rqb;
	
	NWDSSetupBuf(&rqb, rq, sizeof(rq));
	dserr = NWDSCtxBufString(ctx, &rqb, attrName);
	if (dserr)
		return dserr;
	dserr = __NWDSFindSchemaServer(ctx, &conn);
	if (dserr)
		return dserr;
	dserr = __NWDSRemoveAttrDefV0(conn, &rqb);
	NWCCCloseConn(conn);
	return dserr;
}

static NWDSCCODE __NWDSSyncSchemaV0(
		NWCONN_HANDLE		conn,
		nuint32			flags,
		nuint32a		timev
) {
	NW_FRAGMENT rq_frag[1];
	nuint8 rq_b[12];
	
	DSET_LH(rq_b, 0, 0);	/* version */
	DSET_LH(rq_b, 4, flags);
	DSET_LH(rq_b, 8, timev);

	rq_frag[0].fragAddr.ro = rq_b;
	rq_frag[0].fragSize = 12;
	return NWCFragmentRequest(conn, DSV_SYNC_SCHEMA, 1, rq_frag, 0, NULL, NULL);	
}

NWDSCCODE NWDSSyncSchema(
		NWDSContextHandle	ctx,
		const NWDSChar*		serverName,
		nuint32a		seconds
) {
	NWDSCCODE dserr;
	NWCONN_HANDLE conn;
	
	dserr = NWDSOpenConnToNDSServer(ctx, serverName, &conn);
	if (dserr)
		return dserr;
	dserr = __NWDSSyncSchemaV0(conn, 0, seconds);
	NWCCCloseConn(conn);
	return dserr;
}

