/*
 * This is backwards compatibility for CCache API v2 clients to be able to run 
 * against the CCache API v3 library
 *
 * The tricky parts in the compatibility layer are:
 *   - layout of credentials structures changed. we have to remap one to the other in
 *     the compat layer
 *   - semantics of ccaches changed: v2 clients expect one type of credentials per
 *     ccache. ccache iterators in the compat layer have to return dual ccaches twice
 *     (once for v4, once for v5). credentials iterators have to return only a subset
 *     of the credentials that the caller wants.
 */

#include <string.h>

#include "CCache.config.h"

#include "CCache2.h"
 
#include "CCache.internal.h"
#include "CCache.debug.h"
#include "CCache2Compat.h"

#include "CCICCache.h"
#include "CCICCacheIterator.h"
#include "CCIString.h"
#include "CCIPointer.h"
#include "CCICredentialsIterator.h"

#if CCache_v2_compat

cc_result	cc_remap_error (cc_result	inNewError);

cc_result	cc_remap_error (cc_result	inNewError) {
	switch (inNewError) {
		case ccNoError:
			return CC_NOERROR;
		
		case ccIteratorEnd:
			return CC_END;
		
		case ccErrBadParam:
		case ccErrInvalidCredentials:
		case ccErrInvalidCCacheIterator:
		case ccErrInvalidCredentialsIterator:
		case ccErrBadLockType:
			return CC_BAD_PARM;
			
		case ccErrNoMem:
			return CC_NOMEM;
		
		case ccErrInvalidContext:
		case ccErrInvalidCCache:
		case ccErrCCacheNotFound:
			return CC_NO_EXIST;
		
		case ccErrCredentialsNotFound:
			return CC_NOTFOUND;
		
		case ccErrBadName:
			return CC_BADNAME;
			
		case ccErrContextLocked:
		case ccErrContextUnlocked:
		case ccErrCCacheLocked:
		case ccErrCCacheUnlocked:
			return CC_LOCKED;
			
		default:
			CCISignal_ ("Unhandled error in cc_remap_error.");
			return CC_BAD_PARM;
	}
}


cc_result cc_shutdown (
	apiCB**		ioContext) {
	
	cc_result result = cc_context_release (*ioContext);
	
	if (result == ccNoError) {
		*ioContext = nil;
	}
	
	result = cc_remap_error (result);
	
	CCIAssert_ ((result == CC_NOERROR) ||
				(result == CC_NO_EXIST) ||
				(result == CC_BAD_PARM));
	
	return result;
}

cc_result cc_get_NC_info (
	apiCB*			inContext,
	infoNC***		outInfo)
{
	cc_result	result = ccNoError;

	ccache_cit*	iterator = NULL;
	infoNC**	newArray = NULL;
	ccache_p*	ccache = NULL;

	// Preflight the size 
	UInt32		numCaches = 0;

	result = cc_seq_fetch_NCs_begin (inContext, &iterator);
	if (result != CC_NOERROR)
		goto end;

	for (;;) {
		result = cc_seq_fetch_NCs_next (inContext, &ccache, iterator);
		if (result == CC_END) {
			break;
		} else if (result != CC_NOERROR) {
			goto end;
		}
		
		numCaches++;
		cc_close (inContext, &ccache);
		ccache = NULL;
	}
	
	result = cc_seq_fetch_creds_end (inContext, &iterator);
	if (result != CC_NOERROR)
		goto end;
	
	
	// Done preflighting, allocate the array
	CCIBeginSafeTry_ {
		newArray = new infoNC* [numCaches + 1];
		for (UInt32 i = 0; i < numCaches + 1; i++) {
			newArray [i] = NULL;
		}
		
		// Fill the array	
		result = cc_seq_fetch_NCs_begin (inContext, &iterator);
		if (result != CC_NOERROR)
			goto end;

		for (UInt32 j = 0; j < numCaches; j++) {
			result = cc_seq_fetch_NCs_next (inContext, &ccache, iterator);
			if (result == CC_END) {
				break;
			} else if (result != CC_NOERROR) {
				goto end;
			}

			infoNC*		newItem = new infoNC;
			newItem -> name = NULL;
			newItem -> principal = NULL;
			newItem -> vers = CC_CRED_UNKNOWN;
			
			newArray [j] = newItem;

			result = cc_get_name (inContext, ccache, &newItem -> name);
			if (result != CC_NOERROR) {
				goto end;
			}
				
			result = cc_get_principal (inContext, ccache, &newItem -> principal);
			if (result != CC_NOERROR) {
				goto end;
			}
			
			result = cc_get_cred_version (inContext, ccache, &newItem -> vers);
			if (result != CC_NOERROR) {
				goto end;
			}
					
			cc_close (inContext, &ccache);
			ccache = NULL;
		}
		
		result = cc_seq_fetch_creds_end (inContext, &iterator);
		if (result != CC_NOERROR)
			goto end;

		*outInfo = newArray;
	} catch (std::bad_alloc&) {
		result = CC_NOMEM;
		goto end;
	} catch (...) {
		result = CC_BAD_PARM;
		goto end;
	}
	
end:
	if ((newArray != NULL) && (result != CC_NOERROR))
		cc_free_NC_info (inContext, &newArray);
	if (iterator != NULL)
		cc_seq_fetch_NCs_end (inContext, &iterator);
	if (ccache != NULL) 
		cc_close (inContext, &ccache);
		
	CCIAssert_ ((result == CC_NOERROR) ||
				(result == CC_NO_EXIST) ||
				(result == CC_NOMEM) ||
				(result == CC_BAD_PARM));

	return result;
}
	

/*cc_result cc_get_NC_info (
	apiCB*			inContext,
	infoNC***		outInfo) {
	
#pragma unused (inContext)

	// This function has no counterpart in CCAPI v3
	infoNC** info = nil;
	cc_uint32 i;
	
	cc_result result = ccNoError;
	
	// Note that we overallocate here. We are creating the initial array to be
	// able to accomodate one v4 and one v5 ccache for each ccache in the list
	// +1 for the terminating null. That's okay, because the specification says
	// "NULL terminated array of pointers", so other the potential waste of space
	// there is no problem
	info = (infoNC**) cci_malloc ((2 * gGlobalCCacheData.numCCaches + 1) * sizeof (infoNC*));
	
	if (info == nil) {
		result = ccErrNoMem;
	} else {
		for (i = 0; i < 2 * gGlobalCCacheData.numCCaches + 1; i++) {
			info [i] = nil;
		}
	}

	
	if (result == ccNoError) {
		// Note that this loop us pretty perverse. With i we are indexing over the array
		// allocated above, and ccache data is walking the list of ccaches. However,
		// a single ccachemight generate more than one array entry (because of
		// ccache duality in ccapi v3), so ccacheData is not advanced every time.
		// Throughout, seenCount is the number of times the ccache at ccacheData has
		// already been entered in the array (seenCount = 0 or 1)

		cci_ccache_data* ccacheData = gGlobalCCacheData.ccacheListHead;
		cc_int32 seenCount = 0;
		cc_bool advance = cc_false;
		
		for (i = 0; ccacheData != nil; i++) {
			char* principal;
			cc_int32 version;

			CCIAssert_ (i < 2 * gGlobalCCacheData.numCCaches);
			CCIAssert_ ((seenCount == 0) || (seenCount == 1));
			
			// See if we are adding the v4 principal, which happens only the first
			// time we see a ccache that has a v4 principal
			if ((seenCount == 0) && (ccacheData -> v4principal != nil)) {
				// We haven't seen this ccache already
				// If it has a v4 principal, we will add it now
				if (ccacheData -> v4principal != nil) {
					principal = ccacheData -> v4principal;
					version = CC_CRED_V4;
					
					// If it _only_ has v4 principal, we will advance the pointer
					// for the next iteration
					if (ccacheData -> v5principal == nil) {
						advance = cc_true;
					} else {
						advance = cc_false;
					}
				}
			} else {
				// We are adding a v5 principal, either because this is the
				// second time we are seeing this ccache, or because it has no v4
				// principal
				CCIAssert_ (ccacheData -> v5principal != nil);
				principal = ccacheData -> v5principal;
				advance = cc_true;
				version = CC_CRED_V5;
			}
					

			info [i] = (infoNC*) cci_malloc (sizeof (infoNC));
			if (info [i] == nil) {
				result = ccErrNoMem;
				break;
			}
			info [i] -> name = info [i] -> principal = nil;
			info [i] -> vers = version;
			
			result = cci_string_copy (principal, &info [i] -> principal);
			result = cci_string_copy (ccacheData -> name, &info [i] -> name);
			
			if (advance) {
				ccacheData = ccacheData -> next;
				seenCount = 0;
			} else {
				seenCount++;
			}
		}
	}
	
	if (result == ccNoError) {
		*outInfo = info;
	}
	
	if (result != ccNoError) {
		cci_compat_deep_free_NC_info (info);
	}
		
	result = cc_remap_error (result);
	
	CCIAssert_ ((result == CC_NOERROR) ||
				(result == CC_NO_EXIST) ||
				(result == CC_NOMEM) ||
				(result == CC_BAD_PARM));
	
	return result;
}*/

cc_result cc_get_change_time (
	apiCB*		inContext,
	cc_time_t*	outTime) {
	
	cc_result result = cc_context_get_change_time (inContext, outTime);
	
	result = cc_remap_error (result);
	
	CCIAssert_ ((result == CC_NOERROR) ||
				(result == CC_NO_EXIST) ||
				(result == CC_NOMEM) ||
				(result == CC_BAD_PARM));
	
	return result;
}

cc_int32 cc_open (
	apiCB*		inContext,
	const char*	inName,
	cc_int32	inVersion,
	cc_uint32	/* inFlags */,
	ccache_p**	outCCache) {

	cc_ccache_t	returnedCCache = nil;
	cc_result result = ccNoError;
	
	if ((inVersion != CC_CRED_V4) && (inVersion != CC_CRED_V5)) {
		result = ccErrBadCredentialsVersion;
	}
	
	if (result == ccNoError) {
		result = cc_context_open_ccache (inContext, inName, &returnedCCache);
	}

	// We must not allow a CCAPI v2 caller to open a v5-only ccache
	// as a v4 ccache and vice versa. Allowing that would break (valid)
	// assumptions made by CCAPI v2 callers.
	if (result == ccNoError) {
		CCIBeginSafeTry_ {
			StCCache			ccache (returnedCCache);
			
			if (inVersion == CC_CRED_V4) {
				if ((ccache -> GetCredentialsVersion () & cc_credentials_v4) != 0) {
					ccache -> CompatSetVersion (cc_credentials_v4);
					*outCCache = ccache;
				} else {
					// ccache version mismatch, bail with an error
					cc_ccache_release (returnedCCache);
					result = ccErrInvalidCCache;
				}
			} else if (inVersion == CC_CRED_V5) {
				if ((ccache -> GetCredentialsVersion () & cc_credentials_v5) != 0) {
					ccache -> CompatSetVersion (cc_credentials_v5);
					*outCCache = ccache;
				} else {
					// ccache version mismatch, bail with an error
					cc_ccache_release (returnedCCache);
					result = ccErrInvalidCCache;
				}
			} else {
				result = ccErrBadCredentialsVersion;
			}
		} CCIEndSafeTry_ (result, ccErrBadParam)
	}
	
	result = cc_remap_error (result);
	
	CCIAssert_ ((result == CC_NOERROR) ||
				(result == CC_BADNAME) ||
				(result == CC_ERR_CRED_VERSION) ||
				(result == CC_NO_EXIST) ||
				(result == CC_NOMEM) ||
				(result == CC_BAD_PARM));
	
	return result;
}

cc_result cc_create (
	apiCB*		inContext,
	const char*	inName,
	const char*	inPrincipal,
	cc_int32	inVersion,
	cc_uint32	/* inFlags */,
	ccache_p**	outCCache) {
	
	cc_result result =
		cc_context_create_ccache (inContext, inName, inVersion, inPrincipal, outCCache);
	
	if (result == ccNoError) {
		CCIBeginSafeTry_ {
			StCCache		ccache (*outCCache);
			if (inVersion == CC_CRED_V4) {
				ccache -> CompatSetVersion (cc_credentials_v4);
			} else if (inVersion == CC_CRED_V5) {
				ccache -> CompatSetVersion (cc_credentials_v5);
			} else {
				result = ccErrBadCredentialsVersion;
			}
		} CCIEndSafeTry_ (result, ccErrBadParam)
	}
	
	result = cc_remap_error (result);
	
	CCIAssert_ ((result == CC_NOERROR) ||
				(result == CC_BADNAME) ||
				(result == CC_ERR_CRED_VERSION) ||
				(result == CC_NO_EXIST) ||
				(result == CC_NOMEM) ||
				(result == CC_BAD_PARM));
	
	return result;
}

cc_result cc_close (
	apiCB*		/* inContext */,
	ccache_p**	ioCCache) {
	
	cc_result result = cc_ccache_release (*ioCCache);
	
	if (result == ccNoError) {
		*ioCCache = nil;
	}
	result = cc_remap_error (result);
	
	CCIAssert_ ((result == CC_NOERROR) ||
				(result == CC_NO_EXIST) ||
				(result == CC_BAD_PARM));
	
	return result;
}

cc_result cc_destroy (
	apiCB*		/* inContext */,
	ccache_p**	ioCCache) {
	
	cc_result result = cc_ccache_destroy (*ioCCache);
	
	if (result == ccNoError) {
		*ioCCache = nil;
	}

	result = cc_remap_error (result);
	
	CCIAssert_ ((result == CC_NOERROR) ||
				(result == CC_NO_EXIST) ||
				(result == CC_BAD_PARM));
	
	return result;
}

// CCache iterators need to return some ccaches twice (when v3 ccache has
// two kinds of credentials). To do that, we use a single v3 iterator, but
// sometimes don't advance it.

cc_result cc_seq_fetch_NCs_begin (
	apiCB*				inContext,
	ccache_cit**		outIterator) {
	
	cc_result result = cc_context_new_ccache_iterator (inContext, (ccache_cit_ccache**) outIterator);
	
	result = cc_remap_error (result);
	
	CCIAssert_ ((result == CC_NOERROR) ||
				(result == CC_NOMEM) ||
				(result == CC_NO_EXIST) ||
				(result == CC_BAD_PARM));
	
	return result;
}

cc_result cc_seq_fetch_NCs_next (
	apiCB*				/* inContext */,
	ccache_p**			outCCache,
	ccache_cit*			inIterator) {
	
	CCIResult	result = ccNoError;
	
	CCIBeginSafeTry_ {
		StCCacheIterator			iterator (reinterpret_cast <ccache_cit_ccache*> (inIterator));
		
		if (iterator -> HasMore ()) {
			StPointer <cc_ccache_t>		newCCache (outCCache);

			// We've never returned this ccache before
			if (iterator -> CompatGetRepeatCount () == 0) {
				StCCache					current =
					new CCICCache (iterator -> Current ());
				newCCache = current;
				iterator -> CompatIncrementRepeatCount ();

				// We are opening it in single-version mode v4 if possible first
				if ((current -> GetCredentialsVersion () & cc_credentials_v4) != 0) {
					current -> CompatSetVersion (cc_credentials_v4);
				} else {
					current -> CompatSetVersion (cc_credentials_v5);
				}

			// We've returned this cache once before
			} else {
				CCICCache		current (iterator -> Current ());
				
				CCIUInt32		version = cc_credentials_none;
				bool			alreadyDeleted = false;
				// It is possible that the ccache has been deleted since our first visit.
				// If that's the case, GetCredentialsVersion will throw an appropriate
				// error. We handle that case by proceeding to the next ccache
				try {
					version = current.GetCredentialsVersion ();
				} catch (CCIException& e) {
					// If we encounter an error other than ccErrCCacheNotFound, we
					// don't know what to do with it, so we rethrow. 
					if (e.Error () != ccErrCCacheNotFound)
						throw;
					else
						alreadyDeleted = true;
				}
				
				// The ccache has only one version, or it has been deleted; go to next
				if (version != cc_credentials_v4_v5) {
					iterator -> Next ();
					StCCache	next = new CCICCache (iterator -> Current ());
					newCCache = next;

					// We are opening it in single-version mode v4 if possible first
					if ((next -> GetCredentialsVersion () & cc_credentials_v4) != 0) {
						next -> CompatSetVersion (cc_credentials_v4);
					} else {
						next -> CompatSetVersion (cc_credentials_v5);
					}

					iterator -> CompatResetRepeatCount ();
					iterator -> CompatIncrementRepeatCount ();
					
				// The ccache has two versions, and we are seeing it the second time, so
				// it must be opened in v5 mode
				} else {
					StCCache	ccache = new CCICCache (current.GetCCache ());
					ccache -> CompatSetVersion (cc_credentials_v5);

					newCCache = ccache;
				}
			}
		} else {
			result = ccIteratorEnd;
		}
	} CCIEndSafeTry_ (result, ccErrBadParam)

	result = cc_remap_error (result);
	
	CCIAssert_ ((result == CC_NOERROR) ||
				(result == CC_END) ||
				(result == CC_NOMEM) ||
				(result == CC_NO_EXIST) ||
				(result == CC_BAD_PARM));
	
	return result;
}

cc_result cc_seq_fetch_NCs_end (
	apiCB*				/* inContext */,
	ccache_cit**		ioIterator) {
	
	cc_result result = cc_ccache_iterator_release (*(ccache_cit_ccache**)ioIterator);
	
	if (result == ccNoError) {
		*ioIterator = nil;
	}

	result = cc_remap_error (result);
	
	CCIAssert_ ((result == CC_NOERROR) ||
				(result == CC_NO_EXIST) ||
				(result == CC_BAD_PARM));
	
	return result;
}

cc_result cc_get_name (
	apiCB*		/* inContext */,
	ccache_p*	inCCache,
	char**		outName) {

	cc_result	result = ccNoError;
	
	CCIBeginSafeTry_ {
		StCCache			ccache (inCCache);
		std::string			ccacheName = ccache -> GetName ();
		StPointer <char*>	name (outName);
		
		char*				newName = new char [ccacheName.length () + 1];
		ccacheName.copy (newName, ccacheName.length ());
		newName [ccacheName.length ()] = '\0';
		
		name = newName;
		
	} CCIEndSafeTry_ (result, ccErrBadParam)

	result = cc_remap_error (result);
	
	CCIAssert_ ((result == CC_NOERROR) ||
				(result == CC_NOMEM) ||
				(result == CC_NO_EXIST) ||
				(result == CC_BAD_PARM));
	
	return result;
}

cc_result cc_get_cred_version (
	apiCB*		/* inContext */,
	ccache_p*	inCCache,
	cc_int32*	outVersion) {
	
	cc_result	result = ccNoError;
	
	CCIBeginSafeTry_ {
		StCCache				ccache (inCCache);
		StPointer <cc_int32>	version (outVersion);
		
		if (ccache -> CompatGetVersion () == cc_credentials_v4) {
			version = CC_CRED_V4;
		} else {
			version = CC_CRED_V5;
		}
	} CCIEndSafeTry_ (result, ccErrBadParam);

	result = cc_remap_error (result);
	
	CCIAssert_ ((result == CC_NOERROR) ||
				(result == CC_NO_EXIST) ||
				(result == CC_BAD_PARM));
	
	return result;
}

cc_result cc_set_principal (
	apiCB*			/* inContext */,
	ccache_p*		inCCache,
	cc_int32		inVersion,
	char*			inPrincipal) {
	
	// In ccapi v3 set_principal also deleted credentials. That wasn't the case
	// in ccapi v2, so we can't just call through
	
	CCIResult	result = ccNoError;

	CCIBeginSafeTry_ {
		StCCache					ccache (inCCache);
		ccache -> CompatSetPrincipal (inVersion, inPrincipal);

	} CCIEndSafeTry_ (result, ccErrBadParam) 
	
	result = cc_remap_error (result);
	
	CCIAssert_ ((result == CC_NOERROR) ||
				(result == CC_NO_EXIST) ||
				(result == CC_ERR_CRED_VERSION) ||
				(result == CC_NOMEM) ||
				(result == CC_BAD_PARM));
	
	return result;
}
	
cc_result cc_get_principal (
	apiCB*		/* inContext */,
	ccache_p*	inCCache,
	char**		outPrincipal) {

	cc_result	result = ccNoError;
	
	CCIBeginSafeTry_ {
		StCCache			ccache (inCCache);
		std::string			ccachePrincipal = ccache -> GetPrincipal (ccache -> CompatGetVersion ());
		StPointer <char*>	principal (outPrincipal);
		
		char*				newPrincipal = new char [ccachePrincipal.length () + 1];
		ccachePrincipal.copy (newPrincipal, ccachePrincipal.length ());
		newPrincipal [ccachePrincipal.length ()] = '\0';
		
		principal = newPrincipal;
	} CCIEndSafeTry_ (result, ccErrBadParam)

	result = cc_remap_error (result);
	
	CCIAssert_ ((result == CC_NOERROR) ||
				(result == CC_NOMEM) ||
				(result == CC_NO_EXIST) ||
				(result == CC_BAD_PARM));
	
	return result;
}

cc_result cc_store (
	apiCB*			/* inContext */,
	ccache_p*		inCCache,
	cred_union		inCredentials) {
	
	CCIResult	result = ccNoError;

	CCIBeginSafeTry_ {
		StCCache					ccache (inCCache);
		ccache -> CompatStoreCredentials (inCredentials);
	} CCIEndSafeTry_ (result, ccErrBadParam) 

	result = cc_remap_error (result);
	
	// v2 spec says we can't return CC_NOMEM (cool, huh?)
	if (result == CC_NOMEM)
		result = CC_ERR_CACHE_FULL;
	
	CCIAssert_ ((result == CC_NOERROR) ||
				(result == CC_ERR_CRED_VERSION) ||
				(result == CC_ERR_CACHE_FULL) ||
				(result == CC_NO_EXIST) ||
				(result == CC_BAD_PARM));
	
	return result;
}
	
	
// In CCAPI v2, you create fake credentials and pass them in; the library
// compares them with all the credentials and removes the appropriate ones.
// In CCAPI v3, you can only remove what you got out of an iterator.
// Unfortunately, we need to fake some error codes...
cc_result cc_remove_cred (
	apiCB*			/* inContext */,
	ccache_p*		inCCache,
	cred_union		inCredentials) {
	
	cc_result	result = ccNoError;
	cc_credentials_iterator_t iterator;
	cc_credentials_t creds;
	
	result = cc_ccache_new_credentials_iterator (inCCache, &iterator);
	if (result != ccNoError)
		result = CC_NOTFOUND;
	
	if (result == noErr) {
		for (;;) {
			result = cc_credentials_iterator_next (iterator, &creds);
			if (result != ccNoError) {
				result = CC_NOTFOUND;
				break;
			}
			
			if (cci_compat_credentials_equal (&inCredentials, creds -> data)) {
				result = cc_ccache_remove_credentials (inCCache, creds);
				cc_credentials_release (creds);
				if (result != noErr)
					result = CC_NOTFOUND;
				
				break;
			}
		}
	}
	
	if (result == ccNoError)
		result = CC_NOERROR;
	
	CCIAssert_ ((result == CC_NOERROR) ||
				(result == CC_NOTFOUND) ||
				(result == CC_ERR_CACHE_FULL) ||
				(result == CC_NO_EXIST) ||
				(result == CC_BAD_PARM));
	
	return result;
}	
	
cc_result cc_seq_fetch_creds_begin (
	apiCB*				/* inContext */,
	const ccache_p*		inCCache,
	ccache_cit**		outIterator) {
	
	cc_result result = cc_ccache_new_credentials_iterator ((ccache_p*) inCCache, (ccache_cit_creds**) outIterator);
	
	result = cc_remap_error (result);
	
	CCIAssert_ ((result == CC_NOERROR) ||
				(result == CC_NOMEM) ||
				(result == CC_NO_EXIST) ||
				(result == CC_BAD_PARM));
	
	return result;
}

cc_result cc_seq_fetch_creds_next (
	apiCB*				/* inContext */,
	cred_union**		outCreds,
	ccache_cit*			inIterator) {
	
	CCIResult	result = ccNoError;
	
	CCIBeginSafeTry_ {
		StCredentialsIterator			iterator (reinterpret_cast <ccache_cit_creds*> (inIterator));
		StPointer <cred_union*>			newCredentials (outCreds);

		bool found = false;

		// Find creds of the appropriate version
		while (iterator -> HasMore ()) {
			StCompatCredentials					credentials =
				new CCICompatCredentials (iterator -> Next ());
					
			if ((credentials -> GetCredentialsVersion () & iterator -> CompatGetVersion ()) != 0) {
				found = true;
				newCredentials = credentials;
				break;
			}

			delete credentials.Get ();
		}

		if (!found) {
			result = ccIteratorEnd;
		}
	} CCIEndSafeTry_ (result, ccErrBadParam)
	
	result = cc_remap_error (result);
	
	CCIAssert_ ((result == CC_NOERROR) ||
				(result == CC_END) ||
				(result == CC_NOMEM) ||
				(result == CC_NO_EXIST) ||
				(result == CC_BAD_PARM));
	
	return result;
}

cc_result cc_seq_fetch_creds_end (
	apiCB*				/* inContext */,
	ccache_cit**		ioIterator) {
	
	cc_result result = cc_credentials_iterator_release (*(ccache_cit_creds**)ioIterator);
	
	if (result == ccNoError) {
		*ioIterator = nil;
	}
	
	result = cc_remap_error (result);
	
	CCIAssert_ ((result == CC_NOERROR) ||
				(result == CC_NO_EXIST) ||
				(result == CC_BAD_PARM));
	
	return result;
}

cc_result cc_free_principal (
	apiCB*			/* inContext */,
	char**			ioPrincipal) {
	
	delete [] *ioPrincipal;
	*ioPrincipal = nil;
	return CC_NOERROR;
}

cc_result cc_free_name (
	apiCB*			/* inContext */,
	char**			ioName) {
	
	delete [] *ioName;
	*ioName = nil;
	return CC_NOERROR;
}

cc_result cc_free_creds (
	apiCB*			/* inContext */,
	cred_union**	inCredentials) {
	
	CCIResult	result = ccNoError;
	
	CCIBeginSafeTry_ {
		StCompatCredentials			credentials (*inCredentials);
		
		delete credentials.Get ();
		*inCredentials = nil;
		return CC_NOERROR;
	} CCIEndSafeTry_ (result, ccErrBadParam)
	
	result = cc_remap_error (result);
	
	CCIAssert_ (result == CC_NOERROR);
	
	return result;
}

cc_result cc_free_NC_info (
	apiCB*			inContext,
	infoNC***		ioInfo) {
	
	cci_compat_deep_free_NC_info (inContext, *ioInfo);
	
	*ioInfo = nil;
	
	return CC_NOERROR;
}

void cci_compat_deep_free_NC_info (
	apiCB*			inContext,
	infoNC**		data) {
	
	cc_uint32 i;
	
	for (i = 0; data [i] != nil; i++) {
		cc_free_principal (inContext, &data [i] -> principal);
		cc_free_name (inContext, &data [i] -> name);
		delete (data [i]);
	}
	
	delete [] data;
}

cc_bool
cci_compat_credentials_equal (
	const cred_union*				inOldCreds,
	const cc_credentials_union*		inNewCreds) {

	cc_credentials_v4_compat*		oldCredsV4;
	cc_credentials_v4_t*			newCredsV4;
	cc_credentials_v5_compat*		oldCredsV5;
	cc_credentials_v5_t*			newCredsV5;
	
	CCIAssert_ ((inOldCreds -> cred_type == CC_CRED_V4) || (inOldCreds -> cred_type == CC_CRED_V5));
	CCIAssert_ ((inNewCreds -> version == cc_credentials_v4) || (inNewCreds -> version == cc_credentials_v5));
	
	if (((inOldCreds -> cred_type == CC_CRED_V4) && (inNewCreds -> version != cc_credentials_v4)) ||
	    ((inOldCreds -> cred_type == CC_CRED_V5) && (inNewCreds -> version != cc_credentials_v5))) {
	    return false;
	}
	
	// if one of the credentials is NULL, then match is false		
	if (inOldCreds -> cred_type == CC_CRED_V4) {
		oldCredsV4 = inOldCreds -> cred.pV4Cred;
		newCredsV4 = inNewCreds -> credentials.credentials_v4;
		
		if ((oldCredsV4 == NULL) || (newCredsV4 == NULL))
			return cc_false;
		
		if ((strcmp (oldCredsV4 -> principal, newCredsV4 -> principal) == 0) &&
		    (strcmp (oldCredsV4 -> principal_instance, newCredsV4 -> principal_instance) == 0) &&
		    (strcmp (oldCredsV4 -> realm, newCredsV4 -> realm) == 0) &&
		    (oldCredsV4 -> issue_date == newCredsV4 -> issue_date))
			return cc_true;
		else
			return cc_false;
	} else if (inOldCreds -> cred_type == CC_CRED_V5) {
		// for v5 creds, we are doing SRVNAME_ONLY | EXACT_TIMES comparison
		
		oldCredsV5 = inOldCreds -> cred.pV5Cred;
		newCredsV5 = inNewCreds -> credentials.credentials_v5;
		
		if ((oldCredsV5 == NULL) || (oldCredsV5 == NULL))
			return cc_false;

		// I am not sure this is right! The correct thing to do is is krb5_parse and krb5_compare
		if ((strcmp (oldCredsV5 -> client, newCredsV5 -> client) == 0) &&
		    (strcmp (oldCredsV5 -> server, oldCredsV5 -> server) == 0) &&
		    (oldCredsV5 -> starttime == newCredsV5 -> starttime))
			return cc_true;
		else
			return cc_false;
	}
	
	return cc_false;
}


#endif // CCache_v2_compat