/*
	File     :	aod.c

	Contains :	Open directory routines for Apple Open Directory

	Copyright:	© 2003 by Apple Computer, Inc., all rights reserved.

	NOT_FOR_OPEN_SOURCE <to be reevaluated at a later time>

	Change History (most recent first):

	To Do:
*/


#include "aod.h"

#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include <syslog.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sysctl.h>

#include <CoreFoundation/CFData.h>
#include <CoreFoundation/CFString.h>
#include <CoreFoundation/CFNumber.h>
#include <CoreFoundation/CFPropertyList.h>

#include <DirectoryService/DirServices.h>
#include <DirectoryService/DirServicesUtils.h>
#include <DirectoryService/DirServicesConst.h>

/* -----------------------------------------------------------------
	Prototypes 
   ----------------------------------------------------------------- */

static tDirStatus	sOpen_ds			( tDirReference *inOutDirRef );
static tDirStatus	sGet_search_node	( tDirReference inDirRef, tDirNodeReference *outSearchNodeRef );
static tDirStatus	sLook_up_user		( tDirReference inDirRef, tDirNodeReference inSearchNodeRef, const char *inUserID, char **outUserLocation );
static tDirStatus	sGet_user_attributes( tDirReference inDirRef, tDirNodeReference inSearchNodeRef, const char *inUserID, struct od_user_opts *inOutOpts );
static tDirStatus	sOpen_user_node		( tDirReference inDirRef, const char *inUserLoc, tDirNodeReference *outUserNodeRef );
static int			sVerify_version		( CFDictionaryRef inCFDictRef );
static void			sGet_mail_values	( char *inMailAttribute, struct od_user_opts *inOutOpts );
static void			sGet_acct_state		( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts );
static void			sGet_auto_forward	( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts );
static void			sGet_IMAP_login		( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts );
static void			sGet_POP3_login		( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts );
static void			sGet_disk_quota		( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts );
static void			sGet_acct_loc		( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts );
static void			sGet_alt_loc		( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts );
static int			sDoCryptAuth		( tDirReference inDirRef, tDirNodeReference inUserNodeRef, const char *inUserID, const char *inPasswd );
static int			sValidateResponse	( const char *inUserID, const char *inChallenge, const char *inResponse, const char *inAuthType );
static void			sSetErrorText		( eErrorType inErrType, int inError, const char *inStr );

/* -----------------------------------------------------------------
	Globals
   ----------------------------------------------------------------- */

char	gErrStr[ kONE_K_BUF ];

/* -----------------------------------------------------------------
	aodClearTextCrypt ()
   ----------------------------------------------------------------- */

int aodClearTextCrypt ( const char *inUserID, const char *inPasswd )
{
	int					iResult			= eAODNoErr;
	tDirStatus			dsStatus		= eDSNoErr;
	tDirReference		dirRef			= 0;
	tDirNodeReference	searchNodeRef	= 0;
	tDirNodeReference	userNodeRef		= 0;
	char			   *userLoc			= NULL;

	if ( (inUserID == NULL) || (inPasswd == NULL) )
	{
		sSetErrorText( eTypeParamErr, (inUserID == NULL ? 1 : 2 ), NULL );
		return( eAODParamErr );
	}

	dsStatus = sOpen_ds( &dirRef );
	if ( dsStatus == eDSNoErr )
	{
		dsStatus = sGet_search_node( dirRef, &searchNodeRef );
		if ( dsStatus == eDSNoErr )
		{
			dsStatus = sLook_up_user( dirRef, searchNodeRef, inUserID, &userLoc );
			if ( dsStatus == eDSNoErr )
			{
				dsStatus = sOpen_user_node( dirRef, userLoc, &userNodeRef );
				if ( dsStatus == eDSNoErr )
				{
					dsStatus = sDoCryptAuth( dirRef, userNodeRef, inUserID, inPasswd );
					switch ( dsStatus )
					{
						case eDSNoErr:
							iResult = eAODNoErr;
							break;

						case eDSAuthNewPasswordRequired:
							sSetErrorText( eTypeAuthWarnNewPW, dsStatus, inUserID );
							iResult = eAODNoErr;
							break;

						case eDSAuthPasswordExpired:
							sSetErrorText( eTypeAuthWarnExpirePW, dsStatus, inUserID );
							iResult = eAODNoErr;
							break;

						default:
							sSetErrorText( eTypeAuthFailed, dsStatus, inUserID );
							iResult = eAODAuthFailed;
							break;
					}
					(void)dsCloseDirNode( userNodeRef );
				}
				else
				{
					sSetErrorText( eTypeCantOpenUserNode, dsStatus, inUserID );
					iResult = eAODCantOpenUserNode;
				}
			}
			else
			{
				sSetErrorText( eTypeUserNotFound, dsStatus, inUserID );
				iResult = eAODUserNotFound;
			}
			(void)dsCloseDirNode( searchNodeRef );
		}
		else
		{
			sSetErrorText( eTypeOpenSearchFailed, dsStatus, NULL );
			iResult = eAODOpenSearchFailed;
		}
		(void)dsCloseDirService( dirRef );
	}
	else
	{
		sSetErrorText( eTypeOpenDSFailed, dsStatus, NULL );
		iResult = eAODOpenDSFailed;
	}

	return( iResult );

} /* aodClearTextCrypt */


/* -----------------------------------------------------------------
	aodAPOP ()
   ----------------------------------------------------------------- */

int aodAPOP ( char **inOutUserID, const char *inChallenge, const char *inResponse  )
{
	char	*p		= NULL;
	int		len		= 0;
	char	tmp[ 2046 ];

	/* check for bogus data */
	if ( (inChallenge == NULL) || (inResponse == NULL) )
	{
		return( eAODConfigError );
	}

	/* get the user id */
	p = strchr( inResponse, ' ' );
	if ( p != NULL )
	{
		/* make a copy */
		len = (p - inResponse);

		/* create user id container */
		*inOutUserID = (char *)malloc( len + 1 );
		if ( *inOutUserID != NULL )
		{
			memset( *inOutUserID, 0, len + 1 );
			strncpy( *inOutUserID, inResponse, len );

			if ( strlen( inResponse ) + 1 < 2048 );
			{
				/* consuem any spaces */
				while ( *p == ' ' )
				{
					p++;
				}
	
				if ( p != NULL )
				{
					strcpy( tmp, p );

					/* make the call */
					return( sValidateResponse( *inOutUserID, inChallenge, tmp, kDSStdAuthAPOP ) );
				}
			}
		}
	}

	/* something went wrong before we could make the call */

	return( eAODConfigError );

} /* aodAPOP */


/* -----------------------------------------------------------------
	aodCRAM_MD5 ()
   ----------------------------------------------------------------- */

int aodCRAM_MD5 ( const char *inUserID, const char *inChallenge, const char *inResponse  )
{
	return( sValidateResponse( inUserID, inChallenge, inResponse, kDSStdAuthCRAM_MD5 ) );
} /* aodCRAM_MD5 */

#define CHAR64(c)  (((c) < 0 || (c) > 127) ? -1 : index_64[(c)])

static char basis_64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????";

static char index_64[ 128 ] =
{
    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
    52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
    -1, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
    15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
    -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
    41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
};


/* -----------------------------------------------------------------
	aodEncodeBase64 ()
   ----------------------------------------------------------------- */

int aodEncodeBase64 ( const char *inStr, const int inLen, char *outStr, int outLen )
{
	int						i			= 0;
	const unsigned char	   *pStr		= NULL;
	unsigned long			uiInLen		= 0;
	unsigned char			ucVal		= 0;

	if ( (inStr == NULL) || (outStr == NULL) || (inLen <= 0) || (outLen <= 0) )
	{
		return( -1 );
	}

	pStr = (const unsigned char *)inStr;
	uiInLen = inLen;

	memset( outStr, 0, outLen );

	while ( uiInLen >= 3 )
	{
		if ( (i + 4) > outLen )
		{
			return( -1 );
		}
		outStr[ i++ ] = ( basis_64[ pStr[ 0 ] >> 2 ] );
		outStr[ i++ ] = ( basis_64[ ((pStr[ 0 ] << 4) & 0x30) | (pStr[ 1 ] >> 4) ] );
		outStr[ i++ ] = ( basis_64[ ((pStr[ 1 ] << 2) & 0x3c) | (pStr[ 2 ] >> 6) ] );
		outStr[ i++ ] = ( basis_64[ pStr[ 2 ] & 0x3f ] );

		pStr += 3;
		uiInLen -= 3;
	}

	if ( uiInLen > 0 )
	{
		if ( (i + 4) > outLen )
		{
			return( -1 );
		}

		outStr[ i++ ] = ( basis_64[ pStr[0] >> 2] );
		ucVal = (pStr[0] << 4) & 0x30;
		if ( uiInLen > 1 )
		{
			ucVal |= pStr[1] >> 4;
		}
		outStr[ i++ ] = ( basis_64[ ucVal ] );
		outStr[ i++ ] = ( (uiInLen < 2) ? '=' : basis_64[ (pStr[ 1 ] << 2) & 0x3c ] );
		outStr[ i++ ] = ( '=' );
	}

	return( 0 );

} /* aodEncodeBase64 */


/* -----------------------------------------------------------------
	aodDecodeBase64 ()
   ----------------------------------------------------------------- */

int aodDecodeBase64 ( const char *inStr, const int inLen, char *outStr, int outLen, int *destLen )
{
	int				iResult		= 0;
	unsigned long	i			= 0;
	unsigned long	j			= 0;
	unsigned long	c1			= 0;
	unsigned long	c2			= 0;
	unsigned long	c3			= 0;
	unsigned long	c4			= 0;
	const char	   *pStr		= NULL;

	if ( (inStr == NULL) || (outStr == NULL) || (inLen <= 0) || (outLen <= 0) )
	{
		return( -1 );
	}

	pStr = (const unsigned char *)inStr;

	/* Skip past the '+ ' */
	if ( (pStr[ 0 ] == '+') && (pStr[ 1 ] == ' ') )
	{
		pStr += 2;
	}
	if ( *pStr == '\r')
	{
		iResult = -1;
	}
	else
	{
		for ( i = 0; i < inLen / 4; i++ )
		{
			c1 = pStr[ 0 ];
			if ( CHAR64( c1 ) == -1 )
			{
				iResult = -1;
				break;
			}

			c2 = pStr[ 1 ];
			if ( CHAR64( c2 ) == -1 )
			{
				iResult = -1;
				break;
			}

			c3 = pStr[ 2 ];
			if ( (c3 != '=') && (CHAR64( c3 ) == -1) )
			{
				iResult = -1;
				break;
			}

			c4 = pStr[ 3 ];
			if (c4 != '=' && CHAR64( c4 ) == -1)
			{
				iResult = -1;
				break;
			}

			pStr += 4;

			outStr[ j++ ] = ( (CHAR64(c1) << 2) | (CHAR64(c2) >> 4) );

			if ( j >= outLen )
			{
				return( -1 );
			}

			if ( c3 != '=' )
			{
				outStr[ j++ ] = ( ((CHAR64(c2) << 4) & 0xf0) | (CHAR64(c3) >> 2) );
				if ( j >= outLen )
				{
					return( -1 );
				}
				if ( c4 != '=' )
				{
					outStr[ j++ ] = ( ((CHAR64(c3) << 6) & 0xc0) | CHAR64(c4) );
					if ( j >= outLen )
					{
						return( -1 );
					}
				}
			}
		}
		outStr[ j ] = 0;
	}

	if ( destLen )
	{
		*destLen = j;
	}

	return( iResult );

} /* aodDecodeBase64 */


/* -----------------------------------------------------------------
	aodGetUserOptions ()
   ----------------------------------------------------------------- */

int aodGetUserOptions ( const char *inUserID, struct od_user_opts *inOutOpts )
{
	tDirStatus			dsStatus		= eDSNoErr;
	tDirReference		dirRef			= 0;
	tDirNodeReference	searchNodeRef	= 0;

	if ( (inUserID == NULL) || (inOutOpts == NULL) )
	{
		return( -1 );
	}

	dsStatus = sOpen_ds( &dirRef );
	if ( dsStatus == eDSNoErr )
	{
		dsStatus = sGet_search_node( dirRef, &searchNodeRef );
		if ( dsStatus == eDSNoErr )
		{
			dsStatus = sGet_user_attributes( dirRef, searchNodeRef, inUserID, inOutOpts );
			(void)dsCloseDirNode( searchNodeRef );
		}
		(void)dsCloseDirService( dirRef );
	}

	return( dsStatus );

} /* aodGetUserOptions */


/* -----------------------------------------------------------------
	aodIsMember ()
   ----------------------------------------------------------------- */

int aodIsMember ( const char *inUserName, const char *inGroupName )
{
	int					i				= 0;
	int					recCount		= 0;
	int					isMember		= false;
	char			   *pRecName		= NULL;
	tDirStatus			dsStatus		= eDSNoErr;
	tDirReference		dirRef			= 0;
	tDirNodeReference	searchNodeRef	= 0;
	tDataBuffer		   *pTDataBuff		= NULL;
	tDataListPtr		pRecTypeList	= NULL;
	tDataNodePtr		pAttrType		= NULL;
	tDataNodePtr		pUserName		= NULL;
	tAttributeListRef	attrListRef		= 0;
	tRecordEntry	   *pRecEntry		= NULL;

	if ( (inUserName == NULL) || (inGroupName == NULL) )
	{
		return( -1 );
	}

	dsStatus = sOpen_ds( &dirRef );
	if ( dsStatus == eDSNoErr )
	{
		dsStatus = sGet_search_node( dirRef, &searchNodeRef );
		if ( dsStatus == eDSNoErr )
		{
			pTDataBuff = dsDataBufferAllocate( dirRef, 8192 );
			if ( pTDataBuff != NULL )
			{
				pRecTypeList = dsBuildListFromStrings( dirRef, kDSStdRecordTypeGroups, NULL );
				if ( pRecTypeList != NULL )
				{
					pAttrType = dsDataNodeAllocateString( dirRef, "dsAttrTypeNative:users" );
					if ( pAttrType != NULL )
					{
						pUserName = dsDataNodeAllocateString( dirRef, inUserName );
						if ( pUserName != NULL )
						{
							dsStatus = dsDoAttributeValueSearch (	searchNodeRef,
																	pTDataBuff,
																	pRecTypeList,
																	pAttrType,
																	eDSExact,
																	pUserName,
																	(unsigned long *)&recCount,
																	NULL );

							if ( (dsStatus == eDSNoErr) && (recCount != 0) )
							{
								for ( i = 1; i <= recCount; i++ )
								{
									// Get the record entry form the buffer.
									dsStatus = dsGetRecordEntry( searchNodeRef, pTDataBuff, i, &attrListRef, &pRecEntry );
									if ( dsStatus == eDSNoErr )
									{
										dsStatus = dsGetRecordNameFromEntry( pRecEntry, &pRecName );
										if ( dsStatus == eDSNoErr )
										{
											if ( strcasecmp( pRecName, inGroupName ) == 0 )
											{
												isMember = true;
												break;
											}
											free( pRecName );
											pRecName = NULL;
										}
									}
									if ( pRecEntry != NULL )
									{
										(void)dsCloseAttributeList( attrListRef );
										(void)dsDeallocRecordEntry( searchNodeRef, pRecEntry );
										pRecEntry = NULL;
									}
								}
							}
							(void )dsDataNodeDeAllocate( dirRef, pUserName );
							pUserName = NULL;
						}
						(void )dsDataNodeDeAllocate( dirRef, pAttrType );
						pAttrType = NULL;
					}
					(void )dsDataListDeallocate( dirRef, pRecTypeList );
					free( pRecTypeList );
					pRecTypeList = NULL;
				}
				(void)dsDataBufferDeAllocate( dirRef, pTDataBuff );
				pTDataBuff = NULL;
			}
			(void)dsCloseDirNode( searchNodeRef );
			searchNodeRef = 0;
		}
		(void)dsCloseDirService( dirRef );
		dirRef = 0;
	}

	return( isMember );

} /* aodIsMember */


/* -----------------------------------------------------------------
	aodGetServerPrincipal ()
   ----------------------------------------------------------------- */

char * aodGetServerPrincipal ( const char *inServerKey )
{
    FILE			   *pFile		= NULL;
	char			   *outStr		= NULL;
	char			   *buf			= NULL;
	ssize_t				bytes		= 0;
	struct stat			fileStat;
	bool				bFound		= FALSE;
	CFStringRef			cfStringRef	= NULL;
	char			   *pValue		= NULL;
	CFDataRef			cfDataRef	= NULL;
	CFPropertyListRef	cfPlistRef	= NULL;
	CFDictionaryRef		cfDictRef	= NULL;
	CFDictionaryRef		cfDictCyrus	= NULL;

    pFile = fopen( kPlistFilePath, "r" );
    if ( pFile == NULL )
	{
		syslog( LOG_ERR, "Cannot open principal file" );
		return( NULL );
	}

	if ( -1 == fstat( fileno( pFile ), &fileStat ) )
	{
		fclose( pFile );
		syslog( LOG_ERR, "Cannot get stat on principal file" );
		return( NULL );
	}

	buf = (char *)malloc( fileStat.st_size + 1 );
	if ( buf == NULL )
	{
		fclose( pFile );
		syslog( LOG_ERR, "Cannot alloc principal buffer" );
		return( NULL );
	}

	memset( buf, 0, fileStat.st_size + 1 );
	bytes = read( fileno( pFile ), buf, fileStat.st_size );
	if ( -1 == bytes )
	{
		fclose( pFile );
		free( buf );
		syslog( LOG_ERR, "Cannot read principal file" );
		return( NULL );
	}

	cfDataRef = CFDataCreate( NULL, (const UInt8 *)buf, fileStat.st_size );
	if ( cfDataRef != NULL )
	{
		cfPlistRef = CFPropertyListCreateFromXMLData( kCFAllocatorDefault, cfDataRef, kCFPropertyListImmutable, NULL );
		if ( cfPlistRef != NULL )
		{
			if ( CFDictionaryGetTypeID() == CFGetTypeID( cfPlistRef ) )
			{
				cfDictRef = (CFDictionaryRef)cfPlistRef;

				bFound = CFDictionaryContainsKey( cfDictRef, CFSTR( kXMLDictionary ) );
				if ( bFound == true )
				{
					cfDictCyrus = (CFDictionaryRef)CFDictionaryGetValue( cfDictRef, CFSTR( kXMLDictionary ) );
					if ( cfDictCyrus != NULL )
					{
						if ( strcmp( inServerKey, kXMLIMAP_Principal ) == 0 )
						{
							cfStringRef = (CFStringRef)CFDictionaryGetValue( cfDictCyrus, CFSTR( kXMLIMAP_Principal ) );
						}
						else
						{
							cfStringRef = (CFStringRef)CFDictionaryGetValue( cfDictCyrus, CFSTR( kXMLPOP3_Principal ) );
						}
						if ( cfStringRef != NULL )
						{
							if ( CFGetTypeID( cfStringRef ) == CFStringGetTypeID() )
							{
								pValue = (char *)CFStringGetCStringPtr( cfStringRef, kCFStringEncodingMacRoman );
								if ( pValue != NULL )
								{
									outStr = malloc( strlen( pValue ) + 1 );
									if ( outStr != NULL )
									{
										strcpy( outStr, pValue );
									}
								}
							}
						}
					}
				}
			}
			CFRelease( cfPlistRef );
		}
		CFRelease( cfDataRef );
	}

	return( outStr );

} /* aodGetServerPrincipal */


/* -----------------------------------------------------------------
   -----------------------------------------------------------------
   -----------------------------------------------------------------
	Static functions
   -----------------------------------------------------------------
   -----------------------------------------------------------------
   ----------------------------------------------------------------- */

/* -----------------------------------------------------------------
	sOpen_ds ()
   ----------------------------------------------------------------- */

tDirStatus sOpen_ds ( tDirReference *inOutDirRef )
{
	tDirStatus		dsStatus	= eDSNoErr;

	dsStatus = dsOpenDirService( inOutDirRef );

	return( dsStatus );

} /* sOpen_ds */


/* -----------------------------------------------------------------
	sGet_search_node ()
   ----------------------------------------------------------------- */

tDirStatus sGet_search_node ( tDirReference inDirRef,
							 tDirNodeReference *outSearchNodeRef )
{
	tDirStatus		dsStatus	= eMemoryAllocError;
	unsigned long	uiCount		= 0;
	tDataBuffer	   *pTDataBuff	= NULL;
	tDataList	   *pDataList	= NULL;

	pTDataBuff = dsDataBufferAllocate( inDirRef, 8192 );
	if ( pTDataBuff != NULL )
	{
		dsStatus = dsFindDirNodes( inDirRef, pTDataBuff, NULL, eDSSearchNodeName, &uiCount, NULL );
		if ( dsStatus == eDSNoErr )
		{
			dsStatus = eDSNodeNotFound;
			if ( uiCount == 1 )
			{
				dsStatus = dsGetDirNodeName( inDirRef, pTDataBuff, 1, &pDataList );
				if ( dsStatus == eDSNoErr )
				{
					dsStatus = dsOpenDirNode( inDirRef, pDataList, outSearchNodeRef );
				}

				if ( pDataList != NULL )
				{
					(void)dsDataListDeAllocate( inDirRef, pDataList, true );

					free( pDataList );
					pDataList = NULL;
				}
			}
		}
		(void)dsDataBufferDeAllocate( inDirRef, pTDataBuff );
		pTDataBuff = NULL;
	}

	return( dsStatus );

} /* sGet_search_node */


/* -----------------------------------------------------------------
	sGet_user_attributes ()
   ----------------------------------------------------------------- */

tDirStatus sGet_user_attributes ( tDirReference inDirRef,
									tDirNodeReference inSearchNodeRef,
									const char *inUserID,
									struct od_user_opts *inOutOpts )
{
	char				   *p				= NULL;
	tDirStatus				dsStatus		= eMemoryAllocError;
	int						done			= FALSE;
	int						i				= 0;
	char				   *pAcctName		= NULL;
	unsigned long			uiRecCount		= 0;
	tDataBuffer			   *pTDataBuff		= NULL;
	tDataList			   *pUserRecType	= NULL;
	tDataList			   *pUserAttrType	= NULL;
	tRecordEntry		   *pRecEntry		= NULL;
	tAttributeEntry		   *pAttrEntry		= NULL;
	tAttributeValueEntry   *pValueEntry		= NULL;
	tAttributeValueListRef	valueRef		= 0;
	tAttributeListRef		attrListRef		= 0;
	tContextData			pContext		= NULL;
	tDataList				tdlRecName;

	memset( &tdlRecName,  0, sizeof( tDataList ) );

	pTDataBuff = dsDataBufferAllocate( inDirRef, 8192 );
	if ( pTDataBuff != NULL )
	{
		dsStatus = dsBuildListFromStringsAlloc( inDirRef, &tdlRecName, inUserID, NULL );
		if ( dsStatus == eDSNoErr )
		{
			dsStatus = eMemoryAllocError;

			pUserRecType = dsBuildListFromStrings( inDirRef, kDSStdRecordTypeUsers, NULL );
			if ( pUserRecType != NULL )
			{
				pUserAttrType = dsBuildListFromStrings( inDirRef, kDS1AttrMailAttribute, kDS1AttrUniqueID, kDS1AttrGeneratedUID, kDSNAttrRecordName, NULL );
				if ( pUserAttrType != NULL );
				{
					do {
						/* Get the user record(s) that matches the user id */
						dsStatus = dsGetRecordList( inSearchNodeRef, pTDataBuff, &tdlRecName, eDSiExact, pUserRecType,
													pUserAttrType, FALSE, &uiRecCount, &pContext );

						if ( dsStatus == eDSNoErr )
						{
							dsStatus = eDSInvalidName;
							/* do we have more than 1 match */
							if ( uiRecCount == 1 ) 
							{
								dsStatus = dsGetRecordEntry( inSearchNodeRef, pTDataBuff, 1, &attrListRef, &pRecEntry );
								if ( dsStatus == eDSNoErr )
								{
									/* Get the record name */
									(void)dsGetRecordNameFromEntry( pRecEntry, &pAcctName );

									if ( pAcctName != NULL )
									{
										if ( strlen( pAcctName ) < kONE_K_BUF )
										{
											strcpy( inOutOpts->fUserID, pAcctName );
										}
									}
									/* Get the attributes we care about for the record */
									for ( i = 1; i <= pRecEntry->fRecordAttributeCount; i++ )
									{
										dsStatus = dsGetAttributeEntry( inSearchNodeRef, pTDataBuff, attrListRef, i, &valueRef, &pAttrEntry );
										if ( (dsStatus == eDSNoErr) && (pAttrEntry != NULL) )
										{
											if ( strcasecmp( pAttrEntry->fAttributeSignature.fBufferData, kDS1AttrMailAttribute ) == 0 )
											{
												/* Only get the first attribute value */
												dsStatus = dsGetAttributeValue( inSearchNodeRef, pTDataBuff, 1, valueRef, &pValueEntry );
												if ( dsStatus == eDSNoErr )
												{
													/* Get the individual mail attribute values */
													sGet_mail_values( (char *)pValueEntry->fAttributeValueData.fBufferData, inOutOpts );

													/* If we don't find duplicate users in the same node, we take the first one with
														a valid mail attribute */
													done = true;
												}
											}
											else if ( strcasecmp( pAttrEntry->fAttributeSignature.fBufferData, kDS1AttrUniqueID ) == 0 )
											{
												dsStatus = dsGetAttributeValue( inSearchNodeRef, pTDataBuff, 1, valueRef, &pValueEntry );
												if ( dsStatus == eDSNoErr )
												{
													/* Get uid */
													inOutOpts->fUID = strtol( pValueEntry->fAttributeValueData.fBufferData, &p, 10 );
												}
											}
											else if ( strcasecmp( pAttrEntry->fAttributeSignature.fBufferData, kDS1AttrGeneratedUID ) == 0 )
											{
												/* Only get the first attribute value */
												dsStatus = dsGetAttributeValue( inSearchNodeRef, pTDataBuff, 1, valueRef, &pValueEntry );
												if ( dsStatus == eDSNoErr )
												{
													/* Get the generated uid */
													if ( pValueEntry->fAttributeValueData.fBufferLength < kMAX_GUID_LEN )
													{
														strncpy( inOutOpts->fGUID, pValueEntry->fAttributeValueData.fBufferData, pValueEntry->fAttributeValueData.fBufferLength );
													}
													else
													{
														strncpy( inOutOpts->fGUID, pValueEntry->fAttributeValueData.fBufferData, kMAX_GUID_LEN - 1 );
													}
												}
											}
											else if ( strcasecmp( pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrRecordName ) == 0 )
											{
												/* Only get the first attribute value */
												dsStatus = dsGetAttributeValue( inSearchNodeRef, pTDataBuff, 1, valueRef, &pValueEntry );
												if ( dsStatus == eDSNoErr )
												{
													/* Get the generated uid */
													if ( pValueEntry->fAttributeValueData.fBufferLength < kONE_K_BUF )
													{
														strncpy( inOutOpts->fRecName, pValueEntry->fAttributeValueData.fBufferData, pValueEntry->fAttributeValueData.fBufferLength );
													}
													else
													{
														strncpy( inOutOpts->fRecName, pValueEntry->fAttributeValueData.fBufferData, kONE_K_BUF - 1 );
													}
												}
											}

											if ( pValueEntry != NULL )
											{
												(void)dsDeallocAttributeValueEntry( inSearchNodeRef, pValueEntry );
												pValueEntry = NULL;
											}
										}
										if ( pAttrEntry != NULL )
										{
											(void)dsCloseAttributeValueList( valueRef );
											(void)dsDeallocAttributeEntry( inSearchNodeRef, pAttrEntry );
											pAttrEntry = NULL;
										}
									}

									if ( pRecEntry != NULL )
									{
										(void)dsDeallocRecordEntry( inSearchNodeRef, pRecEntry );
										pRecEntry = NULL;
									}
								}
							}
							else
							{
								done = true;
								if ( uiRecCount > 1 )
								{
									syslog( LOG_NOTICE, "Duplicate users %s found in directory.", inUserID );
								}
								inOutOpts->fUserID[ 0 ] = '\0';
								dsStatus = eDSUserUnknown;
							}
						}
					} while ( (pContext != NULL) && (dsStatus == eDSNoErr) && (!done) );

					if ( pContext != NULL )
					{
						(void)dsReleaseContinueData( inDirRef, pContext );
						pContext = NULL;
					}
					(void)dsDataListDeallocate( inDirRef, pUserAttrType );
					pUserAttrType = NULL;
				}
				(void)dsDataListDeallocate( inDirRef, pUserRecType );
				pUserRecType = NULL;
			}
			(void)dsDataListDeAllocate( inDirRef, &tdlRecName, TRUE );
		}
		(void)dsDataBufferDeAllocate( inDirRef, pTDataBuff );
		pTDataBuff = NULL;
	}
	
	return( dsStatus );

} /* sGet_user_attributes */


/* -----------------------------------------------------------------
	sLook_up_user ()
   ----------------------------------------------------------------- */

tDirStatus sLook_up_user ( tDirReference inDirRef,
						  tDirNodeReference inSearchNodeRef,
						  const char *inUserID,
						  char **outUserLocation )
{
	tDirStatus				dsStatus		= eMemoryAllocError;
	int						done			= FALSE;
	char				   *pAcctName		= NULL;
	unsigned long			uiRecCount		= 0;
	tDataBuffer			   *pTDataBuff		= NULL;
	tDataList			   *pUserRecType	= NULL;
	tDataList			   *pUserAttrType	= NULL;
	tRecordEntry		   *pRecEntry		= NULL;
	tAttributeEntry		   *pAttrEntry		= NULL;
	tAttributeValueEntry   *pValueEntry		= NULL;
	tAttributeValueListRef	valueRef		= 0;
	tAttributeListRef		attrListRef		= 0;
	tContextData			pContext		= NULL;
	tDataList				tdlRecName;

	memset( &tdlRecName,  0, sizeof( tDataList ) );

	pTDataBuff = dsDataBufferAllocate( inDirRef, 8192 );
	if ( pTDataBuff != NULL )
	{
		dsStatus = dsBuildListFromStringsAlloc( inDirRef, &tdlRecName, inUserID, NULL );
		if ( dsStatus == eDSNoErr )
		{
			dsStatus = eMemoryAllocError;

			pUserRecType = dsBuildListFromStrings( inDirRef, kDSStdRecordTypeUsers, NULL );
			if ( pUserRecType != NULL )
			{
				pUserAttrType = dsBuildListFromStrings( inDirRef, kDSNAttrMetaNodeLocation, NULL );
				if ( pUserAttrType != NULL );
				{
					do {
						/* Get the user record(s) that matches the name */
						dsStatus = dsGetRecordList( inSearchNodeRef, pTDataBuff, &tdlRecName, eDSiExact, pUserRecType,
													pUserAttrType, FALSE, &uiRecCount, &pContext );

						if ( dsStatus == eDSNoErr )
						{
							dsStatus = eDSInvalidName;
							if ( uiRecCount == 1 ) 
							{
								dsStatus = dsGetRecordEntry( inSearchNodeRef, pTDataBuff, 1, &attrListRef, &pRecEntry );
								if ( dsStatus == eDSNoErr )
								{
									/* Get the record name */
									(void)dsGetRecordNameFromEntry( pRecEntry, &pAcctName );
			
									dsStatus = dsGetAttributeEntry( inSearchNodeRef, pTDataBuff, attrListRef, 1, &valueRef, &pAttrEntry );
									if ( (dsStatus == eDSNoErr) && (pAttrEntry != NULL) )
									{
										dsStatus = dsGetAttributeValue( inSearchNodeRef, pTDataBuff, 1, valueRef, &pValueEntry );
										if ( (dsStatus == eDSNoErr) && (pValueEntry != NULL) )
										{
											if ( strcmp( pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrMetaNodeLocation ) == 0 )
											{
												/* Get the user location */
												*outUserLocation = (char *)calloc( pValueEntry->fAttributeValueData.fBufferLength + 1, sizeof( char ) );
												memcpy( *outUserLocation, pValueEntry->fAttributeValueData.fBufferData, pValueEntry->fAttributeValueData.fBufferLength );

												/* If we don't find duplicate users in the same node, we take the first one with
													a valid mail attribute */
												done = TRUE;
											}
											(void)dsCloseAttributeValueList( valueRef );
										}

										(void)dsDeallocAttributeEntry( inSearchNodeRef, pAttrEntry );
										pAttrEntry = NULL;

										(void)dsCloseAttributeList( attrListRef );
									}

									if ( pRecEntry != NULL )
									{
										(void)dsDeallocRecordEntry( inSearchNodeRef, pRecEntry );
										pRecEntry = NULL;
									}
								}
							}
							else
							{
								done = true;
								if ( uiRecCount > 1 )
								{
									syslog( LOG_NOTICE, "Duplicate users %s found in directory.", inUserID );
								}
								dsStatus = eDSAuthInvalidUserName;
							}
						}
					} while ( (pContext != NULL) && (dsStatus == eDSNoErr) && (!done) );

					if ( pContext != NULL )
					{
						(void)dsReleaseContinueData( inDirRef, pContext );
						pContext = NULL;
					}
					(void)dsDataListDeallocate( inDirRef, pUserAttrType );
					pUserAttrType = NULL;
				}
				(void)dsDataListDeallocate( inDirRef, pUserRecType );
				pUserRecType = NULL;
			}
			(void)dsDataListDeAllocate( inDirRef, &tdlRecName, TRUE );
		}
		(void)dsDataBufferDeAllocate( inDirRef, pTDataBuff );
		pTDataBuff = NULL;
	}

	if ( pAcctName != NULL )
	{
		free( pAcctName );
		pAcctName = NULL;
	}

	return( dsStatus );

} /* sLook_up_user */


/* -----------------------------------------------------------------
	sOpen_user_node ()
   ----------------------------------------------------------------- */

tDirStatus sOpen_user_node (  tDirReference inDirRef, const char *inUserLoc, tDirNodeReference *outUserNodeRef )
{
	tDirStatus		dsStatus	= eMemoryAllocError;
	tDataList	   *pUserNode	= NULL;

	pUserNode = dsBuildFromPath( inDirRef, inUserLoc, "/" );
	if ( pUserNode != NULL )
	{
		dsStatus = dsOpenDirNode( inDirRef, pUserNode, outUserNodeRef );

		(void)dsDataListDeAllocate( inDirRef, pUserNode, TRUE );
		free( pUserNode );
		pUserNode = NULL;
	}

	return( dsStatus );

} /* sOpen_user_node */


/* -----------------------------------------------------------------
	sDoCryptAuth ()
   ----------------------------------------------------------------- */

static int sDoCryptAuth ( tDirReference inDirRef, tDirNodeReference inUserNodeRef, const char *inUserID, const char *inPasswd )
{
	tDirStatus				dsStatus		= eDSNoErr;
	int						iResult			= -1;
	long					nameLen			= 0;
	long					passwdLen		= 0;
	unsigned long			curr			= 0;
	unsigned long			len				= 0;
	unsigned long			uiBuffSzie		= 0;
	tDataBuffer			   *pAuthBuff		= NULL;
	tDataBuffer			   *pStepBuff		= NULL;
	tDataNode			   *pAuthType		= NULL;

	if ( (inUserID == NULL) || (inPasswd == NULL) )
	{
		return( eDSAuthParameterError );
	}

	nameLen = strlen( inUserID );
	passwdLen = strlen( inPasswd );

	uiBuffSzie = nameLen + passwdLen + 32;

	pAuthBuff = dsDataBufferAllocate( inDirRef, uiBuffSzie );
	if ( pAuthBuff != NULL )
	{
		/* We don't use this buffer for clear text auth */
		pStepBuff = dsDataBufferAllocate( inDirRef, 256 );
		if ( pStepBuff != NULL )
		{
			pAuthType = dsDataNodeAllocateString( inDirRef, kDSStdAuthNodeNativeClearTextOK );
			if ( pAuthType != NULL )
			{
				/* User Name */
				len = nameLen;
				memcpy( &(pAuthBuff->fBufferData[ curr ]), &len, sizeof( unsigned long ) );
				curr += sizeof( unsigned long );
				memcpy( &(pAuthBuff->fBufferData[ curr ]), inUserID, len );
				curr += len;

				/* Password */
				len = passwdLen;
				memcpy( &(pAuthBuff->fBufferData[ curr ]), &len, sizeof( unsigned long ) );
				curr += sizeof( unsigned long );
				memcpy( &(pAuthBuff->fBufferData[ curr ]), inPasswd, len );
				curr += len;

				pAuthBuff->fBufferLength = curr;

				dsStatus = dsDoDirNodeAuth( inUserNodeRef, pAuthType, true, pAuthBuff, pStepBuff, NULL );
				if ( dsStatus == eDSNoErr )
				{
					iResult = 0;
				}
				else
				{
					syslog( LOG_ERR, "AOD: Authentication failed for user %s. (Open Directroy error: %d)", inUserID, dsStatus );
					iResult = -7;
				}

				(void)dsDataNodeDeAllocate( inDirRef, pAuthType );
				pAuthType = NULL;
			}
			else
			{
				syslog( LOG_ERR, "AOD: Authentication failed for user %s.  Unable to allocate memory.", inUserID );
				iResult = -6;
			}
			(void)dsDataNodeDeAllocate( inDirRef, pStepBuff );
			pStepBuff = NULL;
		}
		else
		{
			syslog( LOG_ERR, "AOD: Authentication failed for user %s.  Unable to allocate memory.", inUserID );
			iResult = -5;
		}
		(void)dsDataNodeDeAllocate( inDirRef, pAuthBuff );
		pAuthBuff = NULL;
	}

	return( iResult );

} /* sDoCryptAuth */


/* -----------------------------------------------------------------
	sValidateResponse ()
   ----------------------------------------------------------------- */

int sValidateResponse ( const char *inUserID, const char *inChallenge, const char *inResponse, const char *inAuthType )
{
	int					iResult			= -1;
	tDirStatus			dsStatus		= eDSNoErr;
	tDirReference		dirRef			= 0;
	tDirNodeReference	searchNodeRef	= 0;
	tDirNodeReference	userNodeRef		= 0;
	tDataBuffer		   *pAuthBuff		= NULL;
	tDataBuffer		   *pStepBuff		= NULL;
	tDataNode		   *pAuthType		= NULL;
	char			   *userLoc			= NULL;
	unsigned long		uiNameLen		= 0;
	unsigned long		uiChalLen		= 0;
	unsigned long		uiRespLen		= 0;
	unsigned long		uiBuffSzie		= 0;
	unsigned long		uiCurr			= 0;
	unsigned long		uiLen			= 0;

	if ( (inUserID == NULL) || (inChallenge == NULL) || (inResponse == NULL) || (inAuthType == NULL) )
	{
		return( -1 );
	}

	uiNameLen = strlen( inUserID );
	uiChalLen = strlen( inChallenge );
	uiRespLen = strlen( inResponse );

	uiBuffSzie = uiNameLen + uiChalLen + uiRespLen + 32;

	dsStatus = sOpen_ds( &dirRef );
	if ( dsStatus == eDSNoErr )
	{
		dsStatus = sGet_search_node( dirRef, &searchNodeRef );
		if ( dsStatus == eDSNoErr )
		{
			dsStatus = sLook_up_user( dirRef, searchNodeRef, inUserID, &userLoc );
			if ( dsStatus == eDSNoErr )
			{
				dsStatus = sOpen_user_node( dirRef, userLoc, &userNodeRef );
				if ( dsStatus == eDSNoErr )
				{
					pAuthBuff = dsDataBufferAllocate( dirRef, uiBuffSzie );
					if ( pAuthBuff != NULL )
					{
						pStepBuff = dsDataBufferAllocate( dirRef, 256 );
						if ( pStepBuff != NULL )
						{
							pAuthType = dsDataNodeAllocateString( dirRef, inAuthType );
							if ( pAuthType != NULL )
							{
								/* User name */
								uiLen = uiNameLen;
								memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), &uiLen, sizeof( unsigned long ) );
								uiCurr += sizeof( unsigned long );
								memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), inUserID, uiLen );
								uiCurr += uiLen;

								/* Challenge */
								uiLen = uiChalLen;
								memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), &uiLen, sizeof( unsigned long ) );
								uiCurr += sizeof( unsigned long );
								memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), inChallenge, uiLen );
								uiCurr += uiLen;

								/* Response */
								uiLen = uiRespLen;
								memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), &uiLen, sizeof( unsigned long ) );
								uiCurr += sizeof( unsigned long );
								memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), inResponse, uiLen );
								uiCurr += uiLen;

								pAuthBuff->fBufferLength = uiCurr;

								dsStatus = dsDoDirNodeAuth( userNodeRef, pAuthType, true, pAuthBuff, pStepBuff, NULL );
								switch ( dsStatus )
								{
									case eDSNoErr:
										iResult = eAODNoErr;
										break;
			
									case eDSAuthNewPasswordRequired:
										sSetErrorText( eTypeAuthWarnNewPW, dsStatus, inUserID );
										iResult = eAODNoErr;
										break;
			
									case eDSAuthPasswordExpired:
										sSetErrorText( eTypeAuthWarnExpirePW, dsStatus, inUserID );
										iResult = eAODNoErr;
										break;

									default:
										sSetErrorText( eTypeAuthFailed, dsStatus, inUserID );
										iResult = eAODAuthFailed;
										break;
								}
								(void)dsDataNodeDeAllocate( dirRef, pAuthType );
								pAuthType = NULL;
							}
							else
							{
								sSetErrorText( eTypeUserNotFound, dsStatus, inUserID );
								syslog( LOG_ERR, "AOD: Authentication failed for user %s.  Unable to allocate memory.", inUserID );
								iResult = eAODAllocError;
							}
							(void)dsDataNodeDeAllocate( dirRef, pStepBuff );
							pStepBuff = NULL;
						}
						else
						{
							sSetErrorText( eTypeUserNotFound, dsStatus, inUserID );
							syslog( LOG_ERR, "AOD: Authentication failed for user %s.  Unable to allocate memory.", inUserID );
							iResult = eAODAllocError;
						}
						(void)dsDataNodeDeAllocate( dirRef, pAuthBuff );
						pAuthBuff = NULL;
					}
					else
					{
						sSetErrorText( eTypeAllocError, dsStatus, inUserID );
						syslog( LOG_ERR, "AOD: Authentication failed for user %s.  Unable to allocate memory.", inUserID );
						iResult = eAODAllocError;
					}
					(void)dsCloseDirNode( userNodeRef );
					userNodeRef = 0;
				}
				else
				{
					sSetErrorText( eTypeCantOpenUserNode, dsStatus, inUserID );
					syslog( LOG_ERR, "AOD: Unable to open user directory node for user %s. (Open Directroy error: %d)", inUserID, dsStatus );
					iResult = eAODCantOpenUserNode;
				}
			}
			else
			{
				sSetErrorText( eTypeUserNotFound, dsStatus, inUserID );
				syslog( LOG_ERR, "AOD: Unable to find user %s. (Open Directroy error: %d)", inUserID, dsStatus );
				iResult = eAODUserNotFound;
			}
			(void)dsCloseDirNode( searchNodeRef );
			searchNodeRef = 0;
		}
		else
		{
			sSetErrorText( eTypeOpenSearchFailed, dsStatus, inUserID );
			syslog( LOG_ERR, "AOD: Unable to open directroy search node. (Open Directroy error: %d)", dsStatus );
			iResult = eAODOpenSearchFailed;
		}
		(void)dsCloseDirService( dirRef );
		dirRef = 0;
	}
	else
	{
		sSetErrorText( eTypeOpenDSFailed, dsStatus, inUserID );
		syslog( LOG_ERR, "AOD: Unable to open directroy. (Open Directroy error: %d)", dsStatus );
		iResult = eAODOpenDSFailed;
	}

	return( iResult );

} /* sValidateResponse */


/* -----------------------------------------------------------------
	sGet_mail_values ()
   ----------------------------------------------------------------- */

void sGet_mail_values ( char *inMailAttribute, struct od_user_opts *inOutOpts )
{
	int					iResult 	= 0;
	unsigned long		uiDataLen	= 0;
	CFDataRef			cfDataRef	= NULL;
	CFPropertyListRef	cfPlistRef	= NULL;
	CFDictionaryRef		cfDictRef	= NULL;

	if ( inMailAttribute != NULL )
	{
		uiDataLen = strlen( inMailAttribute );
		cfDataRef = CFDataCreate( NULL, (const UInt8 *)inMailAttribute, uiDataLen );
		if ( cfDataRef != NULL )
		{
			cfPlistRef = CFPropertyListCreateFromXMLData( kCFAllocatorDefault, cfDataRef, kCFPropertyListImmutable, NULL );
			if ( cfPlistRef != NULL )
			{
				if ( CFDictionaryGetTypeID() == CFGetTypeID( cfPlistRef ) )
				{
					cfDictRef = (CFDictionaryRef)cfPlistRef;
					iResult = sVerify_version( cfDictRef );
					if ( iResult == eAODNoErr )
					{
						sGet_acct_state( cfDictRef, inOutOpts );
						sGet_IMAP_login( cfDictRef, inOutOpts );
						sGet_POP3_login( cfDictRef, inOutOpts );
						sGet_acct_loc( cfDictRef, inOutOpts );
						sGet_alt_loc( cfDictRef, inOutOpts );
						sGet_disk_quota( cfDictRef, inOutOpts );
					}
				}
				CFRelease( cfPlistRef );
			}
			CFRelease( cfDataRef );
		}
	}
} /* sGet_mail_values */


/* -----------------------------------------------------------------
	sVerify_version ()
   ----------------------------------------------------------------- */

int sVerify_version ( CFDictionaryRef inCFDictRef )
{
	int				iResult 	= 0;
	bool			bFound		= FALSE;
	CFStringRef		cfStringRef	= NULL;
	char		   *pValue		= NULL;

	bFound = CFDictionaryContainsKey( inCFDictRef, CFSTR( kXMLKeyAttrVersion ) );
	if ( bFound == true )
	{
		iResult = eInvalidDataType;

		cfStringRef = (CFStringRef)CFDictionaryGetValue( inCFDictRef, CFSTR( kXMLKeyAttrVersion ) );
		if ( cfStringRef != NULL )
		{
			if ( CFGetTypeID( cfStringRef ) == CFStringGetTypeID() )
			{
				iResult = eItemNotFound;

				pValue = (char *)CFStringGetCStringPtr( cfStringRef, kCFStringEncodingMacRoman );
				if ( pValue != NULL )
				{
					iResult = eWrongVersion;

					if ( strcasecmp( pValue, kXMLValueVersion ) == 0 )
					{
						iResult = eAODNoErr;
					}
				}
			}
		}
	}

	return( iResult );

} /* sVerify_version */


/* -----------------------------------------------------------------
	sGet_acct_state ()
   ----------------------------------------------------------------- */

void sGet_acct_state ( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts )
{
	bool			bFound		= FALSE;
	CFStringRef		cfStringRef	= NULL;
	char		   *pValue		= NULL;

	/* Default value */
	inOutOpts->fAcctState = eUnknownAcctState;

	bFound = CFDictionaryContainsKey( inCFDictRef, CFSTR( kXMLKeyAcctState ) );
	if ( bFound == true )
	{
		cfStringRef = (CFStringRef)CFDictionaryGetValue( inCFDictRef, CFSTR( kXMLKeyAcctState ) );
		if ( cfStringRef != NULL )
		{
			if ( CFGetTypeID( cfStringRef ) == CFStringGetTypeID() )
			{
				pValue = (char *)CFStringGetCStringPtr( cfStringRef, kCFStringEncodingMacRoman );
				if ( pValue != NULL )
				{
					if ( strcasecmp( pValue, kXMLValueAcctEnabled ) == 0 )
					{
						inOutOpts->fAcctState = eAcctEnabled;
					}
					else if ( strcasecmp( pValue, kXMLValueAcctDisabled ) == 0 )
					{
						inOutOpts->fAcctState = eAcctDisabled;
					}
					else if ( strcasecmp( pValue, kXMLValueAcctFwd ) == 0 )
					{
						sGet_auto_forward( inCFDictRef, inOutOpts );
					}
				}
			}
		}
	}
} /* sGet_acct_state */


/* -----------------------------------------------------------------
	sGet_auto_forward ()
   ----------------------------------------------------------------- */

void sGet_auto_forward ( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts )
{
	bool			bFound		= FALSE;
	CFStringRef		cfStringRef	= NULL;
	char		   *pValue		= NULL;

	bFound = CFDictionaryContainsKey( inCFDictRef, CFSTR( kXMLKeyAutoFwd ) );
	if ( bFound == true )
	{
		cfStringRef = (CFStringRef)CFDictionaryGetValue( inCFDictRef, CFSTR( kXMLKeyAutoFwd ) );
		if ( cfStringRef != NULL )
		{
			if ( CFGetTypeID( cfStringRef ) == CFStringGetTypeID() )
			{
				pValue = (char *)CFStringGetCStringPtr( cfStringRef, kCFStringEncodingMacRoman );
				if ( pValue != NULL )
				{
					if ( strlen( pValue ) < kONE_K_BUF )
					{
						inOutOpts->fAcctState = eAcctForwarded;
						inOutOpts->fPOP3Login = eAcctDisabled;
						inOutOpts->fIMAPLogin = eAcctDisabled;

						strcpy( inOutOpts->fAutoFwdAddr, pValue );
					}
				}
			}
		}
	}
} /* sGet_auto_forward */


/* -----------------------------------------------------------------
	sGet_IMAP_login ()
   ----------------------------------------------------------------- */

void sGet_IMAP_login ( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts )
{
	bool			bFound		= FALSE;
	CFStringRef		cfStringRef	= NULL;
	char		   *pValue		= NULL;

	/* Default value */
	inOutOpts->fIMAPLogin = eAcctDisabled;

	bFound = CFDictionaryContainsKey( inCFDictRef, CFSTR( kXMLKeykIMAPLoginState ) );
	if ( bFound == true )
	{
		cfStringRef = (CFStringRef)CFDictionaryGetValue( inCFDictRef, CFSTR( kXMLKeykIMAPLoginState ) );
		if ( cfStringRef != NULL )
		{
			if ( CFGetTypeID( cfStringRef ) == CFStringGetTypeID() )
			{
				pValue = (char *)CFStringGetCStringPtr( cfStringRef, kCFStringEncodingMacRoman );
				if ( pValue != NULL )
				{
					if ( strcasecmp( pValue, kXMLValueIMAPLoginOK ) == 0 )
					{
						inOutOpts->fIMAPLogin = eAcctIMAPLoginOK;
					}
				}
			}
		}
	}
} /* sGet_IMAP_login */


/* -----------------------------------------------------------------
	sGet_POP3_login ()
   ----------------------------------------------------------------- */

void sGet_POP3_login ( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts )
{
	bool			bFound		= FALSE;
	CFStringRef		cfStringRef	= NULL;
	char		   *pValue		= NULL;

	/* Default value */
	inOutOpts->fPOP3Login = eAcctDisabled;

	bFound = CFDictionaryContainsKey( inCFDictRef, CFSTR( kXMLKeyPOP3LoginState ) );
	if ( bFound == true )
	{
		cfStringRef = (CFStringRef)CFDictionaryGetValue( inCFDictRef, CFSTR( kXMLKeyPOP3LoginState ) );
		if ( cfStringRef != NULL )
		{
			if ( CFGetTypeID( cfStringRef ) == CFStringGetTypeID() )
			{
				pValue = (char *)CFStringGetCStringPtr( cfStringRef, kCFStringEncodingMacRoman );
				if ( pValue != NULL )
				{
					if ( strcasecmp( pValue, kXMLValuePOP3LoginOK ) == 0 )
					{
						inOutOpts->fPOP3Login = eAcctPOP3LoginOK;
					}
				}
			}
		}
	}
} /* sGet_POP3_login */


/* -----------------------------------------------------------------
	sGet_acct_loc ()
   ----------------------------------------------------------------- */

void sGet_acct_loc ( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts )
{
	bool			bFound		= FALSE;
	CFStringRef		cfStringRef	= NULL;
	char		   *pValue		= NULL;

	/* Default value */
	inOutOpts->fAccountLoc[ 0 ] = '\0';

	bFound = CFDictionaryContainsKey( inCFDictRef, CFSTR( kXMLKeyAcctLoc ) );
	if ( bFound == true )
	{
		cfStringRef = (CFStringRef)CFDictionaryGetValue( inCFDictRef, CFSTR( kXMLKeyAcctLoc ) );
		if ( cfStringRef != NULL )
		{
			if ( CFGetTypeID( cfStringRef ) == CFStringGetTypeID() )
			{
				pValue = (char*)CFStringGetCStringPtr( cfStringRef, kCFStringEncodingMacRoman );
				if ( (pValue != NULL) && (strlen( pValue ) < kONE_K_BUF) )
				{
					if ( inOutOpts->fAccountLoc != NULL )
					{
						strcpy( inOutOpts->fAccountLoc, pValue );
					}
				}
			}
		}
	}
} /* sGet_acct_loc */


/* -----------------------------------------------------------------
	sGet_alt_loc ()
   ----------------------------------------------------------------- */

void sGet_alt_loc ( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts )
{
	bool			bFound		= FALSE;
	CFStringRef		cfStringRef	= NULL;
	char		   *pValue		= NULL;

	/* Default value */
	inOutOpts->fAltDataLoc[ 0 ] = '\0';

	bFound = CFDictionaryContainsKey( inCFDictRef, CFSTR( kXMLKeyAltDataStoreLoc ) );
	if ( bFound == true )
	{
		cfStringRef = (CFStringRef)CFDictionaryGetValue( inCFDictRef, CFSTR( kXMLKeyAltDataStoreLoc ) );
		if ( cfStringRef != NULL )
		{
			if ( CFGetTypeID( cfStringRef ) == CFStringGetTypeID() )
			{
				pValue = (char*)CFStringGetCStringPtr( cfStringRef, kCFStringEncodingMacRoman );
				if ( (pValue != NULL) && (strlen( pValue ) < kONE_K_BUF) )
				{
					if ( inOutOpts->fAltDataLoc != NULL )
					{
						strcpy( inOutOpts->fAltDataLoc, pValue );
					}
				}
			}
		}
	}
} /* sGet_alt_loc */


/* -----------------------------------------------------------------
	sGet_disk_quota ()
   ----------------------------------------------------------------- */

void sGet_disk_quota ( CFDictionaryRef inCFDictRef, struct od_user_opts *inOutOpts )
{
	bool			bFound		= FALSE;
	CFStringRef		cfStringRef	= NULL;
	char		   *pValue		= NULL;

	/* Default value */
	inOutOpts->fDiskQuota = 0;

	bFound = CFDictionaryContainsKey( inCFDictRef, CFSTR( kXMLKeyDiskQuota ) );
	if ( bFound == true )
	{
		cfStringRef = (CFStringRef)CFDictionaryGetValue( inCFDictRef, CFSTR( kXMLKeyDiskQuota ) );
		if ( cfStringRef != NULL )
		{
			if ( CFGetTypeID( cfStringRef ) == CFStringGetTypeID() )
			{
				pValue = (char *)CFStringGetCStringPtr( cfStringRef, kCFStringEncodingMacRoman );
				if ( pValue != NULL )
				{
					inOutOpts->fDiskQuota = atol( pValue );
				}
			}
		}
	}
} /* sGet_disk_quota */


/* -----------------------------------------------------------------
	sSetErrorText ()
   ----------------------------------------------------------------- */

void sSetErrorText ( eErrorType inErrType, int inError, const char *inStr )
{
	const char *pErrText	= NULL;

	if ( inErrType <= eMaxErrors )
	{
		pErrText = odText[ inErrType ];

		if ( inStr != NULL )
		{
			sprintf( gErrStr, pErrText, inStr, inError );
		}
		else
		{
			sprintf( gErrStr, pErrText, inError );
		}
	}
	else
	{
		sprintf( gErrStr, "Unknown error type: %d", inErrType );
	}
} /* sSetErrorText */

