# HG changeset patch
# Node ID 1dcc72097d1c1515cdcddee60d734311bcbae1fa
# Parent  71801bc98f3960117ed42b9a671f6cb193d17ee9
diff --git a/hasl/haslgs2header.c b/hasl/haslgs2header.c
--- a/hasl/haslgs2header.c
+++ b/hasl/haslgs2header.c
@@ -197,6 +197,13 @@
 
 HaslGs2Header *
 hasl_gs2_header_parse(const char *str, GError **error) {
+	g_return_val_if_fail(str != NULL, NULL);
+
+	return hasl_gs2_header_parse_full(str, NULL, error);
+}
+
+HaslGs2Header *
+hasl_gs2_header_parse_full(const char *str, int *length, GError **error) {
 	HaslGs2Header *header = NULL;
 	GError *local_error = NULL;
 	GMatchInfo *info = NULL;
@@ -289,6 +296,15 @@
 		g_clear_pointer(&value, g_free);
 	}
 
+	if(length != NULL) {
+		int start = 0;
+		int end = 0;
+
+		g_match_info_fetch_pos(info, 0, &start, &end);
+
+		*length = (end - start);
+	}
+
 	g_clear_pointer(&regex, g_regex_unref);
 	g_clear_pointer(&info, g_match_info_unref);
 
diff --git a/hasl/haslgs2header.h b/hasl/haslgs2header.h
--- a/hasl/haslgs2header.h
+++ b/hasl/haslgs2header.h
@@ -130,6 +130,24 @@
 HaslGs2Header *hasl_gs2_header_parse(const char *str, GError **error);
 
 /**
+ * hasl_gs2_header_parse_full:
+ * @str: the string to parse
+ * @length: (out) (optional): the length of the string that matched.
+ * @error: (out) (nullable): a return address for a #GError
+ *
+ * Parses a string into a GS2 Header.
+ *
+ * The length parameter can be used to determine the length of the match when
+ * the header is part of a larger string.
+ *
+ * Returns: (transfer full) (nullable): The new header if found.
+ *
+ * Since: 0.5
+ */
+HASL_AVAILABLE_IN_0_5
+HaslGs2Header *hasl_gs2_header_parse_full(const char *str, int *length, GError **error);
+
+/**
  * hasl_gs2_header_to_string:
  * @header: The instance.
  * @error: (out): A return address for an error.
diff --git a/hasl/tests/test-gs2-header.c b/hasl/tests/test-gs2-header.c
--- a/hasl/tests/test-gs2-header.c
+++ b/hasl/tests/test-gs2-header.c
@@ -227,6 +227,7 @@
  *****************************************************************************/
 static inline void
 test_hasl_gs2_parse(const char *input,
+                    int *length,
                     gboolean expected_standard_mechanism,
                     HaslGs2HeaderChannelBinding expected_binding,
                     const char *expected_cbname,
@@ -238,7 +239,7 @@
 	const char *cbname = NULL;
 	const char *authzid = NULL;
 
-	header = hasl_gs2_header_parse(input, &error);
+	header = hasl_gs2_header_parse_full(input, length, &error);
 	g_assert_no_error(error);
 
 	g_assert_true(HASL_IS_GS2_HEADER(header));
@@ -264,6 +265,7 @@
 static void
 test_hasl_gs2_header_parse_rfc5801_no_binding(void) {
 	test_hasl_gs2_parse("n,,",
+	                    NULL,
 	                    TRUE,
 	                    HaslGs2HeaderChannelBindingClientNotSupported,
 	                    NULL,
@@ -273,6 +275,7 @@
 static void
 test_hasl_gs2_header_parse_rfc5801_no_binding_authzid(void) {
 	test_hasl_gs2_parse("n,a=someuser,",
+	                    NULL,
 	                    TRUE,
 	                    HaslGs2HeaderChannelBindingClientNotSupported,
 	                    NULL,
@@ -282,6 +285,7 @@
 static void
 test_hasl_gs2_header_parse_rfc5801_nonstd_no_binding(void) {
 	test_hasl_gs2_parse("F,n,,",
+	                    NULL,
 	                    FALSE,
 	                    HaslGs2HeaderChannelBindingClientNotSupported,
 	                    NULL,
@@ -291,6 +295,7 @@
 static void
 test_hasl_gs2_header_parse_rfc5801_binding_tls_unique(void) {
 	test_hasl_gs2_parse("p=tls-unique,,",
+	                    NULL,
 	                    TRUE,
 	                    HaslGs2HeaderChannelBindingClientSupported,
 	                    "tls-unique",
@@ -300,6 +305,7 @@
 static void
 test_hasl_gs2_header_parse_rfc5801_binding_tls_unique_authzid(void) {
 	test_hasl_gs2_parse("p=tls-unique,a=someuser,",
+	                    NULL,
 	                    TRUE,
 	                    HaslGs2HeaderChannelBindingClientSupported,
 	                    "tls-unique",
@@ -309,6 +315,7 @@
 static void
 test_hasl_gs2_header_parse_rfc5801_binding_tls_server_end_point(void) {
 	test_hasl_gs2_parse("p=tls-server-end-point,,",
+	                    NULL,
 	                    TRUE,
 	                    HaslGs2HeaderChannelBindingClientSupported,
 	                    "tls-server-end-point",
@@ -318,6 +325,59 @@
 static void
 test_hasl_gs2_header_parse_rfc5801_binding_authzid(void) {
 	test_hasl_gs2_parse("y,a=someuser,",
+	                    NULL,
+	                    TRUE,
+	                    HaslGs2HeaderChannelBindingClientSupportedServerUnknown,
+	                    NULL,
+	                    "someuser");
+}
+
+static void
+test_hasl_gs2_header_parse_with_trailing(void) {
+	test_hasl_gs2_parse("n,,a=foo",
+	                    NULL,
+	                    TRUE,
+	                    HaslGs2HeaderChannelBindingClientNotSupported,
+	                    NULL,
+	                    NULL);
+
+	test_hasl_gs2_parse("n,a=someuser,a=foo",
+	                    NULL,
+	                    TRUE,
+	                    HaslGs2HeaderChannelBindingClientNotSupported,
+	                    NULL,
+	                    "someuser");
+
+	test_hasl_gs2_parse("F,n,,a=foo",
+	                    NULL,
+	                    FALSE,
+	                    HaslGs2HeaderChannelBindingClientNotSupported,
+	                    NULL,
+	                    NULL);
+
+	test_hasl_gs2_parse("p=tls-unique,,a=foo",
+	                    NULL,
+	                    TRUE,
+	                    HaslGs2HeaderChannelBindingClientSupported,
+	                    "tls-unique",
+	                    NULL);
+
+	test_hasl_gs2_parse("p=tls-unique,a=someuser,a=foo",
+	                    NULL,
+	                    TRUE,
+	                    HaslGs2HeaderChannelBindingClientSupported,
+	                    "tls-unique",
+	                    "someuser");
+
+	test_hasl_gs2_parse("p=tls-server-end-point,,a=foo",
+	                    NULL,
+	                    TRUE,
+	                    HaslGs2HeaderChannelBindingClientSupported,
+	                    "tls-server-end-point",
+	                    NULL);
+
+	test_hasl_gs2_parse("y,a=someuser,a=foo",
+	                    NULL,
 	                    TRUE,
 	                    HaslGs2HeaderChannelBindingClientSupportedServerUnknown,
 	                    NULL,
@@ -360,6 +420,74 @@
 	g_assert_null(header);
 }
 
+static void
+test_hasl_gs2_header_parse_full_with_trailing(void) {
+	int length = 0;
+
+	test_hasl_gs2_parse("n,,a=foo",
+	                    &length,
+	                    TRUE,
+	                    HaslGs2HeaderChannelBindingClientNotSupported,
+	                    NULL,
+	                    NULL);
+	g_assert_cmpint(length, ==, 3);
+
+	length = 0;
+	test_hasl_gs2_parse("n,a=someuser,a=foo",
+	                    &length,
+	                    TRUE,
+	                    HaslGs2HeaderChannelBindingClientNotSupported,
+	                    NULL,
+	                    "someuser");
+	g_assert_cmpint(length, ==, 13);
+
+	length = 0;
+	test_hasl_gs2_parse("F,n,,a=foo",
+	                    &length,
+	                    FALSE,
+	                    HaslGs2HeaderChannelBindingClientNotSupported,
+	                    NULL,
+	                    NULL);
+	g_assert_cmpint(length, ==, 5);
+
+	length = 0;
+	test_hasl_gs2_parse("p=tls-unique,,a=foo",
+	                    &length,
+	                    TRUE,
+	                    HaslGs2HeaderChannelBindingClientSupported,
+	                    "tls-unique",
+	                    NULL);
+	g_assert_cmpint(length, ==, 14);
+
+	length = 0;
+	test_hasl_gs2_parse("p=tls-unique,a=someuser,a=foo",
+	                    &length,
+	                    TRUE,
+	                    HaslGs2HeaderChannelBindingClientSupported,
+	                    "tls-unique",
+	                    "someuser");
+	g_assert_cmpint(length, ==, 24);
+
+	length = 0;
+	test_hasl_gs2_parse("p=tls-server-end-point,,a=foo",
+	                    &length,
+	                    TRUE,
+	                    HaslGs2HeaderChannelBindingClientSupported,
+	                    "tls-server-end-point",
+	                    NULL);
+	g_assert_cmpint(length, ==, 24);
+
+	length = 0;
+	test_hasl_gs2_parse("y,a=someuser,a=foo",
+	                    &length,
+	                    TRUE,
+	                    HaslGs2HeaderChannelBindingClientSupportedServerUnknown,
+	                    NULL,
+	                    "someuser");
+	g_assert_cmpint(length, ==, 13);
+}
+
+
 /******************************************************************************
  * Main
  *****************************************************************************/
@@ -403,10 +531,15 @@
 	g_test_add_func("/hasl/gs2-header/parse/rfc5801/binding-authzid",
 	                test_hasl_gs2_header_parse_rfc5801_binding_authzid);
 
-	g_test_add_func("/hasl/gs2-hedaer/parse/missing-cbname",
+	g_test_add_func("/hasl/gs2-header/parse/with-trailing",
+	                test_hasl_gs2_header_parse_with_trailing);
+	g_test_add_func("/hasl/gs2-header/parse/missing-cbname",
 	                test_hasl_gs2_header_parse_missing_cbname);
-	g_test_add_func("/hasl/gs2-hedaer/parse/unexpected-cbname",
+	g_test_add_func("/hasl/gs2-header/parse/unexpected-cbname",
 	                test_hasl_gs2_header_parse_unexpected_cbname);
 
+	g_test_add_func("/hasl/gs2-header/parse-full/with-trailing",
+	                test_hasl_gs2_header_parse_full_with_trailing);
+
 	return g_test_run();
 }
