/* cfmglue.c
   by Rolf Braun, for CMU SASL on Mac OS X

   This file provides routines to allow CFM (os 9 linkage) Carbon applications
   to use the native Mach-O SASL libraries on Mac OS X, using CFBundle to
   load the backend libraries and automatically allocated assembly callbacks
   $Id: cfmglue.c,v 1.1 2004/03/31 18:08:41 dasenbro Exp $
*/
/* 
 * Copyright (c) 1998-2003 Carnegie Mellon University.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer. 
 *
 * 2. 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.
 *
 * 3. The name "Carnegie Mellon University" must not be used to
 *    endorse or promote products derived from this software without
 *    prior written permission. For permission or any other legal
 *    details, please contact  
 *      Office of Technology Transfer
 *      Carnegie Mellon University
 *      5000 Forbes Avenue
 *      Pittsburgh, PA  15213-3890
 *      (412) 268-4387, fax: (412) 268-7395
 *      tech-transfer@andrew.cmu.edu
 *
 * 4. Redistributions of any form whatsoever must retain the following
 *    acknowledgment:
 *    "This product includes software developed by Computing Services
 *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
 *
 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <Carbon.h>
#include <string.h>
#include <stdlib.h>
#include "sasl.h"
#include "saslutil.h"
#include "prop.h"

/* prototypes for internal functions (from saslint.h) */
int _sasl_add_string(char **out, int *alloclen, int *outlen, const char *add);
int _buf_alloc(char **rwbuf, unsigned *curlen, unsigned newlen);
void _sasl_get_errorbuf(sasl_conn_t *conn, char ***bufhdl, unsigned **lenhdl);

void	*MachOFunctionPointerForCFMFunctionPointer( void *cfmfp );
sasl_callback_t *GetCFMCallbacks(const sasl_callback_t *callbacks);
void DisposeCFMCallbacks(sasl_callback_t *callbacks);

int _cfmsasl_haveCustomAlloc = 0;
int _cfmsasl_haveCustomMutex = 0;
int _cfmsasl_initted = 0;

/* DO NOT change the order of this struct! It MUST match that
   of the iovec struct in /usr/include/sys/uio.h on Mac OS X!
   It MUST also match that of the Mac OS 9 Carbon config.h since
   Carbon CFM stuff links at runtime against whatever it's running on! */
struct iovec {
    char *iov_base; 
    long iov_len;
};

typedef struct			
{
	CFURLRef		bundleURL;
	CFBundleRef		myBundle;
	sasl_callback_t *clientCallbacks;
	sasl_callback_t *serverCallbacks;
	int (*SASLClientNewPtr)(const char *service,
							const char *serverFQDN,
							const char *iplocalport,
							const char *ipremoteport,
							const sasl_callback_t *prompt_supp, 
							unsigned flags,
							sasl_conn_t **pconn);
	int (*SASLClientStartPtr)(sasl_conn_t *conn,
								const char *mechlist,
								sasl_interact_t **prompt_need,
								const char **clientout,
								unsigned *clientoutlen,
								const char **mech);
	int (*SASLClientStepPtr)(sasl_conn_t *conn,
								const char *serverin,
								unsigned serverinlen,
								sasl_interact_t **prompt_need,
								const char **clientout,
								unsigned *clientoutlen);
	const char * (*SASLErrStringPtr)(int saslerr,
							const char *langlist,
							const char **outlang);
	const char *(*sasl_errdetailPtr)(sasl_conn_t *conn);
	int (*SASLGetPropPtr)(sasl_conn_t *conn, int propnum, const void **pvalue);
	int (*SASLSetPropPtr)(sasl_conn_t *conn,
							int propnum,
							const void *value);
	int (*SASLIdlePtr)(sasl_conn_t *conn);
	int (*SASLEncodePtr)(sasl_conn_t *conn,
							const char *input, unsigned inputlen,
							const char **output, unsigned *outputlen);
	int (*SASLDecodePtr)(sasl_conn_t *conn,
							const char *input, unsigned inputlen,
							const char **output, unsigned *outputlen);
	int (*SASLEncodeVPtr)(sasl_conn_t *conn,
			     const struct iovec *invec, unsigned numiov,
			     const char **output, unsigned *outputlen);
	int (*SASLDisposePtr)(sasl_conn_t **pconn);
	int (*SASLDonePtr)();
	void (*SASLSetAllocPtr)(sasl_malloc_t *, sasl_calloc_t *, sasl_realloc_t *, sasl_free_t *);
	int (*sasl_decode64Ptr)(const char *in, unsigned inlen,
			      char *out, unsigned outmax, unsigned *outlen);
	int (*sasl_encode64Ptr)(const char *in, unsigned inlen,
			      char *out, unsigned outmax, unsigned *outlen);
	int (*sasl_mkchalPtr)(sasl_conn_t *conn, char *buf,
			    unsigned maxlen, unsigned hostflag);
	int (*sasl_utf8verifyPtr)(const char *str, unsigned len);
	void (*sasl_churnPtr)(sasl_rand_t *rpool, 
			    const char *data,
			    unsigned len);
	void (*sasl_randPtr)(sasl_rand_t *rpool,
			   char *buf,
			   unsigned len);
	void (*sasl_randseedPtr)(sasl_rand_t *rpool,
			       const char *seed,
			       unsigned len);
	void (*sasl_randfreePtr)(sasl_rand_t **rpool);
	int (*sasl_randcreatePtr)(sasl_rand_t **rpool);
	void (*SASLSetMutexPtr)(sasl_mutex_alloc_t *mn, sasl_mutex_lock_t *ml,
                                sasl_mutex_unlock_t *mu, sasl_mutex_free_t *md);
	int (*SASLServerNewPtr)(const char *service,
						const char *serverFQDN,
						const char *user_realm,
						const char *iplocalport,
						const char *ipremoteport,
						const sasl_callback_t *callbacks, 
						unsigned flags,
						sasl_conn_t **pconn);
	int (*sasl_listmechPtr)(sasl_conn_t *conn,
			      const char *user,
			      const char *prefix,
			      const char *sep,
			      const char *suffix,
			      const char **result,
			      unsigned *plen,
			      int *pcount);
	int (*SASLServerStartPtr)(sasl_conn_t *conn,
				  const char *mech,
				  const char *clientin,
				  unsigned clientinlen,
				  const char **serverout,
				  unsigned *serveroutlen);
	int (*SASLServerStepPtr)(sasl_conn_t *conn,
		     const char *clientin,
		     unsigned clientinlen,
		     const char **serverout,
		     unsigned *serveroutlen);
	int (*sasl_checkpassPtr)(sasl_conn_t *conn,
			       const char *user,
			       unsigned userlen,
			       const char *pass,
			       unsigned passlen);
	int (*sasl_user_existsPtr)(sasl_conn_t *conn,
			 const char *service,
		     const char *user_realm,
		     const char *user);
	int (*sasl_setpassPtr)(sasl_conn_t *conn,
		 const char *user,
		 const char *pass,
		 unsigned passlen,
		 const char *oldpass, unsigned oldpasslen,
		 unsigned flags);
	int (*sasl_checkapopPtr)(sasl_conn_t *conn,
			       const char *challenge, unsigned challen,
			       const char *response, unsigned resplen);
	int (*sasl_auxprop_requestPtr)(sasl_conn_t *conn,
				     const char **propnames);
	struct propctx *(*sasl_auxprop_getctxPtr)(sasl_conn_t *conn);
	void (*sasl_erasebufferPtr)(char *pass, unsigned len);
	struct propctx *(*prop_newPtr)(unsigned estimate);
	int (*prop_dupPtr)(struct propctx *src_ctx, struct propctx **dst_ctx);
	const struct propval *(*prop_getPtr)(struct propctx *ctx);
	int (*prop_getnamesPtr)(struct propctx *ctx, const char **names,
		  struct propval *vals);
	void (*prop_clearPtr)(struct propctx *ctx, int requests);
	void (*prop_erasePtr)(struct propctx *ctx, const char *name);
	void (*prop_disposePtr)(struct propctx **ctx);
	int (*prop_formatPtr)(struct propctx *ctx, const char *sep, int seplen,
		char *outbuf, unsigned outmax, unsigned *outlen);
	int (*prop_setPtr)(struct propctx *ctx, const char *name,
	     const char *value, int vallen);
	int (*prop_setvalsPtr)(struct propctx *ctx, const char *name,
		 const char **values);
	int (*_sasl_add_stringPtr)(char **out, int *alloclen, int *outlen, const char *add);
	int (*_buf_allocPtr)(char **rwbuf, unsigned *curlen, unsigned newlen);
	void (*_sasl_get_errorbufPtr)(sasl_conn_t *conn, char ***bufhdl, unsigned **lenhdl);

} GlobalsRec;

typedef struct
{
	sasl_malloc_t *custMalloc;
	sasl_calloc_t *custCalloc;
	sasl_realloc_t *custRealloc;
	sasl_free_t *custFree;

	sasl_mutex_alloc_t *custMutexNew;
	sasl_mutex_lock_t *custMutexLock;
	sasl_mutex_unlock_t *custMutexUnlock;
	sasl_mutex_free_t *custMutexDispose;
} GlobalParamsRec;

typedef struct
{
	sasl_conn_t			*ctx;
	sasl_callback_t		*cbk;
} cfm_sasl_conn_t;

GlobalsRec	saslcfmglob;						//	The globals
GlobalParamsRec saslcfmglobp;

// From Apple sample code... (CarbonLib SDK, CFM->MachO->CFM)
//
//	This function allocates a block of CFM glue code which contains the instructions to call CFM routines
//
void	*MachOFunctionPointerForCFMFunctionPointer( void *cfmfp )
{
	UInt32 template[6] = {0x3D800000, 0x618C0000, 0x800C0000, 0x804C0004, 0x7C0903A6, 0x4E800420};
    UInt32	*mfp = (UInt32*) NewPtr( sizeof(template) );		//	Must later dispose of allocated memory
    mfp[0] = template[0] | ((UInt32)cfmfp >> 16);
    mfp[1] = template[1] | ((UInt32)cfmfp & 0xFFFF);
    mfp[2] = template[2];
    mfp[3] = template[3];
    mfp[4] = template[4];
    mfp[5] = template[5];
    MakeDataExecutable( mfp, sizeof(template) );
    return( mfp );
}

sasl_callback_t *GetCFMCallbacks(const sasl_callback_t *callbacks)
{
	int cbksize = 0;
	const sasl_callback_t *cbk = callbacks;
	sasl_callback_t *ncbk, *new_callbacks;
	while (cbk->id) {
		cbksize++; cbk++;
	}
	cbksize++; cbk = callbacks;

	ncbk = new_callbacks = (sasl_callback_t *)NewPtr(cbksize * sizeof(sasl_callback_t));
	if (!ncbk) return nil;
	while (cbksize--) {
		ncbk->id = cbk->id;
		ncbk->context = cbk->context;
		if (cbk->proc)
			ncbk->proc = MachOFunctionPointerForCFMFunctionPointer(cbk->proc);
		else ncbk->proc = nil;
		ncbk++; cbk++;
	}
	return new_callbacks;
}

void DisposeCFMCallbacks(sasl_callback_t *callbacks)
{
	sasl_callback_t *cbk = callbacks;
	if (!cbk) return;
	while (cbk->id) {
		if (cbk->proc) DisposePtr((Ptr)cbk->proc);
		cbk++;
	}
	DisposePtr((Ptr)callbacks);
}

int _cfmsasl_common_init(const sasl_callback_t *callbacks, int isServer, const char *appname);

int _cfmsasl_common_init(const sasl_callback_t *callbacks, int isServer, const char *appname)
{
	int (*SASLClientInitPtr)(const sasl_callback_t *callbacks);
	int (*SASLServerInitPtr)(const sasl_callback_t *callbacks, const char *appname);
	int result = SASL_NOMEM;

  if (!_cfmsasl_initted) {
// The skeleton for this code originally came from the CFM->MachO->CFM sample
// provided by Aple with the Carbon SDK. It shows how to use CFBundle to call MachO
// libraries from CFM and how to encapsulate callbacks into CFM.
	memset( &saslcfmglob, 0, sizeof(GlobalsRec) );						//	Initialize the globals

	//	Make a CFURLRef from the CFString representation of the bundle's path.
	//	See the Core Foundation URL Services chapter for details.
	saslcfmglob.bundleURL	= CFURLCreateWithFileSystemPath( 
				nil, 										 //	workaround for Radar # 2452789
				CFSTR("/Library/Frameworks/SASL2.framework"), //	hard coded path for sample
				0,
				true );
	if ( saslcfmglob.bundleURL != NULL )

	// Make a bundle instance using the URLRef.
	saslcfmglob.myBundle	= CFBundleCreate(
				NULL /* kCFAllocatorDefault */, 			//	workaround for Radar # 2452789
				saslcfmglob.bundleURL );

	if ( saslcfmglob.myBundle && CFBundleLoadExecutable( saslcfmglob.myBundle )) {	//	Try to load the executable from my bundle.

		// Now that the code is loaded, search for the functions we want by name.
		saslcfmglob.SASLClientNewPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_client_new") );

		saslcfmglob.SASLClientStartPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_client_start") );

		saslcfmglob.SASLClientStepPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_client_step") );

		saslcfmglob.SASLErrStringPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_errstring") );

		saslcfmglob.sasl_errdetailPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_errdetail") );

		saslcfmglob.SASLGetPropPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_getprop") );

		saslcfmglob.SASLSetPropPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_setprop") );

		saslcfmglob.SASLIdlePtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_idle") );

		saslcfmglob.SASLEncodePtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_encode") );

		saslcfmglob.SASLEncodeVPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_encodev") );

		saslcfmglob.SASLDecodePtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_decode") );

		saslcfmglob.SASLDisposePtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_dispose") );

		saslcfmglob.SASLDonePtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_done") );

		saslcfmglob.SASLSetAllocPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_set_alloc") );

		saslcfmglob.sasl_encode64Ptr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_encode64") );

		saslcfmglob.sasl_decode64Ptr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_decode64") );
		
		saslcfmglob.sasl_mkchalPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_mkchal") );

		saslcfmglob.sasl_utf8verifyPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_utf8verify") );

		saslcfmglob.sasl_churnPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_churn") );

		saslcfmglob.sasl_randPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_rand") );

		saslcfmglob.sasl_randseedPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_randseed") );

		saslcfmglob.sasl_randcreatePtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_randcreate") );

		saslcfmglob.sasl_randfreePtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_randfree") );

		saslcfmglob.SASLSetMutexPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_set_mutex") );

		saslcfmglob.SASLServerNewPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_server_new") );

		saslcfmglob.SASLServerStartPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_server_start") );

		saslcfmglob.SASLServerStepPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_server_step") );

		saslcfmglob.sasl_listmechPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_listmech") );

		saslcfmglob.sasl_checkpassPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_checkpass") );

		saslcfmglob.sasl_setpassPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_setpass") );

		saslcfmglob.sasl_user_existsPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_user_exists") );

		saslcfmglob.sasl_checkapopPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_checkapop") );

		saslcfmglob.sasl_auxprop_requestPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_auxprop_request") );

		saslcfmglob.sasl_auxprop_getctxPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_auxprop_getctx") );

		saslcfmglob.sasl_erasebufferPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_erasebuffer") );

		saslcfmglob.prop_newPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("prop_new") );

		saslcfmglob.prop_dupPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("prop_dup") );

		saslcfmglob.prop_getPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("prop_get") );

		saslcfmglob.prop_getnamesPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("prop_getnames") );

		saslcfmglob.prop_clearPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("prop_clear") );

		saslcfmglob.prop_erasePtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("prop_erase") );

		saslcfmglob.prop_disposePtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("prop_dispose") );

		saslcfmglob.prop_formatPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("prop_format") );

		saslcfmglob.prop_setPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("prop_set") );

		saslcfmglob.prop_setvalsPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("prop_setvals") );

/* These are internal functions used by our sasl_seterror */
		saslcfmglob._sasl_add_stringPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("_sasl_add_string") );

		saslcfmglob._buf_allocPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("_buf_alloc") );

		saslcfmglob._sasl_get_errorbufPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("_sasl_get_errorbuf") );

		if (!_cfmsasl_haveCustomAlloc) {
			saslcfmglobp.custMalloc = MachOFunctionPointerForCFMFunctionPointer(malloc);
			saslcfmglobp.custCalloc = MachOFunctionPointerForCFMFunctionPointer(calloc);
			saslcfmglobp.custRealloc = MachOFunctionPointerForCFMFunctionPointer(realloc);
			saslcfmglobp.custFree = MachOFunctionPointerForCFMFunctionPointer(free);
			
			_cfmsasl_haveCustomAlloc = 1;
		}

		saslcfmglob.SASLSetAllocPtr(saslcfmglobp.custMalloc, saslcfmglobp.custCalloc,
									saslcfmglobp.custRealloc, saslcfmglobp.custFree);

		if (_cfmsasl_haveCustomMutex) 
			saslcfmglob.SASLSetMutexPtr(saslcfmglobp.custMutexNew, saslcfmglobp.custMutexLock,
										saslcfmglobp.custMutexUnlock, saslcfmglobp.custMutexDispose);

	} else if (saslcfmglob.myBundle) {
		CFRelease(saslcfmglob.myBundle);
		saslcfmglob.myBundle = nil;
	}

	if (saslcfmglob.bundleURL && !saslcfmglob.myBundle) {
		CFRelease(saslcfmglob.bundleURL);
		saslcfmglob.bundleURL = nil;
	}

	if (saslcfmglob.myBundle)
		_cfmsasl_initted = 1;
  }

	if (_cfmsasl_initted) {
		if (isServer) {
			SASLServerInitPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_server_init") );
			if (SASLServerInitPtr != nil) {
	    		sasl_callback_t *new_callbacks = NULL;
				if (callbacks)
					new_callbacks = GetCFMCallbacks(callbacks);
				result = SASLServerInitPtr(new_callbacks, appname);
				saslcfmglob.serverCallbacks = new_callbacks;
			}
		} else {
			SASLClientInitPtr = (void*)CFBundleGetFunctionPointerForName(
						saslcfmglob.myBundle, CFSTR("sasl_client_init") );
			if (SASLClientInitPtr != nil) {
	    		sasl_callback_t *new_callbacks = NULL;
				if (callbacks)
					new_callbacks = GetCFMCallbacks(callbacks);
				result = SASLClientInitPtr(new_callbacks);
				saslcfmglob.clientCallbacks = new_callbacks;
			}
		}
	}
	return result;
}

int sasl_server_init(const sasl_callback_t *callbacks,
				 const char *appname)
{
	return _cfmsasl_common_init(callbacks, true, appname);
}

int sasl_client_init(const sasl_callback_t *callbacks)
{
	return _cfmsasl_common_init(callbacks, false, nil);
}

void sasl_done(void)
{
	if (!_cfmsasl_initted)
		return;
	if (!saslcfmglob.SASLDonePtr) return;

	saslcfmglob.SASLDonePtr();
	DisposeCFMCallbacks(saslcfmglob.clientCallbacks);
	DisposeCFMCallbacks(saslcfmglob.serverCallbacks);
	CFBundleUnloadExecutable(saslcfmglob.myBundle);
	CFRelease(saslcfmglob.myBundle);
	CFRelease(saslcfmglob.bundleURL);
	saslcfmglob.myBundle = NULL;
	saslcfmglob.bundleURL = NULL;

        _cfmsasl_initted = 0;
}

int sasl_client_new (const char *service,
						const char *serverFQDN,
						const char *iplocalport,
						const char *ipremoteport,
						const sasl_callback_t *prompt_supp, 
						unsigned flags,
						sasl_conn_t **pconn)
{
	sasl_callback_t *new_ps = NULL;
	int result;
	cfm_sasl_conn_t *myconn;

	if (!_cfmsasl_initted)
		return SASL_NOMEM;
	if (!saslcfmglob.SASLClientNewPtr) return SASL_NOMEM;

	if (prompt_supp)
		new_ps = GetCFMCallbacks(prompt_supp);

// this is commented out because sasl.h incorrectly described the api
//	if (*pconn)
//		DisposeCFMCallbacks(((cfm_sasl_conn_t *)*pconn)->cbk);
//	else {
	myconn = (cfm_sasl_conn_t *) NewPtr(sizeof(cfm_sasl_conn_t));
	if (myconn == NULL) {
		return SASL_NOMEM;
	}

	myconn->ctx = NULL;
//	}

	result = saslcfmglob.SASLClientNewPtr(service, serverFQDN, iplocalport,
				ipremoteport, new_ps, flags,
				&(myconn->ctx));
	myconn->cbk = new_ps;

	*pconn = (sasl_conn_t *)myconn;

	return result;
}

int sasl_server_new (const char *service,
						const char *serverFQDN,
						const char *user_realm,
						const char *iplocalport,
						const char *ipremoteport,
						const sasl_callback_t *callbacks, 
						unsigned flags,
						sasl_conn_t **pconn)
{
	sasl_callback_t *new_ps = NULL;
	int result;

	if (!_cfmsasl_initted)
		return SASL_NOMEM;
	if (!saslcfmglob.SASLServerNewPtr) return SASL_NOMEM;

	if (callbacks)
		new_ps = GetCFMCallbacks(callbacks);
	*pconn = (sasl_conn_t *)NewPtr(sizeof(cfm_sasl_conn_t));
	((cfm_sasl_conn_t *)*pconn)->ctx = nil;

	result = saslcfmglob.SASLServerNewPtr(service, serverFQDN, user_realm,
				iplocalport, ipremoteport, new_ps, flags,
				&((cfm_sasl_conn_t *)*pconn)->ctx);
	((cfm_sasl_conn_t *)*pconn)->cbk = new_ps;

	return result;
}

void sasl_dispose(sasl_conn_t **pconn)
{
	if (!_cfmsasl_initted)
		return;
	if (!saslcfmglob.SASLDisposePtr) return;

	if (!pconn) return;
	if (!*pconn) return;

	saslcfmglob.SASLDisposePtr(&((cfm_sasl_conn_t *)*pconn)->ctx);
	DisposeCFMCallbacks(((cfm_sasl_conn_t *)*pconn)->cbk);
	DisposePtr((Ptr)*pconn);
	*pconn = NULL;
}

int sasl_client_start (sasl_conn_t *conn,
						const char *mechlist,
						sasl_interact_t **prompt_need,
						const char **clientout,
						unsigned *clientoutlen,
						const char **mech)
{
	if (!_cfmsasl_initted)
		return SASL_NOMEM;
	if (!saslcfmglob.SASLClientStartPtr) return SASL_NOMEM;

	return saslcfmglob.SASLClientStartPtr(((cfm_sasl_conn_t *)conn)->ctx, mechlist,
				prompt_need, clientout, clientoutlen, mech);
}

int sasl_client_step (sasl_conn_t *conn,
						const char *serverin,
						unsigned serverinlen,
						sasl_interact_t **prompt_need,
						const char **clientout,
						unsigned *clientoutlen)
{
	if (!_cfmsasl_initted)
		return SASL_NOMEM;
	if (!saslcfmglob.SASLClientStepPtr) return SASL_NOMEM;

	return saslcfmglob.SASLClientStepPtr(((cfm_sasl_conn_t *)conn)->ctx, serverin,
							serverinlen, prompt_need, clientout, clientoutlen);
}

const char *sasl_errstring(int saslerr,
							const char *langlist,
							const char **outlang)
{
	if (!_cfmsasl_initted)
		return NULL;
	if (!saslcfmglob.SASLErrStringPtr) return NULL;

	return saslcfmglob.SASLErrStringPtr(saslerr, langlist, outlang);
}

const char *sasl_errdetail(sasl_conn_t *conn)
{
	if (!_cfmsasl_initted)
		return NULL;
	if (!saslcfmglob.sasl_errdetailPtr) return NULL;

	return saslcfmglob.sasl_errdetailPtr(((cfm_sasl_conn_t *)conn)->ctx);
}

int sasl_getprop(sasl_conn_t *conn, int propnum, const void **pvalue)
{
	if (!_cfmsasl_initted)
		return SASL_NOMEM;
	if (!saslcfmglob.SASLGetPropPtr) return SASL_NOMEM;

	return saslcfmglob.SASLGetPropPtr(((cfm_sasl_conn_t *)conn)->ctx, propnum, pvalue);
}

int sasl_setprop(sasl_conn_t *conn,
					int propnum,
					const void *value)
{
	if (!_cfmsasl_initted)
		return SASL_NOMEM;
	if (!saslcfmglob.SASLSetPropPtr) return SASL_NOMEM;

	return saslcfmglob.SASLSetPropPtr(((cfm_sasl_conn_t *)conn)->ctx, propnum, value);
}

int sasl_idle(sasl_conn_t *conn)
{
	if (!_cfmsasl_initted)
		return SASL_NOMEM;
	if (!saslcfmglob.SASLIdlePtr) return SASL_NOMEM;

	return saslcfmglob.SASLIdlePtr(((cfm_sasl_conn_t *)conn)->ctx);
}

int sasl_encode(sasl_conn_t *conn,
				const char *input, unsigned inputlen,
				const char **output, unsigned *outputlen)
{
	if (!_cfmsasl_initted)
		return SASL_NOMEM;
	if (!saslcfmglob.SASLEncodePtr) return SASL_NOMEM;

	return saslcfmglob.SASLEncodePtr(((cfm_sasl_conn_t *)conn)->ctx, input, inputlen,
											output, outputlen);
}

int sasl_encodev(sasl_conn_t *conn,
			     const struct iovec *invec, unsigned numiov,
			     const char **output, unsigned *outputlen)
{
	if (!_cfmsasl_initted)
		return SASL_NOMEM;
	if (!saslcfmglob.SASLEncodeVPtr) return SASL_NOMEM;

	return saslcfmglob.SASLEncodeVPtr(((cfm_sasl_conn_t *)conn)->ctx, invec, numiov,
											output, outputlen);
}

int sasl_decode(sasl_conn_t *conn,
				const char *input, unsigned inputlen,
				const char **output, unsigned *outputlen)
{
	if (!_cfmsasl_initted)
		return SASL_NOMEM;
	if (!saslcfmglob.SASLDecodePtr) return SASL_NOMEM;

	return saslcfmglob.SASLDecodePtr(((cfm_sasl_conn_t *)conn)->ctx, input, inputlen,
											output, outputlen);
}

int sasl_decode64(const char *in, unsigned inlen,
			      char *out, unsigned outmax, unsigned *outlen)
{
	if (!_cfmsasl_initted)
		return SASL_NOMEM;
	if (!saslcfmglob.sasl_decode64Ptr) return SASL_NOMEM;

	return saslcfmglob.sasl_decode64Ptr(in, inlen, out, outmax, outlen);
}

int sasl_encode64(const char *in, unsigned inlen,
			      char *out, unsigned outmax, unsigned *outlen)
{
	if (!_cfmsasl_initted)
		return SASL_NOMEM;
	if (!saslcfmglob.sasl_encode64Ptr) return SASL_NOMEM;

	return saslcfmglob.sasl_encode64Ptr(in, inlen, out, outmax, outlen);
}

void sasl_set_alloc(sasl_malloc_t *ma, sasl_calloc_t *ca, sasl_realloc_t *rea, sasl_free_t *fr)
{
	if (_cfmsasl_haveCustomAlloc) {
		DisposePtr((Ptr)saslcfmglobp.custMalloc);
		DisposePtr((Ptr)saslcfmglobp.custCalloc);
		DisposePtr((Ptr)saslcfmglobp.custRealloc);
		DisposePtr((Ptr)saslcfmglobp.custFree);
	}
	saslcfmglobp.custMalloc = MachOFunctionPointerForCFMFunctionPointer(ma);
	saslcfmglobp.custCalloc = MachOFunctionPointerForCFMFunctionPointer(ca);
	saslcfmglobp.custRealloc = MachOFunctionPointerForCFMFunctionPointer(rea);
	saslcfmglobp.custFree = MachOFunctionPointerForCFMFunctionPointer(fr);
	_cfmsasl_haveCustomAlloc = 1;
}

int sasl_mkchal(sasl_conn_t *conn, char *buf,
			    unsigned maxlen, unsigned hostflag)
{
	if (!_cfmsasl_initted)
		return SASL_NOMEM;
	if (!saslcfmglob.sasl_mkchalPtr) return SASL_NOMEM;

	return saslcfmglob.sasl_mkchalPtr(((cfm_sasl_conn_t *)conn)->ctx, buf, maxlen, hostflag);
}

int sasl_utf8verify(const char *str, unsigned len)
{
	if (!_cfmsasl_initted)
		return SASL_NOMEM;
	if (!saslcfmglob.sasl_utf8verifyPtr) return SASL_NOMEM;

	return saslcfmglob.sasl_utf8verifyPtr(str, len);
}

void sasl_churn(sasl_rand_t *rpool, 
			    const char *data,
			    unsigned len)
{
	if (!_cfmsasl_initted)
		return;
	if (!saslcfmglob.sasl_churnPtr) return;

	saslcfmglob.sasl_churnPtr(rpool, data, len);
}

void sasl_rand(sasl_rand_t *rpool,
			   char *buf,
			   unsigned len)
{
	if (!_cfmsasl_initted)
		return;
	if (!saslcfmglob.sasl_randPtr) return;

	saslcfmglob.sasl_randPtr(rpool, buf, len);
}

void sasl_randseed(sasl_rand_t *rpool,
			       const char *seed,
			       unsigned len)
{
	if (!_cfmsasl_initted)
		return;
	if (!saslcfmglob.sasl_randseedPtr) return;

	saslcfmglob.sasl_randseedPtr(rpool, seed, len);
}

void sasl_randfree(sasl_rand_t **rpool)
{
	if (!_cfmsasl_initted)
		return;
	if (!saslcfmglob.sasl_randfreePtr) return;

	saslcfmglob.sasl_randfreePtr(rpool);
}

int sasl_randcreate(sasl_rand_t **rpool)
{
	if (!_cfmsasl_initted)
		return SASL_NOMEM;
	if (!saslcfmglob.sasl_randcreatePtr) return SASL_NOMEM;

	return saslcfmglob.sasl_randcreatePtr(rpool);
}

void sasl_set_mutex(sasl_mutex_alloc_t *mn, sasl_mutex_lock_t *ml,
                                sasl_mutex_unlock_t *mu, sasl_mutex_free_t *md)
{
	if (_cfmsasl_haveCustomMutex) {
		DisposePtr((Ptr)saslcfmglobp.custMutexNew);
		DisposePtr((Ptr)saslcfmglobp.custMutexLock);
		DisposePtr((Ptr)saslcfmglobp.custMutexUnlock);
		DisposePtr((Ptr)saslcfmglobp.custMutexDispose);
	}
	saslcfmglobp.custMutexNew = MachOFunctionPointerForCFMFunctionPointer(mn);
	saslcfmglobp.custMutexLock = MachOFunctionPointerForCFMFunctionPointer(ml);
	saslcfmglobp.custMutexUnlock = MachOFunctionPointerForCFMFunctionPointer(mu);
	saslcfmglobp.custMutexDispose = MachOFunctionPointerForCFMFunctionPointer(md);
	_cfmsasl_haveCustomMutex = 1;
}

int sasl_listmech(sasl_conn_t *conn,
			      const char *user,
			      const char *prefix,
			      const char *sep,
			      const char *suffix,
			      const char **result,
			      unsigned *plen,
			      int *pcount)
{
	if (!_cfmsasl_initted)
		return SASL_NOMEM;
	if (!saslcfmglob.sasl_listmechPtr) return SASL_NOMEM;

	return saslcfmglob.sasl_listmechPtr(((cfm_sasl_conn_t *)conn)->ctx, user, prefix, sep,
											suffix, result, plen, pcount);
}

int sasl_server_start(sasl_conn_t *conn,
				  const char *mech,
				  const char *clientin,
				  unsigned clientinlen,
				  const char **serverout,
				  unsigned *serveroutlen)
{
	if (!_cfmsasl_initted)
		return SASL_NOMEM;
	if (!saslcfmglob.SASLServerStartPtr) return SASL_NOMEM;

	return saslcfmglob.SASLServerStartPtr(((cfm_sasl_conn_t *)conn)->ctx, mech, clientin,
											clientinlen, serverout, serveroutlen);
}

int sasl_server_step(sasl_conn_t *conn,
		     const char *clientin,
		     unsigned clientinlen,
		     const char **serverout,
		     unsigned *serveroutlen)
{
	if (!_cfmsasl_initted)
		return SASL_NOMEM;
	if (!saslcfmglob.SASLServerStepPtr) return SASL_NOMEM;

	return saslcfmglob.SASLServerStepPtr(((cfm_sasl_conn_t *)conn)->ctx, clientin, clientinlen,
											serverout, serveroutlen);
}

int sasl_checkpass(sasl_conn_t *conn,
			       const char *user,
			       unsigned userlen,
			       const char *pass,
			       unsigned passlen)
{
	if (!_cfmsasl_initted)
		return SASL_NOMEM;
	if (!saslcfmglob.sasl_checkpassPtr) return SASL_NOMEM;

	return saslcfmglob.sasl_checkpassPtr(((cfm_sasl_conn_t *)conn)->ctx, user, userlen, pass,
											passlen);
}

int sasl_user_exists(sasl_conn_t *conn,
			 const char *service,
		     const char *user_realm,
		     const char *user)
{
	if (!_cfmsasl_initted)
		return SASL_NOMEM;
	if (!saslcfmglob.sasl_user_existsPtr) return SASL_NOMEM;

	return saslcfmglob.sasl_user_existsPtr(((cfm_sasl_conn_t *)conn)->ctx,
												service, user_realm, user);
}

int sasl_setpass(sasl_conn_t *conn,
		 const char *user,
		 const char *pass,
		 unsigned passlen,
		 const char *oldpass, unsigned oldpasslen,
		 unsigned flags)
{
	if (!_cfmsasl_initted)
		return SASL_NOMEM;
	if (!saslcfmglob.sasl_setpassPtr) return SASL_NOMEM;

	return saslcfmglob.sasl_setpassPtr(((cfm_sasl_conn_t *)conn)->ctx, user, pass,
											passlen, oldpass, oldpasslen, flags);
}

int sasl_checkapop(sasl_conn_t *conn,
			       const char *challenge, unsigned challen,
			       const char *response, unsigned resplen)
{
	if (!_cfmsasl_initted)
		return SASL_NOMEM;
	if (!saslcfmglob.sasl_checkapopPtr) return SASL_NOMEM;

	return saslcfmglob.sasl_checkapopPtr(((cfm_sasl_conn_t *)conn)->ctx, challenge, challen,
												response, resplen);
}

int sasl_auxprop_request(sasl_conn_t *conn,
				     const char **propnames)
{
	if (!_cfmsasl_initted)
		return SASL_NOMEM;
	if (!saslcfmglob.sasl_auxprop_requestPtr) return SASL_NOMEM;

	return saslcfmglob.sasl_auxprop_requestPtr(((cfm_sasl_conn_t *)conn)->ctx, propnames);
}

struct propctx *sasl_auxprop_getctx(sasl_conn_t *conn)
{
	if (!_cfmsasl_initted)
		return NULL;
	if (!saslcfmglob.sasl_auxprop_getctxPtr) return NULL;

	return saslcfmglob.sasl_auxprop_getctxPtr(((cfm_sasl_conn_t *)conn)->ctx);
}

void sasl_erasebuffer(char *pass, unsigned len)
{
	if (!_cfmsasl_initted)
		return;
	if (!saslcfmglob.sasl_erasebufferPtr) return;

	saslcfmglob.sasl_erasebufferPtr(pass, len);
}

struct propctx *prop_new(unsigned estimate)
{
	if (!_cfmsasl_initted)
		return NULL;
	if (!saslcfmglob.prop_newPtr) return NULL;

	return saslcfmglob.prop_newPtr(estimate);
}

int prop_dup(struct propctx *src_ctx, struct propctx **dst_ctx)
{
	if (!_cfmsasl_initted)
		return SASL_NOMEM;
	if (!saslcfmglob.prop_dupPtr) return SASL_NOMEM;

	return saslcfmglob.prop_dupPtr(src_ctx, dst_ctx);
}

const struct propval *prop_get(struct propctx *ctx)
{
	if (!_cfmsasl_initted)
		return NULL;
	if (!saslcfmglob.prop_getPtr) return NULL;

	return saslcfmglob.prop_getPtr(ctx);
}

int prop_getnames(struct propctx *ctx, const char **names,
		  struct propval *vals)
{
	if (!_cfmsasl_initted)
		return SASL_NOMEM;
	if (!saslcfmglob.prop_getnamesPtr) return SASL_NOMEM;

	return saslcfmglob.prop_getnamesPtr(ctx, names, vals);
}

void prop_clear(struct propctx *ctx, int requests)
{
	if (!_cfmsasl_initted)
		return;
	if (!saslcfmglob.prop_clearPtr) return;

	saslcfmglob.prop_clearPtr(ctx, requests);
}

void prop_erase(struct propctx *ctx, const char *name)
{
	if (!_cfmsasl_initted)
		return;
	if (!saslcfmglob.prop_erasePtr) return;

	saslcfmglob.prop_erasePtr(ctx, name);
}

void prop_dispose(struct propctx **ctx)
{
	if (!_cfmsasl_initted)
		return;
	if (!saslcfmglob.prop_disposePtr) return;

	saslcfmglob.prop_disposePtr(ctx);
}

int prop_format(struct propctx *ctx, const char *sep, int seplen,
		char *outbuf, unsigned outmax, unsigned *outlen)
{
	if (!_cfmsasl_initted)
		return SASL_NOMEM;
	if (!saslcfmglob.prop_formatPtr) return SASL_NOMEM;

	return saslcfmglob.prop_formatPtr(ctx, sep, seplen, outbuf, outmax, outlen);
}

int prop_set(struct propctx *ctx, const char *name,
	     const char *value, int vallen)
{
	if (!_cfmsasl_initted)
		return SASL_NOMEM;
	if (!saslcfmglob.prop_setPtr) return SASL_NOMEM;

	return saslcfmglob.prop_setPtr(ctx, name, value, vallen);
}

int prop_setvals(struct propctx *ctx, const char *name,
		 const char **values)
{
	if (!_cfmsasl_initted)
		return SASL_NOMEM;
	if (!saslcfmglob.prop_setvalsPtr) return SASL_NOMEM;

	return saslcfmglob.prop_setvalsPtr(ctx, name, values);
}

/* internal functions used by sasl_seterror follow */
int _sasl_add_string(char **out, int *alloclen, int *outlen, const char *add)
{
	if (!_cfmsasl_initted)
		return SASL_NOMEM;
	if (!saslcfmglob._sasl_add_stringPtr) return SASL_NOMEM;

	return saslcfmglob._sasl_add_stringPtr(out, alloclen, outlen, add);
}

int _buf_alloc(char **rwbuf, unsigned *curlen, unsigned newlen)
{
	if (!_cfmsasl_initted)
		return SASL_NOMEM;
	if (!saslcfmglob._buf_allocPtr) return SASL_NOMEM;

	return saslcfmglob._buf_allocPtr(rwbuf, curlen, newlen);
}

void _sasl_get_errorbuf(sasl_conn_t *conn, char ***bufhdl, unsigned **lenhdl)
{
	if (!_cfmsasl_initted)
		return;
	if (!saslcfmglob._sasl_add_stringPtr) return;

	saslcfmglob._sasl_get_errorbufPtr(((cfm_sasl_conn_t *)conn)->ctx, bufhdl, lenhdl);
}
