+Index: Makefile.in
+===================================================================
+--- Makefile.in (.../tags/RELEASE_0_1_0)
++++ Makefile.in (.../trunk)
+@@ -1,8 +0,0 @@
+-# $Id$
+-
+-LTLIBRARY_NAME = libsasl.la
+-LTLIBRARY_SOURCES = sasl.c
+-LTLIBRARY_SHARED_NAME = sasl.la
+-LTLIBRARY_SHARED_LIBADD = $(SASL_SHARED_LIBADD)
+-
+-include $(top_srcdir)/build/dynlib.mk
+Index: sasl.c
+===================================================================
+--- sasl.c (.../tags/RELEASE_0_1_0)
++++ sasl.c (.../trunk)
+@@ -41,13 +41,493 @@
+ #define le_conn_name "SASL Connection Context"
+ static int le_conn;
+
+-/* {{{ sasl_callbacks[]
+- Global callbacks. These have no per-session context. */
+-static sasl_callback_t sasl_callbacks[] = {
+- { SASL_CB_LIST_END, 0, 0 }
+-};
+-/* }}}*/
++PHPAPI ZEND_DECLARE_MODULE_GLOBALS(sasl);
+
++/* SASL Callback Functions */
++/* {{{ php_sasl_cb_getopt
++ */
++static int php_sasl_cb_getopt(void *context, const char *plugin_name,
++ const char *option, const char **result,
++ unsigned *len)
++{
++ zval *function = context;
++ zval *args[2];
++ zval *retval;
++ int ret = SASL_FAIL;
++
++ MAKE_STD_ZVAL(args[0]);
++ MAKE_STD_ZVAL(args[1]);
++ MAKE_STD_ZVAL(retval);
++
++ ZVAL_STRING(args[0], (plugin_name) ? (char *)plugin_name : "", 1);
++ ZVAL_STRING(args[1], (char *)option, 1);
++
++ if (call_user_function(CG(function_table), NULL, function, retval,
++ 2, args TSRMLS_CC) == SUCCESS) {
++ ret = Z_LVAL_P(retval);
++ *result = estrdup(Z_STRVAL_P(retval));
++ *len = Z_STRLEN_P(retval);
++ }
++
++ zval_ptr_dtor(&args[0]);
++ zval_ptr_dtor(&args[1]);
++ zval_ptr_dtor(&retval);
++
++ return ret;
++}
++/* }}} */
++/* {{{ php_sasl_cb_log
++ */
++static int php_sasl_cb_log(void *context, int level, const char *message)
++{
++ zval *function = context;
++ zval *args[2];
++ zval *retval;
++ int ret = SASL_FAIL;
++
++ MAKE_STD_ZVAL(args[0]);
++ MAKE_STD_ZVAL(args[1]);
++ MAKE_STD_ZVAL(retval);
++
++ ZVAL_LONG(args[0], level);
++ ZVAL_STRING(args[1], (char *)message, 1);
++
++ if (call_user_function(CG(function_table), NULL, function, retval,
++ 2, args TSRMLS_CC) == SUCCESS) {
++ ret = Z_LVAL_P(retval);
++ }
++
++ zval_ptr_dtor(&args[0]);
++ zval_ptr_dtor(&args[1]);
++ zval_ptr_dtor(&retval);
++
++ return ret;
++}
++/* }}} */
++/* {{{ php_sasl_cb_getpath
++ */
++static int php_sasl_cb_getpath(void *context, const char **path)
++{
++ zval *function = context;
++ zval *retval;
++ int ret = SASL_FAIL;
++
++ MAKE_STD_ZVAL(retval);
++
++ if (call_user_function(CG(function_table), NULL, function, retval,
++ 0, NULL TSRMLS_CC) == SUCCESS) {
++ ret = Z_LVAL_P(retval);
++ *path = estrdup(Z_STRVAL_P(retval));
++ }
++
++ zval_ptr_dtor(&retval);
++
++ return ret;
++}
++/* }}} */
++/* {{{ php_sasl_cb_verifyfile
++ */
++static int php_sasl_cb_verifyfile(void *context, const char *file,
++ sasl_verify_type_t type)
++{
++ zval *function = context;
++ zval *args[2];
++ zval *retval;
++ int ret = SASL_FAIL;
++
++ MAKE_STD_ZVAL(args[0]);
++ MAKE_STD_ZVAL(args[1]);
++ MAKE_STD_ZVAL(retval);
++
++ ZVAL_STRING(args[0], (char *)file, 1);
++ ZVAL_LONG(args[1], type);
++
++ if (call_user_function(CG(function_table), NULL, function, retval,
++ 2, args TSRMLS_CC) == SUCCESS) {
++ ret = Z_LVAL_P(retval);
++ }
++
++ zval_ptr_dtor(&args[0]);
++ zval_ptr_dtor(&args[1]);
++ zval_ptr_dtor(&retval);
++
++ return ret;
++}
++/* }}} */
++/* {{{ php_sasl_cb_getconfpath
++ */
++static int php_sasl_cb_getconfpath(void *context, char **path)
++{
++ zval *function = context;
++ zval *retval;
++ int ret = SASL_FAIL;
++
++ MAKE_STD_ZVAL(retval);
++
++ if (call_user_function(CG(function_table), NULL, function, retval,
++ 0, NULL TSRMLS_CC) == SUCCESS) {
++ ret = Z_LVAL_P(retval);
++ *path = estrdup(Z_STRVAL_P(retval));
++ }
++
++ zval_ptr_dtor(&retval);
++
++ return ret;
++}
++/* }}} */
++/* {{{ php_sasl_cb_getsimple
++ */
++static int php_sasl_cb_getsimple(void *context, int id, const char **result,
++ unsigned *len)
++{
++ zval *function = context;
++ zval *args[1];
++ zval *retval;
++
++ MAKE_STD_ZVAL(args[0]);
++ MAKE_STD_ZVAL(retval);
++
++ ZVAL_LONG(args[0], id);
++
++ if (call_user_function(CG(function_table), NULL, function, retval,
++ 1, args TSRMLS_CC) == SUCCESS) {
++ *result = estrdup(Z_STRVAL_P(retval));
++ *len = Z_STRLEN_P(retval);
++ }
++
++ zval_ptr_dtor(&args[0]);
++ zval_ptr_dtor(&retval);
++
++ return SASL_OK;
++}
++/* }}} */
++/* {{{ php_sasl_cb_getsecret
++ */
++static int php_sasl_cb_getsecret(sasl_conn_t *conn, void *context, int id,
++ sasl_secret_t **psecret)
++{
++ zval *function = context;
++ zval *args[2];
++ zval *retval;
++
++ MAKE_STD_ZVAL(args[0]);
++ MAKE_STD_ZVAL(args[1]);
++ MAKE_STD_ZVAL(retval);
++
++ ZVAL_NULL(args[0]);
++ //ZVAL_RESOURCE(args[0], r);
++ ZVAL_LONG(args[1], id);
++
++ if (call_user_function(CG(function_table), NULL, function, retval,
++ 2, args TSRMLS_CC) == SUCCESS) {
++ int len = Z_STRLEN_P(retval);
++ *psecret = (sasl_secret_t *)emalloc(sizeof(sasl_secret_t) +
++ (len * sizeof(char)));
++ if (*psecret) {
++ (*psecret)->len = len;
++ memcpy((*psecret)->data, Z_STRVAL_P(retval), len);
++ }
++ }
++
++ zval_ptr_dtor(&args[0]);
++ zval_ptr_dtor(&args[1]);
++ zval_ptr_dtor(&retval);
++
++ return SASL_OK;
++}
++/* }}} */
++/* {{{ php_sasl_cb_chalprompt
++ */
++static int php_sasl_cb_chalprompt(void *context, int id, const char *challenge,
++ const char *prompt, const char *defresult,
++ const char **result, unsigned *len)
++{
++ zval *function = context;
++ zval *args[4];
++ zval *retval;
++
++ MAKE_STD_ZVAL(args[0]);
++ MAKE_STD_ZVAL(args[1]);
++ MAKE_STD_ZVAL(args[2]);
++ MAKE_STD_ZVAL(args[3]);
++ MAKE_STD_ZVAL(retval);
++
++ ZVAL_LONG(args[0], id);
++ ZVAL_STRING(args[1], (char *)challenge, 1);
++ ZVAL_STRING(args[2], (char *)prompt, 1);
++ ZVAL_STRING(args[3], (char *)defresult, 1);
++
++ if (call_user_function(CG(function_table), NULL, function, retval,
++ 4, args TSRMLS_CC) == SUCCESS) {
++ *result = estrdup(Z_STRVAL_P(retval));
++ *len = Z_STRLEN_P(retval);
++ }
++
++ zval_ptr_dtor(&args[0]);
++ zval_ptr_dtor(&args[1]);
++ zval_ptr_dtor(&args[2]);
++ zval_ptr_dtor(&args[3]);
++ zval_ptr_dtor(&retval);
++
++ return SASL_OK;
++}
++/* }}} */
++/* {{{ php_sasl_cb_getrealm
++ */
++static int php_sasl_cb_getrealm(void *context, int id,
++ const char **availrealms, const char **result)
++{
++ zval *function = context;
++ zval *args[2];
++ zval *retval;
++
++ MAKE_STD_ZVAL(args[0]);
++ MAKE_STD_ZVAL(args[1]);
++ MAKE_STD_ZVAL(retval);
++
++ ZVAL_LONG(args[0], id);
++ ZVAL_NULL(args[1]); // TODO: Make an array of available realms
++
++ if (call_user_function(CG(function_table), NULL, function, retval,
++ 2, args TSRMLS_CC) == SUCCESS) {
++ *result = estrdup(Z_STRVAL_P(retval));
++ }
++
++ zval_ptr_dtor(&args[0]);
++ zval_ptr_dtor(&args[1]);
++ zval_ptr_dtor(&retval);
++
++ return SASL_OK;
++}
++/* }}} */
++/* {{{ php_sasl_cb_authorize
++ */
++static int php_sasl_cb_authorize(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)
++{
++ zval *function = context;
++ zval *args[2];
++ zval *retval;
++ int ret = SASL_NOAUTHZ;
++
++#if 0
++ MAKE_STD_ZVAL(args[0]);
++ MAKE_STD_ZVAL(args[1]);
++ MAKE_STD_ZVAL(retval);
++
++ ZVAL_LONG(args[0], id);
++ ZVAL_NULL(args[1]); // TODO: Make an array of available realms
++
++ if (call_user_function(CG(function_table), NULL, function, retval,
++ 2, args TSRMLS_CC) == SUCCESS) {
++ *result = estrdup(Z_STRVAL_P(retval));
++ }
++
++ zval_ptr_dtor(&args[0]);
++ zval_ptr_dtor(&args[1]);
++ zval_ptr_dtor(&retval);
++#endif
++
++ return ret;
++}
++/* }}} */
++/* {{{ php_sasl_cb_userdb_checkpass
++ */
++static int php_sasl_cb_userdb_checkpass(sasl_conn_t *conn, void *context,
++ const char *user, const char *pass,
++ unsigned passlen,
++ struct propctx *propctx)
++{
++ zval *function = context;
++ zval *args[2];
++ zval *retval;
++ int ret = SASL_FAIL;
++
++#if 0
++ MAKE_STD_ZVAL(args[0]);
++ MAKE_STD_ZVAL(args[1]);
++ MAKE_STD_ZVAL(retval);
++
++ ZVAL_LONG(args[0], id);
++ ZVAL_NULL(args[1]); // TODO: Make an array of available realms
++
++ if (call_user_function(CG(function_table), NULL, function, retval,
++ 2, args TSRMLS_CC) == SUCCESS) {
++ *result = estrdup(Z_STRVAL_P(retval));
++ }
++
++ zval_ptr_dtor(&args[0]);
++ zval_ptr_dtor(&args[1]);
++ zval_ptr_dtor(&retval);
++#endif
++
++ return ret;
++}
++/* }}} */
++/* {{{ php_sasl_cb_userdb_setpass
++ */
++static int php_sasl_cb_userdb_setpass(sasl_conn_t *conn, void *context,
++ const char *user, const char *pass,
++ unsigned passlen,
++ struct propctx *propctx, unsigned flags)
++{
++ zval *function = context;
++ zval *args[2];
++ zval *retval;
++ int ret = SASL_FAIL;
++
++#if 0
++ MAKE_STD_ZVAL(args[0]);
++ MAKE_STD_ZVAL(args[1]);
++ MAKE_STD_ZVAL(retval);
++
++ ZVAL_LONG(args[0], id);
++ ZVAL_NULL(args[1]); // TODO: Make an array of available realms
++
++ if (call_user_function(CG(function_table), NULL, function, retval,
++ 2, args TSRMLS_CC) == SUCCESS) {
++ *result = estrdup(Z_STRVAL_P(retval));
++ }
++
++ zval_ptr_dtor(&args[0]);
++ zval_ptr_dtor(&args[1]);
++ zval_ptr_dtor(&retval);
++#endif
++
++ return ret;
++}
++/* }}} */
++/* {{{ php_sasl_cb_canon_user
++ */
++static int php_sasl_cb_canon_user(sasl_conn_t *conn, void *context,
++ const char *in, unsigned inlen,
++ unsigned flags, const char *user_realm,
++ char *out, unsigned out_max,
++ unsigned *out_len)
++{
++ zval *function = context;
++ zval *args[2];
++ zval *retval;
++ int ret = SASL_FAIL;
++
++#if 0
++ MAKE_STD_ZVAL(args[0]);
++ MAKE_STD_ZVAL(args[1]);
++ MAKE_STD_ZVAL(retval);
++
++ ZVAL_LONG(args[0], id);
++ ZVAL_NULL(args[1]); // TODO: Make an array of available realms
++
++ if (call_user_function(CG(function_table), NULL, function, retval,
++ 2, args TSRMLS_CC) == SUCCESS) {
++ *result = estrdup(Z_STRVAL_P(retval));
++ }
++
++ zval_ptr_dtor(&args[0]);
++ zval_ptr_dtor(&args[1]);
++ zval_ptr_dtor(&retval);
++#endif
++
++ return ret;
++}
++/* }}} */
++
++/* {{{ sasl_callback_function_t php_sasl_get_callback_function(unsigned long id)
++ */
++typedef int (*sasl_callback_function_t)();
++static sasl_callback_function_t php_sasl_get_callback_function(unsigned long id)
++{
++ switch (id) {
++ case SASL_CB_GETOPT: return php_sasl_cb_getopt;
++ case SASL_CB_LOG: return php_sasl_cb_log;
++ case SASL_CB_GETPATH: return php_sasl_cb_getpath;
++ case SASL_CB_VERIFYFILE: return php_sasl_cb_verifyfile;
++ case SASL_CB_GETCONFPATH: return php_sasl_cb_getconfpath;
++ case SASL_CB_USER: return php_sasl_cb_getsimple;
++ case SASL_CB_AUTHNAME: return php_sasl_cb_getsimple;
++ case SASL_CB_LANGUAGE: return php_sasl_cb_getsimple;
++ case SASL_CB_CNONCE: return php_sasl_cb_getsimple;
++ case SASL_CB_PASS: return php_sasl_cb_getsecret;
++ case SASL_CB_ECHOPROMPT: return php_sasl_cb_chalprompt;
++ case SASL_CB_NOECHOPROMPT: return php_sasl_cb_chalprompt;
++ case SASL_CB_GETREALM: return php_sasl_cb_getrealm;
++ case SASL_CB_PROXY_POLICY: return php_sasl_cb_authorize;
++ case SASL_CB_SERVER_USERDB_CHECKPASS: return php_sasl_cb_userdb_checkpass;
++ case SASL_CB_SERVER_USERDB_SETPASS: return php_sasl_cb_userdb_setpass;
++ case SASL_CB_CANON_USER: return php_sasl_cb_canon_user;
++ }
++
++ return NULL;
++}
++/* }}} */
++/* {{{ php_sasl_create_callbacks(HashTable *ht)
++ */
++static int php_sasl_create_callbacks(HashTable *ht, sasl_callback_t *callbacks)
++{
++ int n = 0;
++ zval **entry;
++ char *string_key;
++ long num_key;
++
++ /*
++ * Start by counting the number of legitimate callbacks that exist in
++ * the array. This allows us to appropriately size our callback array.
++ */
++ zend_hash_internal_pointer_reset(ht);
++ while (zend_hash_get_current_data(ht, (void **)&entry) == SUCCESS) {
++ switch (zend_hash_get_current_key(ht, &string_key, &num_key, 0)) {
++ case HASH_KEY_IS_LONG:
++ if (php_sasl_get_callback_function(num_key)) {
++ n++;
++ }
++ break;
++ }
++ zend_hash_move_forward(ht);
++ }
++
++ /*
++ * Now that we know how many callbacks we have, allocate the array. We
++ * include an additional entry for the final SASL_CB_LIST_END sentinel.
++ */
++ callbacks = (sasl_callback_t *)emalloc(sizeof(sasl_callback_t) * (n + 1));
++
++ if (callbacks) {
++ /* Assign the sentinel to the last entry in the callback array. */
++ callbacks[n].id = SASL_CB_LIST_END;
++
++ /*
++ * Now we iterate through the array again, except this time we will
++ * actually create the callback entries.
++ */
++ n = 0;
++
++ zend_hash_internal_pointer_reset(ht);
++ while (zend_hash_get_current_data(ht, (void **)&entry) == SUCCESS) {
++ sasl_callback_t *cb = &callbacks[n];
++
++ switch (zend_hash_get_current_key(ht, &string_key, &num_key, 0)) {
++ case HASH_KEY_IS_LONG:
++ {
++ cb->proc = php_sasl_get_callback_function(num_key);
++ if (cb->proc) {
++ cb->id = num_key;
++ cb->context = *entry;
++ n++;
++ }
++ }
++ break;
++ }
++ zend_hash_move_forward(ht);
++ }
++ }
++
++ return n;
++}
++/* }}} */
++
+ /* {{{ php_sasl_error(int level, int reason)
+ */
+ static void php_sasl_error(int level, int code TSRMLS_DC)
+@@ -56,6 +536,29 @@
+ "%s", sasl_errstring(code, NULL, NULL));
+ }
+ /* }}} */
++/* {{{ php_sasl_rinit_globals(TSRMLS_D)
++ */
++static void php_sasl_rinit_globals(TSRMLS_D)
++{
++ SASLG(client_callbacks) = NULL;
++ SASLG(server_callbacks) = NULL;
++}
++/* }}} */
++/* {{{ php_sasl_rshutdown_globals(TSRMLS_D)
++ */
++static void php_sasl_rshutdown_globals(TSRMLS_D)
++{
++ if (SASLG(client_callbacks)) {
++ efree(SASLG(client_callbacks));
++ SASLG(client_callbacks) = NULL;
++ }
++
++ if (SASLG(server_callbacks)) {
++ efree(SASLG(server_callbacks));
++ SASLG(server_callbacks) = NULL;
++ }
++}
++/* }}} */
+ /* {{{ php_sasl_destroy_conn(zend_rsrc_list_entry *rsrc TSRMLS_DC)
+ */
+ static void php_sasl_destroy_conn(zend_rsrc_list_entry *rsrc TSRMLS_DC)
+@@ -67,29 +570,30 @@
+
+ /* {{{ sasl_functions[]
+ */
+-function_entry sasl_functions[] = {
++zend_function_entry sasl_functions[] = {
+ /* Common Functions */
+- PHP_FE(sasl_version, NULL)
+- PHP_FE(sasl_errstring, NULL)
++ PHP_FE(sasl_version, NULL)
++ PHP_FE(sasl_errstring, NULL)
+ #if SASL_VERSION_MAJOR >= 2
+- PHP_FE(sasl_errdetail, NULL)
++ PHP_FE(sasl_errdetail, NULL)
++ PHP_FE(sasl_seterror, NULL)
+ #endif
+- PHP_FE(sasl_encode, NULL)
+- PHP_FE(sasl_decode, NULL)
++ PHP_FE(sasl_encode, NULL)
++ PHP_FE(sasl_decode, NULL)
+
+ /* Client Functions */
+- PHP_FE(sasl_client_init, NULL)
+- PHP_FE(sasl_client_new, NULL)
+- PHP_FE(sasl_client_start, NULL)
+- PHP_FE(sasl_client_step, NULL)
++ PHP_FE(sasl_client_init, NULL)
++ PHP_FE(sasl_client_new, NULL)
++ PHP_FE(sasl_client_start, NULL)
++ PHP_FE(sasl_client_step, NULL)
+
+ /* Server Functions */
+- PHP_FE(sasl_server_init, NULL)
+- PHP_FE(sasl_server_new, NULL)
+- PHP_FE(sasl_server_start, NULL)
+- PHP_FE(sasl_server_step, NULL)
+- PHP_FE(sasl_listmech, NULL)
+- PHP_FE(sasl_checkpass, NULL)
++ PHP_FE(sasl_server_init, NULL)
++ PHP_FE(sasl_server_new, NULL)
++ PHP_FE(sasl_server_start, NULL)
++ PHP_FE(sasl_server_step, NULL)
++ PHP_FE(sasl_listmech, NULL)
++ PHP_FE(sasl_checkpass, NULL)
+
+ {NULL, NULL, NULL}
+ };
+@@ -102,10 +606,10 @@
+ sasl_functions,
+ PHP_MINIT(sasl),
+ PHP_MSHUTDOWN(sasl),
+- NULL,
+- NULL,
++ PHP_RINIT(sasl),
++ PHP_RSHUTDOWN(sasl),
+ PHP_MINFO(sasl),
+- NO_VERSION_YET,
++ PHP_SASL_VERSION,
+ STANDARD_MODULE_PROPERTIES
+ };
+
+@@ -173,6 +677,31 @@
+ SASL_CONSTANT(SASL_SEC_MAXIMUM);
+ #endif
+
++ /* SASL callback identifiers */
++ SASL_CONSTANT(SASL_CB_GETOPT);
++ SASL_CONSTANT(SASL_CB_LOG);
++ SASL_CONSTANT(SASL_CB_GETPATH);
++ SASL_CONSTANT(SASL_CB_VERIFYFILE);
++ SASL_CONSTANT(SASL_CB_GETCONFPATH);
++ SASL_CONSTANT(SASL_CB_USER);
++ SASL_CONSTANT(SASL_CB_AUTHNAME);
++ SASL_CONSTANT(SASL_CB_LANGUAGE);
++ SASL_CONSTANT(SASL_CB_CNONCE);
++ SASL_CONSTANT(SASL_CB_PASS);
++ SASL_CONSTANT(SASL_CB_ECHOPROMPT);
++ SASL_CONSTANT(SASL_CB_NOECHOPROMPT);
++ SASL_CONSTANT(SASL_CB_GETREALM);
++ SASL_CONSTANT(SASL_CB_PROXY_POLICY);
++ SASL_CONSTANT(SASL_CB_SERVER_USERDB_CHECKPASS);
++ SASL_CONSTANT(SASL_CB_SERVER_USERDB_SETPASS);
++ SASL_CONSTANT(SASL_CB_CANON_USER);
++
++ /* SASL_CB_VERIFYFILE flags */
++ SASL_CONSTANT(SASL_VRFY_PLUGIN);
++ SASL_CONSTANT(SASL_VRFY_CONF);
++ SASL_CONSTANT(SASL_VRFY_PASSWD);
++ SASL_CONSTANT(SASL_VRFY_OTHER);
++
+ return SUCCESS;
+ }
+ /* }}} */
+@@ -185,6 +714,22 @@
+ return SUCCESS;
+ }
+ /* }}} */
++/* {{{ PHP_RINIT_FUNCTION
++ */
++PHP_RINIT_FUNCTION(sasl)
++{
++ php_sasl_rinit_globals(TSRMLS_C);
++ return SUCCESS;
++}
++/* }}} */
++/* {{{ PHP_RSHUTDOWN_FUNCTION
++ */
++PHP_RSHUTDOWN_FUNCTION(sasl)
++{
++ php_sasl_rshutdown_globals(TSRMLS_C);
++ return SUCCESS;
++}
++/* }}} */
+ /* {{{ PHP_MINFO_FUNCTION
+ */
+ PHP_MINFO_FUNCTION(sasl)
+@@ -214,7 +759,8 @@
+ php_info_print_table_row(2, "SASL Support", "enabled");
+ php_info_print_table_row(2, "SASL API Version", api_version);
+ php_info_print_table_row(2, "SASL Library Version", lib_version);
+- php_info_print_table_row(2, "Extension Version", "$Revision$");
++ php_info_print_table_row(2, "Extension Version", PHP_SASL_VERSION);
++ php_info_print_table_row(2, "CVS Revision", "$Revision$");
+ php_info_print_table_end();
+ }
+ /* }}} */
+@@ -239,7 +785,7 @@
+ libsasl_step = libsasl_version & 0xFFFF;
+ #endif
+
+- snprintf(version, 64, "%u.%u.%u (%s)",
++ snprintf(version, sizeof(version), "%u.%u.%u (%s)",
+ libsasl_major, libsasl_minor, libsasl_step, sasl_implementation);
+
+ RETURN_STRING(version, 1);
+@@ -267,7 +813,7 @@
+ Returns the string translation of the given error code. */
+ PHP_FUNCTION(sasl_errstring)
+ {
+- long code;
++ long code;
+ char *languages = NULL;
+ int languages_len;
+
+@@ -279,6 +825,27 @@
+ RETURN_STRING((char *)sasl_errstring(code, languages, NULL), 1);
+ }
+ /* }}} */
++/* {{{ proto void sasl_seterror(resource conn, string message)
++ Set the error string which will be returned by sasl_errdetail. */
++#if SASL_VERSION_MAJOR >= 2
++PHP_FUNCTION(sasl_seterror)
++{
++ zval *rsrc;
++ sasl_conn_t *conn;
++ char *message;
++ int message_len;
++
++ if (zend_parse_parameters(2 TSRMLS_CC, "rs",
++ &rsrc, &message, &message_len) == FAILURE) {
++ return;
++ }
++
++ ZEND_FETCH_RESOURCE(conn, sasl_conn_t *, &rsrc, -1, le_conn_name, le_conn);
++
++ sasl_seterror(conn, 0, message);
++}
++#endif
++/* }}} */
+ /* {{{ proto string sasl_encode(resource conn, string input)
+ Encodes a block of data for tranmission using the security layer. */
+ PHP_FUNCTION(sasl_encode)
+@@ -345,10 +912,25 @@
+ Initializes the SASL client drivers. */
+ PHP_FUNCTION(sasl_client_init)
+ {
+- if (sasl_client_init(NULL) != SASL_OK) {
++ zval *array;
++
++ if (zend_parse_parameters(1 TSRMLS_CC, "a", &array) == FAILURE) {
++ return;
++ }
++
++ /*
++ * Convert the supplied array into a set of callback functions. We
++ * don't bother spending any time validating its contents because the
++ * sasl_client_init() function will do that for us (and return an error
++ * if it encounters something that it doesn't like).
++ */
++ php_sasl_create_callbacks(Z_ARRVAL_P(array), SASLG(client_callbacks));
++
++ if (sasl_client_init(SASLG(client_callbacks)) != SASL_OK) {
++ /* XXX: Free the callbacks? */
+ RETURN_FALSE;
+ }
+-
++
+ RETURN_TRUE;
+ }
+ /* }}} */
+@@ -384,7 +966,7 @@
+ ZEND_REGISTER_RESOURCE(return_value, conn, le_conn);
+ }
+ /* }}} */
+-/* {{{ proto bool sasl_client_start(resource conn, string mechlist [, string &$output [, string &$mech]])
++/* {{{ proto int sasl_client_start(resource conn, string mechlist [, string &$output [, string &$mech]])
+ Starts an authentication session. */
+ PHP_FUNCTION(sasl_client_start)
+ {
+@@ -421,10 +1003,13 @@
+ r = sasl_client_start(conn, mechlist, NULL, &data, &data_len, &chosenmech);
+ #endif
+
+- /* Print a warning and return false if we receive an unexpected result. */
++ /*
++ * Issue a warning and return the error code if we receive an unexpected
++ * result code.
++ */
+ if ((r != SASL_OK) && (r != SASL_CONTINUE)) {
+ php_sasl_error(E_WARNING, r TSRMLS_CC);
+- RETURN_FALSE;
++ RETURN_LONG(r);
+ }
+
+ /* Store the output in the "output" parameter (by reference). */
+@@ -434,7 +1019,7 @@
+ /* Store the chosen mechanism in the "mech" parameter (by reference). */
+ ZVAL_STRING(mech, (char *)chosenmech, 1);
+
+- RETURN_TRUE;
++ RETURN_LONG(r);
+ }
+ /* }}} */
+ /* {{{ proto int sasl_client_step(resource conn, string input, string &$output)
+@@ -483,14 +1068,25 @@
+ Initializes the session and loads the shared authentication mechanisms. */
+ PHP_FUNCTION(sasl_server_init)
+ {
++ zval *array;
+ char *name;
+ int name_len;
+
+- if (zend_parse_parameters(1 TSRMLS_CC, "s", &name, &name_len) == FAILURE) {
++ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "as",
++ &array, &name, &name_len) == FAILURE) {
+ return;
+ }
+
+- if (sasl_server_init(NULL, name) != SASL_OK) {
++ /*
++ * Convert the supplied array into a set of callback functions. We
++ * don't bother spending any time validating its contents because the
++ * sasl_client_init() function will do that for us (and return an error
++ * if it encounters something that it doesn't like).
++ */
++ php_sasl_create_callbacks(Z_ARRVAL_P(array), SASLG(server_callbacks));
++
++ if (sasl_server_init(SASLG(server_callbacks), name) != SASL_OK) {
++ /* XXX: Free the callbacks? */
+ RETURN_FALSE;
+ }
+
+
+Property changes on: sasl.c
+___________________________________________________________________
+Modified: cvs2svn:cvs-rev
+## -1 +1 ##
+-1.3
+\ No newline at end of property
++1.6
+\ No newline at end of property
+Index: config.m4
+===================================================================
+--- config.m4 (.../tags/RELEASE_0_1_0)
++++ config.m4 (.../trunk)
+@@ -6,7 +6,7 @@
+
+ if test "$PHP_SASL" != "no"; then
+ SEARCH_DIRS="/usr/local /usr"
+- SEARCH_SUBS="sasl sasl1 sasl2"
++ SEARCH_SUBS="sasl2 sasl sasl1"
+ SEARCH_LIBS="sasl2 sasl"
+
+ if test -r $PHP_SASL; then
+@@ -19,6 +19,7 @@
+ SASL_DIR=$i
+ SASL_SUB=$j
+ AC_MSG_RESULT(found in $i)
++ break
+ fi
+ done
+ done
+@@ -31,9 +32,10 @@
+
+ AC_MSG_CHECKING(for SASL library in $SASL_DIR/lib)
+ for i in $SEARCH_LIBS; do
+- if test -r $SASL_DIR/lib/lib$i.a; then
++ if test -r $SASL_DIR/lib/lib$i.a -o -r $SASL_DIR/lib/lib$i.$SHLIB_SUFFIX_NAME; then
+ SASL_LIB=$i
+ AC_MSG_RESULT(found -l$i)
++ break
+ fi
+ done
+
+
+Property changes on: config.m4
+___________________________________________________________________
+Modified: cvs2svn:cvs-rev
+## -1 +1 ##
+-1.2
+\ No newline at end of property
++1.4
+\ No newline at end of property
+Index: docs/guide.txt
+===================================================================
+--- docs/guide.txt (.../tags/RELEASE_0_1_0)
++++ docs/guide.txt (.../trunk)
+@@ -0,0 +1,50 @@
++============================
++ The SASL Extension for PHP
++============================
++
++--------------------
++ User Documentation
++--------------------
++
++:Author: Jon Parise
++:Contact: jon@php.net
++:Date: $Date$
++:Revision: $Revision$
++
++.. contents:: Contents
++.. section-numbering::
++
++Frequently Asked Questions
++--------------------------
++
++How does the SASL extension relate to the Auth_SASL PEAR package?
++~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++The `Auth_SASL`_ PEAR package provides native PHP implementations of many of
++the SASL authentication mechanisms. In theory, those methods could call out
++to the SASL extension to compute those strings using the native C library, but
++I'm not sure the effort is worth it. I wouldn't object to the idea if someone
++did the work, though.
++
++.. _Auth_SASL: http://pear.php.net/Auth_SASL
++
++Does saslauthd need to be running locally?
++~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++``saslauthd`` needs to be running on the same machine as the SASL client (the
++PHP process, in your case). The libsasl library (that the SASL extension
++wraps) will communicate with the ``saslauthd`` process via a local Unix domain
++socket.
++
++Can saslauthd be used to authenticate against a remote server?
++~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++
++The ``saslauthd`` process loads the SASL authentication modules and performs
++the actual authentication. This may involve contacting a foreign machine
++(such as a remote Kerberos or LDAP server).
++
++To complicate things even further, ``saslauthd`` can use `PAM`_ as its
++authentication backend, which opens up even more authentication possibilities
++(many of which may involve other hosts).
++
++.. _PAM: http://www.freebsd.org/doc/en_US.ISO8859-1/articles/pam/
++
++.. vim: tabstop=4 shiftwidth=4 softtabstop=4 expandtab textwidth=78 ft=rst:
+
+Property changes on: docs/guide.txt
+___________________________________________________________________
+Added: cvs2svn:cvs-rev
+## -0,0 +1 ##
++1.1
+\ No newline at end of property
+Added: svn:mime-type
+## -0,0 +1 ##
++text/plain
+\ No newline at end of property
+Added: svn:keywords
+## -0,0 +1 ##
++Id Rev Revision Date LastChangedDate LastChangedRevision Author LastChangedBy HeadURL URL
+\ No newline at end of property
+Added: svn:eol-style
+## -0,0 +1 ##
++native
+\ No newline at end of property
+Index: package.xml
+===================================================================
+Cannot display: file marked as a binary type.
+svn:mime-type = application/xml
+Index: package.xml
+===================================================================
+--- package.xml (.../tags/RELEASE_0_1_0)
++++ package.xml (.../trunk)
+
+Property changes on: package.xml
+___________________________________________________________________
+Modified: cvs2svn:cvs-rev
+## -1 +1 ##
+-1.3
+\ No newline at end of property
++1.5
+\ No newline at end of property
+Deleted: svn:mime-type
+## -1 +0,0 ##
+-application/xml
+\ No newline at end of property
+Index: php_sasl.h
+===================================================================
+--- php_sasl.h (.../tags/RELEASE_0_1_0)
++++ php_sasl.h (.../trunk)
+@@ -26,6 +26,8 @@
+ extern zend_module_entry sasl_module_entry;
+ #define phpext_sasl_ptr &sasl_module_entry
+
++#define PHP_SASL_VERSION "0.2.0-dev"
++
+ #ifdef PHP_WIN32
+ #define PHP_SASL_API __declspec(dllexport)
+ #else
+@@ -38,6 +40,8 @@
+
+ PHP_MINIT_FUNCTION(sasl);
+ PHP_MSHUTDOWN_FUNCTION(sasl);
++PHP_RINIT_FUNCTION(sasl);
++PHP_RSHUTDOWN_FUNCTION(sasl);
+ PHP_MINFO_FUNCTION(sasl);
+
+ /* Common Functions */
+@@ -45,6 +49,7 @@
+ PHP_FUNCTION(sasl_errstring);
+ #if SASL_VERSION_MAJOR >= 2
+ PHP_FUNCTION(sasl_errdetail);
++PHP_FUNCTION(sasl_seterror);
+ #endif
+ PHP_FUNCTION(sasl_encode);
+ PHP_FUNCTION(sasl_decode);
+@@ -63,12 +68,16 @@
+ PHP_FUNCTION(sasl_listmech);
+ PHP_FUNCTION(sasl_checkpass);
+
+-#if 0
++/* Global Variables */
++ZEND_BEGIN_MODULE_GLOBALS(sasl)
++ sasl_callback_t *client_callbacks;
++ sasl_callback_t *server_callbacks;
++ZEND_END_MODULE_GLOBALS(sasl)
++
+ #ifdef ZTS
+ #define SASLG(v) TSRMG(sasl_globals_id, zend_sasl_globals *, v)
+ #else
+ #define SASLG(v) (sasl_globals.v)
+ #endif
+-#endif
+
+ #endif /* PHP_SASL_H */
+
+Property changes on: php_sasl.h
+___________________________________________________________________
+Modified: cvs2svn:cvs-rev
+## -1 +1 ##
+-1.2
+\ No newline at end of property
++1.4
+\ No newline at end of property
+Index: package.php
+===================================================================
+--- package.php (.../tags/RELEASE_0_1_0)
++++ package.php (.../trunk)
+@@ -0,0 +1,67 @@
++<?php
++
++require_once 'PEAR/PackageFileManager2.php';
++PEAR::setErrorHandling(PEAR_ERROR_DIE);
++
++$desc = <<<EOT
++SASL is the Simple Authentication and Security Layer (as defined by RFC 2222). It provides a system for adding plugable authenticating support to connection-based protocols. The SASL Extension for PHP makes the Cyrus SASL library functions available to PHP. It aims to provide a 1-to-1 wrapper around the SASL library to provide the greatest amount of implementation flexibility. To that end, it is possible to build both a client-side and server-side SASL implementation entirely in PHP.
++EOT;
++
++$version = '0.2.0';
++$notes = <<<EOT
++- The build system now searches for both static and shared versions of the SASL library. (Bug #13097)
++EOT;
++
++$package = new PEAR_PackageFileManager2();
++
++$result = $package->setOptions(array(
++ 'filelistgenerator' => 'svn',
++ 'changelogoldtonew' => false,
++ 'simpleoutput' => true,
++ 'baseinstalldir' => '/',
++ 'packagefile' => 'package.xml',
++ 'packagedirectory' => '.',
++ 'clearcontents' => true,
++ 'ignore' => array('package.php', 'package.xml'),
++ 'dir_roles' => array(
++ 'docs' => 'doc',
++ 'tests' => 'test',
++ ),
++));
++
++if (PEAR::isError($result)) {
++ echo $result->getMessage();
++ die();
++}
++
++$package->clearDeps();
++$package->setPackage('sasl');
++$package->setPackageType('extsrc');
++$package->setSummary('Cyrus SASL Extensions');
++$package->setDescription($desc);
++$package->setChannel('pecl.php.net');
++$package->setLicense('PHP License');
++$package->addMaintainer('lead', 'jon', 'Jon Parise', 'jon@php.net');
++
++$package->addRelease();
++$package->setProvidesExtension('sasl');
++$package->setAPIVersion('0.6.0');
++$package->setAPIStability('alpha');
++$package->setReleaseVersion($version);
++$package->setReleaseStability('alpha');
++$package->setNotes($notes);
++$package->setPhpDep('5.3.0');
++$package->setPearInstallerDep('1.4.3');
++
++$package->generateContents();
++
++if (isset($_SERVER['argv'][1]) && $_SERVER['argv'][1] == 'commit') {
++ $result = $package->writePackageFile();
++} else {
++ $result = $package->debugPackageFile();
++}
++
++if (PEAR::isError($result)) {
++ echo $result->getMessage();
++ die();
++}
+
+Property changes on: package.php
+___________________________________________________________________
+Added: cvs2svn:cvs-rev
+## -0,0 +1 ##
++1.3
+\ No newline at end of property
+Added: svn:keywords
+## -0,0 +1 ##
++Id Rev Revision Date LastChangedDate LastChangedRevision Author LastChangedBy HeadURL URL
+\ No newline at end of property
+Added: svn:eol-style
+## -0,0 +1 ##
++native
+\ No newline at end of property
+Index: tests/sasl_version.phpt
+===================================================================
+--- tests/sasl_version.phpt (.../tags/RELEASE_0_1_0)
++++ tests/sasl_version.phpt (.../trunk)
+@@ -4,11 +4,7 @@
+ <?php if (!extension_loaded("sasl")) die('skip'); ?>
+ --FILE--
+ <?php
++echo sasl_version();
+
+-$version = sasl_version();
+-if (strlen($version) > 0) {
+- echo "OK";
+-}
+-
+---EXPECT--
+-OK
++--EXPECTREGEX--
++\d+\.\d+\.\d+ \(.*\)
+
+Property changes on: tests/sasl_version.phpt
+___________________________________________________________________
+Modified: cvs2svn:cvs-rev
+## -1 +1 ##
+-1.2
+\ No newline at end of property
++1.3
+\ No newline at end of property
+Index: tests
+===================================================================
+--- tests (.../tags/RELEASE_0_1_0)
++++ tests (.../trunk)
+
+Property changes on: tests
+___________________________________________________________________
+Added: svn:ignore
+## -0,0 +1,4 ##
++*.diff
++*.exp
++*.log
++*.out
+Index: .
+===================================================================
+--- . (.../tags/RELEASE_0_1_0)
++++ . (.../trunk)
+
+Property changes on: .
+___________________________________________________________________
+Added: svn:ignore
+## -0,0 +1,32 ##
++.deps
++Makefile
++*.lo
++*.la
++.libs
++libs.mk
++Makefile.fragments
++Makefile.global
++Makefile.objects
++autom4te.cache
++acinclude.m4
++aclocal.m4
++build
++config.cache
++config.guess
++config.h
++config.h.in
++config.log
++config.nice
++config.status
++config.sub
++configure
++configure.in
++install-sh
++libtool
++ltmain.sh
++missing
++mkinstalldirs
++modules
++scan_makefile_in.awk
++conftest*
++*.tgz