--- openconnect-7.06-orig/auth-juniper.c 2015-03-17 09:29:49.000000000 -0400 +++ openconnect-7.06/auth-juniper.c 2015-07-23 23:22:42.628688636 -0400 @@ -245,6 +245,189 @@ return form; } +// Helper structs and functions for role selector +struct RolesListNode { + char *name; + char *link; + struct RolesListNode* next; +}; + +struct RolesList { + unsigned int size; + struct RolesListNode *head; + struct RolesListNode *tail; +}; + +static void init_roles_list(struct RolesList *list) +{ + list->size = 0; + list->head = NULL; + list->tail = NULL; +} + +static void inner_free_roles_list(struct RolesList *list) +{ + struct RolesListNode *cur = list->head; + struct RolesListNode *next = NULL; + while (cur) + { + next = cur->next; + if (cur->name) free(cur->name); + if (cur->link) free(cur->link); + free(cur); + cur = next; + } +} + +static void add_role_list_node(struct RolesList *list, struct RolesListNode *node) +{ + if (!list || !node) return; + + if (!list->head) { + list->head = node; + list->tail = node; + } + else { + list->tail->next = node; + list->tail = node; + } + + list->tail->next = NULL; + list->size++; +} + +static struct RolesListNode *select_role(struct RolesList *roles) +{ + // TODO: using some simple scanf here. + // Figure out if better way is needed and how it should be handled. + // --role parameter too? + unsigned int selection = roles->size + 1; // making it bigger than roles.size first. + struct RolesListNode *role = NULL; + + while ((roles->size > 0) && ((selection < 1) || (selection > roles->size))) { + unsigned int i; + + printf(_("You have these roles available:\n")); + role = roles->head; + for (i = 1; i <= roles->size; i++) { + printf(_("[%u] %s\n"), i, role->name); + role = role->next; + } + + printf(_("Please select your role [1-%u]: "), roles->size); + scanf("%u", &selection); + while (getchar()!='\n'); // clearing trailing chars + + if ((selection < 1) || (selection > roles->size)) { + printf(_("Invalid selection!\n")); + } + else { + role = roles->head; + for (i = 1; i < selection; i++) role = role->next; + break; + } + } + + return role; // it should be NULL if roles.size is 0 +} + +static int parse_roles_table_node(struct oc_auth_form *form, xmlNodePtr node) +{ + xmlNodePtr table_itr; + xmlNodePtr row_itr; + xmlNodePtr data_itr; + struct RolesList roles; + struct RolesListNode *role = NULL; + + init_roles_list(&roles); + + 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) { + char *role_link = NULL; + char *role_name = NULL; + struct RolesListNode *list_node = NULL; + + if (!data_itr->name || strcasecmp((const char *)data_itr->name, "a")) + continue; + + // Discovered 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; + } + + list_node = calloc(1, sizeof(struct RolesListNode)); + list_node->name = role_name; + list_node->link = role_link; + list_node->next = NULL; + add_role_list_node(&roles, list_node); + } + } + } + + role = select_role(&roles); + + if (!role) { + form->action = NULL; + printf(_("No role was found!")); + } + else { + form->action = role->link; // moving the pointer + role->link = NULL; // setting moved to NULL to avoid freeing in list destruction. + printf(_("Connecting with role %s\n"), role->name); + } + + inner_free_roles_list(&roles); + return 0; +} + +static struct oc_auth_form *parse_roles_form_node(xmlNodePtr node) +{ + struct oc_auth_form *form = calloc(1, sizeof(*form)); + xmlNodePtr child; + + if (!form) + return NULL; + + form->method = NULL; + form->action = NULL; + + // 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")) { + parse_roles_table_node(form, child); + } + free(table_id); + + if (form->action) break; // Redirect URL was constructed + } + } + } + + if (!form->action) { + free_auth_form(form); + form = NULL; + } + + return form; +} + static int oncp_https_submit(struct openconnect_info *vpninfo, struct oc_text_buf *req_buf, xmlDocPtr *doc) { @@ -541,6 +724,13 @@ } /* 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; + } + goto form_done; } else { vpn_progress(vpninfo, PRG_ERR, _("Unknown form ID '%s'\n"),