David, This worked PERFECTLY! Wow, thanks, this is so much easier to set up and use than my previous solution. :) Also, I'm on KDE 5.5... I'm guessing I can't test the NetworkManager integration from here? BJ On Tue, Jul 19, 2016 at 3:24 PM, David Woodhouse <dwmw2 at infradead.org> wrote: > On Tue, 2016-07-19 at 14:08 -0600, Benjamin Cardon wrote: >> Attached is a scrubbed for session values version of the select roles >> page. > > Try this please. Should work both on the command line and from > NetworkManager. I'll clean it up a little if it's working. Mostly based > on Hillel's original HTML parsing code. > > diff --git a/auth-juniper.c b/auth-juniper.c > index a037ffa..6729807 100644 > --- a/auth-juniper.c > +++ b/auth-juniper.c > @@ -456,6 +456,106 @@ static int tncc_preauth(struct openconnect_info *vpninfo) > } > #endif > > +static struct oc_auth_form *parse_roles_table_node(xmlNodePtr node) > +{ > + struct oc_auth_form *form; > + xmlNodePtr table_itr; > + xmlNodePtr row_itr; > + xmlNodePtr data_itr; > + struct oc_form_opt_select *opt; > + struct oc_choice *choice; > + > + > + form = calloc(1, sizeof(*form)); > + if (!form) > + return NULL; > + > + opt = calloc(1, sizeof(*opt)); > + if (!opt) { > + free(form); > + return NULL; > + } > + > + form->opts = &opt->form; > + opt->form.label = strdup("frmSelectRoles"); > + opt->form.name = strdup("frmSelectRoles"); > + opt->form.type = OC_FORM_OPT_SELECT; > + > + for (table_itr = node->children; table_itr; table_itr = table_itr->next) { > + if (!table_itr->name || strcasecmp((const char *)table_itr->name, "tr")) > + continue; > + for (row_itr = table_itr->children; row_itr; row_itr = row_itr->next) { > + if (!row_itr->name || strcasecmp((const char *)row_itr->name, "td")) > + continue; > + for (data_itr = row_itr->children; data_itr; data_itr = data_itr->next) { > + struct oc_choice **new_choices; > + char *role_link = NULL; > + char *role_name = NULL; > + > + if (!data_itr->name || strcasecmp((const char *)data_itr->name, "a")) > + continue; > + > + // Discovered <a> tag with role selection. > + role_link = (char *)xmlGetProp(data_itr, (unsigned char *)"href"); > + if (!role_link) continue; > + > + role_name = (char *)xmlNodeGetContent(data_itr); > + if (!role_name) { > + // some weird case? > + free(role_link); > + continue; > + } > + > + choice = calloc(1, sizeof(*choice)); > + if (!choice) { > + free_auth_form(form); > + return NULL; > + } > + > + choice->label = role_name; > + choice->name = role_link; > + new_choices = realloc(opt->choices, sizeof(opt->choices[0]) * (opt->nr_choices+1)); > + if (!new_choices) { > + free(choice); > + free_auth_form(form); > + return NULL; > + } > + opt->choices = new_choices; > + opt->choices[opt->nr_choices++] = choice; > + } > + } > + } > + > + return form; > +} > + > +static struct oc_auth_form *parse_roles_form_node(xmlNodePtr node) > +{ > + struct oc_auth_form *form; > + xmlNodePtr child; > + > + // Set form->action here as a redirect url with keys and ids. > + for (child = htmlnode_next(node, node); child && child != node; child = htmlnode_next(node, child)) { > + if (!child->name) > + continue; > + > + if (!strcasecmp((char *)child->name, "table")) { > + char *table_id = (char *)xmlGetProp(child, (unsigned char *)"id"); > + > + if (table_id) { > + if (!strcmp(table_id, "TABLE_SelectRole_1")) > + form = parse_roles_table_node(child); > + > + free(table_id); > + > + if (form) break; // Redirect URL was constructed > + } > + } > + } > + > + return form; > +} > + > int oncp_obtain_cookie(struct openconnect_info *vpninfo) > { > int ret; > @@ -472,6 +572,7 @@ int oncp_obtain_cookie(struct openconnect_info *vpninfo) > > while (1) { > char *form_buf = NULL; > + int role_select = 0; > struct oc_text_buf *url; > > if (resp_buf && resp_buf->pos) > @@ -560,6 +661,13 @@ int oncp_obtain_cookie(struct openconnect_info *vpninfo) > } > /* XXX: Actually ask the user? */ > goto form_done; > + } else if (!strcmp(form_id, "frmSelectRoles")) { > + form = parse_roles_form_node(node); > + if (!form) { > + ret = -EINVAL; > + break; > + } > + role_select = 1; > } else { > vpn_progress(vpninfo, PRG_ERR, > _("Unknown form ID '%s'\n"), > @@ -584,6 +692,10 @@ int oncp_obtain_cookie(struct openconnect_info *vpninfo) > goto out; > } > > + if (role_select) { > + vpninfo->redirect_url = strdup(form->opts[0]._value); > + goto do_redirect; > + } > form_done: > append_form_opts(vpninfo, form, resp_buf); > ret = buf_error(resp_buf); > @@ -592,6 +704,7 @@ int oncp_obtain_cookie(struct openconnect_info *vpninfo) > > vpninfo->redirect_url = form->action; > form->action = NULL; > + do_redirect: > free_auth_form(form); > form = NULL; > handle_redirect(vpninfo); > -- > David Woodhouse Open Source Technology Centre > David.Woodhouse at intel.com Intel Corporation >