/*	VarBind.c - Generic OID VarBind routines
*
*******************************************************************************
* Copyright (c) 1996-97 Digi International Inc., All Rights Reserved          *
*                                                                             *
* This  software  contains  proprietary  and  confidential   information   of *
* Digi International  Inc.   By  accepting  transfer  of this copy, Recipient *
* agrees to retain this software in  confidence,  to  prevent  disclosure  to *
* others,  and  to make no  use of this software other than that for which it *
* was delivered.  This is an unpublished copyright work  Digi   International *
* Inc.    Except   as   permitted   by  federal  law,  17 USC 117, copying is *
* strictly prohibited.                                                        *
*                                                                             *
* Digi  International  Inc.  CONFIDENTIAL  -   (Digi    International    Inc. *
* Confidential  when   combined   with   the   aggregated  modules  for  this *
* product) OBJECT CODE ONLY SOURCE MATERIALS                                  *
*                                                                             *
* (C) COPYRIGHT Digi International Inc. 1996-97                               *
*                                                                             *
* All Rights Reserved                                                         *
*                                                                             *
* US   Government   Users   Restricted   Rights   -   Use,   duplication   or *
* disclosure  restricted by GSA ADP Schedule Contract with Digi International *
* Inc.                                                                        *
*                                                                             *
*******************************************************************************
*
*/

#include "DS_RAS.h"
#include "VarBind.h"
#include "ToString.h"

BOOL Index_SparseLinear(IndexFunction_t const eType, size_t const cIndicies, size_t const iOffset, AsnObjectIdentifier * const poia, AsnInteger * const piaErrorStatus, MAX_CALLBACK const pfnMax, VALID_CALLBACK const pfnValid)
{
	BOOL fSuccess = FALSE;
	BOOL fValid;

	if (pfnValid == NULL) goto Error;

	switch (eType)
	{
		case Index_First:
		{
			size_t i;

			for (i = (poia->idLength - iOffset); i < cIndicies; i++)
				if (!_SnmpUtilOidAppend(poia, &ASN_OBJECT_ID_VAR(Index))) goto Error;

			if (!pfnValid(iOffset, poia, &fValid)) goto Error;
			if (fValid)
				break;

			/* no break; */
		}

		case Index_Next:
		{
			size_t iIndex;

			/*
			 * WARNING: This code assumes that if the first index is not valid,
			 * WARNING: no indicies below it are. It needs to be rewritten to
			 * WARNING: traverse the indicies recursively.
			 */ 
			for (iIndex = cIndicies; iIndex != 0; iIndex--)
			{
				size_t i = iIndex - 1;
				UINT * const pid = &poia->ids[iOffset + i];
				UINT idMax;

				if (!pfnMax(iOffset, poia, i, &idMax)) goto Error;
				for ((*pid)++; *pid <= idMax; (*pid)++)
				{
					if (!pfnValid(iOffset, poia, &fValid)) goto Error;
					if (fValid) break;
				}
				
				if (fValid)
					break;

				*pid = 1;
			}

			if (!fValid)
			{
				*piaErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
				break;
			}

			break;
		}

		default:
		{
			goto Error;
			break;
		}
	}

	fSuccess = TRUE;

Error:

	return fSuccess;
}

static BOOL AlwaysValid(size_t const iOffset, AsnObjectIdentifier const * const poia, BOOL * const pfValid)
{
	*pfValid = TRUE;

	return TRUE;
}

BOOL Index_Linear(IndexFunction_t const eType, size_t const cIndicies, size_t const iOffset, AsnObjectIdentifier * const poia, AsnInteger * const piaErrorStatus, MAX_CALLBACK const pfnMax)
{
	return Index_SparseLinear(eType, cIndicies, iOffset, poia, piaErrorStatus, pfnMax, AlwaysValid);
}

BOOL SetCacheObject_Generic(size_t const iOffset, RFC1157VarBind const * const pvb, CacheObject_t * const pco)
{
	return TRUE;
}

BOOL VarBind_CachedObject(BYTE const eType, CacheObject_t const * const pco, CacheValue_t const * const pcv, AsnObjectSyntax * const posa, AsnInteger * const piaErrorStatus)
{
	BOOL fSuccess = FALSE;

	switch (eType)
	{
		case ASN_RFC1157_GETREQUEST:
		{
			if (!ReadCachedObject(pco, pcv, posa, piaErrorStatus))
				goto Error;

			break;
		}

		case ASN_RFC1157_SETREQUEST:
		{
			if (!WriteCachedObject(pco, pcv, posa, piaErrorStatus))
				goto Error;

			break;
		}

		default:
		{
			goto Error;
		}
	}

	fSuccess = TRUE;

Error:

	return fSuccess;

}

BOOL VarBind_GenericData(BYTE const eType, void const * const pvMulti, size_t const iOffset, RFC1157VarBind * const pvb, AsnInteger * const piaErrorStatus)
{
	OID_DATA const * const pod = pvMulti;
	CacheObject_t co = { pod->eType };
	
	DebugMessage2("VarBind_GenericData: Request %s - %s\n", RequestToString(eType), OidToString(&pvb->name));
	pod->pfnSetCacheObject(iOffset, pvb, &co);

	return VarBind_CachedObject(eType, &co, &pod->cv, &pvb->value, piaErrorStatus);
}

BOOL VarBind_GenericList(BYTE const eType, void const * const pvMulti, size_t const iOffset, RFC1157VarBind * const pvb, AsnInteger * const piaErrorStatus)
{
	BOOL fSuccess = FALSE;
	OID_LIST const * const pol = pvMulti;
	size_t const cMaps = pol->om.cMaps;
	BYTE eCurrentType = eType;
	size_t iMap;

	if (eCurrentType == ASN_RFC1157_GETNEXTREQUEST)
	{
		pvb->name.idLength = iOffset;
		if (!_SnmpUtilOidAppend(&pvb->name, &pol->om.aome[0].poiaPartial)) goto Error;

		eCurrentType = ASN_RFC1157_GETREQUEST;
	}

	for (iMap = 0; iMap < cMaps; iMap++)
	{
		OID_MAP_ENTRY const * const pome = &pol->om.aome[iMap];

		if (OidPartialMatch(&pvb->name, iOffset, pome->poiaPartial))
		{
			Object_t eObjectType = pome->ohd.eObjectType;
			OID_HANDLER pfnVarBind = pome->pfnVarBind;

			if (pfnVarBind == NULL)
			{
				switch (eObjectType)
				{
					case Object_Data:	pfnVarBind = VarBind_GenericData;		break;
					default:			goto Error;								break;
				}
			}

			if (!pfnVarBind(eCurrentType, pome->ohd.pvMulti, iOffset, pvb, piaErrorStatus))
				goto Error;

			break;
		}
	}

	if (iMap == cMaps)
		*piaErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;

	fSuccess = TRUE;

Error:

	return fSuccess;
}

BOOL VarBind_GenericTable(BYTE const eType, void const * const pvMulti, size_t const iOffset, RFC1157VarBind * const pvb, AsnInteger * const piaErrorStatus)
{
	BOOL fSuccess = FALSE;
	OID_TABLE const * const pot = pvMulti;
	size_t const cMaps = pot->om.cMaps;
	BYTE eCurrentType = eType;
	size_t iMap;

	DebugMessage2("VarBind_GenericTable: Request %s - %s\n", RequestToString(eType), OidToString(&pvb->name));

	for (iMap = 0; iMap < cMaps; iMap++)
	{
		OID_MAP_ENTRY const * const pome = &pot->om.aome[iMap];

		DebugMessage3("VarBind_GenericTable: Checking map %d, %d - %s\n", iMap, iOffset, OidToString(pome->poiaPartial));
		if (OidPartialMatch(&pvb->name, iOffset, pome->poiaPartial))
		{
			Object_t eObjectType = pome->ohd.eObjectType;
			OID_HANDLER pfnVarBind = pome->pfnVarBind;

			DebugMessage2("VarBind_GenericTable: Matched map %d - %s\n", iMap, ObjectTypeToString(eObjectType));
			if (eCurrentType == ASN_RFC1157_GETNEXTREQUEST)
			{
				eCurrentType = ASN_RFC1157_GETREQUEST;

				if (!pot->pfnIndex(Index_Next, pot->cIndicies, iOffset + pome->poiaPartial->idLength, &pvb->name, piaErrorStatus))
					goto Error;

				if (*piaErrorStatus == SNMP_ERRORSTATUS_NOSUCHNAME)
				{
					size_t iNextMap = iMap + 1;

					if (iNextMap == cMaps)
						break;

					// We still have more, point to next OID.
					pvb->name.idLength = iOffset;
					if (!_SnmpUtilOidAppend(&pvb->name, pot->om.aome[iNextMap].poiaPartial)) goto Error;
					if (!pot->pfnIndex(Index_First, pot->cIndicies, pvb->name.idLength, &pvb->name, piaErrorStatus)) goto Error;

					DebugMessage2("VarBind_GenericTable: Converting to %s - %s\n", RequestToString(eCurrentType), OidToString(&pvb->name));

					*piaErrorStatus = SNMP_ERRORSTATUS_NOERROR;
					continue;
				}
				else
				{
					DebugMessage2("VarBind_GenericTable: Converting to %s - %s\n", RequestToString(eCurrentType), OidToString(&pvb->name));
				}
			}

			if (pfnVarBind == NULL) goto Error;
			if (!pfnVarBind(eCurrentType, pome->ohd.pvMulti, iOffset, pvb, piaErrorStatus))
				goto Error;

			break;
		}
	}

	if (iMap == cMaps)
		*piaErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;

	fSuccess = TRUE;

Error:

	return fSuccess;
}

BOOL VarBind_Root(BYTE const eType, ROOT_MAP const * const prm, RFC1157VarBind * const pvb, AsnInteger * const piaErrorStatus)
{
	BOOL fSuccess = FALSE;
	size_t const cMaps = prm->om.cMaps;
	size_t const iMatch = prm->poiaPartial->idLength;
	BYTE eCurrentType = eType;
	size_t iMap;

	DebugMessage2("VarBind_Root: Request %s - %s\n", RequestToString(eType), OidToString(&pvb->name));

	// We've been asked for the root, so append the first OID onto it.
	if ((eCurrentType == ASN_RFC1157_GETNEXTREQUEST) &&
		(iMatch == pvb->name.idLength))
	{
		if (!_SnmpUtilOidAppend(&pvb->name, prm->om.aome[0].poiaPartial)) goto Error;
		eCurrentType = ASN_RFC1157_GETREQUEST;
		DebugMessage2("VarBind_Root: Root request, converting to %s - %s\n", RequestToString(eCurrentType), OidToString(&pvb->name));
	}
			
	for (iMap = 0; iMap < cMaps; iMap++)
	{
		OID_MAP_ENTRY const * const pome = &prm->om.aome[iMap];
		size_t cIdsMatched = OidPartialMatchCount(&pvb->name, iMatch, pome->poiaPartial);

		DebugMessage3("VarBind_Root: Checking map %d, %d - %s\n", iMap, iMatch, OidToString(pome->poiaPartial));
		if (cIdsMatched > 0)
		{
			Object_t const eObjectType = pome->ohd.eObjectType;
			OID_HANDLER const pfnVarBind = pome->pfnVarBind;
			size_t const iOffset = iMatch + cIdsMatched;
			size_t const cIdsPartial = pome->poiaPartial->idLength;
			size_t cIdsRemaining = pvb->name.idLength - iOffset;

			DebugMessage2("VarBind_Root: Matched map %d - %s\n", iMap, ObjectTypeToString(eObjectType));
			if (eType == ASN_RFC1157_GETNEXTREQUEST) 
			{
				if ((eCurrentType == ASN_RFC1157_GETNEXTREQUEST) &&
					(eObjectType == Object_Data))
				{
					size_t iNextMap = iMap + 1;

					if (iNextMap == cMaps)
					{
						*piaErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;
						break;
					}

					// We still have more, point to next OID.
					pvb->name.idLength = iMatch;
					eCurrentType = ASN_RFC1157_GETREQUEST;
					if (!_SnmpUtilOidAppend(&pvb->name, prm->om.aome[iNextMap].poiaPartial)) goto Error;
					DebugMessage2("VarBind_Root: Converting to %s - %s\n", RequestToString(eCurrentType), OidToString(&pvb->name));
					continue;
				}
				
				switch (eObjectType)
				{
					case Object_Data:
					{
						DebugMessage1("VarBind_Root: Data - IDs = %d\n", cIdsRemaining);

						if (cIdsRemaining == 0)
						{
							eCurrentType = ASN_RFC1157_GETREQUEST;
							if (!_SnmpUtilOidAppend(&pvb->name, &ASN_OBJECT_ID_VAR(Scalar))) goto Error;
							DebugMessage2("VarBind_Root: Converting to %s - %s\n", RequestToString(eCurrentType), OidToString(&pvb->name));
						}
						break;
					}

					case Object_List:
					{
						OID_LIST const * const pol = pome->ohd.pvMulti;

						DebugMessage1("VarBind_Root: List - IDs = %d\n", cIdsRemaining);
						if (cIdsRemaining == 0)
						{
							eCurrentType = ASN_RFC1157_GETREQUEST;
							if (!_SnmpUtilOidAppend(&pvb->name, &pol->om.aome[0].poiaPartial)) goto Error;
							DebugMessage2("VarBind_Root: Converting to %s - %s\n", RequestToString(eCurrentType), OidToString(&pvb->name));
						}
						break;
					}

					case Object_Table:
					{
						OID_TABLE const * const pot = pome->ohd.pvMulti;
						size_t const cIdsNeeded = pot->cIndicies + cIdsPartial - cIdsMatched;

						DebugMessage5("VarBind_Root: Table - Partial = %u, Matched = %u, Remaining = %u, Needed = %u, Indicies = %u\n", cIdsPartial, cIdsMatched, cIdsRemaining, cIdsNeeded, pot->cIndicies);
						if (cIdsRemaining < cIdsNeeded)
						{
							eCurrentType = ASN_RFC1157_GETREQUEST;

							if (cIdsMatched < cIdsPartial)
							{
								pvb->name.idLength -= cIdsMatched;
								if (!_SnmpUtilOidAppend(&pvb->name, pome->poiaPartial)) goto Error;
								cIdsRemaining = 0;
							}

							if (cIdsRemaining == 0)
								if (!_SnmpUtilOidAppend(&pvb->name, pot->om.aome[0].poiaPartial)) goto Error;

							/*
							 * WARNING: This is the danger of trying to do indexing at this level.
							 * WARNING: We add one here *assuming* that all of the table entries
							 * WARNING:	are one long. If that assuption changes, indexing should be
							 * WARNING: handled in the Table code (which of course means having lots
							 * WARNING:	of copies of that code lying around, forgettting to add it,
							 * WARNING: etc). We'll just eat it here and hope for the best.
							 */
							if (!pot->pfnIndex(Index_First, pot->cIndicies, iMatch + cIdsPartial + 1, &pvb->name, piaErrorStatus))
								goto Error;

							DebugMessage2("VarBind_Root: Converting to %s - %s\n", RequestToString(eCurrentType), OidToString(&pvb->name));
						}
						break;
					}

					default:
					{
						goto Error;
						break;
					}
				}
			}
			else
			{
				switch (eObjectType)
				{
					case Object_Data:
					{
						OID_DATA const * const pod = pome->ohd.pvMulti;

						if (cIdsRemaining != 1)
							*piaErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;

						if (!OidPartialMatch(&pvb->name, iOffset, &ASN_OBJECT_ID_VAR(Scalar)))
							*piaErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;

						if ((eCurrentType == ASN_RFC1157_SETREQUEST) && (pod->eAccess != ACCESS_READWRITE))
							*piaErrorStatus = SNMP_ERRORSTATUS_READONLY;

						break;
					}

					case Object_List:
					{
						if (cIdsRemaining != 1)
							*piaErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;

						break;
					}

					case Object_Table:
					{
						OID_TABLE const * const pot = pome->ohd.pvMulti;
						size_t const cIdsWanted = (pot->cIndicies + 1);

						if (cIdsRemaining != cIdsWanted)
							*piaErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;

						break;
					}

					default:
					{
						goto Error;
						break;
					}
				}

				if (*piaErrorStatus == SNMP_ERRORSTATUS_NOSUCHNAME)
					break;
			}

			DebugAssert0(pfnVarBind != NULL, "VarBind_Root: pfnVarbind not defined\n");
			if (pfnVarBind == NULL) goto Error;
			if (!pfnVarBind(eCurrentType, pome->ohd.pvMulti, iMatch + cIdsPartial, pvb, piaErrorStatus))
				goto Error;
			DebugMessage1("VarBind_Root: VarBind returned %s\n", ErrorStatusToString(*piaErrorStatus));

			// That handler is done, let's see what else we can find.
			if ((*piaErrorStatus == SNMP_ERRORSTATUS_NOSUCHNAME) && 
				(eType == ASN_RFC1157_GETNEXTREQUEST))
			{
				size_t iNextMap = iMap + 1;

				if (iNextMap < cMaps)
				{
					*piaErrorStatus = SNMP_ERRORSTATUS_NOERROR;

					// We still have more, point to next OID.
					pvb->name.idLength = iMatch;
					if (!_SnmpUtilOidAppend(&pvb->name, prm->om.aome[iNextMap].poiaPartial)) goto Error;
					eCurrentType = ASN_RFC1157_GETREQUEST;
					DebugMessage2("VarBind_Root: Converting to %s - %s\n", RequestToString(eCurrentType), OidToString(&pvb->name));
					continue;
				}
			}
			
			break;
		}
	}

	if (iMap == cMaps)
		*piaErrorStatus = SNMP_ERRORSTATUS_NOSUCHNAME;

	fSuccess = TRUE;

Error:

	DebugMessage2("VarBind_Root: %s - %s\n", SuccessToString(fSuccess), ErrorStatusToString(*piaErrorStatus));
	return fSuccess;
}
