CConfigurePlugin.cpp   [plain text]


/*
 * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 1.1 (the
 * "License").  You may not use this file except in compliance with the
 * License.  Please obtain a copy of the License at
 * http://www.apple.com/publicsource and read it before using this file.
 * 
 * This Original Code and all software distributed under the License are
 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */

/*!
 * @header CConfigurePlugin
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>			//used for mkdir and stat

#include <CoreFoundation/CoreFoundation.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <Security/Authorization.h>

#include "DirServices.h"
#include "DirServicesUtils.h"
#include "DirServicesConst.h"

#include "SharedConsts.h"
#include "CSharedData.h"
#include "PrivateTypes.h"
#include "DSUtils.h"
#include "CAttributeList.h"
#include "CPlugInRef.h"
#include "CBuff.h"
#include "CDataBuff.h"
#include "CLog.h"

#include "CConfigurePlugin.h"
#include "ServerModuleLib.h"
#include "PluginData.h"
#include "DSCThread.h"
#include "DSEventSemaphore.h"
#include "CContinue.h"
#include "CPlugInList.h"
#include "CServerPlugin.h"
#include "ServerControl.h"
#include "CPluginConfig.h"

typedef struct {
	uInt32				fRecNameIndex;
	uInt32				fRecTypeIndex;
	uInt32				fAllRecIndex;
	uInt32				fAttrIndex;
} sConfigContinueData;


// Globals ---------------------------------------------------------------------------

static	CPlugInRef	 	*gConfigNodeRef			= nil;
static	CContinue	 	*gConfigContinue		= nil;
static	DSEventSemaphore	*gKickConfigRequests	= nil;

extern	CPlugInList		*gPlugins;

#define	kDSConfigPluginsRecType		"dsConfigType::Plugins"
#define	kDSConfigRecordsType		"dsConfigType::RecordTypes"
#define	kDSConfigAttributesType		"dsConfigType::AttributeTypes"
#define	kDSConfigRecordsAll			"dsConfigType::GetAllRecords"

#define	kDSConfigAttrVersion		"dsConfigAttrType::Version"
#define	kDSConfigAttrState			"dsConfigAttrType::State"
#define	kDSConfigAttrConfigAvail	"dsConfigAttrType::ConfigAvailable"
#define	kDSConfigAttrConfigFile		"dsConfigAttrType::ConfigFile"
#define	kDSConfigAttrPlugInIndex 	"dsConfigAttrType::PlugInIndex"

#define		kAttrTypeConsts		67

//re-ordered more closely in terms of frequency of use
static const char *sAttrTypes[ kAttrTypeConsts ] =
{
	kDSNAttrRecordName,
	kDS1AttrDistinguishedName,
	kDS1AttrPassword,
	kDS1AttrUniqueID,
	kDS1AttrPrimaryGroupID,
	kDS1AttrUserShell,
	kDS1AttrNFSHomeDirectory,
	kDSNAttrAuthenticationAuthority,
	kDSNAttrHomeDirectory,
	kDS1StandardAttrHomeLocOwner,
	kDS1AttrHomeDirectoryQuota,
	kDS1AttrPicture,
	kDS1AttrInternetAlias,
	kDS1AttrMailAttribute,
	kDS1AttrAuthenticationHint,
	kDSNAttrGroupMembership,
	kDSNAttrEMailAddress,
	kDSNAttrURL,
	kDSNAttrURLForNSL,
	kDSNAttrMIME,
	kDSNAttrHTML,
	kDSNAttrNBPEntry,
	kDSNAttrDNSName,
	kDSNAttrIPAddress,
	kDS1AttrENetAddress,
	kDSNAttrComputers,
	kDS1AttrMCXFlags,
	kDS1AttrMCXSettings,
	kDS1AttrPrintServiceInfoText,
	kDS1AttrPrintServiceInfoXML,
	kDS1AttrPrintServiceUserData,
	kDS1AttrVFSType,
	kDS1AttrVFSPassNo,
	kDS1AttrVFSDumpFreq,
	kDS1AttrVFSLinkDir,
	kDSNAttrVFSOpts,
	kDS1AttrAliasData,
	kDSNAttrPhoneNumber,
	kDS1AttrCapabilities,
	kDSNAttrProtocols,
	kDSNAttrMember,
	kDS1AttrComment,
	kDS1AttrChange,
	kDS1AttrExpire,
	kDSNAttrGroup,
	kDS1AttrFirstName,
	kDS1AttrMiddleName,
	kDS1AttrLastName,
	kDSNAttrAreaCode,
	kDSNAttrAddressLine1,
	kDSNAttrAddressLine2,
	kDSNAttrAddressLine3,
	kDSNAttrCity,
	kDSNAttrState,
	kDSNAttrPostalCode,
	kDSNAttrOrganizationName,
	kDS1AttrSetupOccupation,
	kDS1AttrSetupLocation,
	kDS1AttrSetupAdvertising,
	kDS1AttrSetupAutoRegister,
	kDSNAttrBootParams,
	kDSNAttrNetGroups,
	kDS1AttrPresetUserIsAdmin,
	kDS1AttrHomeLocOwner,
	kDS1AttrPasswordServerLocation,
	kDS1AttrAdminLimits,
	kDS1AttrDataStamp,
};


#define		kRecTypeConsts		34


static const char *sRecTypes[ kRecTypeConsts ] =
{
	kDSStdRecordTypeUsers,
	kDSStdRecordTypeUserAliases,
	kDSStdRecordTypeGroups,
	kDSStdRecordTypeGroupAliases,
	kDSStdRecordTypeMachines,
	kDSStdRecordTypeComputers,
	kDSStdRecordTypeComputerLists,
	kDSStdRecordTypePrinters,
	kDSStdRecordTypeHosts,
	kDSStdRecordTypeAliases,
	kDSStdRecordTypeNetworks,
	kDSStdRecordTypeServer,
	kDSStdRecordTypeWebServer,
	kDSStdRecordTypeFTPServer,
	kDSStdRecordTypeAFPServer,
	kDSStdRecordTypeLDAPServer,
	kDSStdRecordTypeNFS,
	kDSStdRecordTypeServices,
	kDSStdRecordTypePrintService,
	kDSStdRecordTypePrintServiceUser,
	kDSStdRecordTypeConfig,
	kDSStdRecordTypeMeta,
	kDSStdRecordTypeMounts,
	kDSStdRecordTypePresetUsers,
	kDSStdRecordTypePresetGroups,
	kDSStdRecordTypePresetComputerLists,
	kDSStdRecordTypeHosts,
	kDSStdRecordTypeProtocols,
	kDSStdRecordTypeRPC,
	kDSStdRecordTypeBootp,
	kDSStdRecordTypeNetDomains,
	kDSStdRecordTypeEthernets,
	kDSStdRecordTypeNetGroups,
	kDSStdRecordTypeHostServices,
};

// Consts ----------------------------------------------------------------------------

static const	uInt32		kBuffPad			= 16;

// --------------------------------------------------------------------------------
//	* CConfigurePlugin ()
// --------------------------------------------------------------------------------

CConfigurePlugin::CConfigurePlugin ( FourCharCode inSig, const char *inName )
{
	fConfigNodeName	= nil;
	fNodeCount		= 0;
	fState			= kUnknownState;

	//parent class member variable assignments
	fPlugInSignature	= inSig;
	fPlugInName			= nil;
	if ( inName != nil )
	{
		fPlugInName = new char[ ::strlen( inName ) + 1 ];
		::strcpy( fPlugInName, inName );
	}
	
	if ( gConfigNodeRef == nil )
	{
		gConfigNodeRef = new CPlugInRef( CConfigurePlugin::ContextDeallocProc );
		if ( gConfigNodeRef == nil ) throw((sInt32)eMemoryAllocError);
	}

	if ( gConfigContinue == nil )
	{
		gConfigContinue = new CContinue( CConfigurePlugin::ContinueDeallocProc );
		if ( gConfigContinue == nil ) throw((sInt32)eMemoryAllocError);
	}

	if ( gKickConfigRequests == nil )
	{
		gKickConfigRequests = new DSEventSemaphore();
		if ( gKickConfigRequests == nil ) throw((sInt32)eMemoryAllocError);
	}
} // CConfigurePlugin


// --------------------------------------------------------------------------------
//	* ~CConfigurePlugin ()
// --------------------------------------------------------------------------------

CConfigurePlugin::~CConfigurePlugin ( void )
{
} // ~CConfigurePlugin


// --------------------------------------------------------------------------------
//	* Validate ()
// --------------------------------------------------------------------------------

sInt32 CConfigurePlugin::Validate ( const char *inVersionStr, const uInt32 inSignature )
{
	fToken = inSignature;

	return( eDSNoErr );
} // Validate


// --------------------------------------------------------------------------------
//	* Configure ()
// --------------------------------------------------------------------------------

sInt32 CConfigurePlugin::Configure ( void )
{
	return( eDSNoErr );
} // Configure


// --------------------------------------------------------------------------------
//	* Shutdown ()
// --------------------------------------------------------------------------------

sInt32 CConfigurePlugin::Shutdown ( void )
{
	return( eDSNoErr );
} // Shutdown


// --------------------------------------------------------------------------------
//	* SetPluginState ()
// --------------------------------------------------------------------------------

sInt32 CConfigurePlugin::SetPluginState ( const uInt32 inState )
{
//does nothing yet
	return( eDSNoErr );
} // SetPluginState


// --------------------------------------------------------------------------------
//	* PeriodicTask ()
// --------------------------------------------------------------------------------

sInt32 CConfigurePlugin::PeriodicTask ( void )
{
	return( eDSNoErr );
} // PeriodicTask


// --------------------------------------------------------------------------------
//	* Initialize ()
// --------------------------------------------------------------------------------

sInt32 CConfigurePlugin::Initialize ( void )
{
	sInt32			siResult		= eDSNoErr;

	// maybe do some config file reading stuff
	fConfigNodeName = ::dsBuildFromPathPriv( "Configure", "/" );

	if ( fConfigNodeName != nil )
	{
		CServerPlugin::_RegisterNode( fToken, fConfigNodeName, kConfigNodeType );
	}

	fState = 	kUnknownState;
	fState += 	kInitalized;
	fState += 	kActive;

	return( siResult );

} // Initialize


//--------------------------------------------------------------------------------------------------
//	* WakeUpRequests() (static)
//
//--------------------------------------------------------------------------------------------------

void CConfigurePlugin::WakeUpRequests ( void )
{
	gKickConfigRequests->Signal();
} // WakeUpRequests


// ---------------------------------------------------------------------------
//	* WaitForInit
//
// ---------------------------------------------------------------------------

void CConfigurePlugin::WaitForInit ( void )
{
	volatile	uInt32		uiAttempts	= 0;

	while ( !(fState & kInitalized) &&
			!(fState & kFailedToInit) )
	{
		// Try for 2 minutes before giving up
		if ( uiAttempts++ >= 240 )
		{
			return;
		}

		// Now wait until we are told that there is work to do or
		//	we wake up on our own and we will look for ourselves

		gKickConfigRequests->Wait( (uInt32)(.5 * kMilliSecsPerSec) );
	}
} // WaitForInit


// ---------------------------------------------------------------------------
//	* ProcessRequest
//
// ---------------------------------------------------------------------------

sInt32 CConfigurePlugin::ProcessRequest ( void *inData )
{
	sInt32		siResult	= eDSNoErr;
	char	   *pathStr		= nil;

	try
	{
		if ( inData == nil )
		{
			throw( (sInt32)ePlugInDataError );
		}

		if (((sHeader *)inData)->fType == kOpenDirNode)
		{
			if (((sOpenDirNode *)inData)->fInDirNodeName != nil)
			{
				pathStr = ::dsGetPathFromListPriv( ((sOpenDirNode *)inData)->fInDirNodeName, "/" );
				if ( (pathStr != nil) && (strncmp(pathStr,"/Configure",10) != 0) )
				{
					throw( (sInt32)eDSOpenNodeFailed);
				}
			}
		}
		
		WaitForInit();

        if ( (fState & kFailedToInit) || !(fState & kInitalized) )
        {
            throw( (sInt32)ePlugInFailedToInitialize );
        }

        if ( (fState & kInactive) || !(fState & kActive) )
        {
            throw( (sInt32)ePlugInNotActive );
        }
        
		siResult = HandleRequest( inData );
	}

	catch( sInt32 err )
	{
		siResult = err;
	}

	if (pathStr != nil)
	{
		free(pathStr);
		pathStr = nil;
	}
	return( siResult );

} // ProcessRequest


// ---------------------------------------------------------------------------
//	* HandleRequest
//
// ---------------------------------------------------------------------------

sInt32 CConfigurePlugin::HandleRequest ( void *inData )
{
	sInt32		siResult	= eDSNoErr;
	sHeader	   *pMsgHdr		= nil;

	try
	{
		pMsgHdr = (sHeader *)inData;

		switch ( pMsgHdr->fType )
		{
			case kReleaseContinueData:
				siResult = ReleaseContinueData( (sReleaseContinueData *)inData );
				break;

			case kOpenDirNode:
				siResult = OpenDirNode( (sOpenDirNode *)inData );
				break;

			case kCloseDirNode:
				siResult = CloseDirNode( (sCloseDirNode *)inData );
				break;

			case kGetRecordList:
				siResult = GetRecordList( (sGetRecordList *)inData );
				break;

			case kGetRecordEntry:
				siResult = GetRecordEntry( (sGetRecordEntry *)inData );
				break;

			case kGetAttributeEntry:
				siResult = GetAttributeEntry( (sGetAttributeEntry *)inData );
				break;

			case kGetAttributeValue:
				siResult = GetAttributeValue( (sGetAttributeValue *)inData );
				break;

			case kCloseAttributeList:
				siResult = CloseAttributeList( (sCloseAttributeList *)inData );
				break;

			case kCloseAttributeValueList:
				siResult = CloseAttributeValueList( (sCloseAttributeValueList *)inData );
				break;

            case kDoPlugInCustomCall:
                siResult = DoPlugInCustomCall( (sDoPlugInCustomCall *)inData );
                break;
                
			case kHandleNetworkTransition:
			case kServerRunLoop:
				siResult = eDSNoErr;
				break;

			default:
				siResult = eNotHandledByThisNode;
				break;
		}

		pMsgHdr->fResult = siResult;
	}

	catch( sInt32 err )
	{
		siResult = err;
	}

	return( siResult );

} // HandleRequest


//------------------------------------------------------------------------------------
//	* ReleaseContinueData
//------------------------------------------------------------------------------------

sInt32 CConfigurePlugin::ReleaseContinueData ( sReleaseContinueData *inData )
{
	sInt32	siResult	= eDSNoErr;

	// RemoveItem calls our ContinueDeallocProc to clean up
	if ( gConfigContinue->RemoveItem( inData->fInContinueData ) != eDSNoErr )
	{
		siResult = eDSInvalidContext;
	}

	return( siResult );

} // ReleaseContinueData


//------------------------------------------------------------------------------------
//	* OpenDirNode
//------------------------------------------------------------------------------------

sInt32 CConfigurePlugin::OpenDirNode ( sOpenDirNode *inData )
{
	sInt32			siResult	= eDSOpenNodeFailed;
	char		       *pathStr		= nil;
	sConfigContextData	       *pContext	= nil;

	try
	{
		
		if ( inData != nil )
		{
			pathStr = ::dsGetPathFromListPriv( inData->fInDirNodeName, "/" );
			if ( pathStr != nil )
			{
				if ( ::strcmp( pathStr, "/Configure" ) == 0 )
				{
					siResult = eDSNoErr;
					pContext = MakeContextData();
					if (pContext == nil ) throw( (sInt32)eMemoryAllocError);

					gConfigNodeRef->AddItem( inData->fOutNodeRef, pContext );
				}

				delete( pathStr );
				pathStr = nil;
			}
		}

	}

	catch( sInt32 err )
	{
		siResult = err;
	}
	return( siResult );

} // OpenDirNode


//------------------------------------------------------------------------------------
//	* CloseDirNode
//------------------------------------------------------------------------------------

sInt32 CConfigurePlugin::CloseDirNode ( sCloseDirNode *inData )
{
	sInt32			siResult		= eDSNoErr;
	sConfigContextData   *pContext		= nil;

	try
	{
		pContext = (sConfigContextData *) gConfigNodeRef->GetItemData( inData->fInNodeRef );
		if ( pContext == nil ) throw( (sInt32)eDSInvalidNodeRef );

		gConfigNodeRef->RemoveItem( inData->fInNodeRef );
		gConfigContinue->RemoveItems( inData->fInNodeRef );
	}

	catch( sInt32 err )
	{
		siResult = err;
	}

	return( siResult );

} // CloseDirNode


//------------------------------------------------------------------------------------
//	* GetRecordList
//------------------------------------------------------------------------------------

sInt32 CConfigurePlugin::GetRecordList ( sGetRecordList *inData )
{
	sInt32						siResult		= eDSNoErr;
	uInt32						i				= 0;
	uInt32						uiTotal			= 0;
	uInt32						uiCount			= 0;
	char					   *pRecName		= nil;
	char					   *pRecType		= nil;
	char					   *pNIRecType		= nil;
	bool						bAttribOnly		= false;
	tDirPatternMatch			pattMatch		= eDSNoMatch1;
	CAttributeList 			   *cpRecNameList	= nil;
	CAttributeList 			   *cpRecTypeList	= nil;
	CAttributeList 			   *cpAttrTypeList 	= nil;
	sConfigContextData			   *pContext		= nil;
	sConfigContinueData		   *pContinue		= nil;
	CBuff					   *outBuff			= nil;
	CPlugInList::sTableData	   *pPIInfo			= nil;
	const char				   *typeName		= nil;
    sInt32						siValCnt		= 0;
	uInt32						fillIndex		= 0;
	CDataBuff				   *aRecData		= nil;
	CDataBuff				   *aAttrData		= nil;
	CDataBuff				   *aTmpData		= nil;

	try
	{
		aRecData	= new CDataBuff();
		if ( aRecData == nil ) throw((sInt32)eMemoryAllocError);

		aAttrData	= new CDataBuff();
		if ( aAttrData == nil ) throw((sInt32)eMemoryAllocError);

		aTmpData	= new CDataBuff();
		if ( aTmpData == nil ) throw((sInt32)eMemoryAllocError);

		// Verify all the parameters
		if ( inData == nil ) throw( (sInt32)eMemoryError );
		if ( inData->fInDataBuff == nil ) throw( (sInt32)eDSEmptyBuffer );
		if (inData->fInDataBuff->fBufferSize == 0) throw( (sInt32)eDSEmptyBuffer );

		if ( inData->fInRecNameList == nil ) throw( (sInt32)eDSEmptyRecordNameList );
		if ( inData->fInRecTypeList == nil ) throw( (sInt32)eDSEmptyRecordTypeList );
		if ( inData->fInAttribTypeList == nil ) throw( (sInt32)eDSEmptyAttributeTypeList );

		// Node context data
		pContext = (sConfigContextData *)gConfigNodeRef->GetItemData( inData->fInNodeRef );
		if ( pContext == nil ) throw( (sInt32)eDSInvalidNodeRef );

		if ( inData->fIOContinueData == nil )
		{
			pContinue = (sConfigContinueData *)::calloc( 1, sizeof( sConfigContinueData ) );
			gConfigContinue->AddItem( pContinue, inData->fInNodeRef );

			pContinue->fRecNameIndex = 1;
			pContinue->fRecTypeIndex = 1;
			pContinue->fAttrIndex = 1;
			pContinue->fAllRecIndex = 0;
		}
		else
		{
			pContinue = (sConfigContinueData *)inData->fIOContinueData;
			if ( gConfigContinue->VerifyItem( pContinue ) == false )
			{
				throw( (sInt32)eDSInvalidContinueData );
			}
		}

		inData->fIOContinueData = nil;

		outBuff = new CBuff();
		if ( outBuff == nil ) throw( (sInt32)eMemoryError );

		siResult = outBuff->Initialize( inData->fInDataBuff, true );
		if ( siResult != eDSNoErr ) throw( siResult );

		siResult = outBuff->GetBuffStatus();
		if ( siResult != eDSNoErr ) throw( siResult );

		siResult = outBuff->SetBuffType( 'StdA' );
		if ( siResult != eDSNoErr ) throw( siResult );

		// Get the record name list
		cpRecNameList = new CAttributeList( inData->fInRecNameList );
		if ( cpRecNameList == nil ) throw( (sInt32)eDSEmptyRecordNameList );
		if (cpRecNameList->GetCount() == 0) throw( (sInt32)eDSEmptyRecordNameList );

		// Get the record pattern match
		pattMatch = inData->fInPatternMatch;

		// Get the record type list
		cpRecTypeList = new CAttributeList( inData->fInRecTypeList );
		if ( cpRecTypeList == nil ) throw( (sInt32)eDSEmptyRecordTypeList );
		if (cpRecTypeList->GetCount() == 0) throw( (sInt32)eDSEmptyRecordTypeList );

		// Get the attribute list
		cpAttrTypeList = new CAttributeList( inData->fInAttribTypeList );
		if ( cpAttrTypeList == nil ) throw( (sInt32)eDSEmptyAttributeTypeList );
		if (cpAttrTypeList->GetCount() == 0) throw( (sInt32)eDSEmptyAttributeTypeList );

		// Get the attribute info only flag
		bAttribOnly = inData->fInAttribInfoOnly;

		// get these type of records
		while ( cpRecTypeList->GetAttribute( pContinue->fRecTypeIndex, &pRecType ) == eDSNoErr )
		{
			// get this record type
			if ( ::strcmp( pRecType, kDSConfigPluginsRecType ) == 0 )
			{
				// get these names
				while ( cpRecNameList->GetAttribute( pContinue->fRecNameIndex, &pRecName ) == eDSNoErr )
				{
					// Get all records of this name
					if ( ::strcmp( pRecName, kDSConfigRecordsAll ) == 0 )
					{
						//setup to work with the continue data
						i = pContinue->fAllRecIndex;

						//search over all the plugins in the table
						while ( i < CPlugInList::kMaxPlugIns )
						{

                            pPIInfo = gPlugins->GetPlugInInfo( i );

                            if ( pPIInfo != nil )
                            {
                            	aRecData->Clear();
                            	siValCnt = 0;

                                if ( (pPIInfo->fName != nil) && ( ::strcmp(pPIInfo->fName,"Configure") != 0) && ( ::strcmp(pPIInfo->fName,"Search") != 0) )
                                {
                                    // Add the record type which in this case is config node
                                    aRecData->AppendShort( ::strlen( kDSConfigPluginsRecType ) );
                                    aRecData->AppendString( kDSConfigPluginsRecType );

                                    // Add the record name which in this case is the config node name
                                    aRecData->AppendShort( ::strlen( pPIInfo->fName ) );
                                    aRecData->AppendString( pPIInfo->fName );

                                    aAttrData->Clear();

                                    //let's get the attributes in this order
                                    //plugin table index, plugin status, plugin software version, plugin config HI avail, config file

                                    siValCnt = 5;
                                    
                                    aTmpData->Clear();

                                    //append the plugin table index attr name
                                    aTmpData->AppendShort( ::strlen( kDSConfigAttrPlugInIndex ) );
                                    aTmpData->AppendString( kDSConfigAttrPlugInIndex );
                                    // Append the attribute value count
                                    aTmpData->AppendShort( 1 );
                                    // Append attribute value
									//special case as we will pass the attribute inside the length of the attribute value
									//instead and the value string here will be simply filler to ensure the buffer logic works
                                    aTmpData->AppendLong( i );
									for (fillIndex = 0; fillIndex < i; fillIndex++)
									{
										aTmpData->AppendString( "x" );
									}

									// Add the attribute length
									aAttrData->AppendLong( aTmpData->GetLength() );
									aAttrData->AppendBlock( aTmpData->GetData(), aTmpData->GetLength() );
                                    aTmpData->Clear();

                                    //append the plugin status attr name
                                    aTmpData->AppendShort( ::strlen( kDSConfigAttrState ) );
                                    aTmpData->AppendString( kDSConfigAttrState );
                                    // Append the attribute value count
                                    aTmpData->AppendShort( 1 );
                                    // Append attribute value
                                    if (pPIInfo->fState & kActive)
                                    {
                                        aTmpData->AppendLong( ::strlen("Active" ));
                                        aTmpData->AppendString( "Active" );
                                    } else if (pPIInfo->fState & kInitalized)
                                    {
                                       aTmpData->AppendLong( ::strlen("Initted" ));
                                       aTmpData->AppendString( "Initted" );
                                   	} else if (pPIInfo->fState & kFailedToInit)
                                    {
                                       aTmpData->AppendLong( ::strlen("FailedToInit" ));
                                       aTmpData->AppendString( "FailedToInit" );
                                    } else
                                    {
                                       aTmpData->AppendLong( ::strlen("Unknown" ));
                                       aTmpData->AppendString( "Unknown" );
                                    }

									// Add the attribute length
									aAttrData->AppendLong( aTmpData->GetLength() );
									aAttrData->AppendBlock( aTmpData->GetData(), aTmpData->GetLength() );
                                    aTmpData->Clear();

                                    //append the plugin version attr name
                                    aTmpData->AppendShort( ::strlen( kDSConfigAttrVersion ) );
                                    aTmpData->AppendString( kDSConfigAttrVersion );
                                    // Append the attribute value count
                                    aTmpData->AppendShort( 1 );
                                    // Append attribute value
                                    aTmpData->AppendLong( ::strlen( pPIInfo->fVersion ));
                                    aTmpData->AppendString( pPIInfo->fVersion );
									
									// Add the attribute length
									aAttrData->AppendLong( aTmpData->GetLength() );
									aAttrData->AppendBlock( aTmpData->GetData(), aTmpData->GetLength() );
                                    aTmpData->Clear();

                                    //append the plugin config avail attr name
                                    aTmpData->AppendShort( ::strlen( kDSConfigAttrConfigAvail ) );
                                    aTmpData->AppendString( kDSConfigAttrConfigAvail );
                                    // Append the attribute value count
                                    aTmpData->AppendShort( 1 );
                                    // Append attribute value
                                    aTmpData->AppendLong( ::strlen( pPIInfo->fConfigAvail ));
                                    aTmpData->AppendString( pPIInfo-> fConfigAvail );
									
									// Add the attribute length
									aAttrData->AppendLong( aTmpData->GetLength() );
									aAttrData->AppendBlock( aTmpData->GetData(), aTmpData->GetLength() );
                                    aTmpData->Clear();

                                    //append the plugin config file attr name
                                    aTmpData->AppendShort( ::strlen( kDSConfigAttrConfigFile ) );
                                    aTmpData->AppendString( kDSConfigAttrConfigFile );
                                    // Append the attribute value count
                                    aTmpData->AppendShort( 1 );
                                    // Append attribute value
                                    aTmpData->AppendLong( ::strlen( pPIInfo->fConfigFile ));
                                    aTmpData->AppendString( pPIInfo-> fConfigFile );
									
									// Add the attribute length
									aAttrData->AppendLong( aTmpData->GetLength() );
									aAttrData->AppendBlock( aTmpData->GetData(), aTmpData->GetLength() );
                                    aTmpData->Clear();

                        			// Attribute count
                        			aRecData->AppendShort( siValCnt );
                       				aRecData->AppendBlock( aAttrData->GetData(), aAttrData->GetLength() );

									//add the record (plugin) data to the buffer
									siResult = outBuff->AddData( aRecData->GetData(), aRecData->GetLength() );
									if ( siResult != eDSNoErr )
									{
										pContinue->fAllRecIndex = i;
										throw( siResult );
									}
								} // if there is a name and it is not /Configure or /Search ie. this plugin itself or the search node

							} // this is a registered plugin that is not nil
							i++;
						} // while: search over all the registered plugins

						outBuff->GetDataBlockCount( &uiCount );

						if ( siResult == CBuff::kBuffFull )
						{
							uiTotal += uiCount;

							if ( uiTotal == 0 )
							{
								throw( (sInt32)eDSBufferTooSmall );
							}
							else
							{
								inData->fIOContinueData = pContinue;
								inData->fOutRecEntryCount = uiTotal;
								outBuff->SetLengthToSize();

								throw( (sInt32)eDSNoErr );
							}
						}
						else if ( siResult == eDSNoErr )
						{
							uiTotal += uiCount;
						}

					} // single record name
					
				pContinue->fRecNameIndex++;
				
				} // loop over record names

			} // single record type
			else if ( ::strcmp( pRecType, kDSConfigRecordsType ) == 0 )
			{
				// get these names
				while ( cpRecNameList->GetAttribute( pContinue->fRecNameIndex, &pRecName ) == eDSNoErr )
				{
					// Get all records of this name
					if ( ::strcmp( pRecName, kDSConfigRecordsAll ) == 0 )
					{
						//setup to work with the continue data
						i = pContinue->fAllRecIndex;

						//search over all the record types in the table
						while ( i < kRecTypeConsts )
						{

                            typeName = sRecTypes[i];

                            if ( typeName != nil )
                            {
								aRecData->Clear();
								siValCnt = 0;

								// Add the record type which in this case is config node
								aRecData->AppendShort( ::strlen( pRecType ) );
								aRecData->AppendString( pRecType );

								// Add the record name which in this case is the config node name
								aRecData->AppendShort( ::strlen( typeName ) );
								aRecData->AppendString( typeName );

								aAttrData->Clear();

								//let's get the attributes in this order
								//plugin table index, plugin status, plugin software version, plugin config HI avail, config file

								siValCnt = 0;

								//aTmpData->Clear();

								// Attribute count
								aRecData->AppendShort( siValCnt );
								aRecData->AppendBlock( aAttrData->GetData(), aAttrData->GetLength() );

								//add the record (plugin) data to the buffer
								siResult = outBuff->AddData( aRecData->GetData(), aRecData->GetLength() );
								if ( siResult != eDSNoErr )
								{
									pContinue->fAllRecIndex = i;
									throw( siResult );
								}

							} // name is not nil
							i++;
						} // while: search over all the registered plugins

						outBuff->GetDataBlockCount( &uiCount );

						if ( siResult == CBuff::kBuffFull )
						{
							uiTotal += uiCount;

							if ( uiTotal == 0 )
							{
								throw( (sInt32)eDSBufferTooSmall );
							}
							else
							{
								inData->fIOContinueData = pContinue;
								inData->fOutRecEntryCount = uiTotal;
								outBuff->SetLengthToSize();

								throw( (sInt32)eDSNoErr );
							}
						}
						else if ( siResult == eDSNoErr )
						{
							uiTotal += uiCount;
						}

					} // single record name

					pContinue->fRecNameIndex++;

				} // loop over record names

			} // single record type
			else if ( ::strcmp( pRecType, kDSConfigAttributesType ) == 0 )
			{
				// get these names
				while ( cpRecNameList->GetAttribute( pContinue->fRecNameIndex, &pRecName ) == eDSNoErr )
				{
					// Get all records of this name
					if ( ::strcmp( pRecName, kDSConfigRecordsAll ) == 0 )
					{
						//setup to work with the continue data
						i = pContinue->fAllRecIndex;

						//search over all the record types in the table
						while ( i < kAttrTypeConsts )
						{

                            typeName = sAttrTypes[i];

                            if ( typeName != nil )
                            {
								aRecData->Clear();
								siValCnt = 0;

								// Add the record type which in this case is config node
								aRecData->AppendShort( ::strlen( pRecType ) );
								aRecData->AppendString( pRecType );

								// Add the record name which in this case is the config node name
								aRecData->AppendShort( ::strlen( typeName ) );
								aRecData->AppendString( typeName );

								aAttrData->Clear();

								//let's get the attributes in this order
								//plugin table index, plugin status, plugin software version, plugin config HI avail, config file

								siValCnt = 0;

								//aTmpData->Clear();

								// Attribute count
								aRecData->AppendShort( siValCnt );
								aRecData->AppendBlock( aAttrData->GetData(), aAttrData->GetLength() );

								//add the record (plugin) data to the buffer
								siResult = outBuff->AddData( aRecData->GetData(), aRecData->GetLength() );
								if ( siResult != eDSNoErr )
								{
									pContinue->fAllRecIndex = i;
									throw( siResult );
								}

							} // name is not nil
							i++;
						} // while: search over all the registered plugins

						outBuff->GetDataBlockCount( &uiCount );

						if ( siResult == CBuff::kBuffFull )
						{
							uiTotal += uiCount;

							if ( uiTotal == 0 )
							{
								throw( (sInt32)eDSBufferTooSmall );
							}
							else
							{
								inData->fIOContinueData = pContinue;
								inData->fOutRecEntryCount = uiTotal;
								outBuff->SetLengthToSize();

								throw( (sInt32)eDSNoErr );
							}
						}
						else if ( siResult == eDSNoErr )
						{
							uiTotal += uiCount;
						}

					} // single record name

					pContinue->fRecNameIndex++;

				} // loop over record names

			} // single record type
			else
			{
				siResult = eDSInvalidRecordType;
			}
			
			pContinue->fRecTypeIndex++;
			
		} // loop over record types

		if ( siResult == eDSNoErr )
		{
			if ( uiTotal == 0 )
			{
				//siResult = eDSRecordNotFound;
				outBuff->ClearBuff();
			}
			else
			{
				outBuff->SetLengthToSize();
			}

			inData->fOutRecEntryCount = uiTotal;
		} // if no error
	} // try block

	catch( sInt32 err )
	{
		siResult = err;
	}

	if ( (inData->fIOContinueData == nil) && (pContinue != nil) )
	{
		// we've decided not to return continue data, so we should clean up
		gConfigContinue->RemoveItem( pContinue );
		pContinue = nil;
	}

	if ( outBuff != nil )
	{
		delete( outBuff );
		outBuff = nil;
	}

	if ( pNIRecType != nil )
	{
		delete( pNIRecType );
		pNIRecType = nil;
	}

	if ( cpRecNameList != nil )
	{
		delete( cpRecNameList );
		cpRecNameList = nil;
	}

	if ( cpRecTypeList != nil )
	{
		delete( cpRecTypeList );
		cpRecTypeList = nil;
	}
	
	if ( cpAttrTypeList != nil )
	{
		delete( cpAttrTypeList );
		cpAttrTypeList = nil;
	}

	if ( aRecData != nil )
	{
		delete( aRecData );
		aRecData = nil;
	}

	if ( aAttrData != nil )
	{
		delete( aAttrData );
		aAttrData = nil;
	}

	if ( aTmpData != nil )
	{
		delete( aTmpData );
		aTmpData = nil;
	}

	return( siResult ); 

} // GetRecordList


//------------------------------------------------------------------------------------
//	* GetRecordEntry
//------------------------------------------------------------------------------------

sInt32 CConfigurePlugin::GetRecordEntry ( sGetRecordEntry *inData )
{
	sInt32					siResult		= eDSNoErr;
	uInt32					uiIndex			= 0;
	uInt32					uiCount			= 0;
	uInt32					uiOffset		= 0;
	uInt32					uberOffset		= 0;
	char 				   *pData			= nil;
	tRecordEntryPtr			pRecEntry		= nil;
	sConfigContextData		   *pContext		= nil;
	CBuff					inBuff;
	uInt32					offset			= 0;
	uInt16					usTypeLen		= 0;
	char				   *pRecType		= nil;
	uInt16					usNameLen		= 0;
	char				   *pRecName		= nil;
	uInt16					usAttrCnt		= 0;
	uInt32					buffLen			= 0;

	try
	{
		if ( inData  == nil ) throw( (sInt32)eMemoryError );
		if ( inData->fInOutDataBuff  == nil ) throw( (sInt32)eDSEmptyBuffer );
		if (inData->fInOutDataBuff->fBufferSize == 0) throw( (sInt32)eDSEmptyBuffer );

		siResult = inBuff.Initialize( inData->fInOutDataBuff );
		if ( siResult != eDSNoErr ) throw( siResult );

		siResult = inBuff.GetDataBlockCount( &uiCount );
		if ( siResult != eDSNoErr ) throw( siResult );

		uiIndex = inData->fInRecEntryIndex;
		if ((uiIndex > uiCount) || (uiIndex == 0)) throw( (sInt32)eDSInvalidIndex );

		pData = inBuff.GetDataBlock( uiIndex, &uberOffset );
		if ( pData  == nil ) throw( (sInt32)eDSCorruptBuffer );

		//assume that the length retrieved is valid
		buffLen = inBuff.GetDataBlockLength( uiIndex );
		
		// Skip past the record length
		pData	+= 4;
		offset	= 0; //buffLen does not include first four bytes

		// Do record check, verify that offset is not past end of buffer, etc.
		if (2 + offset > buffLen)  throw( (sInt32)eDSInvalidBuffFormat );
		
		// Get the length for the record type
		::memcpy( &usTypeLen, pData, 2 );
		
		pData	+= 2;
		offset	+= 2;

		pRecType = pData;
		
		pData	+= usTypeLen;
		offset	+= usTypeLen;

		// Do record check, verify that offset is not past end of buffer, etc.
		if (2 + offset > buffLen)  throw( (sInt32)eDSInvalidBuffFormat );
		
		// Get the length for the record name
		::memcpy( &usNameLen, pData, 2 );
		
		pData	+= 2;
		offset	+= 2;

		pRecName = pData;
		
		pData	+= usNameLen;
		offset	+= usNameLen;

		// Do record check, verify that offset is not past end of buffer, etc.
		if (2 + offset > buffLen)  throw( (sInt32)eDSInvalidBuffFormat );
		
		// Get the attribute count
		::memcpy( &usAttrCnt, pData, 2 );

		pRecEntry = (tRecordEntry *)::calloc( 1, sizeof( tRecordEntry ) + usNameLen + usTypeLen + 4 + kBuffPad );

		pRecEntry->fRecordNameAndType.fBufferSize	= usNameLen + usTypeLen + 4 + kBuffPad;
		pRecEntry->fRecordNameAndType.fBufferLength	= usNameLen + usTypeLen + 4;

		// Add the record name length
		::memcpy( pRecEntry->fRecordNameAndType.fBufferData, &usNameLen, 2 );
		uiOffset += 2;

		// Add the record name
		::memcpy( pRecEntry->fRecordNameAndType.fBufferData + uiOffset, pRecName, usNameLen );
		uiOffset += usNameLen;

		// Add the record type length
		::memcpy( pRecEntry->fRecordNameAndType.fBufferData + uiOffset, &usTypeLen, 2 );

		// Add the record type
		uiOffset += 2;
		::memcpy( pRecEntry->fRecordNameAndType.fBufferData + uiOffset, pRecType, usTypeLen );

		pRecEntry->fRecordAttributeCount = usAttrCnt;

		pContext = MakeContextData();
		if ( pContext  == nil ) throw( (sInt32)eMemoryAllocError );

		pContext->offset = uberOffset + offset + 4;	// context used by next calls of GetAttributeEntry
													// include the four bytes of the buffLen
		
		gConfigNodeRef->AddItem( inData->fOutAttrListRef, pContext );

		inData->fOutRecEntryPtr = pRecEntry;
	}

	catch( sInt32 err )
	{
		siResult = err;
	}

	return( siResult );

} // GetRecordEntry


//------------------------------------------------------------------------------------
//	* GetAttributeEntry
//------------------------------------------------------------------------------------

sInt32 CConfigurePlugin::GetAttributeEntry ( sGetAttributeEntry *inData )
{
	sInt32					siResult			= eDSNoErr;
	uInt16					usAttrTypeLen		= 0;
	uInt16					usAttrCnt			= 0;
	uInt32					usAttrLen			= 0;
	uInt16					usValueCnt			= 0;
	uInt32					usValueLen			= 0;
	uInt32					i					= 0;
	uInt32					uiIndex				= 0;
	uInt32					uiAttrEntrySize		= 0;
	uInt32					uiOffset			= 0;
	uInt32					uiTotalValueSize	= 0;
	uInt32					offset				= 0;
	uInt32					buffSize			= 0;
	uInt32					buffLen				= 0;
	char				   *p			   		= nil;
	char				   *pAttrType	   		= nil;
	tDataBuffer			   *pDataBuff			= nil;
	tAttributeValueListRef	attrValueListRef	= 0;
	tAttributeEntryPtr		pAttribInfo			= nil;
	sConfigContextData		   *pAttrContext		= nil;
	sConfigContextData		   *pValueContext		= nil;

	try
	{
		if ( inData  == nil ) throw( (sInt32)eMemoryError );

		pAttrContext = (sConfigContextData *)gConfigNodeRef->GetItemData( inData->fInAttrListRef );
		if ( pAttrContext  == nil ) throw( (sInt32)eDSBadContextData );

		uiIndex = inData->fInAttrInfoIndex;
		if (uiIndex == 0) throw( (sInt32)eDSInvalidIndex );
		
		pDataBuff = inData->fInOutDataBuff;
		if ( pDataBuff  == nil ) throw( (sInt32)eDSNullDataBuff );
		
		buffSize	= pDataBuff->fBufferSize;
		//buffLen		= pDataBuff->fBufferLength;
		//here we can't use fBufferLength for the buffLen SINCE the buffer is packed at the END of the data block
		//and the fBufferLength is the overall length of the data for all blocks at the end of the data block
		//the value ALSO includes the bookkeeping data at the start of the data block
		//so we need to read it here

		p		= pDataBuff->fBufferData + pAttrContext->offset;
		offset	= pAttrContext->offset;

		// Do record check, verify that offset is not past end of buffer, etc.
		if (2 + offset > buffSize)  throw( (sInt32)eDSInvalidBuffFormat );
		
		// Get the attribute count
		::memcpy( &usAttrCnt, p, 2 );
		if (uiIndex > usAttrCnt) throw( (sInt32)eDSInvalidIndex );

		// Move 2 bytes
		p		+= 2;
		offset	+= 2;

		// Skip to the attribute that we want
		for ( i = 1; i < uiIndex; i++ )
		{
			// Do record check, verify that offset is not past end of buffer, etc.
			if (4 + offset > buffSize)  throw( (sInt32)eDSInvalidBuffFormat );
		
			// Get the length for the attribute
			::memcpy( &usAttrLen, p, 4 );

			// Move the offset past the length word and the length of the data
			p		+= 4 + usAttrLen;
			offset	+= 4 + usAttrLen;
		}

		// Get the attribute offset
		uiOffset = offset;

		// Do record check, verify that offset is not past end of buffer, etc.
		if (4 + offset > buffSize)  throw( (sInt32)eDSInvalidBuffFormat );
		
		// Get the length for the attribute block
		::memcpy( &usAttrLen, p, 4 );

		// Skip past the attribute length
		p		+= 4;
		offset	+= 4;

		//set the bufLen to stricter range
		buffLen = offset + usAttrLen;

		// Do record check, verify that offset is not past end of buffer, etc.
		if (2 + offset > buffLen)  throw( (sInt32)eDSInvalidBuffFormat );
		
		// Get the length for the attribute type
		::memcpy( &usAttrTypeLen, p, 2 );
		
		pAttrType = p + 2;
		p		+= 2 + usAttrTypeLen;
		offset	+= 2 + usAttrTypeLen;
		
		// Do record check, verify that offset is not past end of buffer, etc.
		if (2 + offset > buffLen)  throw( (sInt32)eDSInvalidBuffFormat );
		
		// Get number of values for this attribute
		::memcpy( &usValueCnt, p, 2 );
		
		p		+= 2;
		offset	+= 2;
		
		for ( i = 0; i < usValueCnt; i++ )
		{
			// Do record check, verify that offset is not past end of buffer, etc.
			if (4 + offset > buffLen)  throw( (sInt32)eDSInvalidBuffFormat );
		
			// Get the length for the value
			::memcpy( &usValueLen, p, 2 );
			
			p		+= 4 + usValueLen;
			offset	+= 4 + usValueLen;
			
			uiTotalValueSize += usValueLen;
		}

		uiAttrEntrySize = sizeof( tAttributeEntry ) + usAttrTypeLen + kBuffPad;
		pAttribInfo = (tAttributeEntry *)::calloc( 1, uiAttrEntrySize );

		pAttribInfo->fAttributeValueCount				= usValueCnt;
		pAttribInfo->fAttributeDataSize					= uiTotalValueSize;
		pAttribInfo->fAttributeValueMaxSize				= 512;				// KW this is not used anywhere
		pAttribInfo->fAttributeSignature.fBufferSize	= usAttrTypeLen + kBuffPad;
		pAttribInfo->fAttributeSignature.fBufferLength	= usAttrTypeLen;
		::memcpy( pAttribInfo->fAttributeSignature.fBufferData, pAttrType, usAttrTypeLen );

		attrValueListRef = inData->fOutAttrValueListRef;

		pValueContext = MakeContextData();
		if ( pValueContext  == nil ) throw( (sInt32)eMemoryAllocError );

		pValueContext->offset = uiOffset;

		gConfigNodeRef->AddItem( inData->fOutAttrValueListRef, pValueContext );

		inData->fOutAttrInfoPtr = pAttribInfo;
	}

	catch( sInt32 err )
	{
		siResult = err;
	}

	return( siResult );

} // GetAttributeEntry


//------------------------------------------------------------------------------------
//	* GetAttributeValue
//------------------------------------------------------------------------------------

sInt32 CConfigurePlugin::GetAttributeValue ( sGetAttributeValue *inData )
{
	sInt32						siResult		= eDSNoErr;
	uInt16						usValueCnt		= 0;
	uInt32						usValueLen		= 0;
	uInt16						usAttrNameLen	= 0;
	uInt32						i				= 0;
	uInt32						uiIndex			= 0;
	uInt32						offset			= 0;
	char					   *p				= nil;
	tDataBuffer				   *pDataBuff		= nil;
	tAttributeValueEntry	   *pAttrValue		= nil;
	sConfigContextData			   *pValueContext	= nil;
	uInt32						buffSize		= 0;
	uInt32						buffLen			= 0;
	uInt32						attrLen			= 0;

	try
	{
		pValueContext = (sConfigContextData *)gConfigNodeRef->GetItemData( inData->fInAttrValueListRef );
		if ( pValueContext  == nil ) throw( (sInt32)eDSBadContextData );

		uiIndex = inData->fInAttrValueIndex;
		if (uiIndex == 0) throw( (sInt32)eDSInvalidIndex );

		pDataBuff = inData->fInOutDataBuff;
		if ( pDataBuff  == nil ) throw( (sInt32)eDSNullDataBuff );

		buffSize	= pDataBuff->fBufferSize;
		//buffLen		= pDataBuff->fBufferLength;
		//here we can't use fBufferLength for the buffLen SINCE the buffer is packed at the END of the data block
		//and the fBufferLength is the overall length of the data for all blocks at the end of the data block
		//the value ALSO includes the bookkeeping data at the start of the data block
		//so we need to read it here

		p		= pDataBuff->fBufferData + pValueContext->offset;
		offset	= pValueContext->offset;

		// Do record check, verify that offset is not past end of buffer, etc.
		if (4 + offset > buffSize)  throw( (sInt32)eDSInvalidBuffFormat );
				
		// Get the buffer length
		::memcpy( &attrLen, p, 4 );

		//now add the offset to the attr length for the value of buffLen to be used to check for buffer overruns
		//AND add the length of the buffer length var as stored ie. 4 bytes
		buffLen		= attrLen + pValueContext->offset + 4;
		if (buffLen > buffSize)  throw( (sInt32)eDSInvalidBuffFormat );

		// Skip past the attribute length
		p		+= 4;
		offset	+= 4;

		// Do record check, verify that offset is not past end of buffer, etc.
		if (2 + offset > buffLen)  throw( (sInt32)eDSInvalidBuffFormat );
		
		// Get the attribute name length
		::memcpy( &usAttrNameLen, p, 2 );
		
		p		+= 2 + usAttrNameLen;
		offset	+= 2 + usAttrNameLen;

		// Do record check, verify that offset is not past end of buffer, etc.
		if (2 + offset > buffLen)  throw( (sInt32)eDSInvalidBuffFormat );
		
		// Get the value count
		::memcpy( &usValueCnt, p, 2 );
		
		p		+= 2;
		offset	+= 2;

		if (uiIndex > usValueCnt)  throw( (sInt32)eDSInvalidIndex );

		// Skip to the value that we want
		for ( i = 1; i < uiIndex; i++ )
		{
			// Do record check, verify that offset is not past end of buffer, etc.
			if (4 + offset > buffLen)  throw( (sInt32)eDSInvalidBuffFormat );
		
			// Get the length for the value
			::memcpy( &usValueLen, p, 4 );
			
			p		+= 4 + usValueLen;
			offset	+= 4 + usValueLen;
		}

		// Do record check, verify that offset is not past end of buffer, etc.
		if (4 + offset > buffLen)  throw( (sInt32)eDSInvalidBuffFormat );
		
		::memcpy( &usValueLen, p, 4 );
		
		p		+= 4;
		offset	+= 4;

		//if (usValueLen == 0)  throw( (sInt32)eDSInvalidBuffFormat ); //if zero is it okay?

		pAttrValue = (tAttributeValueEntry *)::calloc( 1, sizeof( tAttributeValueEntry ) + usValueLen + kBuffPad );

		pAttrValue->fAttributeValueData.fBufferSize		= usValueLen + kBuffPad;
		pAttrValue->fAttributeValueData.fBufferLength	= usValueLen;
		
		// Do record check, verify that offset is not past end of buffer, etc.
		if ( usValueLen + offset > buffLen ) throw( (sInt32)eDSInvalidBuffFormat );
		
		::memcpy( pAttrValue->fAttributeValueData.fBufferData, p, usValueLen );

		// Set the attribute value ID
		pAttrValue->fAttributeValueID = 0x00;

		inData->fOutAttrValue = pAttrValue;
	}

	catch( sInt32 err )
	{
		siResult = err;
	}

	return( siResult );

} // GetAttributeValue
	

// ---------------------------------------------------------------------------
//	* MakeContextData
// ---------------------------------------------------------------------------

sConfigContextData* CConfigurePlugin::MakeContextData ( void )
{
	sConfigContextData	*pOut	= nil;

	pOut = new sConfigContextData;
	if ( pOut != nil )
	{
		::memset( pOut, 0, sizeof( sConfigContextData ) );
	}

	return( pOut );

} // MakeContextData

//------------------------------------------------------------------------------------
//      * DoPlugInCustomCall
//------------------------------------------------------------------------------------

sInt32 CConfigurePlugin::DoPlugInCustomCall ( sDoPlugInCustomCall *inData )
{
	sInt32						siResult	= eDSNoErr;
	unsigned long				aRequest	= 0;
	uInt32						pluginIndex	= 0;
	CPlugInList::sTableData    *pPIInfo		= nil;
	CServerPlugin			   *plugInPtr	= nil;
	uInt32						thePIState	= 0;
	unsigned long				bufLen		= 0;
	AuthorizationRef			authRef		= 0;
	AuthorizationItemSet	   *resultRightSet = NULL;
	sConfigContextData		   *pContext	= nil;

	try
	{
		pContext = (sConfigContextData *)gConfigNodeRef->GetItemData( inData->fInNodeRef );
		if ( pContext == nil ) throw( (sInt32)eDSInvalidNodeRef );
		if ( inData == nil ) throw( (sInt32)eDSNullParameter );
		if ( inData->fInRequestData == nil ) throw( (sInt32)eDSNullDataBuff );
		if ( inData->fOutRequestResponse == nil ) throw( (sInt32)eDSNullDataBuff );
		if ( inData->fInRequestData->fBufferData == nil ) throw( (sInt32)eDSEmptyBuffer );

		aRequest = inData->fInRequestCode;
		AuthorizationItem rights[] = { {"system.services.directory.configure", 0, 0, 0} };
		AuthorizationItemSet rightSet = { sizeof(rights)/ sizeof(*rights), rights };
		bufLen = inData->fInRequestData->fBufferLength;
		
		if ( aRequest == 111 )
		{
			// we need to get an authref set up in this case
			// support for Directory Setup over proxy
			uInt32 userNameLength = 0;
			char* userName = NULL;
			uInt32 passwordLength = 0;
			char* password = NULL;
			char* current = inData->fInRequestData->fBufferData;
			uInt32 offset = 0;
			if ( bufLen < 2 * sizeof( uInt32 ) + 1 ) throw( (sInt32)eDSInvalidBuffFormat );
			
			memcpy( &userNameLength, current, sizeof( uInt32 ) );
			current += sizeof( uInt32 );
			offset += sizeof( uInt32 );
			if ( bufLen - offset < userNameLength ) throw( (sInt32)eDSInvalidBuffFormat );
			
			userName = current; //don't free this
			current += userNameLength;
			offset += userNameLength;
			if ( bufLen - offset < sizeof( uInt32 ) ) throw( (sInt32)eDSInvalidBuffFormat );
			
			memcpy( &passwordLength, current, sizeof( uInt32 ) );
			current += sizeof( uInt32 );
			offset += sizeof( uInt32 );
			if ( passwordLength == 0 )
			{
				password = "";
			}
			else
			{
				if ( bufLen - offset < passwordLength ) throw( (sInt32)eDSInvalidBuffFormat );
				password = current;
			}
			
			AuthorizationItem params[] = { {"username", userNameLength, (void*)userName, 0}, {"password", passwordLength, (void*)password, 0} };
			AuthorizationEnvironment environment = { sizeof(params)/ sizeof(*params), params };
			
			siResult = AuthorizationCreate( &rightSet, &environment, kAuthorizationFlagExtendRights, &authRef);
			if (siResult != errAuthorizationSuccess)
			{
				throw( (sInt32)eDSPermissionError );
			}
			if ( inData->fOutRequestResponse->fBufferSize < sizeof( AuthorizationExternalForm ) ) throw( (sInt32)eDSInvalidBuffFormat );
			siResult = AuthorizationMakeExternalForm(authRef, (AuthorizationExternalForm*)inData->fOutRequestResponse->fBufferData);
			if (siResult != errAuthorizationSuccess)
			{
				throw( (sInt32)eDSPermissionError );
			}
			// should we free this authRef? probably not since it will be coming back to us
			inData->fOutRequestResponse->fBufferLength = sizeof( AuthorizationExternalForm );
			siResult = eDSNoErr;
			authRef = 0;
		}
		else if (aRequest == 222)
		{
			// version check, no AuthRef required
			uInt32 versLength = strlen( "1" );
			char* current = inData->fOutRequestResponse->fBufferData;
			inData->fOutRequestResponse->fBufferLength = 0;
			if ( inData->fOutRequestResponse->fBufferSize < sizeof(versLength) + versLength ) throw( (sInt32)eDSInvalidBuffFormat );
			memcpy(current, &versLength, sizeof(versLength));
			current += sizeof(versLength);
			inData->fOutRequestResponse->fBufferLength += sizeof(versLength);
			memcpy(current, "1", versLength);
			current += versLength;
			inData->fOutRequestResponse->fBufferLength += versLength;
		}
		else if (aRequest == 444 || aRequest == 445)
		{
			// read SystemConfiguration key, no authref required
			// for Remote Directory Setup
			uInt32 keyLength = bufLen;
			CFStringRef key = NULL;
			CFPropertyListRef dict = NULL;
			CFDataRef xmlData = NULL;
			char* current = inData->fInRequestData->fBufferData;

			key = CFStringCreateWithBytes(NULL, (UInt8*)current, keyLength, kCFStringEncodingUTF8,
					false);

			if (pContext->session == 0)
			{
				// first call, need to set up the SystemConfiguration session
				pContext->session = SCPreferencesCreate( NULL, CFSTR("DSConfigurePlugIn"), NULL );
			}
			
			dict = SCPreferencesPathGetValue( pContext->session, key );
			xmlData = CFPropertyListCreateXMLData( NULL, dict );
			
			if (xmlData != 0)
			{
				CFRange	aRange;
				aRange.location = 0;
				aRange.length = CFDataGetLength(xmlData);
				if (aRequest == 444)
				{
					if ( inData->fOutRequestResponse->fBufferSize < sizeof(CFIndex) ) throw( (sInt32)eDSBufferTooSmall );
					memcpy(inData->fOutRequestResponse->fBufferData,&aRange.length,sizeof(CFIndex));
				}
				else
				{
					if ( inData->fOutRequestResponse->fBufferSize < (uInt32)aRange.length ) throw( (sInt32)eDSBufferTooSmall );
					CFDataGetBytes( xmlData, aRange, 
								(UInt8*)(inData->fOutRequestResponse->fBufferData) );
					inData->fOutRequestResponse->fBufferLength = aRange.length;
				}
				CFRelease(xmlData);
				xmlData = 0;
			}
			
			if (key != NULL)
			{
				CFRelease(key);
				key = NULL;
			}
		}
		else if (aRequest == 446 || aRequest == 447)
		{
			// read SystemConfiguration key, no authref required
			// for Remote Directory Setup
			uInt32 keyLength = bufLen;
			CFStringRef key = NULL;
			CFStringRef stringValue = NULL;
			char* current = inData->fInRequestData->fBufferData;

			key = CFStringCreateWithBytes(NULL, (UInt8*)current, keyLength, kCFStringEncodingUTF8,
								 false);

			if (pContext->session == 0)
			{
				// first call, need to set up the SystemConfiguration session
				pContext->session = SCPreferencesCreate( NULL, CFSTR("DSConfigurePlugIn"), NULL );
			}

			stringValue = (CFStringRef)SCPreferencesGetValue( pContext->session, key );

			if (stringValue != 0)
			{
				CFRange	aRange;
				aRange.location = 0;
				aRange.length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(stringValue),
													  kCFStringEncodingUTF8);
				if (aRequest == 446)
				{
					if ( inData->fOutRequestResponse->fBufferSize < sizeof(CFIndex) ) throw( (sInt32)eDSBufferTooSmall );
					memcpy(inData->fOutRequestResponse->fBufferData,&aRange.length,sizeof(CFIndex));
				}
				else
				{
					if ( inData->fOutRequestResponse->fBufferSize < (uInt32)aRange.length ) throw( (sInt32)eDSBufferTooSmall );
					CFStringGetCString(stringValue, inData->fOutRequestResponse->fBufferData, inData->fOutRequestResponse->fBufferSize, kCFStringEncodingUTF8);
					inData->fOutRequestResponse->fBufferLength = aRange.length;
				}
			}

			if (key != NULL)
			{
				CFRelease(key);
				key = NULL;
			}
		}
		else
		{
			if ( bufLen < sizeof( AuthorizationExternalForm ) ) throw( (sInt32)eDSInvalidBuffFormat );
			siResult = AuthorizationCreateFromExternalForm((AuthorizationExternalForm *)inData->fInRequestData->fBufferData,
				&authRef);
			if (siResult != errAuthorizationSuccess)
			{
				throw( (sInt32)eDSPermissionError );
			}
	
			siResult = AuthorizationCopyRights(authRef, &rightSet, NULL,
				kAuthorizationFlagExtendRights, &resultRightSet);
			if (resultRightSet != NULL)
			{
				AuthorizationFreeItemSet(resultRightSet);
				resultRightSet = NULL;
			}
			if (siResult != errAuthorizationSuccess)
			{
				throw( (sInt32)eDSPermissionError );
			}
		}
        //request to toggle the active versus inactive state of a plugin comes in with the plugin table index plus 1000
        //index could be zero
        if (aRequest > 999)
        {
			//might want to pass in the plugin name within the buffer and check it instead of using an offset from the 1000 value
            pluginIndex = aRequest - 1000;
            if (pluginIndex < CPlugInList::kMaxPlugIns)
            {
				CPluginConfig		pluginConfig;
				pluginConfig.Initialize();

                pPIInfo = gPlugins->GetPlugInInfo( pluginIndex );
                if (pPIInfo->fState & kActive)
                {
                    plugInPtr = gPlugins->GetPlugInPtr( pPIInfo->fName );
                    if ( plugInPtr )
                    {
                        thePIState = pPIInfo->fState;
                        thePIState += (uInt32)kInactive;
                        thePIState -= (uInt32)kActive;
                        gPlugins->SetState( pPIInfo->fName, thePIState );
                        plugInPtr->SetPluginState( thePIState );

						pluginConfig.SetPluginState( pPIInfo->fName, kInactive);
						pluginConfig.SaveConfigData();

						SRVRLOG1( kLogApplication, "Plug-in %s state is now set inactive.", pPIInfo->fName );
                    }
                }
                else if (pPIInfo->fState & kInactive)
                {
                    plugInPtr = gPlugins->GetPlugInPtr( pPIInfo->fName );
                    if ( plugInPtr )
                    {
                        thePIState = pPIInfo->fState;
                        thePIState -= kInactive;
                        thePIState += kActive;
                        gPlugins->SetState( pPIInfo->fName, thePIState );
                        plugInPtr->SetPluginState( thePIState );

						pluginConfig.SetPluginState( pPIInfo->fName, kActive);
						pluginConfig.SaveConfigData();

						SRVRLOG1( kLogApplication, "Plug-in %s state is now set active.", pPIInfo->fName );
                    }
                }
            }
        }
		else if (aRequest == 333)
		{
			// destroy the auth ref
			if (authRef != 0)
			{
				AuthorizationFree(authRef, kAuthorizationFlagDestroyRights);
				authRef = 0;
			}			
		}
		else if (aRequest == 555)
		{
			// write SystemConfiguration
			// for Remote Directory Setup
			bool			success		= false;
			sInt32			xmlDataLength	= 0;
			CFDataRef   	xmlData			= nil;
			CFPropertyListRef propList		= nil;

			//here we accept an XML blob to replace the "/Sets" configuration
			//need to make xmlData large enough to receive the data
			xmlDataLength = (sInt32) bufLen - sizeof( AuthorizationExternalForm );
			if ( xmlDataLength <= 0 ) throw( (sInt32)eDSInvalidBuffFormat );
			
			xmlData = CFDataCreate(NULL,(UInt8 *)(inData->fInRequestData->fBufferData + sizeof( AuthorizationExternalForm )),xmlDataLength);
			if ( xmlData == nil ) throw( (sInt32)eMemoryError );
			propList = CFPropertyListCreateFromXMLData(NULL,xmlData,0,NULL);
			if ( propList == nil ) throw( (sInt32)eMemoryError );
		
			// we're going to assume this is intended to replace the Sets
			if (pContext->session == 0)
			{
				// first call, need to set up the SystemConfiguration session
				pContext->session = SCPreferencesCreate( NULL, CFSTR("DSNetInfoPlugIn"), NULL );
			}
			if (pContext->session != 0)
			{
				CFStringRef key = SCDynamicStoreKeyCreate( NULL, CFSTR("/%@"), kSCPrefSets );
				success = SCPreferencesPathSetValue(pContext->session, key, 
								(CFDictionaryRef)propList);
				if (success)
					success &= SCPreferencesCommitChanges(pContext->session);
				if (success)
					success &= SCPreferencesApplyChanges(pContext->session);
				if (!success)
					siResult = eDSOperationFailed;
				if (key != NULL)
					CFRelease(key);
			}
			CFRelease(propList);
			propList = nil;
			CFRelease(xmlData);
			xmlData = nil;
		}
		else if (aRequest == 777)
		{
			//toggle whether TCP Listener is active or not
			struct stat		statResult;

			siResult = ::stat( "/Library/Preferences/DirectoryService/.DSTCPListening", &statResult );
            if (siResult != eDSNoErr)
			{
				siResult = ::stat( "/Library/Preferences", &statResult );
				//if first sub directory does not exist
				if (siResult != eDSNoErr)
				{
					::mkdir( "/Library/Preferences", 0775 );
					::chmod( "/Library/Preferences", 0775 ); //above 0775 doesn't seem to work - looks like umask modifies it
				}
				siResult = ::stat( "/Library/Preferences/DirectoryService", &statResult );
				//if second sub directory does not exist
				if (siResult != eDSNoErr)
				{
					::mkdir( "/Library/Preferences/DirectoryService", 0775 );
					::chmod( "/Library/Preferences/DirectoryService", 0775 ); //above 0775 doesn't seem to work - looks like umask modifies it
				}
				system( "touch /Library/Preferences/DirectoryService/.DSTCPListening" );
				gSrvrCntl->StartTCPListener(kDSDefaultListenPort);
			}
			else
			{
				system( "rm -f /Library/Preferences/DirectoryService/.DSTCPListening" );
				gSrvrCntl->StopTCPListener();
			}
		}
		
    } // try

    catch( sInt32 err )
    {
        siResult = err;
    }
	
	if (authRef != 0)
	{
		AuthorizationFree(authRef, 0);
		authRef = 0;
	}

	return( siResult );

} // DoPlugInCustomCall

//------------------------------------------------------------------------------------
//	* CloseAttributeList
//------------------------------------------------------------------------------------

sInt32 CConfigurePlugin::CloseAttributeList ( sCloseAttributeList *inData )
{
	sInt32				siResult		= eDSNoErr;
	sConfigContextData	   *pContext		= nil;

	pContext = (sConfigContextData *) gConfigNodeRef->GetItemData( inData->fInAttributeListRef );
	if ( pContext != nil )
	{
		//only "offset" should have been used in the Context
		gConfigNodeRef->RemoveItem( inData->fInAttributeListRef );
	}
	else
	{
		siResult = eDSInvalidAttrListRef;
	}

	return( siResult );

} // CloseAttributeList


//------------------------------------------------------------------------------------
//	* CloseAttributeValueList
//------------------------------------------------------------------------------------

sInt32 CConfigurePlugin::CloseAttributeValueList ( sCloseAttributeValueList *inData )
{
	sInt32				siResult		= eDSNoErr;
	sConfigContextData	   *pContext		= nil;

	pContext = (sConfigContextData *) gConfigNodeRef->GetItemData( inData->fInAttributeValueListRef );
	if ( pContext != nil )
	{
		//only "offset" should have been used in the Context
		gConfigNodeRef->RemoveItem( inData->fInAttributeValueListRef );
	}
	else
	{
		siResult = eDSInvalidAttrValueRef;
	}

	return( siResult );

} // CloseAttributeValueList


// ---------------------------------------------------------------------------
//	* ContinueDeallocProc
// ---------------------------------------------------------------------------

void CConfigurePlugin::ContinueDeallocProc ( void* inContinueData )
{
	sConfigContinueData* pContinue = (sConfigContinueData *)inContinueData;

	if ( pContinue != nil )
	{
		free( pContinue );
		pContinue = nil;
	}
} // ContinueDeallocProc


// ---------------------------------------------------------------------------
//	* ContextDeallocProc
// ---------------------------------------------------------------------------

void CConfigurePlugin::ContextDeallocProc ( void* inContextData )
{
	sConfigContextData *pContext = (sConfigContextData *) inContextData;

	if ( pContext != nil )
	{
		if ( pContext->session != NULL )
		{
			CFRelease( pContext->session );
			pContext->session = NULL;
		}
		free( pContext );
		pContext = nil;
	}
} // ContextDeallocProc