File 0001-GlobalProtect-Add-external-browser-with-CAS-support.patch of Package openconnect

diff '--color=auto' -urN openconnect-9.12/auth-globalprotect.c openconnect-9.12-new/auth-globalprotect.c
--- openconnect-9.12/auth-globalprotect.c	2023-04-11 15:48:19.000000000 +0200
+++ openconnect-9.12-new/auth-globalprotect.c	2024-07-24 15:32:07.127698518 +0200
@@ -24,6 +24,20 @@
 
 #include <ctype.h>
 #include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#ifndef _WIN32
+#include <pwd.h>
+#include <sys/inotify.h>
+#endif
+
+#define MAX_EVENTS 2
+#define LEN_NAME 32
+#define EVENT_SIZE  ( sizeof (struct inotify_event) ) /*size of one event*/
+#define BUF_LEN     ( MAX_EVENTS * ( EVENT_SIZE + LEN_NAME ))
+
+#define GP_DATA_FILE "globalprotect.dat"
 
 struct login_context {
 	char *username;				/* Username that has already succeeded in some form */
@@ -31,6 +45,7 @@
 	char *portal_userauthcookie;		/* portal-userauthcookie (from global-protect/getconfig.esp) */
 	char *portal_prelogonuserauthcookie;	/* portal-prelogonuserauthcookie (from global-protect/getconfig.esp) */
 	struct oc_auth_form *form;
+	int cas_auth;				/* Flag indicating whether cas auth is to be used */
 };
 
 void gpst_common_headers(struct openconnect_info *vpninfo,
@@ -59,6 +74,32 @@
 		return "Windows";
 }
 
+static char *get_user_cache_dir(void)
+{
+	char *cache_dir;
+	int cache_dir_len;
+#ifndef _WIN32
+	struct passwd *pw = getpwuid(getuid());
+	char *home = strdup(pw->pw_dir);
+#else
+	/* TODO: Add WIN32 implementation */
+	char *home = strdup("");
+#endif
+
+	/* Ensure that the openconnect cache dir exists */
+	cache_dir_len = strlen(home) + strlen("/.cache/openconnect/") + 1;
+	cache_dir = malloc(cache_dir_len);
+	snprintf(cache_dir, cache_dir_len, "%s/.cache/openconnect/", home);
+	free(home);
+
+#ifndef _WIN32
+	mkdir(cache_dir, 0700);
+#else
+	CreateDirectory(cache_dir, NULL);
+#endif
+
+	return cache_dir;
+}
 
 /* Parse pre-login response ({POST,GET} /{global-protect,ssl-vpn}/pre-login.esp)
  *
@@ -81,11 +122,18 @@
 	char *prompt = NULL, *username_label = NULL, *password_label = NULL;
 	char *s = NULL, *saml_method = NULL, *saml_path = NULL;
 	int result = -EINVAL;
+	int default_browser = 0;
+	int cas_auth = 0;
 
 	if (!xmlnode_is_named(xml_node, "prelogin-response"))
 		goto out;
 
 	for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
+		if (xmlnode_is_named(xml_node, "saml-default-browser"))
+			default_browser = xmlnode_bool_or_int_value(xml_node);
+		if (xmlnode_is_named(xml_node, "cas-auth"))
+			cas_auth = xmlnode_bool_or_int_value(xml_node);
+
 		xmlnode_get_val(xml_node, "saml-request", &s);
 		xmlnode_get_val(xml_node, "saml-auth-method", &saml_method);
 		xmlnode_get_trimmed_val(xml_node, "authentication-message", &prompt);
@@ -94,6 +142,11 @@
 		/* XX: should we save the certificate username from <ccusername/> ? */
 	}
 
+	if (default_browser != 0) {
+		vpn_progress(vpninfo, PRG_INFO, "Using default browser\n");
+		ctx->cas_auth = cas_auth;
+	}
+
 	if (saml_method && s) {
 		/* Allow the legacy workflow (no GUI setting up open_webview) to keep working */
 		if (!vpninfo->open_webview && ctx->portal_userauthcookie)
@@ -638,8 +691,8 @@
 		}
 		if (!keep_urlpath) {
 			orig_path = vpninfo->urlpath;
-			if (asprintf(&vpninfo->urlpath, "%s/prelogin.esp?tmp=tmp&clientVer=4100&clientos=%s",
-				     portal ? "global-protect" : "ssl-vpn", gpst_os_name(vpninfo)) < 0) {
+			if (asprintf(&vpninfo->urlpath, "%s/prelogin.esp?tmp=tmp&clientVer=4100&clientos=%s&default-browser=4&cas-support=yes",
+				portal ? "global-protect" : "ssl-vpn", gpst_os_name(vpninfo)) < 0) {
 				result = -ENOMEM;
 				goto out;
 			}
@@ -688,6 +741,8 @@
 		append_opt(request_body, "os-version", vpninfo->platname);
 		append_opt(request_body, "server", vpninfo->hostname);
 		append_opt(request_body, "computer", vpninfo->localname);
+		if (ctx->cas_auth && vpninfo->sso_token_cookie)
+			append_opt(request_body, "token", vpninfo->sso_token_cookie);
 		if (ctx->portal_userauthcookie)
 			append_opt(request_body, "portal-userauthcookie", ctx->portal_userauthcookie);
 		if (ctx->portal_prelogonuserauthcookie)
@@ -697,9 +752,11 @@
 			append_opt(request_body, "preferred-ip", vpninfo->ip_info.addr);
 		if (vpninfo->ip_info.addr6)
 			append_opt(request_body, "preferred-ipv6", vpninfo->ip_info.addr);
-		if (ctx->form->action)
-			append_opt(request_body, "inputStr", ctx->form->action);
-		append_form_opts(vpninfo, ctx->form, request_body);
+		if (ctx->form) {
+			if (ctx->form->action)
+				append_opt(request_body, "inputStr", ctx->form->action);
+			append_form_opts(vpninfo, ctx->form, request_body);
+		}
 		if ((result = buf_error(request_body)))
 			goto out;
 
@@ -717,14 +774,18 @@
 			/* Invalid username/password; reuse same form, but blank,
 			 * unless we just did a blind retry.
 			 */
-			nuke_opt_values(ctx->form->opts);
+			if (ctx->form)
+				nuke_opt_values(ctx->form->opts);
+			else
+				blind_retry = 0;
+
 			if (!blind_retry)
 				goto got_form;
 			else
 				blind_retry = 0;
 		} else {
 			/* Save successful username */
-			if (!ctx->username)
+			if (!ctx->username && ctx->form && ctx->form->opts->_value)
 				ctx->username = strdup(ctx->form->opts->_value);
 			if (result == -EAGAIN) {
 				/* New form is already populated from the challenge */
@@ -736,7 +797,7 @@
 				 */
 				portal = 0;
 				if (ctx->portal_userauthcookie || ctx->portal_prelogonuserauthcookie ||
-				    (strcmp(ctx->form->auth_id, "_challenge") && !ctx->alt_secret)) {
+				    (ctx->form &&(strcmp(ctx->form->auth_id, "_challenge")) && !ctx->alt_secret)) {
 					blind_retry = 1;
 					goto replay_form;
 				}
@@ -839,3 +900,233 @@
 	free(xml_buf);
 	return result;
 }
+
+static int parse_callback_file (struct openconnect_info *vpninfo, const char *data_file)
+{
+	FILE *fptr;
+	char *data;
+	int len;
+	void *xml = NULL;
+	xmlDocPtr xml_doc;
+	xmlNode *xml_node;
+	char *comment = NULL;
+	char *prelogin_cookie = NULL;
+	char *saml_username = NULL;
+	int size;
+
+	/* Open callback data file */
+	fptr = fopen(data_file, "r");
+	if (!fptr) {
+		vpn_progress(vpninfo, PRG_INFO, "Failed to open file %s\n", data_file);
+		return -1;
+	}
+
+	/* Get file size */
+	fseek(fptr, 0L, SEEK_END);
+	size = ftell(fptr);
+	fseek(fptr, 0L, SEEK_SET);
+
+	/* Read data */
+	data = malloc(size + 1);
+	memset(data, 0, size + 1);
+	fread(data, size, 1, fptr);
+	fclose(fptr);
+
+	if (strstr(data, "cas-as%3D")) {
+		char *token = strstr (data, "token%3D");
+		char *un = strstr (data, "un%3D");
+
+		if (!token || !un) {
+			free(data);
+			return -1;
+		}
+
+		vpn_progress(vpninfo, PRG_INFO, "CAS method for user %s\n", un);
+
+		saml_username = malloc (token - un);
+		if (saml_username) {
+			strncpy(saml_username, un + 5, token - un - 5 - 3);
+			free(vpninfo->sso_username);
+			vpninfo->sso_username = saml_username;
+		}
+
+		vpninfo->sso_token_cookie = strdup(token + 8);
+		free(data);
+
+		return 0;
+	}
+
+	vpn_progress(vpninfo, PRG_INFO, "Token method\n");
+
+	xml = openconnect_base64_decode (&len, data);
+	free(data);
+
+	vpn_progress(vpninfo, PRG_INFO, "xml (%d): %s\n", len, (char*)xml);
+
+	xml_doc = xmlReadMemory((char*)xml, len, NULL, NULL, XML_PARSE_NOERROR|XML_PARSE_RECOVER);
+	free(xml);
+
+	xml_node = xmlDocGetRootElement(xml_doc);
+	if (!xml_node)
+		return -1;
+
+	for (xmlNode *x = xml_node->children; x; x = x->next) {
+		/* Weird protocol response...
+		 * <html>
+		 *	<!-- <saml-auth-status>1</saml-auth-status>       // Authentication Status 1 = OK
+		 *	<prelogin-cookie>TOKEN</prelogin-cookie>          // Prelogin Cookie
+		 *	<saml-username>NAME</saml-username>               // Username
+		 * 	<saml-slo>no</saml-slo>                           // SAML Single Log-Out (SLO)
+		 *	<saml-SessionNotOnOrAfter>2023-11-18T03:13:37.368Z</saml-SessionNotOnOrAfter> // Session Lifetime, used for reconnect
+		 * -></html>
+		 */
+		if (!xmlnode_get_val(x, "comment", &comment)) {
+			/* Create a parent xml node for parsing */
+			char *buf = malloc(strlen("<parent>") + strlen(comment) + strlen("</parent>"));
+
+			strcpy(buf, "<parent>");
+			strcat(buf, comment);
+			strcat(buf, "</parent>");
+
+			xmlDocPtr xml2_doc = xmlReadMemory((char*)buf, strlen(buf), NULL, NULL, XML_PARSE_NOERROR|XML_PARSE_RECOVER);
+			free(buf);
+			xmlNode *xml2_node = xmlDocGetRootElement(xml2_doc);
+
+			for (xmlNode *y = xml2_node->children; y; y = y->next) {
+				vpn_progress(vpninfo, PRG_DEBUG, "-> Got node: %s\n", y->name);
+				xmlnode_get_val(y, "prelogin-cookie", &prelogin_cookie);
+				xmlnode_get_val(y, "saml-username", &saml_username);
+			}
+			xmlFreeDoc(xml2_doc);
+		}
+	}
+
+	if (saml_username) {
+		vpn_progress(vpninfo, PRG_INFO, "saml_username: %s\n", saml_username);
+		free(vpninfo->sso_username);
+		vpninfo->sso_username = strdup(saml_username);
+	}
+
+	if (prelogin_cookie) {
+		vpn_progress(vpninfo, PRG_INFO, "prelogin_cookie: %s\n", prelogin_cookie);
+		free(vpninfo->sso_token_cookie);
+		free(vpninfo->sso_cookie_value);
+		vpninfo->sso_token_cookie = strdup("prelogin_cookie");
+		vpninfo->sso_cookie_value = strdup(prelogin_cookie);
+	}
+
+	return 0;
+}
+
+int gpst_handle_external_browser(struct openconnect_info *vpninfo)
+{
+	int ret = -1;
+	char *data_file = NULL;
+	char *uri = NULL;
+	char *oc_cache_dir = NULL;
+	int data_file_len;
+
+	oc_cache_dir = get_user_cache_dir ();
+
+	/* Create data */
+	data_file_len = strlen (oc_cache_dir) + 1 + strlen(GP_DATA_FILE) + 1;
+	data_file = malloc(data_file_len);
+	strcpy(data_file, oc_cache_dir);
+	strcat(data_file, "/");
+	strcat(data_file, GP_DATA_FILE);
+
+	/* Check whether the sso_login is actually a data field */
+	if (strlen(vpninfo->sso_login) > 22 && strncmp(vpninfo->sso_login, "data:text/html;base64,", 22) == 0) {
+		int len;
+		void *out = openconnect_base64_decode(&len, vpninfo->sso_login + 22);
+		char *tmp_file;
+		FILE *file;
+		int tmp_file_len;
+
+		vpn_progress(vpninfo, PRG_INFO, "data: %s\n", vpninfo->sso_login);
+		if (!out)
+			goto finish;
+
+		tmp_file_len = strlen(oc_cache_dir) + strlen("/saml.html") + 1;
+		tmp_file = malloc(tmp_file_len);
+		strcpy(tmp_file, oc_cache_dir);
+		strcat(tmp_file, "/saml.html");
+		file = fopen(tmp_file, "w");
+		fwrite((char*)out, 1, len, file);
+		fclose(file);
+
+		free(out);
+
+		uri = malloc(strlen("file:///") + strlen(tmp_file) + 1);
+		strcpy(uri, "file:///");
+		strcat(uri, tmp_file);
+		free(tmp_file);
+	} else {
+		uri = strdup(vpninfo->sso_login);
+	}
+
+#ifndef _WIN32
+	int fd, wd;
+
+	fd = inotify_init();
+	if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
+		goto finish;
+
+	wd = inotify_add_watch(fd, oc_cache_dir, IN_CLOSE_WRITE);
+	if (wd == -1) {
+		vpn_progress(vpninfo, PRG_DEBUG, "Could not watch : %s\n", oc_cache_dir);
+	} else {
+		vpn_progress(vpninfo, PRG_DEBUG, "Watching : %s\n", oc_cache_dir);
+	}
+
+	if (vpninfo->open_ext_browser) {
+		ret = vpninfo->open_ext_browser(vpninfo, uri, vpninfo->cbdata);
+	} else {
+		ret = -EINVAL;
+	}
+
+	if (ret) {
+		vpn_progress(vpninfo, PRG_ERR,
+			     _("Failed to spawn external browser for %s\n"),
+			     vpninfo->sso_login);
+		goto finish;
+	}
+
+	/* TODO: Add timeout check to prevent lock up */
+	 while (1) {
+		int i = 0, length;
+		char buffer[BUF_LEN];
+
+		length = read(fd, buffer, BUF_LEN);
+
+		while (i < length) {
+ 			struct inotify_event *event = (struct inotify_event *) &buffer[i];
+
+			if (event->len) {
+				if ((event->mask & IN_CLOSE_WRITE) && !(event->mask & IN_ISDIR) && strcmp(event->name, GP_DATA_FILE) == 0) {
+					vpn_progress(vpninfo, PRG_INFO, "The file %s was written.\n", event->name);
+					ret = parse_callback_file (vpninfo, data_file);
+					goto finish;
+				}
+			}
+			i += EVENT_SIZE + event->len;
+		}
+	}
+#else
+	vpn_progress(vpninfo, PRG_INFO, "Proper callback data handling missing on Windows\n");
+	Sleep(10 * 1000);
+	ret = parse_callback_file (vpninfo, data_file);
+#endif
+
+finish:
+	if (uri)
+		free(uri);
+
+	if (oc_cache_dir)
+		free(oc_cache_dir);
+
+	if (data_file)
+		free(data_file);
+
+	return ret;
+}
diff '--color=auto' -urN openconnect-9.12/auth-globalprotect.c.orig openconnect-9.12-new/auth-globalprotect.c.orig
diff '--color=auto' -urN openconnect-9.12/gp_browser_helper.c openconnect-9.12-new/gp_browser_helper.c
--- openconnect-9.12/gp_browser_helper.c	1970-01-01 01:00:00.000000000 +0100
+++ openconnect-9.12-new/gp_browser_helper.c	2024-07-24 15:32:07.127698518 +0200
@@ -0,0 +1,101 @@
+/*
+ * OpenConnect (SSL + DTLS) VPN client
+ *
+ * Copyright © 2023 Jan-Michael Brummer <jan-michael.brummer1@volkswagen.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifndef _WIN32
+#include <pwd.h>
+#endif
+
+#define DATA_FILE_NAME "globalprotect.dat"
+
+static char *get_user_cache_dir(void)
+{
+	char *cache_dir;
+	int cache_dir_len;
+#ifndef _WIN32
+	struct passwd *pw = getpwuid(getuid());
+	char *home = strdup(pw->pw_dir);
+#else
+	/* TODO: Add WIN32 implementation */
+	char *home = strdup("");
+#endif
+
+	/* Ensure that the openconnect cache dir exists */
+	cache_dir_len = strlen(home) + strlen("/.cache/openconnect/") + 1;
+	cache_dir = malloc(cache_dir_len);
+	snprintf(cache_dir, cache_dir_len, "%s/.cache/openconnect/", home);
+	free(home);
+
+#ifndef _WIN32
+	mkdir(cache_dir, 0700);
+#else
+	mkdir(cache_dir);
+#endif
+
+	return cache_dir;
+}
+
+int main(int argc, char **argv)
+{
+	FILE *fp;
+	char *callback_data;
+	char *oc_cache_dir;
+	char *data_file;
+	int data_file_len;
+	int ret = 0;
+
+	if (argc < 2)
+		return -1;
+
+	callback_data = argv[1];
+
+	oc_cache_dir = get_user_cache_dir();
+
+	data_file_len = strlen (oc_cache_dir) + strlen (DATA_FILE_NAME) + 2;
+	data_file = malloc (data_file_len);
+	strcpy(data_file, oc_cache_dir);
+	strcat(data_file, "/");
+	strcat(data_file, DATA_FILE_NAME);
+
+	fp = fopen(data_file, "w");
+	if (fp) {
+		/* callback_data format:
+		 * globalprotectcallback:DATA
+		 *
+		 * DATA:
+		 * - Using CAS: cas-as=1&un=<USER>&token=<TOKEN>
+		 * - Without CAS: <TOKEN>
+		 *
+		 * As we just want the data, we skip the prefix
+		 */
+		fwrite(callback_data + strlen("globalprotectcallback:"),
+		       strlen(callback_data) - strlen("globalprotectcallback:"),
+		       1,
+		       fp);
+		fclose (fp);
+		ret = 0;
+	}
+
+	free(data_file);
+	free(oc_cache_dir);
+
+	return ret;
+}
diff '--color=auto' -urN openconnect-9.12/gp_browser_helper.desktop openconnect-9.12-new/gp_browser_helper.desktop
--- openconnect-9.12/gp_browser_helper.desktop	1970-01-01 01:00:00.000000000 +0100
+++ openconnect-9.12-new/gp_browser_helper.desktop	2024-07-24 15:32:07.128698526 +0200
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Version=1.0
+Name=OpenConnect Browser Helper for GlobalProtect
+Type=Application
+Terminal=false
+NoDisplay=true
+Exec=gp_browser_helper %U
+MimeType=x-scheme-handler/globalprotectcallback;
\ Kein Zeilenumbruch am Dateiende.
diff '--color=auto' -urN openconnect-9.12/library.c openconnect-9.12-new/library.c
--- openconnect-9.12/library.c	2023-05-11 17:21:08.000000000 +0200
+++ openconnect-9.12-new/library.c	2024-07-24 15:32:07.128698526 +0200
@@ -176,6 +176,7 @@
 		.obtain_cookie = gpst_obtain_cookie,
 		.sso_detect_done = gpst_sso_detect_done,
 		.udp_protocol = "ESP",
+		.handle_external_browser = gpst_handle_external_browser,
 #ifdef HAVE_ESP
 		.udp_setup = esp_setup,
 		.udp_mainloop = esp_mainloop,
@@ -1709,15 +1710,20 @@
 		vpninfo->sso_cookie_value = NULL;
 		vpninfo->sso_username = NULL;
 
-		/* Handle the special Cisco external browser mode */
-		if (vpninfo->sso_browser_mode && !strcmp(vpninfo->sso_browser_mode, "external")) {
-			ret = handle_external_browser(vpninfo);
-		} else if (vpninfo->open_webview) {
-			ret = vpninfo->open_webview(vpninfo, vpninfo->sso_login, vpninfo->cbdata);
-		} else {
-			vpn_progress(vpninfo, PRG_ERR,
-				     _("No SSO handler\n")); /* XX: print more debugging info */
-			ret = -EINVAL;
+		if (vpninfo->proto->handle_external_browser)
+			ret = vpninfo->proto->handle_external_browser(vpninfo);
+
+		if (ret != 0) {
+			/* Handle the special Cisco external browser mode */
+			if (vpninfo->sso_browser_mode && !strcmp(vpninfo->sso_browser_mode, "external")) {
+				ret = handle_external_browser(vpninfo);
+			} else if (vpninfo->open_webview) {
+				ret = vpninfo->open_webview(vpninfo, vpninfo->sso_login, vpninfo->cbdata);
+			} else {
+				vpn_progress(vpninfo, PRG_ERR,
+					     _("No SSO handler\n")); /* XX: print more debugging info */
+				ret = -EINVAL;
+			}
 		}
 		if (!ret) {
 			for (opt = form->opts; opt; opt = opt->next) {
diff '--color=auto' -urN openconnect-9.12/library.c.orig openconnect-9.12-new/library.c.orig
diff '--color=auto' -urN openconnect-9.12/Makefile.am openconnect-9.12-new/Makefile.am
--- openconnect-9.12/Makefile.am	2023-05-19 18:12:15.000000000 +0200
+++ openconnect-9.12-new/Makefile.am	2024-07-24 15:32:07.126698511 +0200
@@ -16,6 +16,7 @@
 endif
 
 lib_LTLIBRARIES = libopenconnect.la
+bin_PROGRAMS = gp_browser_helper
 sbin_PROGRAMS = openconnect
 man8_MANS = openconnect.8
 noinst_PROGRAMS :=
@@ -23,6 +24,16 @@
 AM_CFLAGS = @WFLAGS@
 AM_CPPFLAGS = -DLOCALEDIR="\"$(localedir)\""
 
+# GlobalProtect Browser Helper
+gp_browser_helper_SOURCES: gp_browser_helper.c
+
+desktopdir = $(datadir)/applications
+dist_desktop_DATA = gp_browser_helper.desktop
+install-data-hook:
+	if which update-desktop-database>/dev/null 2>&1 ; then \
+		update-desktop-database; \
+	fi
+
 openconnect_SOURCES = xml.c main.c
 openconnect_CFLAGS = $(AM_CFLAGS) $(SSL_CFLAGS) $(DTLS_SSL_CFLAGS) \
 	$(LIBXML2_CFLAGS) $(JSON_CFLAGS) $(LIBPROXY_CFLAGS) \
diff '--color=auto' -urN openconnect-9.12/Makefile.am.orig openconnect-9.12-new/Makefile.am.orig
diff '--color=auto' -urN openconnect-9.12/openconnect-internal.h openconnect-9.12-new/openconnect-internal.h
--- openconnect-9.12/openconnect-internal.h	2023-05-19 18:12:15.000000000 +0200
+++ openconnect-9.12-new/openconnect-internal.h	2024-07-24 15:32:07.129698533 +0200
@@ -861,6 +861,9 @@
 
 	/* Catch probe packet confirming the (UDP) session */
 	int (*udp_catch_probe)(struct openconnect_info *vpninfo, struct pkt *p);
+
+	/* External browser */
+	int (*handle_external_browser)(struct openconnect_info *vpninfo);
 };
 
 static inline struct pkt *dequeue_packet(struct pkt_q *q)
@@ -1394,6 +1397,7 @@
 int gpst_esp_send_probes(struct openconnect_info *vpninfo);
 int gpst_esp_catch_probe(struct openconnect_info *vpninfo, struct pkt *pkt);
 int gpst_sso_detect_done(struct openconnect_info *vpninfo, const struct oc_webview_result *result);
+int gpst_handle_external_browser(struct openconnect_info *vpninfo);
 
 /* lzs.c */
 int lzs_decompress(unsigned char *dst, int dstlen, const unsigned char *src, int srclen);
diff '--color=auto' -urN openconnect-9.12/openconnect-internal.h.orig openconnect-9.12-new/openconnect-internal.h.orig
openSUSE Build Service is sponsored by