/*
 * This file was generated automatically by ExtUtils::ParseXS version 3.35 from the
 * contents of XS.xs. Do not edit this file, edit XS.xs instead.
 *
 *    ANY CHANGES MADE HERE WILL BE LOST!
 *
 */

#line 1 "XS.xs"
#if 0
  "Skipped embedded POD."
#endif
#line 40 "XS.xs"


#include <EXTERN.h>
#include <perl.h>
#include <XSUB.h>

#ifdef SASL2

#include <sasl/sasl.h>

#else

#include <sasl.h>

#endif

// Debugging stuff

//#define PERL_SASL_DEBUG

#ifdef PERL_SASL_DEBUG
#define _DEBUG(x,...) { printf("DEBUG: %s:%d: ",__FUNCTION__, __LINE__); printf(x, __VA_ARGS__); printf("\n"); }
#define __DEBUG(x) _DEBUG(x,NULL);
#else
#define _DEBUG(x,...)
#define __DEBUG(x)
#endif

#define SASL_IS_SERVER 0
#define SASL_IS_CLIENT 1

struct authensasl {
  sasl_conn_t *conn;
  sasl_callback_t *callbacks;
  int callback_count;

  char *server;
  char *service;
  char *mech;
  char *user;

  int error_code;
  char *additional_errormsg;

  int is_client;
};

typedef struct authensasl * Authen_SASL_XS;

struct _perlcontext {
  SV *func;
  SV *param;
  int intparam;

};

/* Define missing DEFINES, to help programmers avoiding conflict
 * between SASL v1 and v2 libs.
 * Ignore but allow setting callbacks which are lib version depending
 */

#ifdef SASL2

#define SASL_CB_SERVER_GETSECRET (0)
#define SASL_CB_SERVER_PUTSECRET (0)

#else

#define SASL_CB_SERVER_USERDB_CHECKPASS (0)
#define SASL_CB_SERVER_USERDB_SETPASS (0)

#define SASL_CB_CANON_USER (0x8007)

#define SASL_CU_AUTHID  (0x01)
#define SASL_CU_AUTHZID (0x02)

/* Simulation canon_user Callback in SASL1 */
struct _perlcontext *sp_canon = NULL;

#endif


/* Ulrich Pfeifer: Poor man's XPUSH macros for ancient perls. Note that the
 * stack is extended by a constant 1.  That is OK for the uses below, but
 * insufficient in general
 */

#ifndef dXSTARG
#undef XPUSHi
#undef XPUSHp
#define  XPUSHi(A) \
EXTEND(sp,1); \
PUSHs(sv_2mortal(newSViv(A)));
#define XPUSHp(A,B) \
EXTEND(sp,1); \
PUSHs(sv_2mortal(newSVpvn((char *)(A),(STRLEN)(B))));
#endif
#ifndef SvPV_nolen
#define SvPV_nolen(A) SvPV(A,PL_na)
#endif

// internal method for handling errors and their messages
int SetSaslError(Authen_SASL_XS sasl,int code, const char* msg)
{
	if (sasl == NULL)
#ifdef SASL2
		code = SASL_NOTINIT;
#else
		code = SASL_FAIL;
#endif
	else
	{
		_DEBUG("former error: %s, Code: %d",sasl->additional_errormsg,
				sasl->error_code);

		// Do not overwrite Error which are not handled yet, except this one which
		// aren't errors at all
		if (sasl->error_code == SASL_OK ||
			sasl->error_code == SASL_CONTINUE )
		{
			sasl->error_code = code;

			if (sasl->additional_errormsg != NULL)
				free(sasl->additional_errormsg);

			// Is there a message and is it really an error, otherwise ignore message
			if (msg != NULL &&
				code != SASL_OK &&
				code != SASL_CONTINUE)
				sasl->additional_errormsg = strdup(msg);
			else
				sasl->additional_errormsg = NULL;
		}
		_DEBUG("called Error: %s, Code: %d Client: %d",msg,code,sasl->is_client);
		_DEBUG("now Error: %s, Code: %d",sasl->additional_errormsg,sasl->error_code);
	}
	return code;
}

/*
   This is the wrapper function that calls Perl callback functions. The SASL
   library needs a C function to handle callbacks, and this function forms the
   glue to get from the C library back into Perl. The perlcontext is a wrapper
   around the context given to the "callbacks" method. It tells which Perl
   function should be called and what parameter to pass it.
   Different types of callbacks have different "output" parameters to give data
   back to the C library. This function needs to know how to take information
   returned from the Perl callback subroutine and load it back into the output
   parameters for the C library to read.
   Note that if the callback given to the "callbacks" Perl method is really just
   a string or integer, there is no need to jump into a Perl subroutine.
   The value is loaded directly into the output parameters.
*/

/*
	This function executes the perl sub/code and returns the result
	and its length.
*/
int PerlCallbackSub (struct _perlcontext *cp, char **result, STRLEN *len, AV *args)
{
	int rc = SASL_OK;

	int count;
	SV *rsv;

	if (result == NULL)
		return SASL_FAIL;

	if (*result != NULL)
		free(*result);

	if (len == NULL)
		return SASL_FAIL;

	__DEBUG("Callback Callback");

	if (cp->func == NULL) // No perl function given, but a value
	{
		if (cp->param == NULL)
			rc = SASL_FAIL;
		else {
			_DEBUG("PV: %s",SvPV(cp->param,*len));
			*result = strdup(SvPV(cp->param,*len));
		}
	}
	else // Call the perl function
	{
		/* Make a new call stack */
		dSP;
		/* We'll be making temporary perl variables */
		ENTER ;
		SAVETMPS ;

		PUSHMARK(SP);
		if (cp->param)
			XPUSHs( cp->param );

		// Push all other args from Array Args
		if (args != NULL)
			while (av_len(args) >= 0)
				XPUSHs(av_pop(args));
		PUTBACK ;

		count = call_sv(cp->func, G_SCALAR);

		/* Refresh the local stack in case the function played with it */
		SPAGAIN;

		_DEBUG("Count of retvals: %d",count);

		if (count != 1)
			rc = SASL_FAIL;
		else
		{
			rsv = POPs;
			if (SvOK(rsv)) { // we have to check for undef return values
				if ( (*result = strdup(SvPV(rsv, *len))) == NULL)
					rc = SASL_FAIL;
			} else {
				*result = strdup("");
			}
		}
		/* Final cleanup of the stack, since we may've pop'd one */
		PUTBACK ;

		/* Remember to delete temporary variables */
		FREETMPS ;
		LEAVE ;
	}
	return rc;
}

/* This function wraps sasl_getsimple_t function pointers for perl. Name is
   taken from earlier versions, which made no difference between Callback types */
int PerlCallback(void *context, int id, const char **result, unsigned *len)
{
	struct _perlcontext *cp = (struct _perlcontext *) context;
	int rc=SASL_OK;
        STRLEN llen;
	char *c = NULL;

	if (id != SASL_CB_USER &&
		id != SASL_CB_AUTHNAME &&
		id != SASL_CB_LANGUAGE)
	{
		croak("Authen::SASL::XS:  Don't know how to handle callback: %x\n", id);
		rc = -1;
	}
	else
		rc = PerlCallbackSub(cp,&c,&llen,NULL); // Execute PerlCode

	_DEBUG("simple Callback returns: %s %d",c,llen);

	if (rc == SASL_OK)
	{
		if (result != NULL)
			*result = strdup(c);

		if (len != NULL)
			*len = llen;
	}

	if (c != NULL)
		free(c);

	return rc;
}


int PerlCallbackRealm ( void *context, int id, const char **availrealms, const char **result)
{
	struct _perlcontext *cp = (struct _perlcontext *) context;
	int rc = SASL_OK,i;
        STRLEN len;
	char *c = NULL;

	AV *args = newAV();

	// Create the array
	if (availrealms != NULL)
		for (i=0; availrealms[i] != NULL; i++)
		{
			_DEBUG("added available realm: %s",availrealms[i]);
			av_push(args, newSVpv(availrealms[i],0));
		}

	/* HandlePerlStuff */
	rc = PerlCallbackSub(cp,&c,&len,args);

	// Clear the array
	av_clear(args);
	av_undef(args);

	if (rc == SASL_OK)
	{
		if (result != NULL)
			*result = strdup(c);
		else
			rc = -1;
	}

	if (c != NULL)
		free(c);
	return 1;
}

int FillSecret_t(char * p,int len, sasl_secret_t **psecret)
{
	int rc = SASL_OK;
	sasl_secret_t *pass;

	// Allocate sasl password stuff
	pass = (sasl_secret_t *) malloc( len + sizeof(sasl_secret_t) + 1); // 1 for \0
	if (pass == NULL)
		rc=SASL_FAIL;
	else
	{ // and fill it
		_DEBUG("passlen: %d, %s",len,p);
		pass->len = len;
		strncpy( (char *)pass->data,p,len);
		pass->data[len] = '\0';
		_DEBUG("passlen: %d, %s",pass->len,pass->data);
		*psecret = pass;
	}
	return rc;
}

/* This function wraps the sasl_getsecret_t function pointer for perl */
int PerlCallbackSecret (sasl_conn_t *conn, void *context, int id, sasl_secret_t **psecret)
{
	struct _perlcontext *cp = (struct _perlcontext *) context;
	STRLEN len;
        int rc = SASL_OK;
	char *c = NULL;

	/* HandlePerlStuff */
	rc = PerlCallbackSub(cp,&c,&len,NULL);

	if (rc == SASL_OK && psecret != NULL)
	{
		rc = FillSecret_t(c,len,psecret);
	}
	else
		rc = SASL_FAIL;

	if (c != NULL)
		free(c);

	return rc;
}

int PerlCallbackCanonUser(sasl_conn_t *conn, void *context, const char *user, unsigned ulen,
					unsigned flags, const char *user_realm, char *out_user, unsigned out_umax,
					unsigned *out_ulen)
{
	struct _perlcontext *cp = (struct _perlcontext *) context;
	int rc = SASL_OK;
        STRLEN len;
	char *c = NULL;

	AV *args;

	_DEBUG("Enter CanonUser user(%s,%d) user_realm(%s) out_user(%s) out_umax(%d).",user,ulen,user_realm,out_user,out_umax);

	if (!(flags & SASL_CU_AUTHID) && !(flags & SASL_CU_AUTHZID))
		return SASL_BADPARAM;

	args = newAV();

	// Create the parameter array and fill it
	av_push(args, newSVpv(user,ulen));
	av_push(args, newSViv(out_umax));
	av_push(args, newSVpv(user_realm == NULL ? "" : user_realm,0));
	av_push(args, newSVpv(flags & SASL_CU_AUTHID ? "AUTHID" : "AUTHZID" ,0));

	/* HandlePerlStuff */
	rc = PerlCallbackSub(cp,&c,&len,args);

	// Clear the array
	av_clear(args);
	av_undef(args);

	*out_ulen = len > out_umax ? out_umax : len;
	strncpy(out_user,c,*out_ulen);

	if (c != NULL)
		free(c);

	return rc;
}

#ifdef SASL2
/*
	This function wraps the sasl_server_userdb_checkpass_t function pointer for
	perl.
*/
int PerlCallbackServerCheckPass(sasl_conn_t *conn, void *context, const char *user,
	const char *pass, unsigned passlen, struct propctx *propctx)
{
	struct _perlcontext *cp = (struct _perlcontext *) context;
	int rc = SASL_OK;
        STRLEN len;
	char *c = NULL;

	AV *args = newAV();

	// Create the parameter array and fill it
	av_push(args, newSVpv(pass,0));
	av_push(args, newSVpv(user,0));

	_DEBUG("ServerCheckPass %s %s",user,pass);

	/* HandlePerlStuff */
	rc = PerlCallbackSub(cp,&c,&len,args);

	// Clear the array
	av_clear(args);
	av_undef(args);

	rc = strcmp(c,"1") == 0 ? SASL_OK : SASL_FAIL;

	if (c != NULL)
		free(c);

	_DEBUG("Checkpass retval: %d",rc);

	return rc;
}

int PerlCallbackServerSetPass(sasl_conn_t *conn, void *context,
				const char *user, const char *pass,
				unsigned passlen, struct propctx *propctx, unsigned flags)
{
	struct _perlcontext *cp = (struct _perlcontext *) context;
	AV *args = newAV();
	int rc = SASL_OK;
        STRLEN len;
	char *c = NULL;

	_DEBUG("ServerSetPass: %s, %s, %d",user,pass,passlen);

	av_push(args,newSViv(flags));
	if (passlen == 0)
		av_push(args,newSVpv("",0));
	else
		av_push(args,newSVpv(pass,passlen));
	av_push(args,newSVpv(user,0));

	rc = PerlCallbackSub(cp,&c,&len,args);

	av_clear(args);
	av_undef(args);
	_DEBUG("PerlCallback returns: %s,%d",c,rc);
	if (c != NULL)
		free(c);
	return rc;
}

int PerlCallbackAuthorize( sasl_conn_t *conn, void *context,
				const char *requested_user, unsigned rlen,
				const char *auth_identity, unsigned alen,
				const char *def_realm, unsigned urlen,
				struct propctx *propctx )
{
	struct _perlcontext *cp = (struct _perlcontext *) context;
	AV *args = newAV();
	int rc = SASL_OK;
        STRLEN len;
	char *c = NULL;

	_DEBUG("Authorize: %s, %s, %s",auth_identity,requested_user,def_realm);

	// Create the parameter array and fill it
	av_push(args, newSVpv(auth_identity,alen));
	av_push(args, newSVpv(requested_user,rlen));
// av_push(args, newSVpv(def_realm, urlen));

	/* HandlePerlStuff */
	rc = PerlCallbackSub(cp,&c,&len,args);

	// Clear the array
	av_clear(args);
	av_undef(args);

	rc = strcmp(c,"1") == 0 ? SASL_OK : SASL_FAIL;

	if (c != NULL)
		free(c);

	_DEBUG("Authorize: %x",rc);

	return rc;
}

#else

// Callbacks for SASL 1 (from version 1.5.28)

int PerlCallbackCanonUser1( void *context, const char *auth_identity, const char *requested_user,
					const char **user, const char **errstr)
{
	int rc = SASL_OK,len;
	char *c = malloc(sizeof(char) * 256);

	if (c != NULL)
		strcpy(c,"");
	else
		return SASL_FAIL;

	_DEBUG("%s,%s",auth_identity,requested_user);

	if (strcmp(auth_identity,requested_user))
		rc = PerlCallbackCanonUser(NULL,context,requested_user,strlen(requested_user),SASL_CU_AUTHZID,"",c,255,&len);

	rc = PerlCallbackCanonUser(NULL,context,auth_identity,strlen(auth_identity),SASL_CU_AUTHID,"",c,255,&len);

	*user = strdup(c);

	if (c != NULL)
		free(c);

	return rc;
}

int PerlCallbackAuthorize( void *context, const char *auth_identity, const char *requested_user,
					const char **user, const char **errstr)
{
	struct _perlcontext *cp = (struct _perlcontext *) context;
	int rc = SASL_OK;
        STRLEN len;
	AV *args;
	char *c = NULL;

	// SASL1 canonuser workaround
	if (sp_canon != NULL)
	{
		PerlCallbackCanonUser1( sp_canon, auth_identity, requested_user,(const char**) &c, errstr);
		free(c); // Throw away
		c = NULL;
	}

	_DEBUG("Authorize: %s, %s",auth_identity,requested_user);

	args = newAV();
	av_push(args, newSVpv(auth_identity,0));
	av_push(args, newSVpv(requested_user,0));

	rc = PerlCallbackSub(cp,&c,&len,args);

	av_clear(args);
	av_undef(args);

	*user = strndup(c,255);

	if (c != NULL)
		free(c);

	return rc;
}

int PerlCallbackGetSecret( void *context, const char *mechanism, const char *auth_identity,
							const char *realm, sasl_secret_t ** secret)
{
	struct _perlcontext *cp = (struct _perlcontext *) context;
	int rc = SASL_OK;
        STRLEN len;
	AV *args;
	char *c = NULL;

	args = newAV();
	av_push(args, newSVpv(realm,0));
	av_push(args, newSVpv(auth_identity,0));
	av_push(args, newSVpv(mechanism,0));

	rc = PerlCallbackSub(cp,&c,&len,args);

	av_clear(args);
	av_undef(args);

	_DEBUG("GetSecret, %s ,%s ,%s",mechanism,auth_identity,realm);

	if (rc == SASL_OK && c != NULL)
		rc = FillSecret_t(c,len,secret);
	else
		rc = SASL_FAIL;

	_DEBUG("GetSecret, pass: %s, rc: %x",(*secret)->data,rc);

	if (c != NULL)
		free(c);

	return rc;
}

#endif



#if 0
  "Skipped embedded POD."
#endif
#line 809 "XS.xs"


/* Convert a Perl callback name into a C callback ID */
static
int CallbackNumber(char *name)
{
  if (!strcasecmp(name, "user"))           return(SASL_CB_USER);
  else if (!strcasecmp(name, "username"))  return(SASL_CB_USER);
  else if (!strcasecmp(name, "auth"))      return(SASL_CB_AUTHNAME);
  else if (!strcasecmp(name, "authname"))  return(SASL_CB_AUTHNAME);
  else if (!strcasecmp(name, "language"))  return(SASL_CB_LANGUAGE);
  else if (!strcasecmp(name, "password"))  return(SASL_CB_PASS);
  else if (!strcasecmp(name, "pass"))      return(SASL_CB_PASS);
  else if (!strcasecmp(name, "realm"))     return(SASL_CB_GETREALM);
  else if (!strcasecmp(name, "authorize")) return(SASL_CB_PROXY_POLICY);
  else if (!strcasecmp(name, "canonuser")) return(SASL_CB_CANON_USER);
  else if (!strcasecmp(name, "checkpass")) return(SASL_CB_SERVER_USERDB_CHECKPASS);
  else if (!strcasecmp(name, "setpass"))   return(SASL_CB_SERVER_USERDB_SETPASS);
  else if (!strcasecmp(name, "getsecret")) return(SASL_CB_SERVER_GETSECRET);
  else if (!strcasecmp(name, "putsecret")) return(SASL_CB_SERVER_PUTSECRET);

#ifdef SASL2
  croak("Unknown callback: '%s'. (user|auth|language|pass|realm|checkpass|canonuser|authorize)\n", name);
#else
  croak("Unknown callback: '%s'. (user|auth|language|pass|realm|getsecret|canonuser|authorize)\n", name);
#endif
}

/*
   Fill the passed callback action into the passed Perl/SASL callback. This
   is called either from ExtractParentCallbacks() when the "new" method is
   called, or from callbacks() when that method is called directly.
*/

static
void AddCallback(SV *action, struct _perlcontext *pcb, sasl_callback_t *cb)
{
	__DEBUG("AddCallback");

	if (SvROK(action)) {     /*   user =>  <ref>  */
		__DEBUG("SvROK -> Dereferencing");
		action = SvRV(action);
	}

	pcb->func = NULL;
	pcb->intparam = 0;
	pcb->param = NULL;

	_DEBUG("action type: %d",SvTYPE(action));

	switch (SvTYPE(action)) {
		case SVt_PVCV:	/* user => sub { },  user => \&func */
				pcb->func = action;
				__DEBUG("SVt_PVCV");
			break;

		case SVt_PVAV:	/* user => [ \&func, $param ] */
				pcb->func = av_shift((AV *)action); pcb->param = av_shift((AV *)action);
				_DEBUG("Parametered Callback: %s",SvPV_nolen(pcb->param));
			break;

		case SVt_PV:	/* user => $param */
		case SVt_PVMG:	/* user => $self->{value} */
		case SVt_PVIV:  /* $self->{value} = ""; [...] user => $self->{value} */
				pcb->param = action;
				_DEBUG("SVt- PV PVMG PVIV (%s)",SvPV_nolen(pcb->param));
			break;

		case SVt_IV:	/*  user => 1 */
				pcb->intparam = SvIV(action);
				__DEBUG("SVt_IV");
			break;

		default:
				_DEBUG("Unknown parameter %d %s",SvTYPE(action),SvPV_nolen(action));
				croak("Unknown parameter to %x callback.\n", cb->id);
			break;
	}

	_DEBUG("Callback: %x",cb->id);
	/* Write the C SASL callbacks */
	switch (cb->id)
	{
		case SASL_CB_USER:
		case SASL_CB_AUTHNAME:
		case SASL_CB_LANGUAGE:
				 cb->proc = PerlCallback;
			break;

		case SASL_CB_PASS:
				cb->proc = PerlCallbackSecret;
			break;

		case SASL_CB_GETREALM:
				cb->proc = PerlCallbackRealm;
			break;

		case SASL_CB_ECHOPROMPT:
		case SASL_CB_NOECHOPROMPT:
			break;
		case SASL_CB_PROXY_POLICY:
				cb->proc = PerlCallbackAuthorize;
			break;

		case SASL_CB_CANON_USER:
				cb->proc = PerlCallbackCanonUser;
			break;
#ifdef SASL2
		case SASL_CB_SERVER_USERDB_CHECKPASS:
				cb->proc = PerlCallbackServerCheckPass;
			break;

		case SASL_CB_SERVER_USERDB_SETPASS:
				cb->proc = PerlCallbackServerSetPass;
			break;
#else
		// SASL 1 Servercallbacks:
		case SASL_CB_SERVER_GETSECRET:
				cb->proc = PerlCallbackGetSecret;
			break;
		case SASL_CB_SERVER_PUTSECRET:
				// Not implemented yet maybe TODO, if ever needed
			break;
#endif
		default:
			break;
	}
  cb->context = pcb;
}

/*
 *  Take the callback stored in the parent object and install them into the
 *  current *sasl object.  This is called from the "new" method.
 */

static
void ExtractParentCallbacks(SV *parent, Authen_SASL_XS sasl)
{
	char *key;
	int count=0,i;
	long l;
#ifndef SASL2
	// Missing SASL1 canonuser workaround
	int canon=-1,auth=-1;
#endif
	struct _perlcontext *pcb;
	SV **hashval, *val;
	HV *hash=NULL;
	HE *iter;

	/* Make sure parent is a ref to a hash (with keys like "mechanism"
	and "callback") */
	if (!parent) return;
	if (!SvROK(parent)) return;
	if (SvTYPE(SvRV(parent)) != SVt_PVHV) return;
	hash = (HV *)SvRV(parent);

	/* Get the parent's callbacks */
	hashval = hv_fetch(hash, "callback", 8, 0);
	if (!hashval || !*hashval) return;
	val = *hashval;

	/* Parent's callbacks are another hash (with keys like "user" and "auth") */
	if (!SvROK(val)) return;
	if (SvTYPE(SvRV(val)) != SVt_PVHV) return;
	hash = (HV *)SvRV(val);

	/* Run through all of parent's callback types, counting them
	 * Only valid (non-zero) callbacks are counted.
	 */
	hv_iterinit(hash);
	for (iter=hv_iternext(hash);  iter;  iter=hv_iternext(hash))
	{
		key = hv_iterkey(iter,&l);
		if ((i=CallbackNumber(key))) {
#ifndef SASL2
			// Missing SASL1 canonuser workaround
			if (i == SASL_CB_CANON_USER) canon = count;
			if (i == SASL_CB_PROXY_POLICY) auth = count;
#endif
			count++;
		}
	}

	_DEBUG("Found %d valid callback(s)",count);

	/* Allocate space for the callbacks */
	if (sasl->callbacks) {
		free(sasl->callbacks->context);
		free(sasl->callbacks);
	}
	pcb = (struct _perlcontext *) malloc(count * sizeof(struct _perlcontext));
	if (pcb == NULL)
		croak("Out of memory\n");

	l = (count + 1) * sizeof(sasl_callback_t);
	sasl->callbacks = (sasl_callback_t *)malloc(l);
	if (sasl->callbacks == NULL)
		croak("Out of memory\n");

	memset(sasl->callbacks, 0, l);

	/* Run through all of parent's callback types, fill in the sasl->callbacks
	 * Only valid (non-zero) callbacks will be filled in
	 */
	hv_iterinit(hash);
	count = 0;
	for (iter=hv_iternext(hash);  iter;  iter=hv_iternext(hash)) {
		key = hv_iterkey(iter,&l);
		_DEBUG("Callback %d, %s",count, key);
		if ( (i = CallbackNumber(key))) {
			_DEBUG("Adding Callback %s %d %x.",key,count,i);
			sasl->callbacks[count].id = i;
			val = hv_iterval(hash, iter);
			AddCallback(val, &pcb[count], &sasl->callbacks[count]);
			count++;
		}
		else
			_DEBUG("Ignore Callback %s %d %x.",key,count,i);
	}
	sasl->callbacks[count].id = SASL_CB_LIST_END;
	sasl->callbacks[count].context = pcb;
	sasl->callback_count = count;

#ifndef SASL2
	// Missing-SASL1-canonuser workaround

	// If canon is needed
	if (canon != -1)
	{
		if (auth != -1) // and auth also
			sp_canon = sasl->callbacks[canon].context; // Auth has to call canon
		else
		{
			sasl->callbacks[canon].id = SASL_CB_PROXY_POLICY; // call canon when auth is actually needed
			sasl->callbacks[canon].proc = PerlCallbackCanonUser1;
		}
	}

	_DEBUG("index for auth: %d, index for canon: %d",auth,canon);
#endif

return;
}

#ifdef SASL2
#define SASL_IP_LOCAL 5
#define SASL_IP_REMOTE 6
#endif

static
int PropertyNumber(char *name)
{
  if (!strcasecmp(name, "user"))          return SASL_USERNAME;
  else if (!strcasecmp(name, "ssf"))      return SASL_SSF;
  else if (!strcasecmp(name, "maxout"))   return SASL_MAXOUTBUF;
  else if (!strcasecmp(name, "optctx"))   return SASL_GETOPTCTX;
#ifdef SASL2
  else if (!strcasecmp(name, "realm"))    return SASL_DEFUSERREALM;
  else if (!strcasecmp(name, "iplocalport"))  return SASL_IPLOCALPORT;
  else if (!strcasecmp(name, "ipremoteport"))  return SASL_IPREMOTEPORT;
  else if (!strcasecmp(name, "service"))  return SASL_SERVICE;
  else if (!strcasecmp(name, "serverfqdn"))  return SASL_SERVERFQDN;
  else if (!strcasecmp(name, "authsource"))  return SASL_AUTHSOURCE;
  else if (!strcasecmp(name, "mechname"))  return SASL_MECHNAME;
  else if (!strcasecmp(name, "authuser"))  return SASL_AUTHUSER;
  else if (!strcasecmp(name, "sockname")) return SASL_IP_LOCAL;
  else if (!strcasecmp(name, "peername")) return SASL_IP_REMOTE;
#else
  else if (!strcasecmp(name, "realm"))    return SASL_REALM;
  else if (!strcasecmp(name, "iplocal"))  return SASL_IP_LOCAL;
  else if (!strcasecmp(name, "sockname")) return SASL_IP_LOCAL;
  else if (!strcasecmp(name, "ipremote")) return SASL_IP_REMOTE;
  else if (!strcasecmp(name, "peername")) return SASL_IP_REMOTE;
#endif
#ifdef SASL2
  croak("Unknown SASL property: '%s' (user|ssf|maxout|realm|optctx|iplocalport|ipremoteport|service|serverfqdn|authsource|mechname|authuser)\n", name);
#else
  croak("Unknown SASL property: '%s' (user|ssf|maxout|realm|optctx|sockname|peername)\n", name);
#endif
  return -1;
}


int init_sasl (SV* parent,char* service,char* host, Authen_SASL_XS *sasl,int client)
{
	HV *hash;
	SV **hashval;

	if (sasl == NULL)
		return SASL_FAIL;

	// TODO if struct is already in use and now another type
	if (*sasl != NULL && (*sasl)->is_client != client)
		return SASL_FAIL;

	if (*sasl == NULL)
	{
		// Initialize the given sasl
		*sasl = (Authen_SASL_XS) malloc (sizeof(struct authensasl));
		if (*sasl == NULL)
			croak("Out of memory\n");
		memset(*sasl, 0, sizeof(struct authensasl));
	}

	(*sasl)->is_client = client;
	(*sasl)->additional_errormsg = NULL;
	(*sasl)->error_code = 0;

	if (!host || !*host)
	{
		if (client == SASL_IS_CLIENT)
			SetSaslError((*sasl),SASL_FAIL,"Need a 'hostname' for being a client.");
		(*sasl)->server = NULL; // When server side is needed, NULL forces sasl to lookup the name.
	}
	else
		(*sasl)->server = strdup(host);

	if (!service || !*service)
	{
		SetSaslError((*sasl),SASL_FAIL,"Need a 'service' name.");
		(*sasl)->service = NULL;
	}
	else
		(*sasl)->service = strdup(service);

	/* Extract callback info from the parent object */
	ExtractParentCallbacks(parent, *sasl);

	/* Extract mechanism info from the parent object */
	if (parent && SvROK(parent) && (SvTYPE(SvRV(parent)) == SVt_PVHV))
	{
		hash = (HV *)SvRV(parent);
		hashval = hv_fetch(hash, "mechanism", 9, 0);
		_DEBUG("%d, %d, %s",SvTYPE(*hashval),SVt_PV,SvPV_nolen(*hashval));
		if (hashval  && *hashval && SvTYPE(*hashval) == SVt_PV)
		{
			if ((*sasl)->mech)
				free((*sasl)->mech);
			(*sasl)->mech = strdup(SvPV_nolen(*hashval));
		}
		else
		{
			__DEBUG("Saslmech not recognised:");
		}
	}

	return (*sasl)->error_code;
}

#ifdef SASL2
void set_secprop (Authen_SASL_XS sasl)
{
	sasl_security_properties_t ssp;

	if (sasl == NULL)
		return;

	memset(&ssp, 0, sizeof(ssp));
	ssp.maxbufsize = 0xFFFF;
	ssp.max_ssf = 0xFF;
	sasl_setprop(sasl->conn, SASL_SEC_PROPS, &ssp);
}
#endif



#line 984 "XS.c"
#ifndef PERL_UNUSED_VAR
#  define PERL_UNUSED_VAR(var) if (0) var = var
#endif

#ifndef dVAR
#  define dVAR		dNOOP
#endif


/* This stuff is not part of the API! You have been warned. */
#ifndef PERL_VERSION_DECIMAL
#  define PERL_VERSION_DECIMAL(r,v,s) (r*1000000 + v*1000 + s)
#endif
#ifndef PERL_DECIMAL_VERSION
#  define PERL_DECIMAL_VERSION \
	  PERL_VERSION_DECIMAL(PERL_REVISION,PERL_VERSION,PERL_SUBVERSION)
#endif
#ifndef PERL_VERSION_GE
#  define PERL_VERSION_GE(r,v,s) \
	  (PERL_DECIMAL_VERSION >= PERL_VERSION_DECIMAL(r,v,s))
#endif
#ifndef PERL_VERSION_LE
#  define PERL_VERSION_LE(r,v,s) \
	  (PERL_DECIMAL_VERSION <= PERL_VERSION_DECIMAL(r,v,s))
#endif

/* XS_INTERNAL is the explicit static-linkage variant of the default
 * XS macro.
 *
 * XS_EXTERNAL is the same as XS_INTERNAL except it does not include
 * "STATIC", ie. it exports XSUB symbols. You probably don't want that
 * for anything but the BOOT XSUB.
 *
 * See XSUB.h in core!
 */


/* TODO: This might be compatible further back than 5.10.0. */
#if PERL_VERSION_GE(5, 10, 0) && PERL_VERSION_LE(5, 15, 1)
#  undef XS_EXTERNAL
#  undef XS_INTERNAL
#  if defined(__CYGWIN__) && defined(USE_DYNAMIC_LOADING)
#    define XS_EXTERNAL(name) __declspec(dllexport) XSPROTO(name)
#    define XS_INTERNAL(name) STATIC XSPROTO(name)
#  endif
#  if defined(__SYMBIAN32__)
#    define XS_EXTERNAL(name) EXPORT_C XSPROTO(name)
#    define XS_INTERNAL(name) EXPORT_C STATIC XSPROTO(name)
#  endif
#  ifndef XS_EXTERNAL
#    if defined(HASATTRIBUTE_UNUSED) && !defined(__cplusplus)
#      define XS_EXTERNAL(name) void name(pTHX_ CV* cv __attribute__unused__)
#      define XS_INTERNAL(name) STATIC void name(pTHX_ CV* cv __attribute__unused__)
#    else
#      ifdef __cplusplus
#        define XS_EXTERNAL(name) extern "C" XSPROTO(name)
#        define XS_INTERNAL(name) static XSPROTO(name)
#      else
#        define XS_EXTERNAL(name) XSPROTO(name)
#        define XS_INTERNAL(name) STATIC XSPROTO(name)
#      endif
#    endif
#  endif
#endif

/* perl >= 5.10.0 && perl <= 5.15.1 */


/* The XS_EXTERNAL macro is used for functions that must not be static
 * like the boot XSUB of a module. If perl didn't have an XS_EXTERNAL
 * macro defined, the best we can do is assume XS is the same.
 * Dito for XS_INTERNAL.
 */
#ifndef XS_EXTERNAL
#  define XS_EXTERNAL(name) XS(name)
#endif
#ifndef XS_INTERNAL
#  define XS_INTERNAL(name) XS(name)
#endif

/* Now, finally, after all this mess, we want an ExtUtils::ParseXS
 * internal macro that we're free to redefine for varying linkage due
 * to the EXPORT_XSUB_SYMBOLS XS keyword. This is internal, use
 * XS_EXTERNAL(name) or XS_INTERNAL(name) in your code if you need to!
 */

#undef XS_EUPXS
#if defined(PERL_EUPXS_ALWAYS_EXPORT)
#  define XS_EUPXS(name) XS_EXTERNAL(name)
#else
   /* default to internal */
#  define XS_EUPXS(name) XS_INTERNAL(name)
#endif

#ifndef PERL_ARGS_ASSERT_CROAK_XS_USAGE
#define PERL_ARGS_ASSERT_CROAK_XS_USAGE assert(cv); assert(params)

/* prototype to pass -Wmissing-prototypes */
STATIC void
S_croak_xs_usage(const CV *const cv, const char *const params);

STATIC void
S_croak_xs_usage(const CV *const cv, const char *const params)
{
    const GV *const gv = CvGV(cv);

    PERL_ARGS_ASSERT_CROAK_XS_USAGE;

    if (gv) {
        const char *const gvname = GvNAME(gv);
        const HV *const stash = GvSTASH(gv);
        const char *const hvname = stash ? HvNAME(stash) : NULL;

        if (hvname)
	    Perl_croak_nocontext("Usage: %s::%s(%s)", hvname, gvname, params);
        else
	    Perl_croak_nocontext("Usage: %s(%s)", gvname, params);
    } else {
        /* Pants. I don't think that it should be possible to get here. */
	Perl_croak_nocontext("Usage: CODE(0x%" UVxf ")(%s)", PTR2UV(cv), params);
    }
}
#undef  PERL_ARGS_ASSERT_CROAK_XS_USAGE

#define croak_xs_usage        S_croak_xs_usage

#endif

/* NOTE: the prototype of newXSproto() is different in versions of perls,
 * so we define a portable version of newXSproto()
 */
#ifdef newXS_flags
#define newXSproto_portable(name, c_impl, file, proto) newXS_flags(name, c_impl, file, proto, 0)
#else
#define newXSproto_portable(name, c_impl, file, proto) (PL_Sv=(SV*)newXS(name, c_impl, file), sv_setpv(PL_Sv, proto), (CV*)PL_Sv)
#endif /* !defined(newXS_flags) */

#if PERL_VERSION_LE(5, 21, 5)
#  define newXS_deffile(a,b) Perl_newXS(aTHX_ a,b,file)
#else
#  define newXS_deffile(a,b) Perl_newXS_deffile(aTHX_ a,b)
#endif

#line 1128 "XS.c"

XS_EUPXS(XS_Authen__SASL__XS_server_new); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Authen__SASL__XS_server_new)
{
    dVAR; dXSARGS;
    if (items < 3)
       croak_xs_usage(cv,  "pkg, parent, service, host = NULL, iplocalport=NULL, ipremoteport=NULL ");
    {
	char *	pkg = (char *)SvPV_nolen(ST(0))
;
	SV *	parent = ST(1)
;
	char *	service = (char *)SvPV_nolen(ST(2))
;
	char *	host;
	char *	iplocalport;
	char *	ipremoteport;
	Authen_SASL_XS	RETVAL;

	if (items < 4)
	    host = NULL;
	else {
	    host = (char *)SvPV_nolen(ST(3))
;
	}

	if (items < 5)
	    iplocalport = NULL;
	else {
	    iplocalport = (char *)SvPV_nolen(ST(4))
;
	}

	if (items < 6)
	    ipremoteport = NULL ;
	else {
	    ipremoteport = (char *)SvPV_nolen(ST(5))
;
	}
#line 1204 "XS.xs"
	{
/* TODO realm parameter */
		Authen_SASL_XS sasl = NULL;
		int rc;

		if ((rc = init_sasl(parent,service,host,&sasl,SASL_IS_SERVER)) != SASL_OK)
			croak("Saslinit failed. (%x)\n",rc);

		_DEBUG("server_new: Service: %s Server: %s, %s %s %s %s",sasl->service,sasl->server,service,host,iplocalport,ipremoteport);

		if ((rc = sasl_server_init(NULL,sasl->service)) != SASL_OK)
			SetSaslError(sasl,rc,"server_init error.");
#ifdef SASL2
		rc = sasl_server_new(sasl->service, sasl->server, NULL, iplocalport, ipremoteport, sasl->callbacks, 1, &sasl->conn);
#else
		rc = sasl_server_new(sasl->service, sasl->server, NULL, sasl->callbacks, 1, &sasl->conn);
#endif

		if (SetSaslError(sasl,rc,"server_new error.") == SASL_OK)
		{
#ifdef SASL2
			set_secprop(sasl);
#endif
		}
		RETVAL = sasl;
	}
#line 1195 "XS.c"
	{
	    SV * RETVALSV;
	    RETVALSV = sv_newmortal();
	    sv_setref_pv(RETVALSV, "Authen::SASL::XS",
		    (void*)RETVAL);
	    ST(0) = RETVALSV;
	}
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Authen__SASL__XS_client_new); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Authen__SASL__XS_client_new)
{
    dVAR; dXSARGS;
    if (items < 4)
       croak_xs_usage(cv,  "pkg, parent, service, host, iplocalport = NULL, ipremoteport = NULL");
    {
	char *	pkg = (char *)SvPV_nolen(ST(0))
;
	SV *	parent = ST(1)
;
	char *	service = (char *)SvPV_nolen(ST(2))
;
	char *	host = (char *)SvPV_nolen(ST(3))
;
	char *	iplocalport;
	char *	ipremoteport;
	Authen_SASL_XS	RETVAL;

	if (items < 5)
	    iplocalport = NULL;
	else {
	    iplocalport = (char *)SvPV_nolen(ST(4))
;
	}

	if (items < 6)
	    ipremoteport = NULL;
	else {
	    ipremoteport = (char *)SvPV_nolen(ST(5))
;
	}
#line 1274 "XS.xs"
  {
	Authen_SASL_XS sasl = NULL;
	int rc;

	if ((rc = init_sasl(parent,service,host,&sasl,SASL_IS_CLIENT)) != SASL_OK)
		croak("Saslinit failed. (%x)\n",rc);

    sasl_client_init(NULL);
	_DEBUG("service: %s, host: %s, mech: %s",sasl->service,sasl->server,sasl->mech);
#ifdef SASL2
    rc = sasl_client_new(sasl->service, sasl->server, iplocalport, ipremoteport, sasl->callbacks, 1, &sasl->conn);
#else
    rc = sasl_client_new(sasl->service, sasl->server, sasl->callbacks, 1, &sasl->conn);
#endif

    if (SetSaslError(sasl,rc,"client_new error.") == SASL_OK)
	{
#ifdef SASL2
		set_secprop(sasl);
#endif
    }
    RETVAL = sasl;
  }
#line 1264 "XS.c"
	{
	    SV * RETVALSV;
	    RETVALSV = sv_newmortal();
	    sv_setref_pv(RETVALSV, "Authen::SASL::XS",
		    (void*)RETVAL);
	    ST(0) = RETVALSV;
	}
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Authen__SASL__XS_server_start); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Authen__SASL__XS_server_start)
{
    dVAR; dXSARGS;
    if (items < 1 || items > 2)
       croak_xs_usage(cv,  "sasl, instring=NULL");
    PERL_UNUSED_VAR(ax); /* -Wall */
    SP -= items;
    {
	Authen_SASL_XS	sasl;
	const char *	instring;
#line 1317 "XS.xs"
		int rc;
		unsigned outlen;
                STRLEN inlen;
#ifdef SASL2
		const char *outstring = NULL;
#else
		char *outstring = NULL;
		const char *error =NULL;
#endif

#line 1299 "XS.c"
	char *	RETVAL;
	dXSTARG;

	if (sv_derived_from(ST(0), "Authen::SASL::XS")) {
		IV tmp = SvIV((SV*)SvRV(ST(0)));
		sasl = (Authen_SASL_XS) tmp;
	}
	else
		croak("sasl is not of type Authen::SASL::XS")
;

	if (items < 2)
	    instring = NULL;
	else {
	    instring = (const char *)SvPV_nolen(ST(1))
;
	}
#line 1328 "XS.xs"
		_DEBUG("serverstart mech: %s",sasl->mech);

		if (sasl->error_code)
			XSRETURN_UNDEF;

		if (instring != NULL)
			SvPV(ST(1),inlen);
		else
			inlen = 0;

		_DEBUG("serverstart len: %d",inlen);

		_DEBUG("Server step: %s %d", instring,inlen);
#ifdef SASL2
		rc = sasl_server_start(sasl->conn,sasl->mech, instring, inlen, &outstring, &outlen);
#else
		rc = sasl_server_start(sasl->conn,sasl->mech, instring, inlen, &outstring, &outlen, &error);
#endif
		SetSaslError(sasl,rc,"server_start error."); // SASL_CONTINUE has to be set

		_DEBUG("Server step out: %s %d",outstring, outlen);
		if (rc != SASL_OK && rc != SASL_CONTINUE)
			XSRETURN_UNDEF;
		else // Everything works fine
			XPUSHp(outstring, outlen);
#line 1343 "XS.c"
	PUTBACK;
	return;
    }
}


XS_EUPXS(XS_Authen__SASL__XS_client_start); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Authen__SASL__XS_client_start)
{
    dVAR; dXSARGS;
    if (items != 1)
       croak_xs_usage(cv,  "sasl");
    PERL_UNUSED_VAR(ax); /* -Wall */
    SP -= items;
    {
	Authen_SASL_XS	sasl;
#line 1367 "XS.xs"
	int rc;
	unsigned outlen;
#ifdef SASL2
	const char *outstring;
#else
	char *outstring;
#endif

	const char *mech;
#line 1370 "XS.c"
	char *	RETVAL;
	dXSTARG;

	if (sv_derived_from(ST(0), "Authen::SASL::XS")) {
		IV tmp = SvIV((SV*)SvRV(ST(0)));
		sasl = (Authen_SASL_XS) tmp;
	}
	else
		croak("sasl is not of type Authen::SASL::XS")
;
#line 1377 "XS.xs"
		if (sasl->error_code != SASL_OK)
			XSRETURN_UNDEF;

      _DEBUG("mech: %s",sasl->mech);
#ifdef SASL2
      rc = sasl_client_start(sasl->conn, sasl->mech, NULL, &outstring, &outlen, &mech);
#else
      rc = sasl_client_start(sasl->conn, sasl->mech, NULL, NULL, &outstring, &outlen, &mech);
#endif
	  _DEBUG("client_start. error %x, len: %d",rc,outlen);
	  SetSaslError(sasl,rc,"client_start error. (Callbacks?)");
      if (rc != SASL_OK && rc != SASL_CONTINUE)
		XSRETURN_UNDEF;
	  else
	    XPUSHp(outstring, outlen);
#line 1397 "XS.c"
	PUTBACK;
	return;
    }
}


XS_EUPXS(XS_Authen__SASL__XS_server_step); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Authen__SASL__XS_server_step)
{
    dVAR; dXSARGS;
    if (items != 2)
       croak_xs_usage(cv,  "sasl, instring");
    PERL_UNUSED_VAR(ax); /* -Wall */
    SP -= items;
    {
	Authen_SASL_XS	sasl;
	char *	instring = (char *)SvPV_nolen(ST(1))
;
#line 1408 "XS.xs"
#ifdef SASL2
		const char *outstring=NULL;
#else
		char *outstring=NULL;
		const char *error=NULL;
#endif
		int rc;
		unsigned int outlen=0;
                STRLEN inlen;
#line 1426 "XS.c"
	char *	RETVAL;
	dXSTARG;

	if (sv_derived_from(ST(0), "Authen::SASL::XS")) {
		IV tmp = SvIV((SV*)SvRV(ST(0)));
		sasl = (Authen_SASL_XS) tmp;
	}
	else
		croak("sasl is not of type Authen::SASL::XS")
;
#line 1418 "XS.xs"
		if (sasl->error_code != SASL_CONTINUE)
			XSRETURN_UNDEF;

		SvPV(ST(1),inlen);
		_DEBUG("Server step: %s %d", instring,inlen);
#ifdef SASL2
		rc = sasl_server_step(sasl->conn,instring,inlen,&outstring,&outlen);
#else
		rc = sasl_server_step(sasl->conn,instring,inlen,&outstring,&outlen,NULL);
#endif
		// Setting error, if any
		SetSaslError(sasl,rc,"server_step error.");
		// return undef if error, code() will give the truth
		if (rc != SASL_OK && rc != SASL_CONTINUE)
			XSRETURN_UNDEF;
		else
			XPUSHp(outstring, outlen);
#line 1455 "XS.c"
	PUTBACK;
	return;
    }
}


XS_EUPXS(XS_Authen__SASL__XS_client_step); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Authen__SASL__XS_client_step)
{
    dVAR; dXSARGS;
    if (items != 2)
       croak_xs_usage(cv,  "sasl, instring");
    PERL_UNUSED_VAR(ax); /* -Wall */
    SP -= items;
    {
	Authen_SASL_XS	sasl;
	char *	instring = (char *)SvPV_nolen(ST(1))
;
	char *	RETVAL;
	dXSTARG;

	if (sv_derived_from(ST(0), "Authen::SASL::XS")) {
		IV tmp = SvIV((SV*)SvRV(ST(0)));
		sasl = (Authen_SASL_XS) tmp;
	}
	else
		croak("sasl is not of type Authen::SASL::XS")
;
#line 1463 "XS.xs"
  {
#ifdef SASL2
    const char *outstring=NULL;
#else
    char *outstring=NULL;
#endif
    int rc;
    unsigned int outlen=0;
    STRLEN inlen;

    if (sasl->error_code != SASL_CONTINUE)
      XSRETURN_UNDEF;

    SvPV(ST(1),inlen);

	_DEBUG("client_step: inlen: %d",inlen);

    rc = sasl_client_step(sasl->conn, instring, inlen, NULL, &outstring, &outlen);

	SetSaslError(sasl,rc,"client_step.");

	_DEBUG("client_step: error code: %x, len: %d",rc,outlen);
	if (rc != SASL_OK && rc != SASL_CONTINUE)
		XSRETURN_UNDEF;
	else
		XPUSHp(outstring, outlen);
  }
#line 1512 "XS.c"
	PUTBACK;
	return;
    }
}


XS_EUPXS(XS_Authen__SASL__XS_listmech); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Authen__SASL__XS_listmech)
{
    dVAR; dXSARGS;
    if (items < 1 || items > 4)
       croak_xs_usage(cv,  "sasl, start=\"\", separator=\"|\", end=\"\"");
    PERL_UNUSED_VAR(ax); /* -Wall */
    SP -= items;
    {
	Authen_SASL_XS	sasl;
	const char*	start;
	const char*	separator;
	const char*	end;
	char *	RETVAL;
	dXSTARG;

	if (sv_derived_from(ST(0), "Authen::SASL::XS")) {
		IV tmp = SvIV((SV*)SvRV(ST(0)));
		sasl = (Authen_SASL_XS) tmp;
	}
	else
		croak("sasl is not of type Authen::SASL::XS")
;

	if (items < 2)
	    start = "";
	else {
	    start = (const char *)SvPV_nolen(ST(1))
;
	}

	if (items < 3)
	    separator = "|";
	else {
	    separator = (const char *)SvPV_nolen(ST(2))
;
	}

	if (items < 4)
	    end = "";
	else {
	    end = (const char *)SvPV_nolen(ST(3))
;
	}
#line 1509 "XS.xs"
	{
	    int rc;
#ifdef SASL2
	    const char *mechs;
#else
		char *mechs;
#endif
		int mechcount;
	    unsigned mechlen;

		rc = sasl_listmech(sasl->conn,sasl->user,start,separator,end,&mechs,&mechlen,&mechcount);

		if (rc == SASL_OK)
			XPUSHp(mechs,mechlen);
		else
		{
			SetSaslError(sasl,rc,"listmech error.");
			XSRETURN_UNDEF;
		}
	}
#line 1584 "XS.c"
	PUTBACK;
	return;
    }
}

#ifdef SASL2
#define XSubPPtmpAAAA 1


XS_EUPXS(XS_Authen__SASL__XS_setpass); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Authen__SASL__XS_setpass)
{
    dVAR; dXSARGS;
    if (items < 4 || items > 5)
       croak_xs_usage(cv,  "sasl, user, pass, oldpass, flags=0");
    PERL_UNUSED_VAR(ax); /* -Wall */
    SP -= items;
    {
	Authen_SASL_XS	sasl;
	const char *	user = (const char *)SvPV_nolen(ST(1))
;
	const char *	pass = (const char *)SvPV_nolen(ST(2))
;
	const char *	oldpass = (const char *)SvPV_nolen(ST(3))
;
	int	flags;
#line 1559 "XS.xs"
		int rc;
#line 1613 "XS.c"
	int	RETVAL;
	dXSTARG;

	if (sv_derived_from(ST(0), "Authen::SASL::XS")) {
		IV tmp = SvIV((SV*)SvRV(ST(0)));
		sasl = (Authen_SASL_XS) tmp;
	}
	else
		croak("sasl is not of type Authen::SASL::XS")
;

	if (items < 5)
	    flags = 0;
	else {
	    flags = (int)SvIV(ST(4))
;
	}
#line 1561 "XS.xs"
		_DEBUG("setpass: %s,%s,%s,%d",user,pass,oldpass,flags);
		rc = sasl_setpass (sasl->conn,user,
						pass,strlen(pass),
						oldpass,strlen(oldpass),
						flags);
		XPUSHi(rc);
#line 1638 "XS.c"
	PUTBACK;
	return;
    }
}


XS_EUPXS(XS_Authen__SASL__XS_checkpass); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Authen__SASL__XS_checkpass)
{
    dVAR; dXSARGS;
    if (items != 3)
       croak_xs_usage(cv,  "sasl, user, pass");
    PERL_UNUSED_VAR(ax); /* -Wall */
    SP -= items;
    {
	Authen_SASL_XS	sasl;
	const char *	user = (const char *)SvPV_nolen(ST(1))
;
	const char *	pass = (const char *)SvPV_nolen(ST(2))
;
#line 1574 "XS.xs"
	int rc;
#line 1661 "XS.c"
	int	RETVAL;
	dXSTARG;

	if (sv_derived_from(ST(0), "Authen::SASL::XS")) {
		IV tmp = SvIV((SV*)SvRV(ST(0)));
		sasl = (Authen_SASL_XS) tmp;
	}
	else
		croak("sasl is not of type Authen::SASL::XS")
;
#line 1576 "XS.xs"
	_DEBUG("checkpass: %s,%s",user,pass);
	rc = sasl_checkpass (sasl->conn,
			user, strlen(user),
			pass, strlen(pass));
	XPUSHi(rc);
#line 1678 "XS.c"
	PUTBACK;
	return;
    }
}


XS_EUPXS(XS_Authen__SASL__XS_global_listmech); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Authen__SASL__XS_global_listmech)
{
    dVAR; dXSARGS;
    if (items != 1)
       croak_xs_usage(cv,  "sasl");
    PERL_UNUSED_VAR(ax); /* -Wall */
    SP -= items;
    {
	Authen_SASL_XS	sasl;
#line 1597 "XS.xs"
		int i;
		const char **mechs;
#line 1698 "XS.c"

	if (sv_derived_from(ST(0), "Authen::SASL::XS")) {
		IV tmp = SvIV((SV*)SvRV(ST(0)));
		sasl = (Authen_SASL_XS) tmp;
	}
	else
		croak("sasl is not of type Authen::SASL::XS")
;
#line 1600 "XS.xs"
		if (sasl->error_code)
			XSRETURN_UNDEF;
		mechs = sasl_global_listmech();
		if (mechs)
			for (i = 0; mechs[i]; i++)
				XPUSHs(sv_2mortal(newSVpv(mechs[i],0)));
		else
			XSRETURN_UNDEF;
#line 1716 "XS.c"
	PUTBACK;
	return;
    }
}

#endif

XS_EUPXS(XS_Authen__SASL__XS_encode); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Authen__SASL__XS_encode)
{
    dVAR; dXSARGS;
    if (items != 2)
       croak_xs_usage(cv,  "sasl, instring");
    PERL_UNUSED_VAR(ax); /* -Wall */
    SP -= items;
    {
	Authen_SASL_XS	sasl;
	char *	instring = (char *)SvPV_nolen(ST(1))
;
	char *	RETVAL;
	dXSTARG;

	if (sv_derived_from(ST(0), "Authen::SASL::XS")) {
		IV tmp = SvIV((SV*)SvRV(ST(0)));
		sasl = (Authen_SASL_XS) tmp;
	}
	else
		croak("sasl is not of type Authen::SASL::XS")
;
#line 1632 "XS.xs"
  {
#ifdef SASL2
    const char *outstring=NULL;
#else
    char *outstring=NULL;
#endif
    int rc;
	unsigned int outlen=0;
        STRLEN inlen;
	if (sasl->error_code)
		XSRETURN_UNDEF;

	instring = SvPV(ST(1),inlen);

	rc = sasl_encode(sasl->conn, instring, inlen, &outstring, &outlen);
    if (SetSaslError(sasl,rc,"sasl_encode failed") != SASL_OK)
		XSRETURN_UNDEF;
	else
	    XPUSHp(outstring, outlen);
  }
#line 1767 "XS.c"
	PUTBACK;
	return;
    }
}


XS_EUPXS(XS_Authen__SASL__XS_decode); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Authen__SASL__XS_decode)
{
    dVAR; dXSARGS;
    if (items != 2)
       croak_xs_usage(cv,  "sasl, instring");
    PERL_UNUSED_VAR(ax); /* -Wall */
    SP -= items;
    {
	Authen_SASL_XS	sasl;
	char *	instring = (char *)SvPV_nolen(ST(1))
;
	char *	RETVAL;
	dXSTARG;

	if (sv_derived_from(ST(0), "Authen::SASL::XS")) {
		IV tmp = SvIV((SV*)SvRV(ST(0)));
		sasl = (Authen_SASL_XS) tmp;
	}
	else
		croak("sasl is not of type Authen::SASL::XS")
;
#line 1661 "XS.xs"
  {
#ifdef SASL2
    const char *outstring=NULL;
#else
    char *outstring=NULL;
#endif
    int rc;
    unsigned int outlen=0;
    STRLEN inlen;

    if (sasl->error_code)
       XSRETURN_UNDEF;

    instring = SvPV(ST(1),inlen);

    rc = sasl_decode(sasl->conn, instring, inlen, &outstring, &outlen);
    if (SetSaslError(sasl,rc,"sasl_decode failed.") != SASL_OK)
		XSRETURN_UNDEF;
	else
	    XPUSHp(outstring, outlen);
  }
#line 1818 "XS.c"
	PUTBACK;
	return;
    }
}


XS_EUPXS(XS_Authen__SASL__XS_callback); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Authen__SASL__XS_callback)
{
    dVAR; dXSARGS;
    if (items < 1)
       croak_xs_usage(cv,  "sasl, ...");
    {
	Authen_SASL_XS	sasl;
	int	RETVAL;
	dXSTARG;

	if (sv_derived_from(ST(0), "Authen::SASL::XS")) {
		IV tmp = SvIV((SV*)SvRV(ST(0)));
		sasl = (Authen_SASL_XS) tmp;
	}
	else
		croak("sasl is not of type Authen::SASL::XS")
;
#line 1690 "XS.xs"
/*
 This function is unnecessary since there is no
 chance for changing callbacks in sasl after (server|
 client)_new function calls. But without calling one
 of these functions (from perl) you do not have an
 object of this class. So you cannot call ->callback.
 At least I was not able to use this function to fill in
 a callback with this function.
 -Patrick
*/
	croak("Deprecated. Don't use, it isn't working anymore.");
		RETVAL = 0;
#line 1856 "XS.c"
	XSprePUSH; PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Authen__SASL__XS_error); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Authen__SASL__XS_error)
{
    dVAR; dXSARGS;
    if (items != 1)
       croak_xs_usage(cv,  "sasl");
    PERL_UNUSED_VAR(ax); /* -Wall */
    SP -= items;
    {
	Authen_SASL_XS	sasl;
	char *	RETVAL;
	dXSTARG;

	if (sv_derived_from(ST(0), "Authen::SASL::XS")) {
		IV tmp = SvIV((SV*)SvRV(ST(0)));
		sasl = (Authen_SASL_XS) tmp;
	}
	else
		croak("sasl is not of type Authen::SASL::XS")
;
#line 1723 "XS.xs"
  {
	_DEBUG("Current Error %x",sasl->error_code);

	XPUSHs(newSVpv((char *)sasl_errstring(sasl->error_code,NULL,NULL),0));
#ifdef SASL2
	XPUSHs(newSVpv((char *)sasl_errdetail(sasl->conn),0));
#endif

	if (sasl->additional_errormsg != NULL)
		XPUSHs(newSVpv(sasl->additional_errormsg,0));
	// only real error should be overwritten
	if (sasl->error_code != SASL_OK && sasl->error_code != SASL_CONTINUE)
	{
		sasl->error_code = SASL_OK;
		if (sasl->additional_errormsg != NULL)
			free(sasl->additional_errormsg);
		sasl->additional_errormsg = NULL;
	}
	__DEBUG("End of Error");
  }
#line 1904 "XS.c"
	PUTBACK;
	return;
    }
}


XS_EUPXS(XS_Authen__SASL__XS_code); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Authen__SASL__XS_code)
{
    dVAR; dXSARGS;
    if (items != 1)
       croak_xs_usage(cv,  "sasl");
    {
	Authen_SASL_XS	sasl;
	int	RETVAL;
	dXSTARG;

	if (sv_derived_from(ST(0), "Authen::SASL::XS")) {
		IV tmp = SvIV((SV*)SvRV(ST(0)));
		sasl = (Authen_SASL_XS) tmp;
	}
	else
		croak("sasl is not of type Authen::SASL::XS")
;
#line 1757 "XS.xs"
    RETVAL=sasl->error_code;
#line 1931 "XS.c"
	XSprePUSH; PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Authen__SASL__XS_mechanism); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Authen__SASL__XS_mechanism)
{
    dVAR; dXSARGS;
    if (items != 1)
       croak_xs_usage(cv,  "sasl");
    {
	Authen_SASL_XS	sasl;
	char *	RETVAL;
	dXSTARG;

	if (sv_derived_from(ST(0), "Authen::SASL::XS")) {
		IV tmp = SvIV((SV*)SvRV(ST(0)));
		sasl = (Authen_SASL_XS) tmp;
	}
	else
		croak("sasl is not of type Authen::SASL::XS")
;
#line 1774 "XS.xs"
    RETVAL = sasl->mech;
#line 1958 "XS.c"
	sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG;
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Authen__SASL__XS_host); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Authen__SASL__XS_host)
{
    dVAR; dXSARGS;
    if (items < 1)
       croak_xs_usage(cv,  "sasl, ...");
    {
	Authen_SASL_XS	sasl;
	char *	RETVAL;
	dXSTARG;

	if (sv_derived_from(ST(0), "Authen::SASL::XS")) {
		IV tmp = SvIV((SV*)SvRV(ST(0)));
		sasl = (Authen_SASL_XS) tmp;
	}
	else
		croak("sasl is not of type Authen::SASL::XS")
;
#line 1784 "XS.xs"
    if (items > 1) {
      if (sasl->server) free(sasl->server);
      sasl->server = strdup(SvPV_nolen(ST(1)));
    }
    RETVAL = sasl->server;
#line 1989 "XS.c"
	sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG;
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Authen__SASL__XS_user); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Authen__SASL__XS_user)
{
    dVAR; dXSARGS;
    if (items < 1)
       croak_xs_usage(cv,  "sasl, ...");
    {
	Authen_SASL_XS	sasl;
	char *	RETVAL;
	dXSTARG;

	if (sv_derived_from(ST(0), "Authen::SASL::XS")) {
		IV tmp = SvIV((SV*)SvRV(ST(0)));
		sasl = (Authen_SASL_XS) tmp;
	}
	else
		croak("sasl is not of type Authen::SASL::XS")
;
#line 1798 "XS.xs"
    if (items > 1) {
      if (sasl->user) free(sasl->user);
      sasl->user = strdup(SvPV_nolen(ST(1)));
    }
    RETVAL = sasl->user;
#line 2020 "XS.c"
	sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG;
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Authen__SASL__XS_service); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Authen__SASL__XS_service)
{
    dVAR; dXSARGS;
    if (items < 1)
       croak_xs_usage(cv,  "sasl, ...");
    {
	Authen_SASL_XS	sasl;
	char *	RETVAL;
	dXSTARG;

	if (sv_derived_from(ST(0), "Authen::SASL::XS")) {
		IV tmp = SvIV((SV*)SvRV(ST(0)));
		sasl = (Authen_SASL_XS) tmp;
	}
	else
		croak("sasl is not of type Authen::SASL::XS")
;
#line 1812 "XS.xs"
    if (items > 1) {
      if (sasl->service) free(sasl->service);
      sasl->service = strdup(SvPV_nolen(ST(1)));
    }
    RETVAL = sasl->service;
#line 2051 "XS.c"
	sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG;
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Authen__SASL__XS_need_step); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Authen__SASL__XS_need_step)
{
    dVAR; dXSARGS;
    if (items != 1)
       croak_xs_usage(cv,  "sasl");
    {
	Authen_SASL_XS	sasl;
	int	RETVAL;
	dXSTARG;

	if (sv_derived_from(ST(0), "Authen::SASL::XS")) {
		IV tmp = SvIV((SV*)SvRV(ST(0)));
		sasl = (Authen_SASL_XS) tmp;
	}
	else
		croak("sasl is not of type Authen::SASL::XS")
;
#line 1835 "XS.xs"
		RETVAL = sasl->error_code == SASL_CONTINUE;
#line 2078 "XS.c"
	XSprePUSH; PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_Authen__SASL__XS_property); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Authen__SASL__XS_property)
{
    dVAR; dXSARGS;
    if (items < 1)
       croak_xs_usage(cv,  "sasl, ...");
    PERL_UNUSED_VAR(ax); /* -Wall */
    SP -= items;
    {
	Authen_SASL_XS	sasl;
	int	RETVAL;
	dXSTARG;

	if (sv_derived_from(ST(0), "Authen::SASL::XS")) {
		IV tmp = SvIV((SV*)SvRV(ST(0)));
		sasl = (Authen_SASL_XS) tmp;
	}
	else
		croak("sasl is not of type Authen::SASL::XS")
;
#line 1844 "XS.xs"
{
#ifdef SASL2
	const void *value=NULL;
#else
	void *value=NULL;
#endif
	char *name;
	int rc, x, propnum=-1;
	SV *prop;

	RETVAL = SASL_OK;

	if (!sasl->conn) {
#ifdef SASL2
		SetSaslError(sasl,SASL_NOTINIT,"property failed, init missed.");
		RETVAL = SASL_NOTINIT;
#else
		SetSaslError(sasl,SASL_FAIL,"property failed, init missed.");
		RETVAL = SASL_FAIL;
#endif
		items = 0;
	}
/* Querying the value of a property */
	if (items == 2) {
		name = SvPV_nolen(ST(1));
		propnum = PropertyNumber(name);
		rc = sasl_getprop(sasl->conn, propnum, &value);

		if (value == NULL || rc != SASL_OK)
			XSRETURN_UNDEF;

		switch(propnum){
			case SASL_USERNAME:
#ifdef SASL2
			case SASL_DEFUSERREALM:
#else
			case SASL_REALM:
#endif
				XPUSHp( (char *)value, strlen((char *)value));
			break;
			case SASL_SSF:
			case SASL_MAXOUTBUF:
				XPUSHi((int *)value);
			break;
#ifdef SASL2
			case SASL_IPLOCALPORT:
			case SASL_IPREMOTEPORT:
				XPUSHp( (char *)value, strlen((char *)value));
			break;
			case SASL_IP_LOCAL:
				propnum = SASL_IPLOCALPORT;
				{
					char *addr = inet_ntoa( (*(struct in_addr *)value));
					XPUSHp( addr, strlen(addr));
				}
			break;
			case SASL_IP_REMOTE:
				propnum = SASL_IPREMOTEPORT;
				{
					char *addr = inet_ntoa( (*(struct in_addr *)value));
					XPUSHp( addr, strlen(addr));
				}
			break;
#else
			case SASL_IP_LOCAL:
			case SASL_IP_REMOTE:
				XPUSHp( (char *)value, sizeof(struct sockaddr_in));
			break;
#endif
			default:
				XPUSHi(-1);
		}
		XSRETURN(1);
	}
/* Fill in the properties */
	for(x=1; x<items; x+=2) {

		prop = ST(x);
		value = (void *)SvPV_nolen( ST(x+1) );

		if (SvTYPE(prop) == SVt_IV) {
			propnum = SvIV(prop);
		} else if (SvTYPE(prop) == SVt_PV) {
			name = SvPV_nolen(prop);
			propnum = PropertyNumber(name);
		}
#ifdef SASL2
		if ((propnum == SASL_IP_LOCAL) || (propnum == SASL_IP_REMOTE))
			rc = 0;
		else
#endif
			rc = sasl_setprop(sasl->conn, propnum, value);
		if (SetSaslError(sasl,rc,"sasl_setprop failed.") != SASL_OK)
			RETVAL = 1;
	}
}
#line 2202 "XS.c"
	PUTBACK;
	return;
    }
}


XS_EUPXS(XS_Authen__SASL__XS_DESTROY); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_Authen__SASL__XS_DESTROY)
{
    dVAR; dXSARGS;
    if (items != 1)
       croak_xs_usage(cv,  "sasl");
    {
	Authen_SASL_XS	sasl;

	if (sv_derived_from(ST(0), "Authen::SASL::XS")) {
		IV tmp = SvIV((SV*)SvRV(ST(0)));
		sasl = (Authen_SASL_XS) tmp;
	}
	else
		croak("sasl is not of type Authen::SASL::XS")
;
#line 1945 "XS.xs"
  {
	__DEBUG("DESTROY");
    if (sasl->conn)  sasl_dispose(&sasl->conn);
    if (sasl->callbacks) {
      free(sasl->callbacks[sasl->callback_count].context);
      free(sasl->callbacks);
    }
    if (sasl->service)   free(sasl->service);
    if (sasl->mech)      free(sasl->mech);
	if (sasl->additional_errormsg)  free(sasl->additional_errormsg);
    free(sasl);
	sasl_done();
  }
#line 2239 "XS.c"
    }
    XSRETURN_EMPTY;
}

#ifdef __cplusplus
extern "C"
#endif
XS_EXTERNAL(boot_Authen__SASL__XS); /* prototype to pass -Wmissing-prototypes */
XS_EXTERNAL(boot_Authen__SASL__XS)
{
#if PERL_VERSION_LE(5, 21, 5)
    dVAR; dXSARGS;
#else
    dVAR; dXSBOOTARGSXSAPIVERCHK;
#endif
#if (PERL_REVISION == 5 && PERL_VERSION < 9)
    char* file = __FILE__;
#else
    const char* file = __FILE__;
#endif

    PERL_UNUSED_VAR(file);

    PERL_UNUSED_VAR(cv); /* -W */
    PERL_UNUSED_VAR(items); /* -W */
#if PERL_VERSION_LE(5, 21, 5)
    XS_VERSION_BOOTCHECK;
#  ifdef XS_APIVERSION_BOOTCHECK
    XS_APIVERSION_BOOTCHECK;
#  endif
#endif

        newXS_deffile("Authen::SASL::XS::server_new", XS_Authen__SASL__XS_server_new);
        newXS_deffile("Authen::SASL::XS::client_new", XS_Authen__SASL__XS_client_new);
        newXS_deffile("Authen::SASL::XS::server_start", XS_Authen__SASL__XS_server_start);
        newXS_deffile("Authen::SASL::XS::client_start", XS_Authen__SASL__XS_client_start);
        newXS_deffile("Authen::SASL::XS::server_step", XS_Authen__SASL__XS_server_step);
        newXS_deffile("Authen::SASL::XS::client_step", XS_Authen__SASL__XS_client_step);
        newXS_deffile("Authen::SASL::XS::listmech", XS_Authen__SASL__XS_listmech);
#if XSubPPtmpAAAA
        newXS_deffile("Authen::SASL::XS::setpass", XS_Authen__SASL__XS_setpass);
        newXS_deffile("Authen::SASL::XS::checkpass", XS_Authen__SASL__XS_checkpass);
        newXS_deffile("Authen::SASL::XS::global_listmech", XS_Authen__SASL__XS_global_listmech);
#endif
        newXS_deffile("Authen::SASL::XS::encode", XS_Authen__SASL__XS_encode);
        newXS_deffile("Authen::SASL::XS::decode", XS_Authen__SASL__XS_decode);
        newXS_deffile("Authen::SASL::XS::callback", XS_Authen__SASL__XS_callback);
        newXS_deffile("Authen::SASL::XS::error", XS_Authen__SASL__XS_error);
        newXS_deffile("Authen::SASL::XS::code", XS_Authen__SASL__XS_code);
        newXS_deffile("Authen::SASL::XS::mechanism", XS_Authen__SASL__XS_mechanism);
        newXS_deffile("Authen::SASL::XS::host", XS_Authen__SASL__XS_host);
        newXS_deffile("Authen::SASL::XS::user", XS_Authen__SASL__XS_user);
        newXS_deffile("Authen::SASL::XS::service", XS_Authen__SASL__XS_service);
        newXS_deffile("Authen::SASL::XS::need_step", XS_Authen__SASL__XS_need_step);
        newXS_deffile("Authen::SASL::XS::property", XS_Authen__SASL__XS_property);
        newXS_deffile("Authen::SASL::XS::DESTROY", XS_Authen__SASL__XS_DESTROY);

    /* Initialisation Section */

#if XSubPPtmpAAAA
#endif
#line 2301 "XS.c"

    /* End of Initialisation Section */

#if PERL_VERSION_LE(5, 21, 5)
#  if PERL_VERSION_GE(5, 9, 0)
    if (PL_unitcheckav)
        call_list(PL_scopestack_ix, PL_unitcheckav);
#  endif
    XSRETURN_YES;
#else
    Perl_xs_boot_epilog(aTHX_ ax);
#endif
}

