/* Copyright (c) 2017, Computer History Museum 
All rights reserved. 
Redistribution and use in source and binary forms, with or without modification, are permitted (subject to 
the limitations in the disclaimer below) provided that the following conditions are met: 
 * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 
 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 
   disclaimer in the documentation and/or other materials provided with the distribution. 
 * Neither the name of Computer History Museum nor the names of its contributors may be used to endorse or promote products 
   derived from this software without specific prior written permission. 
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE 
COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 
DAMAGE. */

#include "ldap.h"
#include "lber.h"
#include "dflutils.h"
#include "dflsuppl.h"
#include "ldaplibglue.h"
#include "ldaputils.h"

#define FILE_NUM 107
#pragma segment LDAPUtils


/* MJN *//* new file */



#pragma mark defines

#define ASYNC_LDAP_CALLS 1

#define LDAP_TEXT_ACCUM_CHUNK_SIZE 1024
#define LDAP_ATTR_LIST_GROW_SIZE 10

#define LDAP_ATTR_TRANSLATION_TABLE_ID 32767
#define LDAP_SERVER_LIST_ID 32767


#pragma mark enum constants

enum {
	kLDAPOpenPreflightMemSize = 65536, /* 64K */
	kLDAPSearchPreflightMemSize = 32768 /* 32K */
};
enum {
	kLDAPErrTableRsrcType = 'LECT',
	kLDAPErrTableRsrcID = 128
};
enum {
	kMonitorLDAPCodeGrowMinSize = 131072 /* 128K */
};
enum {
	kLDAPUserCancelCheckDelay = 15 /* in ticks */
};


#pragma mark typedefs

/* WARNING *//* the numbering of these items must match the ordering of items in the LDAP Atrribute lookup table resource */
typedef enum {
	invalidAttr = 0,
	commonNameAttr,
	commonNameAttr2,
	cityAttr,
	stateAttr,
	organizationAttr,
	organizationalUnitAttr,
	countryAttr,
	streetAttr,
	emailAttr,
	emailAlt1Attr,
	emailAlt2Attr,
	lastNameAttr,
	givenNameAttr,
	webLookupURLAttr,
	telephoneAttr
} LDAPAttrType;

typedef struct {
	Handle	dataHdl;
	long		physicalSize;
	long		logicalSize;
} LDAPTextAccumRec, *LDAPTextAccumPtr, **LDAPTextAccumHdl;

/* defined as struct timeval in ":ldap-3.3:libraries:macintosh:tcp:tcp.h";
		the definition in "utime.mac.h" in the Metrowerks Standard Library headers is incompatible */
typedef struct LDAPTimeValRec {
	long	tv_sec;		/* seconds */
	long	tv_usec;	/* and microseconds */
} LDAPTimeValRec, *LDAPTimeValPtr;


#pragma mark globals

static Boolean LDAPCodeLoaded = false;
static Boolean LDAPCodePurgeable = false;
static Boolean LDAPCanUseDFL = false;
static OSErr LDAPCanUseDFLErr = noErr;
static OSErr LDAPLibLoadErr = noErr;

static Str255 LDAPResultCountStr;
static Str255 LDAPResultErrStr;
static Str255 LDAPUserCanceledMsgStr;
Str255 LDAPDividerStr;
static long MinLDAPLabelLen;


/* FINISH *//* does this need to be here? */
#if GENERATING68KSEGLOAD
#pragma mpwc on
#endif

#ifdef LDAP_ENABLED
static void AppendPStr(Str255 targetStr, Str255 appendStr)

{
	long		len;


	len = appendStr[0];
	if (targetStr[0] + len > 255)
		len = 255 - targetStr[0];
	BlockMoveData(appendStr + 1, targetStr + targetStr[0] + 1, len);
	targetStr[0] += len;
}
#endif  //ifdef LDAP_ENABLED


#ifdef LDAP_ENABLED
static void AppendCStr(Str255 targetStr, char* appendStr)

{
	long		len;
	char*		c;


	len = 0;
	c = appendStr;
	while (*c++)
		len++;
	if (len > 255)
		len = 255;
	if (targetStr[0] + len > 255)
		len = 255 - targetStr[0];
	BlockMoveData(appendStr, targetStr + targetStr[0] + 1, len);
	targetStr[0] += len;
}
#endif  //ifdef LDAP_ENABLED


#ifdef LDAP_ENABLED
static OSErr NewLDAPTextAccum(LDAPTextAccumHdl *textAccum)

{
	LDAPTextAccumHdl	accumHdl;
	LDAPTextAccumPtr	accumPtr;
	Handle						h;


	*textAccum = nil;

	accumHdl = (LDAPTextAccumHdl)NewHandle(sizeof(LDAPTextAccumRec));
	if (!accumHdl)
		return MemError();
	h = NewHandle(LDAP_TEXT_ACCUM_CHUNK_SIZE);
	if (!h)
	{
		DisposeHandle((Handle)accumHdl);
		return MemError();
	}

	accumPtr = *accumHdl;
	accumPtr->dataHdl = h;
	accumPtr->physicalSize = LDAP_TEXT_ACCUM_CHUNK_SIZE;
	accumPtr->logicalSize = 0;

	*textAccum = accumHdl;
	return noErr;
}
#endif  //ifdef LDAP_ENABLED


#ifdef LDAP_ENABLED
static void DisposeLDAPTextAccum(LDAPTextAccumHdl textAccum)

{
	if (textAccum)
	{
		if ((**textAccum).dataHdl) DisposeHandle((**textAccum).dataHdl);
		DisposeHandle((Handle)textAccum);
	}
}
#endif  //ifdef LDAP_ENABLED


#ifdef LDAP_ENABLED
static OSErr CompactLDAPTextAccum(LDAPTextAccumHdl textAccum)

{
	LDAPTextAccumPtr	accumPtr;


	accumPtr = *textAccum;
	SetHandleSize(accumPtr->dataHdl, accumPtr->logicalSize);
	return MemError(); /* no error expected */
}
#endif  //ifdef LDAP_ENABLED


#ifdef LDAP_ENABLED
static OSErr AppendLDAPTextAccum(LDAPTextAccumHdl textAccum, unsigned char* dataPtr, long length)

{
	OSErr							err;
	LDAPTextAccumPtr	accumPtr;
	long							neededSize, newPhysicalSize;
	Handle						dataHdl;


	accumPtr = *textAccum;
	neededSize = accumPtr->logicalSize + length;
	if (neededSize > accumPtr->physicalSize)
	{
		newPhysicalSize = accumPtr->physicalSize;
		while (newPhysicalSize < neededSize)
			newPhysicalSize += LDAP_TEXT_ACCUM_CHUNK_SIZE;
		SetHandleSize(accumPtr->dataHdl, newPhysicalSize);
		err = MemError();
		if (err)
			return err;
		(**textAccum).physicalSize = newPhysicalSize;
	}
	accumPtr = *textAccum;
	dataHdl = accumPtr->dataHdl;
	BlockMoveData(dataPtr, *dataHdl + accumPtr->logicalSize, length);
	accumPtr->logicalSize = neededSize;

	return noErr;
}
#endif  //ifdef LDAP_ENABLED


#ifdef LDAP_ENABLED
static OSErr AppendPStrLDAPTextAccum(LDAPTextAccumHdl textAccum, Str255 theString)

{
	return AppendLDAPTextAccum(textAccum, theString + 1, theString[0]);
}
#endif  //ifdef LDAP_ENABLED


#ifdef LDAP_ENABLED
static OSErr AppendCStrLDAPTextAccum(LDAPTextAccumHdl textAccum, char* theString)

{
	long		len;
	char*		c;


	len = 0;
	c = theString;
	while (*c++)
		len++;
	return AppendLDAPTextAccum(textAccum, (unsigned char*)theString, len);
}
#endif  //ifdef LDAP_ENABLED


#ifdef LDAP_ENABLED
static OSErr AppendCRLDAPTextAccum(LDAPTextAccumHdl textAccum)

{
	unsigned char		c;


	c = 13;
	return AppendLDAPTextAccum(textAccum, &c, 1);
}
#endif  //ifdef LDAP_ENABLED


#ifdef LDAP_ENABLED
static OSErr AppendDividerLDAPTextAccum(LDAPTextAccumHdl textAccum)

{
	OSErr						err;


	err = AppendPStrLDAPTextAccum(textAccum, LDAPDividerStr);
	if (!err)
		err = AppendCRLDAPTextAccum(textAccum);
	return err;
}
#endif  //ifdef LDAP_ENABLED


#ifdef LDAP_ENABLED
static OSErr AppendNumLDAPTextAccum(LDAPTextAccumHdl textAccum, long theNum)

{
	Str255	s;


	NumToString(theNum, s);
	return AppendPStrLDAPTextAccum(textAccum, s);
}
#endif  //ifdef LDAP_ENABLED


#ifdef LDAP_ENABLED
static OSErr LDAPTextAccumToHandle(LDAPTextAccumHdl textAccum, Handle *dataHdl)

{
	OSErr		err;


	*dataHdl = nil;

	err = CompactLDAPTextAccum(textAccum); /* no error expected */
	if (!err)
	{
		*dataHdl = (**textAccum).dataHdl;
		(**textAccum).dataHdl = nil;
	}
	DisposeLDAPTextAccum(textAccum);
	return err;
}
#endif  //ifdef LDAP_ENABLED


#ifdef LDAP_ENABLED
RawLDAPAttrList DerefLDAPAttrList(LDAPAttrListHdl attributesList)

{
	if (!attributesList)
		return nil;
	HLock((**attributesList).attrList);
	return (RawLDAPAttrList)(*(**attributesList).attrList);
}
#endif  //ifdef LDAP_ENABLED


#ifdef LDAP_ENABLED
void UnderefLDAPAttrList(LDAPAttrListHdl attributesList)

{
	if (!attributesList)
		return;
	HUnlock((**attributesList).attrList);
}
#endif  //ifdef LDAP_ENABLED


#ifdef LDAP_ENABLED
OSErr NewLDAPAttrList(LDAPAttrListHdl *attributesList)

{
	OSErr							err;
	LDAPAttrListHdl		attrs;
	Handle						attrData;


	*attributesList = nil;

	attrs = (LDAPAttrListHdl)NewHandle(sizeof(LDAPAttrListRec));
	if (!attrs)
		return MemError();
	attrData = NewHandleClear(sizeof(char*));
	if (!attrData)
	{
		err = MemError();
		DisposeHandle((Handle)attrs);
		return err;
	}

	(**attrs).numLogicalAttrs = 0;
	(**attrs).numPhysicalAttrs = 0;
	(**attrs).attrList = attrData;

	*attributesList = attrs;
	return noErr;
}
#endif  //ifdef LDAP_ENABLED


#ifdef LDAP_ENABLED
void DisposeLDAPAttrList(LDAPAttrListHdl attributesList)

{
	RawLDAPAttrList	rawList;
	long						numAttr;
	long						i;


	if (!attributesList)
		return;

	rawList = DerefLDAPAttrList(attributesList);
	numAttr = (**attributesList).numLogicalAttrs;
	for (i = 0; i < numAttr; i++)
	{
		DisposePtr(rawList[i]);
		rawList[i] = nil;
	}
	UnderefLDAPAttrList(attributesList);
	DisposeHandle((**attributesList).attrList);
	DisposeHandle((Handle)attributesList);
}
#endif  //ifdef LDAP_ENABLED


#ifdef LDAP_ENABLED
static OSErr GrowLDAPAttrList(LDAPAttrListHdl attributesList, long growSize)

{
	OSErr	err;
	long	newCount;
	long	curSize, newSize;


	if (!attributesList)
		return paramErr;

	if (!growSize)
		return noErr;

	curSize = GetHandleSize((**attributesList).attrList);
	newCount = (**attributesList).numPhysicalAttrs + growSize;
	newSize = sizeof(char*) * (newCount + 1);
	SetHandleSize((**attributesList).attrList, newSize);
	err = MemError();
	if (!err)
		(**attributesList).numPhysicalAttrs = newCount;
	return err;
}
#endif  //ifdef LDAP_ENABLED


#ifdef LDAP_ENABLED
OSErr AppendLDAPAttrList(LDAPAttrListHdl attributesList, Str255 attrName)

{
	OSErr						err;
	long						newLogCount;
	RawLDAPAttrList	rawList;
	Ptr							p;


	if (!attributesList)
		return paramErr;

	newLogCount = (**attributesList).numLogicalAttrs + 1;
	if (newLogCount > (**attributesList).numPhysicalAttrs)
	{
		err = GrowLDAPAttrList(attributesList, LDAP_ATTR_LIST_GROW_SIZE);
		if (err)
			return err;
	}
	(**attributesList).numLogicalAttrs = newLogCount;

	p = NewPtr(attrName[0] + 1);
	if (!p)
		return MemError();
	BlockMoveData(attrName + 1, p, attrName[0]);
	p[attrName[0]] = 0;
	rawList = DerefLDAPAttrList(attributesList);
	rawList[newLogCount - 1] = p;
	rawList[newLogCount] = nil;
	UnderefLDAPAttrList(attributesList);

	return noErr;
}
#endif  //ifdef LDAP_ENABLED


#ifdef LDAP_ENABLED
OSErr CompactLDAPAttrList(LDAPAttrListHdl attributesList)

{
	OSErr	err;
	long	curSize, newSize;


	if (!attributesList)
		return paramErr;

	curSize = GetHandleSize((**attributesList).attrList);
	newSize = sizeof(char*) * ((**attributesList).numLogicalAttrs + 1);
	if (newSize == curSize)
		return noErr;
	if (newSize > curSize)
		return paramErr;
	SetHandleSize((**attributesList).attrList, newSize);
	err = MemError();
	if (!err)
		(**attributesList).numPhysicalAttrs = (**attributesList).numLogicalAttrs;
	return err;
}
#endif  //ifdef LDAP_ENABLED


#ifdef LDAP_ENABLED
static LDAPAttrType LDAPAttrNameToIndex(char* attrName)

{
	long			len;
	char*			c;
	Str255		attrNamePStr;
	Handle		attrTableHdl;
	long			numAttrNames;
	long			i;
	Boolean		found;
	Str255		attrCode;


	len = 0;
	c = attrName;
	while (*c++)
		len++;
	if (len > 255)
		len = 255;
	BlockMoveData(attrName, attrNamePStr + 1, len);
	attrNamePStr[0] = len;

	attrTableHdl = GetResource('STR#', LDAP_ATTR_TRANSLATION_TABLE_ID);
	if (!attrTableHdl || (GetHandleSize(attrTableHdl) < 2))
		return invalidAttr;

	numAttrNames = (**(short**)attrTableHdl);
	i = 0;
	while (i <= numAttrNames)
	{
		GetIndString(attrCode, LDAP_ATTR_TRANSLATION_TABLE_ID, i + 1);
		found = EqualString(attrNamePStr, attrCode, false, false);
		if (found)
			return (i / 2) + 1;
		i += 2;
	}

	return invalidAttr;
}
#endif  //ifdef LDAP_ENABLED


#ifdef LDAP_ENABLED
static Boolean LDAPAttrIsEmailAddr(char* attrName)

{
	LDAPAttrType	attrType;


	attrType = LDAPAttrNameToIndex(attrName);
	switch (attrType)
	{
		case emailAttr:
		case emailAlt1Attr:
		case emailAlt2Attr:
			return true;
			break;
	}
	return false;
}
#endif  //ifdef LDAP_ENABLED


#ifdef LDAP_ENABLED
static void LDAPAttrNameToLabel(char* attrName, Str255 label)

{
	long			len;
	char*			c;
	Handle		attrTableHdl;
	long			numAttrNames;
	long			i;
	Boolean		found;
	Str255		attrCode;
	Str255		attrLabel;
	long			adjustSize;


	len = 0;
	c = attrName;
	while (*c++)
		len++;
	if (len > 255)
		len = 255;
	BlockMoveData(attrName, label + 1, len);
	label[0] = len;

	attrTableHdl = GetResource('STR#', LDAP_ATTR_TRANSLATION_TABLE_ID);
	if (!attrTableHdl || (GetHandleSize(attrTableHdl) < 2))
		goto Align;
	numAttrNames = (**(short**)attrTableHdl);
	i = 0;
	while (i <= numAttrNames)
	{
		GetIndString(attrCode, LDAP_ATTR_TRANSLATION_TABLE_ID, i + 1);
		found = EqualString(label, attrCode, false, true);
		if (found)
		{
			GetIndString(attrLabel, LDAP_ATTR_TRANSLATION_TABLE_ID, i + 2);
			BlockMoveData(attrLabel, label, attrLabel[0] + 1);
			goto Align;
		}
		i += 2;
	}


Align:
	if (!label[0] || (label[0] >= MinLDAPLabelLen))
		return;
	adjustSize = MinLDAPLabelLen - label[0];
	BlockMoveData(label + 1, label + 1 + adjustSize, label[0]);
	for (i = 0, c = (char*)label + 1; i < adjustSize; i++, c++)
		*c = ' ';
	label[0] = MinLDAPLabelLen;
}
#endif  //ifdef LDAP_ENABLED


OSErr LDAPResultsToText(LDAPSessionHdl ldapSession, Handle *resultText, LDAPAttrListHdl attrList, Boolean translateLabels, LDAPResultsEntryFilterProcPtr entryFilter)

{
#ifdef LDAP_ENABLED
	OSErr 		err, scratchErr;
	Str255		errMsgStr;
	LDAP*			ldapRef;
	LDAPMessage*			searchResults;
	LDAPTextAccumHdl	textAccum;
	LDAPMessage*			curMsg;
/* FINISH *//* REMOVE */
#if 0
	char*			entryName;
#endif
	char*			attr;
	void*			dataPtr;
	char*			*vals;
	long			i;
	long			entryCount;
	Str255		attrLabel;
	long			numEntries;
	char*			tempCharPtr = nil; /* FINISH *//* REMOVE */
	Str255		userNameStr;
	Boolean		needUserNameStr;
	Boolean		foundUserNameStr;
	Str255		emailAddressStr;
	Boolean		needEmailAddressStr;
	Boolean		foundEmailAddressStr;
	long			startOffset;
	long			endOffset;
	short			a;


	*resultText = nil;

	if (!LDAPCodeLoaded)
		return kDFLLibCodeNotLoadedErr;

	ldapRef = (**ldapSession).ldapRef;
	searchResults = (**ldapSession).searchResults;

	err = NewLDAPTextAccum(&textAccum);
	if (err)
		return err;

	if ((**ldapSession).searchErr == LDAP_USER_CANCELLED)
	{
		err = AppendPStrLDAPTextAccum(textAccum, LDAPUserCanceledMsgStr);
		if (!err)
			err = AppendCRLDAPTextAccum(textAccum);

		if (err)
			goto Error;
		err = LDAPTextAccumToHandle(textAccum, resultText);
		return err;
	}
	
#ifdef DEBUG
	if (RunType!=Production)
	{
		AppendPStrLDAPTextAccum(textAccum,PCopy(userNameStr,(**ldapSession).searchFilter));
		AppendPStrLDAPTextAccum(textAccum,Cr);
	}
#endif

	if (!(**ldapSession).searchResultsValid || (**ldapSession).searchErr)
	{
		err = AppendPStrLDAPTextAccum(textAccum, LDAPResultErrStr);
		if (!err)
		{
			scratchErr = LDAPErrCodeToMsgStr((**ldapSession).searchErr, errMsgStr, true);
			if (!scratchErr)
				err = AppendPStrLDAPTextAccum(textAccum, errMsgStr);
			else
				err = AppendNumLDAPTextAccum(textAccum, (**ldapSession).searchErr);
		}
		if (!err)
			err = AppendCRLDAPTextAccum(textAccum);

		/* FINISH *//* use ldapRef->ld_error */
		if (tempCharPtr /* ldapRef->ld_error */)
		{
			if (!err)
				err = AppendCStrLDAPTextAccum(textAccum, tempCharPtr /* ldapRef->ld_error */);
			if (!err)
				err = AppendCRLDAPTextAccum(textAccum);
		}

		if (err)
			goto Error;
		err = LDAPTextAccumToHandle(textAccum, resultText);
		return err;
	}

	numEntries = ldap_count_entries(ldapRef, searchResults);
	err = AppendPStrLDAPTextAccum(textAccum, LDAPResultCountStr);
	if (!err)
		err = AppendNumLDAPTextAccum(textAccum, numEntries);
	if (!err)
		err = AppendCRLDAPTextAccum(textAccum);

	if (!numEntries)
	{
		err = LDAPTextAccumToHandle(textAccum, resultText);
		return err;
	}
	else PhRememberServer();

	for (curMsg = ldap_first_entry(ldapRef, searchResults), entryCount = 1; curMsg != nil; curMsg = ldap_next_entry(ldapRef, curMsg), entryCount++)
	{
		CycleBalls();
		if (CommandPeriod) break;
		err = AppendDividerLDAPTextAccum(textAccum);
		if (err)
			goto Error;

		startOffset = (**textAccum).logicalSize;
		if (entryFilter)
		{
			userNameStr[0] = 0;
			needUserNameStr = true;
			foundUserNameStr = false;
			emailAddressStr[0] = 0;
			needEmailAddressStr = true;
			foundEmailAddressStr = false;
		}

/* FINISH *//* REMOVE */
#if 0
		entryName = ldap_get_dn(ldapRef, curMsg);
		err = AppendPStrLDAPTextAccum(textAccum, "\pname = ");
		if (!err)
			err = AppendCStrLDAPTextAccum(textAccum, entryName);
		if (!err)
			err = AppendCRLDAPTextAccum(textAccum);
		if (err)
			goto Error;
		// free(entryName);
#endif

		if (attrList)
			for (a=0;a<(*attrList)->numLogicalAttrs;a++)
			{
				attr = ((char**)(*(*attrList)->attrList))[a];
				vals = ldap_get_values(ldapRef, curMsg, attr);
				if (vals && vals[0])
				{
					
					if (needUserNameStr)
					{
						int attrIndex = LDAPAttrNameToIndex(attr);
						foundUserNameStr =  attrIndex == commonNameAttr || attrIndex == commonNameAttr2;
					}
					if (needEmailAddressStr)
						foundEmailAddressStr = LDAPAttrIsEmailAddr(attr);

					if (translateLabels)
					{
						LDAPAttrNameToLabel(attr, attrLabel);
						err = AppendPStrLDAPTextAccum(textAccum, attrLabel);
					}
					else
						err = AppendCStrLDAPTextAccum(textAccum, attr);
					if (!err)
						err = AppendPStrLDAPTextAccum(textAccum, "\p: ");
					if (err)
						goto Error;

					err = noErr;
					for (i = 0; !err && (vals[i] != nil); i++)
					{
						if (needUserNameStr && foundUserNameStr)
						{
							needUserNameStr = false;
							AppendCStr(userNameStr, vals[i]);
						}
						if (needEmailAddressStr && foundEmailAddressStr)
						{
							needEmailAddressStr = false;
							AppendCStr(emailAddressStr, vals[i]);
						}

						/* FINISH *//* instead, just put each value on its own line, and repeat the label */
						if (i > 0)
							err = AppendPStrLDAPTextAccum(textAccum, "\p | ");
						if (!err)
							err = AppendCStrLDAPTextAccum(textAccum, vals[i]);
					}
					ldap_value_free(vals);
					err = AppendCRLDAPTextAccum(textAccum);
					if (err)
						goto Error;
				}
			}
		else
			for (attr = ldap_first_attribute(ldapRef, curMsg, &dataPtr); attr != nil; attr = ldap_next_attribute(ldapRef, curMsg, dataPtr))
			{
				if (needUserNameStr)
				{
					int attrIndex = LDAPAttrNameToIndex(attr);
					foundUserNameStr =  attrIndex == commonNameAttr || attrIndex == commonNameAttr2;
				}
				if (needEmailAddressStr)
					foundEmailAddressStr = LDAPAttrIsEmailAddr(attr);

				if (translateLabels)
				{
					LDAPAttrNameToLabel(attr, attrLabel);
					err = AppendPStrLDAPTextAccum(textAccum, attrLabel);
				}
				else
					err = AppendCStrLDAPTextAccum(textAccum, attr);
				if (!err)
					err = AppendPStrLDAPTextAccum(textAccum, "\p: ");
				if (err)
					goto Error;

				vals = ldap_get_values(ldapRef, curMsg, attr);
				err = noErr;
				for (i = 0; !err && (vals[i] != nil); i++)
				{
					if (needUserNameStr && foundUserNameStr)
					{
						needUserNameStr = false;
						AppendCStr(userNameStr, vals[i]);
					}
					if (needEmailAddressStr && foundEmailAddressStr)
					{
						needEmailAddressStr = false;
						AppendCStr(emailAddressStr, vals[i]);
					}

					/* FINISH *//* instead, just put each value on its own line, and repeat the label */
					if (i > 0)
						err = AppendPStrLDAPTextAccum(textAccum, "\p | ");
					if (!err)
						err = AppendCStrLDAPTextAccum(textAccum, vals[i]);
				}
				ldap_value_free(vals);
				err = AppendCRLDAPTextAccum(textAccum);
				if (err)
					goto Error;
			}


		endOffset = (**textAccum).logicalSize;

		if (entryFilter)
			(*entryFilter)(ldapSession, userNameStr, emailAddressStr, startOffset, endOffset);
	}
	err = AppendDividerLDAPTextAccum(textAccum);
	if (err)
		goto Error;

	err = LDAPTextAccumToHandle(textAccum, resultText);
	return err;


Error:
	DisposeLDAPTextAccum(textAccum);
	return err;

#else  //ifdef LDAP_ENABLED
	*resultText = nil;
	return paramErr;
#endif  //ifdef LDAP_ENABLED
}


OSErr ClearLDAPSearchResults(LDAPSessionHdl ldapSession)

{
	if (!LDAPCodeLoaded)
		return kDFLLibCodeNotLoadedErr;
#ifdef LDAP_ENABLED
	ldap_msgfree((**ldapSession).searchResults);
#endif  //ifdef LDAP_ENABLED
	(**ldapSession).searchErr = noErr;
	(**ldapSession).searchResults = nil;
	(**ldapSession).searchResultsValid = false;
	return noErr;
}

#if ASYNC_LDAP_CALLS
static int LDAPWaitForCompletion(LDAPSessionHdl ldapSession, LDAPMessage* *resultData)

{
	int				result;
	LDAP*			ldapRef;
	int				opMsgID;
	LDAPMessage*			localResultData;
	LDAPTimeValRec		timeoutVal;
	long			lastCancelCheckTicks;
	long			curTicks;
	Boolean		done;


	if (resultData)
		*resultData = nil;
	localResultData = nil;

	if (!LDAPCodeLoaded)
		return kDFLLibCodeNotLoadedErr;

	if (!(**ldapSession).opActive)
		return LDAP_SUCCESS;

	ldapRef = (**ldapSession).ldapRef;
	opMsgID = (**ldapSession).opMsgID;

	if (!ldapRef)
		return LDAP_PARAM_ERROR;
	if (opMsgID == -1)
		return LDAP_PARAM_ERROR;

	timeoutVal.tv_sec = 0;
	timeoutVal.tv_usec = 0;
	lastCancelCheckTicks = 0;
	done = false;
	while (!done)
	{
		curTicks = TickCount();
		if ((curTicks - lastCancelCheckTicks) > kLDAPUserCancelCheckDelay)
		{
			lastCancelCheckTicks = curTicks;
			if (MiniEvents())
			{
				result = ldap_abandon(ldapRef, opMsgID);
				if (!result)
				{
					done = true;
					(**ldapSession).opActive = false;
					if (localResultData)
						result = ldap_msgfree(localResultData);
					return LDAP_USER_CANCELLED;
				}
			}
		}

		result = ldap_result(ldapRef, opMsgID, 1, (struct timeval*)&timeoutVal, &localResultData);
		if (result)
		{
			done = true;
			break;
		}
	}

	(**ldapSession).opActive = false;

	if (!result) /* should never occur */
	{
		if (localResultData)
			result = ldap_msgfree(localResultData);
		return LDAP_TIMEOUT;
	}

	if (result > 0)
	{
		if (resultData)
		{
			*resultData = localResultData;
			return ldap_result2error(ldapRef, localResultData, 0);
		}
		else
			return ldap_result2error(ldapRef, localResultData, 1);
	}

	return ldap_result2error(ldapRef, localResultData, 1);
}
#endif  // ASYNC_LDAP_CALLS

OSErr BuildLDAPSearchFilter(PStr searchStr,PStr searchFilter,PStr forHost);
OSErr AddToLDAPSearchFilter(PStr termWord,PStr searchFilter, Byte op, PStr template);
PStr GetLDAPTemplate(PStr template,short id,PStr forHost);

OSErr LDAPSearch(LDAPSessionHdl ldapSession, Str255 searchStr, PStr forHost, Boolean useRawSearchStr, short searchScope, Str255 searchBaseObject, LDAPAttrListHdl attributesList)

{
#ifdef LDAP_ENABLED
	OSErr					err;
	int						result;
#if ASYNC_LDAP_CALLS
	int						opMsgID;
#endif
	LDAPMessage*	searchResults;
	Str255				searchFilter;
	Str255				baseObjectDN;
	RawLDAPAttrList		rawAttrList;
	Ptr						p;


	if (!LDAPCodeLoaded)
		return kDFLLibCodeNotLoadedErr;

	if ((**ldapSession).searchResultsValid)
	{
		err = ClearLDAPSearchResults(ldapSession);
		if (err)
			return err;
	}

	if (!searchStr[0])
		return noErr;

	p = NewPtr(kLDAPSearchPreflightMemSize);
	if (!p)
		return memFullErr;
	DisposePtr(p);

	if (useRawSearchStr)
		BlockMoveData(searchStr, searchFilter, searchStr[0] + 1);
	else
	{
		if (searchStr[0] > 250)
			return paramErr;
		BuildLDAPSearchFilter(searchStr,searchFilter,forHost);
	}
#ifdef DEBUG
	PCopy((**ldapSession).searchFilter,searchFilter);
#endif
	p2cstr(searchFilter);

	if (searchBaseObject)
	{
		BlockMoveData(searchBaseObject, baseObjectDN, searchBaseObject[0] + 1);
		p2cstr(baseObjectDN);
	}
	else
		baseObjectDN[0] = 0;

	if (attributesList)
		rawAttrList = DerefLDAPAttrList(attributesList);
	else
		rawAttrList = nil;

	/* FINISH *//* set time and size limits */
#if ASYNC_LDAP_CALLS
	opMsgID = ldap_search((**ldapSession).ldapRef, baseObjectDN, searchScope, (const char*)searchFilter, rawAttrList, 0);
	(**ldapSession).opActive = true;
	(**ldapSession).opMsgID = opMsgID;
	result = LDAPWaitForCompletion(ldapSession, &searchResults);
#else
	result = ldap_search_s((**ldapSession).ldapRef, baseObjectDN, searchScope, (const char*)searchFilter, rawAttrList, 0, &searchResults);
#endif

	if (attributesList)
		UnderefLDAPAttrList(attributesList);

	(**ldapSession).searchErr = result;
	(**ldapSession).searchResults = searchResults;
	if (searchResults)
		(**ldapSession).searchResultsValid = true;

	return result;

#else  //ifdef LDAP_ENABLED
	return paramErr;
#endif  //ifdef LDAP_ENABLED
}

/************************************************************************
 * BuildLDAPSearchFilter - build the search filter
 ************************************************************************/
OSErr BuildLDAPSearchFilter(PStr searchStr,PStr searchFilter,PStr forHost)
{
	Str255 termWord;
	Str255 template;
	Str63 delims;
	UPtr spot;
	short terms=0;
	
	*searchFilter = 0;
	GetRString(delims,LDAP_SEARCH_TOKEN_DELIMS);
	spot = searchStr+1;
	
	GetLDAPTemplate(template,LDAP_SEARCH_FILTER,forHost);
	
	if (*template)
		// add words to the filter one at a time
		while (PToken(searchStr,termWord,&spot,delims+1))
		{
			terms++;
			AddToLDAPSearchFilter(termWord,searchFilter,'&',template);
		}
	
	// if more than one term or no wordwise search, add whole list, too
	if (!*template || terms>1) AddToLDAPSearchFilter(searchStr,searchFilter,'|',GetLDAPTemplate(template,LDAP_CN_SEARCH_FILTER,forHost));
	
	return(noErr);
}

/************************************************************************
 * AddToLDAPSearchFilter - add a single word to the search filter
 ************************************************************************/
OSErr AddToLDAPSearchFilter(PStr termWord,PStr searchFilter, Byte op, PStr template)
{
	UPtr spot, end;
	short roomLeft = 250 - *searchFilter;
	short maxWordLen;
	short combLen;
	Str255 termFilter;
	Str255 localSearchFilter;
	short numSub=0;
	
	// figure out how much room the combining will take
	combLen = *searchFilter ? *GetRString(localSearchFilter,LDAP_TERM_COMBINER) - 5	// take out the two %p's and one char from %c
														: 0;	// no combining needed

	// count the substitutions in the term template
	if (!*template) return(noErr); // if the template is empty, forget it
	end = template+*template;
	for (spot=template+1;spot<end;spot++)
		if (spot[0]=='^' && spot[1]=='0') numSub++;
	
	// Subtract the fixed stuff
	roomLeft -= combLen + *template - numSub*2;	// add in 2 for each ^0
	
	// calculate the max word that would fit
	maxWordLen = roomLeft/numSub;
	
	if (maxWordLen<2) return(errAEIllegalIndex);	// forget it; doesn't fit
	
	// do we need to trim the word?
	if (*termWord > maxWordLen)
	{
		*termWord = maxWordLen;
		termWord[maxWordLen] = '*';	//becomes a substring match
	}
	
	// Ok, that was fun.  Now we build the term
	utl_PlugParams(template,termFilter,termWord,nil,nil,nil);
	
	// Replace whitespace with *
	end = termFilter+*termFilter;
	for (spot=termFilter+1;spot<end;spot++) if (IsWhite(*spot)) *spot = '*';
	
	// comb the term for ** and reduce to *
	for (spot=termFilter+1;spot<end;spot++)
		if (spot[0]=='*' && spot[1]=='*')
		{
			BMD(spot+1,spot,end-spot);
			end--;
			spot--; // rescan to catch triples, etc
			--*termFilter;
		}
	
	// do we need to combine?
	if (!*searchFilter)
		PCopy(searchFilter,termFilter);	// nope; just copy
	else
	{
		ComposeRString(localSearchFilter,LDAP_TERM_COMBINER,op,termFilter,searchFilter);
		PCopy(searchFilter,localSearchFilter);
	}
	
	// we win
	return(noErr);
}

/************************************************************************
 * GetLDAPTemplate - get a template for an LDAP search
 ************************************************************************/
PStr GetLDAPTemplate(PStr template,short id,PStr forHost)
{
	Handle h;
	OSType type = id==LDAP_SEARCH_FILTER ? LDAP_FILTER_TYPE : LDAP_CN_FILTER_TYPE;
	
	if (h=GetNamedResource(type,forHost))
		PCopy(template,*h);
	else
		GetRString(template,id);
	return(template);
}
	

OSErr OpenLDAPSession(LDAPSessionHdl ldapSession, Str255 ldapServer, int ldapPortNo)

{
#ifdef LDAP_ENABLED
	LDAP*		ldapRef;
	int			result;
#if ASYNC_LDAP_CALLS
	int			opMsgID;
#endif
	Str255	ldapServer_CStr;
	Ptr			p;
	short		oldRes;


	if (!LDAPCodeLoaded)
		return kDFLLibCodeNotLoadedErr;
	if ((**ldapSession).sessionOpen)
		return paramErr;

	p = NewPtr(kLDAPOpenPreflightMemSize);
	if (!p)
		return memFullErr;
	DisposePtr(p);

	BlockMoveData(ldapServer, ldapServer_CStr, ldapServer[0] + 1);
	p2cstr(ldapServer_CStr);
	oldRes = CurResFile();
	ldapRef = ldap_open((const char*)ldapServer_CStr, ldapPortNo);
	UseResFile(oldRes);
	if (!ldapRef)
		return LDAP_SERVER_DOWN; /* FINISH *//* any way to get a real result code here? */

	(**ldapSession).ldapRef = ldapRef;

#if ASYNC_LDAP_CALLS
	opMsgID = ldap_simple_bind(ldapRef, nil, nil);
	(**ldapSession).opActive = true;
	(**ldapSession).opMsgID = opMsgID;
	result = LDAPWaitForCompletion(ldapSession, nil);
#else
	result = ldap_simple_bind_s(ldapRef, nil, nil);
#endif
	if (result != LDAP_SUCCESS)
		return result; /* FINISH *//* probably leaks memory from ldap_open */

	(**ldapSession).sessionOpen = true;
	return noErr;

#else  //ifdef LDAP_ENABLED
	return paramErr;
#endif  //ifdef LDAP_ENABLED
}


OSErr CloseLDAPSession(LDAPSessionHdl ldapSession)

{
#ifdef LDAP_ENABLED
	OSErr		err;
	int			result;


	if (!LDAPCodeLoaded)
		return kDFLLibCodeNotLoadedErr;
	if (!(**ldapSession).sessionOpen)
		return noErr;

	if ((**ldapSession).searchResultsValid)
	{
		err = ClearLDAPSearchResults(ldapSession);
		if (err)
			return err;
	}

	result = ldap_unbind((**ldapSession).ldapRef);
	if (result != LDAP_SUCCESS)
		return result;

	(**ldapSession).sessionOpen = false;
	(**ldapSession).ldapRef = nil;
	return noErr;

#else  //ifdef LDAP_ENABLED
	return paramErr;
#endif  //ifdef LDAP_ENABLED
}


OSErr NewLDAPSession(LDAPSessionHdl *ldapSession)

{
	LDAPSessionHdl	sessionHdl;


	sessionHdl = (LDAPSessionHdl)NewHandleClear(sizeof(LDAPSessionRec));
	if (!sessionHdl)
		return MemError();
	(**sessionHdl).sessionOpen = false;
	(**sessionHdl).opActive = false;
	(**sessionHdl).searchResultsValid = false;
	*ldapSession = sessionHdl;
	return noErr;
}


OSErr DisposeLDAPSession(LDAPSessionHdl ldapSession)

{
	OSErr		err;


	if ((**ldapSession).sessionOpen)
	{
		err = CloseLDAPSession(ldapSession);
		if (err)
			return err;
	}
	DisposeHandle((Handle)ldapSession);
	return noErr;
}


OSErr GetLDAPServerList(Handle *serverList)

{
	OSErr			err;
	Handle		serverListRsrc;
	long			dataSize;
	Handle		h;


	*serverList = nil;

	serverListRsrc = GetResource('TEXT', LDAP_SERVER_LIST_ID);
	err = ResError();
	if (!serverListRsrc && !err)
		err = resNotFound;
	if (err)
		return err;
	HNoPurge(serverListRsrc);

	dataSize = GetHandleSize(serverListRsrc);
	h = NewHandle(dataSize);
	if (!h)
	{
		HPurge(serverListRsrc);
		return MemError();
	}
	BlockMoveData(*serverListRsrc, *h, dataSize);
	HPurge(serverListRsrc);

	*serverList = h;
	return noErr;
}


static OSErr GetLDAPAttrsFromStr(Str255 attrListStr, LDAPAttrListHdl *attributesList)

{
	OSErr							err;
	LDAPAttrListHdl		attrs;
	UPtr							curSpot;
	Str255						curAttrStr;


	*attributesList = nil;

	err = NewLDAPAttrList(&attrs);
	if (err)
		return err;

	curSpot = attrListStr + 1;

	while (PToken(attrListStr, curAttrStr, &curSpot, ","))
		if (curAttrStr[0])
		{
			err = AppendLDAPAttrList(attrs, curAttrStr);
			if (err)
			{
				DisposeLDAPAttrList(attrs);
				return err;
			}
		}

	err = CompactLDAPAttrList(attrs);
	if (err)
	{
		DisposeLDAPAttrList(attrs);
		return err;
	}

	*attributesList = attrs;
	return noErr;
}


static short SearchScopeStrToVal(Str255 searchScopeStr)

{
	Str255		curTestStr;


	GetRString(curTestStr, LDAP_SCOPE_BASE_STR);
	if (EqualString(searchScopeStr, curTestStr, false, true))
		return LDAP_SCOPE_BASE;
	GetRString(curTestStr, LDAP_SCOPE_ONELEVEL_STR);
	if (EqualString(searchScopeStr, curTestStr, false, true))
		return LDAP_SCOPE_ONELEVEL;
	GetRString(curTestStr, LDAP_SCOPE_SUBTREE_STR);
	if (EqualString(searchScopeStr, curTestStr, false, true))
		return LDAP_SCOPE_SUBTREE;
	return LDAP_SCOPE_SUBTREE;	// spec says default is base, I say screw spec.  SD
}


/* FINISH *//* REMOVE */
static Boolean LDAPURLQueryContainsSearchFilter(Str255 urlQueryStr)

{
	Str255						localQueryStr;
	Str255						curURLPart;
	UPtr							curSpot;


	if (!urlQueryStr)
		return false;
	if (!*urlQueryStr)
		return false;

	BlockMoveData(urlQueryStr, localQueryStr, urlQueryStr[0] + 1);
	FixURLString(localQueryStr);

	curSpot = localQueryStr + 1;

	if (!(PToken(localQueryStr, curURLPart, &curSpot, "?") && curURLPart[0]))
		return false;
	if (!(PToken(localQueryStr, curURLPart, &curSpot, "?") && curURLPart[0]))
		return false;
	if (!(PToken(localQueryStr, curURLPart, &curSpot, "?") && curURLPart[0]))
		return false;
	if (!(PToken(localQueryStr, curURLPart, &curSpot, "/") && curURLPart[0]))
		return false;

	return true;
}


OSErr ParseLDAPURLQuery(Str255 urlQueryStr, Str255 baseObjectDN, LDAPAttrListHdl *attributesList, short *searchScope, Str255 searchFilter)

{
	OSErr							err;
	Str255						localQueryStr;
	Str255						curURLPart;
	UPtr							curSpot;
	LDAPAttrListHdl		attrs;


	if (baseObjectDN)
		*baseObjectDN = 0;
	if (attributesList)
		*attributesList = nil;
	if (searchScope)
		*searchScope = LDAP_SCOPE_SUBTREE;	// spec says default is base, I say screw spec.  SD
	if (searchFilter)
		*searchFilter = 0;

	if (!urlQueryStr)
		return paramErr;
	if (!*urlQueryStr)
		return noErr;

	BlockMoveData(urlQueryStr, localQueryStr, urlQueryStr[0] + 1);
	FixURLString(localQueryStr);

	curSpot = localQueryStr + 1;

	if (PToken(localQueryStr, curURLPart, &curSpot, "?") && curURLPart[0])
		if (baseObjectDN)
			BlockMoveData(curURLPart, baseObjectDN, curURLPart[0] + 1);

	if (PToken(localQueryStr, curURLPart, &curSpot, "?") && curURLPart[0])
		if (attributesList)
		{
			err = GetLDAPAttrsFromStr(curURLPart, &attrs);
			if (err)
				return err;
			*attributesList = attrs;
		}

	if (PToken(localQueryStr, curURLPart, &curSpot, "?") && curURLPart[0])
		if (searchScope)
			*searchScope = SearchScopeStrToVal(curURLPart);

	if (PToken(localQueryStr, curURLPart, &curSpot, "/") && curURLPart[0])
		if (searchFilter)
			BlockMoveData(curURLPart, searchFilter, curURLPart[0] + 1);

	return noErr;
}


OSErr LDAPErrCodeToMsgStr(int errCode, Str255 errMsg, Boolean addErrNum)

{
	OSErr			err;
	Handle		errTableHdl;
	long			numEntries; /* minus one */
	long			curIndex;
	Boolean		found;
	Ptr				entryPtr;
	short*		entryCodePtr;
	StringPtr	entryMsgPtr;
	long			entryMsgLen;


	errMsg[0] = 0;
	if (!errCode)
		return noErr;

	if (addErrNum)
		NumToString(errCode, errMsg);

	errTableHdl = GetResource(kLDAPErrTableRsrcType, kLDAPErrTableRsrcID);
	err = ResError();
	if (!errTableHdl && !err)
		err = resNotFound;
	if (err == resNotFound)
		return noErr;
	if (err)
		return err;

	numEntries = **(short**)errTableHdl;
	if (numEntries < 0)
		return noErr;

	curIndex = 0;
	entryPtr = *errTableHdl + 2;
	found = false;
	while (curIndex < numEntries)
	{
		entryCodePtr = (short*)entryPtr;
		entryMsgPtr = (StringPtr)(entryPtr + 2);
		if (*entryCodePtr == errCode)
		{
			found = true;
			break;
		}
		curIndex++;
		entryPtr += 2 + *entryMsgPtr + 1;
		if ((long)entryPtr & 1)
			entryPtr++; /* make it word-aligned */
	}
	if (!found)
		return noErr;

	if (addErrNum)
	{
		errMsg[++errMsg[0]] = ' ';
		errMsg[++errMsg[0]] = ' ';
		entryMsgLen = *entryMsgPtr;
		if ((entryMsgLen + errMsg[0]) > 255)
			entryMsgLen = 255 - errMsg[0];
		BlockMoveData(entryMsgPtr + 1, errMsg + errMsg[0] + 1, entryMsgLen);
		errMsg[0] += entryMsgLen;
	}
	else
		BlockMoveData(entryMsgPtr, errMsg, *entryMsgPtr + 1);

	HPurge(errTableHdl);
	return noErr;
}


short GetLDAPSpecialErrMsgIndex(OSErr theError)

{
	switch (theError)
	{
		case kDFLNeedMixedModeMgrErr:
		case kDFLNeedCFMErr:
			return LDAP_NEEDS_CFM68K_MSG;
		case kDFLBadCFM68KVersErr:
			return LDAP_NEEDS_NEWER_CFM68K_MSG;
		case kDFLBadLibVersErr:
			return LDAP_LIB_VERS_BAD_MSG;
		case cfragNoLibraryErr:
			return LDAP_NOT_LOADED_MSG;
		case memFullErr:
			return LDAP_LIB_OPEN_MEM_ERR_MSG;
		default:
			return 0;
	}
}


/* FINISH *//* put this proc back in, but make it work for a 68K true-link to the .o lib file */
#if 0
static Boolean LDAPSharedLibPresent(void)

{
#ifdef LDAP_ENABLED
	OSErr					err, scratchErr;
	long					response;
	Str255				ldapLibName;
	Str255				errStr;
	ConnectionID	connID;
	Ptr						mainAddr;
	Ptr						symbolAddr;
	SymClass			symbolClass;


	if (Gestalt(gestaltCFMAttr, &response) || !(response & (1 << gestaltCFMPresent)))
		return false;
	if ((long)ldap_open == kUnresolvedCFragSymbolAddress)
		return false;
	GetRString(ldapLibName, LDAP_SHARED_LIB_TRUE_NAME);
	if (!ldapLibName[0])
		return false;
	err = GetSharedLibrary(ldapLibName, kCurrentCFragArch, kFindLib, &connID, &mainAddr, errStr);
	if (err)
		return false;
	err = FindSymbol(connID, "\pldap_open", &symbolAddr, &symbolClass);
	scratchErr = CloseConnection(&connID);
	if (err)
		return false;
	if ((long)symbolAddr == kUnresolvedCFragSymbolAddress)
		return false;
	return true;

#else  //ifdef LDAP_ENABLED
	return false;
#endif  //ifdef LDAP_ENABLED
}
#endif


OSErr LoadLDAPCode(void)

{
#ifdef LDAP_ENABLED
	OSErr		err;


	if (LDAPCodeLoaded)
		return noErr;
	if (PrefIsSet(PREF_LDAP_OVERRIDE_DFL_STUB))
		useDFLStubLib = false;
	err = LoadLDAPSharedLib();
	LDAPLibLoadErr = err;
	if (err)
		return err;
	LDAPCodeLoaded = true;
	LDAPCodePurgeable = false;
	return noErr;

#else  //ifdef LDAP_ENABLED
	return paramErr;
#endif  //ifdef LDAP_ENABLED
}


OSErr UnloadLDAPCode(void)

{
#ifdef LDAP_ENABLED

	if (!LDAPCodeLoaded)
		return noErr;

	LDAPCodePurgeable = true;

	return noErr;

#else  //ifdef LDAP_ENABLED
	return paramErr;
#endif  //ifdef LDAP_ENABLED
}


#pragma segment Ph

#if GENERATING68KSEGLOAD
static void UnloadLDAPUtilsSeg(void)

{
	UnloadSeg(LoadLDAPCode);
}
#endif


/* WARNING: It is probably not be safe to call PurgeLDAPCode from a GrowZoneProc.
		This function will generate a call to CFM's CloseConnection routine, which may
		attempt to allocate or move memory, and may move, purge, or dispose GZSaveHnd(). */

#pragma segment Main

OSErr PurgeLDAPCode(void)

{
#ifdef LDAP_ENABLED

	OSErr		err;


	if (!LDAPCodeLoaded)
		return noErr;
	if (!LDAPCodePurgeable)
		return memPurErr;

	err = UnloadLDAPSharedLib();
	if (err)
		return err;

#if GENERATING68KSEGLOAD
	UnloadLDAPUtilsSeg();
#endif

	LDAPCodeLoaded = false;
	LDAPCodePurgeable = false;

	return noErr;

#else  //ifdef LDAP_ENABLED
	return paramErr;
#endif  //ifdef LDAP_ENABLED
}


#ifdef LDAP_ENABLED

#pragma segment Main

static long MonitorLDAPCodeGrow_FreeMem(void)

{
	long	maxContigAfterCompact;
	long	maxContigAfterPurge;
	long	scratch;


	maxContigAfterCompact = MaxBlock();
	PurgeSpace(&scratch, &maxContigAfterPurge);
	return (maxContigAfterCompact > maxContigAfterPurge) ? maxContigAfterCompact : maxContigAfterPurge;
}
#endif  //ifdef LDAP_ENABLED


#pragma segment Main

long MonitorLDAPCodeGrow(Boolean forcePurge)

{
#ifdef LDAP_ENABLED
	OSErr						err;
	unsigned long		origFree, newFree;


	if (!LDAPCodeLoaded)
		return 0;
	if (!LDAPCodePurgeable)
		return 0;

	if (!forcePurge && (MonitorLDAPCodeGrow_FreeMem() >= kMonitorLDAPCodeGrowMinSize))
		return 0;

	origFree = FreeMem();
	err = PurgeLDAPCode();
	if (err)
		return 0;
	newFree = FreeMem();
	return newFree - origFree;
#else  //ifdef LDAP_ENABLED
	return 0;
#endif  //ifdef LDAP_ENABLED
}


#pragma segment Ph

void RefreshLDAPSettings(void)

{
	if (!LDAPSupportPresent() && (GetCurDirSvcType() == ldapLookup))
		SetCurDirSvcType(phLookup);
}


#pragma segment Ph

Boolean LDAPSupportPresent(void)

{
#ifdef LDAP_ENABLED
	return LDAPCanUseDFL;
#else  //ifdef LDAP_ENABLED
	return false;
#endif  //ifdef LDAP_ENABLED
}


#pragma segment Ph

OSErr LDAPSupportError(void)

{
#ifdef LDAP_ENABLED
	return LDAPCanUseDFLErr;
#else  //ifdef LDAP_ENABLED
	return paramErr;
#endif  //ifdef LDAP_ENABLED
}


#pragma segment Ph

OSErr InitLDAP(void)

{
#ifdef LDAP_ENABLED

#ifndef LDAP_USE_STD_LINK_MECHANISM
	OSType	libArchType;
#endif


#ifndef LDAP_USE_STD_LINK_MECHANISM
	libArchType = GetROSType(LDAP_SHARED_LIB_ARCH_TYPE);
	if (!(long)libArchType)
		return paramErr;
	LDAPCanUseDFLErr = SystemSupportsDFRLibraries(libArchType);
	LDAPCanUseDFL = !LDAPCanUseDFLErr;
#else
	LDAPCanUseDFLErr = SystemSupportsDFRLibraries(kAnyCFragArch);
	LDAPCanUseDFL = !LDAPCanUseDFLErr;
#endif

	GetRString(LDAPResultCountStr, LDAP_RESULT_COUNT);
	GetRString(LDAPResultErrStr, LDAP_RESULT_ERR);
	GetRString(LDAPUserCanceledMsgStr, LDAP_SEARCH_USER_CANCELED_MSG);
	GetRString(LDAPDividerStr, LDAP_SEPARATOR);
	MinLDAPLabelLen = GetRLong(MIN_LDAP_LABEL_LEN);

	return noErr;

#else  //ifdef LDAP_ENABLED

	LDAPCanUseDFLErr = paramErr;
	LDAPCanUseDFL = false;
	return noErr;

#endif  //ifdef LDAP_ENABLED
}


#if GENERATING68KSEGLOAD
#pragma mpwc reset
#endif
