# HG changeset patch
# Node ID 7a1a4bef2263f113a821733e97828d2cdb900cc7
# Parent  529c19020a721b4a0b17040d2e0687bfe1f8d477
diff --git a/hasl/haslcontext.c b/hasl/haslcontext.c
--- a/hasl/haslcontext.c
+++ b/hasl/haslcontext.c
@@ -257,6 +257,15 @@
 /******************************************************************************
  * Public API
  *****************************************************************************/
+void
+hasl_context_done(HaslContext *ctx) {
+	g_return_if_fail(HASL_IS_CONTEXT(ctx));
+
+	g_clear_object(&ctx->current_mechanism);
+
+	ctx->current_mechanism_index = -1;
+}
+
 HaslContext *
 hasl_context_new(void) {
 	return g_object_new(HASL_TYPE_CONTEXT, NULL);
@@ -518,8 +527,8 @@
                   gsize server_in_length, guint8 **client_out,
                   gsize *client_out_length, GError **error)
 {
-
 	g_return_val_if_fail(HASL_IS_CONTEXT(ctx), HASL_MECHANISM_RESULT_ERROR);
+	g_return_val_if_fail(client_out != NULL, HASL_MECHANISM_RESULT_ERROR);
 
 	if(!HASL_IS_MECHANISM(ctx->current_mechanism)) {
 		g_set_error_literal(error, HASL_CONTEXT_DOMAIN, 0,
diff --git a/hasl/haslcontext.h b/hasl/haslcontext.h
--- a/hasl/haslcontext.h
+++ b/hasl/haslcontext.h
@@ -88,6 +88,10 @@
  *     // value stored in mechanism.
  * } else if(res == HASL_MECHANISM_RESULT_SUCCESS) {
  *     // Authentication complete.
+ *
+ *     // Optionally you can call done on the context to reset it for reuse
+ *     // later.
+ *     hasl_context_done(context);
  * } else if(res == HASL_MECHANISM_CONTINUE) {
  *     // Otherwise send client_out to the server.
  * }
@@ -105,6 +109,25 @@
 #include <hasl/haslmechanism.h>
 
 /**
+ * hasl_context_done:
+ * @ctx: the instance
+ *
+ * Sets the context to done and resets it.
+ *
+ * This should be called after after you're done with the context, but might
+ * want to use it again later to negotiate SASL again.
+ *
+ * An example of this is when services on IRC go down and come back up. After
+ * your initial login, we would mark the context as done so that you can reuse
+ * it when they come back. Of course marking it as done after that negotiation
+ * as well.
+ *
+ * Since: 0.4
+ */
+HASL_AVAILABLE_IN_0_4
+void hasl_context_done(HaslContext *ctx);
+
+/**
  * hasl_context_new:
  *
  * Creates a new context.
diff --git a/hasl/tests/test-context.c b/hasl/tests/test-context.c
--- a/hasl/tests/test-context.c
+++ b/hasl/tests/test-context.c
@@ -155,8 +155,8 @@
 	const char *current = NULL;
 
 	context = hasl_context_new();
-	hasl_context_set_username(context, "bob");
-	hasl_context_set_password(context, "hunter2");
+	hasl_context_set_username(context, "homer");
+	hasl_context_set_password(context, "0dy$$3y");
 	hasl_context_set_tls(context, TRUE);
 
 	/* Make sure NULL is returned if set_allowed_mechanisms hasn't been called
@@ -217,8 +217,8 @@
 	const char *next = NULL;
 
 	context = hasl_context_new();
-	hasl_context_set_username(context, "alice");
-	hasl_context_set_password(context, "hunter2");
+	hasl_context_set_username(context, "homer");
+	hasl_context_set_password(context, "0dy$$3y");
 	hasl_context_set_allowed_mechanisms(context, "PLAIN,EXTERNAL");
 	hasl_context_set_allow_clear_text(context, TRUE);
 
@@ -283,6 +283,50 @@
 	g_clear_object(&context);
 }
 
+static void
+test_hasl_context_done_new(void) {
+	HaslContext *context = NULL;
+
+	context = hasl_context_new();
+	hasl_context_done(context);
+	g_assert_finalize_object(context);
+}
+
+static void
+test_hasl_context_done_used(void) {
+	HaslContext *context = NULL;
+	HaslMechanismResult result = HASL_MECHANISM_RESULT_ERROR;
+	GError *error = NULL;
+	const char *mechanism = NULL;
+	guint8 *client_out = NULL;
+
+	/* Create a context and allow PLAIN and EXTERNAL. */
+	context = hasl_context_new();
+	hasl_context_set_allowed_mechanisms(context, "PLAIN,EXTERNAL");
+	hasl_context_set_username(context, "homer");
+	hasl_context_set_password(context, "0dy$$3y");
+	hasl_context_set_allow_clear_text(context, TRUE);
+
+	mechanism = hasl_context_next(context);
+	g_assert_cmpstr(mechanism, ==, "PLAIN");
+
+	/* Step through to PLAIN, but ignore the output. */
+	result = hasl_context_step(context, (guint8 *)"+", 1, &client_out, NULL, &error);
+	g_assert_no_error(error);
+	g_assert_cmpuint(result, ==, HASL_MECHANISM_RESULT_SUCCESS);
+	g_free(client_out);
+
+	/* Mark the context as done and verify that the current mechanism is NULL.
+	 */
+	hasl_context_done(context);
+
+	mechanism = hasl_context_get_current_mechanism(context);
+	g_assert_null(mechanism);
+
+	/* And now we're done. */
+	g_assert_finalize_object(context);
+}
+
 /******************************************************************************
  * Main
  *****************************************************************************/
@@ -315,5 +359,8 @@
 	g_test_add_func("/hasl/context/next/plain_not_possible_only_mechanism",
 	                test_hasl_context_next_plain_not_possible_only_mechanism);
 
+	g_test_add_func("/hasl/context/done/new", test_hasl_context_done_new);
+	g_test_add_func("/hasl/context/done/used", test_hasl_context_done_used);
+
 	return g_test_run();
 }
