Add login handling to WISPr command line client
authorMarcel Holtmann <marcel@holtmann.org>
Tue, 2 Nov 2010 09:47:05 +0000 (10:47 +0100)
committerMarcel Holtmann <marcel@holtmann.org>
Tue, 2 Nov 2010 09:47:05 +0000 (10:47 +0100)
tools/wispr.c

index 99c0b69..fcf99d2 100644 (file)
@@ -46,34 +46,121 @@ static void sig_term(int sig)
        g_main_loop_quit(main_loop);
 }
 
-enum wispr_pages {
-       WISPR_PAGE_NONE,
-       WISPR_PAGE_REDIRECT,
-       WISPR_PAGE_PROXY,
-       WISPR_PAGE_AUTHENTICATION_REPLY,
-       WISPR_PAGE_AUTHENTICATION_POLL_REPLY,
-       WISPR_PAGE_LOGOFF_REPLY,
-       WISPR_PAGE_ABORT_LOGIN_REPLY,
+static const char *message_type_to_string(int message_type)
+{
+       switch (message_type) {
+       case 100:
+               return "Initial redirect message";
+       case 110:
+               return "Proxy notification";
+       case 120:
+               return "Authentication notification";
+       case 130:
+               return "Logoff notification";
+       case 140:
+               return "Response to Authentication Poll";
+       case 150:
+               return "Response to Abort Login";
+       }
+
+       return NULL;
+}
+
+static const char *response_code_to_string(int response_code)
+{
+       switch (response_code) {
+       case 0:
+               return "No error";
+       case 50:
+               return "Login succeeded";
+       case 100:
+               return "Login failed";
+       case 102:
+               return "RADIUS server error/timeout";
+       case 105:
+               return "RADIUS server not enabled";
+       case 150:
+               return "Logoff succeeded";
+       case 151:
+               return "Login aborted";
+       case 200:
+               return "Proxy detection/repeat operation";
+       case 201:
+               return "Authentication pending";
+       case 255:
+               return "Access gateway internal error";
+       }
+
+       return NULL;
+}
+
+struct wispr_msg {
+       gboolean has_error;
+       const char *current_element;
+       int message_type;
+       int response_code;
+       char *login_url;
+       char *logoff_url;
 };
 
-enum wispr_elements {
-       WISPR_NONE,
-       WISPR_ACCESS_PROCEDURE,
-       WISPR_ACCESS_LOCATION,
-       WISPR_LOCATION_NAME,
-       WISPR_LOGIN_URL,
-       WISPR_ABORT_LOGIN_URL,
-       WISPR_MESSAGE_TYPE,
-       WISPR_RESPONSE_CODE,
-       WISPR_NEXT_URL,
-       WISPR_DELAY,
-       WISPR_REPLY_MESSAGE,
-       WISPR_LOGIN_RESULTS_URL,
-       WISPR_LOGOFF_URL,
+static inline void wispr_msg_init(struct wispr_msg *msg)
+{
+       msg->has_error = FALSE;
+       msg->current_element = NULL;
+
+       msg->message_type = -1;
+       msg->response_code = -1;
+
+       g_free(msg->login_url);
+       msg->login_url = NULL;
+
+       g_free(msg->logoff_url);
+       msg->logoff_url = NULL;
+}
+
+struct wispr_session {
+       GWeb *web;
+       GWebParser *parser;
+       guint request;
+       struct wispr_msg msg;
+       const char *username;
+       const char *password;
+       const char *originurl;
+       char *formdata;
 };
 
-static enum wispr_pages current_page = WISPR_NONE;
-static enum wispr_elements current_element = WISPR_NONE;
+static struct {
+       const char *str;
+       enum {
+               WISPR_ELEMENT_NONE,
+               WISPR_ELEMENT_ACCESS_PROCEDURE,
+               WISPR_ELEMENT_ACCESS_LOCATION,
+               WISPR_ELEMENT_LOCATION_NAME,
+               WISPR_ELEMENT_LOGIN_URL,
+               WISPR_ELEMENT_ABORT_LOGIN_URL,
+               WISPR_ELEMENT_MESSAGE_TYPE,
+               WISPR_ELEMENT_RESPONSE_CODE,
+               WISPR_ELEMENT_NEXT_URL,
+               WISPR_ELEMENT_DELAY,
+               WISPR_ELEMENT_REPLY_MESSAGE,
+               WISPR_ELEMENT_LOGIN_RESULTS_URL,
+               WISPR_ELEMENT_LOGOFF_URL,
+       } element;
+} wispr_element_map[] = {
+       { "AccessProcedure",    WISPR_ELEMENT_ACCESS_PROCEDURE  },
+       { "AccessLocation",     WISPR_ELEMENT_ACCESS_LOCATION   },
+       { "LocationName",       WISPR_ELEMENT_LOCATION_NAME     },
+       { "LoginURL",           WISPR_ELEMENT_LOGIN_URL         },
+       { "AbortLoginURL",      WISPR_ELEMENT_ABORT_LOGIN_URL   },
+       { "MessageType",        WISPR_ELEMENT_MESSAGE_TYPE      },
+       { "ResponseCode",       WISPR_ELEMENT_RESPONSE_CODE     },
+       { "NextURL",            WISPR_ELEMENT_NEXT_URL          },
+       { "Delay",              WISPR_ELEMENT_DELAY             },
+       { "ReplyMessage",       WISPR_ELEMENT_REPLY_MESSAGE     },
+       { "LoginResultsURL",    WISPR_ELEMENT_LOGIN_RESULTS_URL },
+       { "LogoffURL",          WISPR_ELEMENT_LOGOFF_URL        },
+       { NULL,                 WISPR_ELEMENT_NONE              },
+};
 
 static void start_element_handler(GMarkupParseContext *context,
                                        const gchar *element_name,
@@ -81,188 +168,69 @@ static void start_element_handler(GMarkupParseContext *context,
                                        const gchar **attribute_values,
                                        gpointer user_data, GError **error)
 {
-       if (g_str_equal(element_name, "Redirect") == TRUE)
-               current_page = WISPR_PAGE_REDIRECT;
-       else if (g_str_equal(element_name, "Proxy") == TRUE)
-               current_page = WISPR_PAGE_PROXY;
-       else if (g_str_equal(element_name, "AuthenticationReply") == TRUE)
-               current_page = WISPR_PAGE_AUTHENTICATION_REPLY;
-       else if (g_str_equal(element_name, "AuthenticationPollReply") == TRUE)
-               current_page = WISPR_PAGE_AUTHENTICATION_POLL_REPLY;
-       else if (g_str_equal(element_name, "LogoffReply") == TRUE)
-               current_page = WISPR_PAGE_LOGOFF_REPLY;
-       else if (g_str_equal(element_name, "AbortLoginReply") == TRUE)
-               current_page = WISPR_PAGE_ABORT_LOGIN_REPLY;
-       else
-               current_page = WISPR_PAGE_NONE;
-
-       if (g_str_equal(element_name, "AccessProcedure") == TRUE)
-               current_element = WISPR_ACCESS_PROCEDURE;
-       else if (g_str_equal(element_name, "AccessLocation") == TRUE)
-               current_element = WISPR_ACCESS_LOCATION;
-       else if (g_str_equal(element_name, "LocationName") == TRUE)
-               current_element = WISPR_LOCATION_NAME;
-       else if (g_str_equal(element_name, "LoginURL") == TRUE)
-               current_element = WISPR_LOGIN_URL;
-       else if (g_str_equal(element_name, "AbortLoginURL") == TRUE)
-               current_element = WISPR_ABORT_LOGIN_URL;
-       else if (g_str_equal(element_name, "MessageType") == TRUE)
-               current_element = WISPR_MESSAGE_TYPE;
-       else if (g_str_equal(element_name, "ResponseCode") == TRUE)
-               current_element = WISPR_RESPONSE_CODE;
-       else if (g_str_equal(element_name, "NextURL") == TRUE)
-               current_element = WISPR_NEXT_URL;
-       else if (g_str_equal(element_name, "Delay") == TRUE)
-               current_element = WISPR_DELAY;
-       else if (g_str_equal(element_name, "ReplyMessage") == TRUE)
-               current_element = WISPR_REPLY_MESSAGE;
-       else if (g_str_equal(element_name, "LoginResultsURL") == TRUE)
-               current_element = WISPR_LOGIN_RESULTS_URL;
-       else if (g_str_equal(element_name, "LogoffURL") == TRUE)
-               current_element = WISPR_LOGOFF_URL;
-       else
-               current_element = WISPR_NONE;
+       struct wispr_msg *msg = user_data;
+
+       msg->current_element = element_name;
 }
 
 static void end_element_handler(GMarkupParseContext *context,
                                        const gchar *element_name,
                                        gpointer user_data, GError **error)
 {
-       current_page = WISPR_PAGE_NONE;
+       struct wispr_msg *msg = user_data;
 
-       current_element = WISPR_NONE;
+       msg->current_element = NULL;
 }
 
 static void text_handler(GMarkupParseContext *context,
                                        const gchar *text, gsize text_len,
                                        gpointer user_data, GError **error)
 {
-       int value;
-
-       switch (current_page) {
-       case WISPR_PAGE_NONE:
-               break;
-       case WISPR_PAGE_REDIRECT:
-               printf("[ Redirect ]\n");
-               break;
-       case WISPR_PAGE_PROXY:
-               printf("[ Proxy ]\n");
-               break;
-       case WISPR_PAGE_AUTHENTICATION_REPLY:
-               printf("[ Authentication reply ]\n");
-               break;
-       case WISPR_PAGE_AUTHENTICATION_POLL_REPLY:
-               printf("[ Authentication poll reply ]\n");
-               break;
-       case WISPR_PAGE_LOGOFF_REPLY:
-               printf("[ Logoff reply ]\n");
-               break;
-       case WISPR_PAGE_ABORT_LOGIN_REPLY:
-               printf("[ Abort login reply ]\n");
-               break;
-       }
-
-       switch (current_element) {
-       case WISPR_NONE:
-               break;
-       case WISPR_ACCESS_PROCEDURE:
-               printf("Access procedure: %s\n", text);
-               break;
-       case WISPR_ACCESS_LOCATION:
-               printf("Access location: %s\n", text);
-               break;
-       case WISPR_LOCATION_NAME:
-               printf("Location name: %s\n", text);
-               break;
-       case WISPR_LOGIN_URL:
-               printf("Login URL: %s\n", text);
-               break;
-       case WISPR_ABORT_LOGIN_URL:
-               printf("Abort login URL: %s\n", text);
-               break;
-       case WISPR_MESSAGE_TYPE:
-               value = atoi(text);
-               printf("Message type: %d\n", value);
-               switch (value) {
-               case 100:
-                       printf("  Initial redirect message\n");
-                       break;
-               case 110:
-                       printf("  Proxy notification\n");
-                       break;
-               case 120:
-                       printf("  Authentication notification\n");
-                       break;
-               case 130:
-                       printf("  Logoff notification\n");
-                       break;
-               case 140:
-                       printf("  Response to Authentication Poll\n");
-                       break;
-               case 150:
-                       printf("  Response to Abort Login\n");
-                       break;
-               }
-               break;
-       case WISPR_RESPONSE_CODE:
-               value = atoi(text);
-               printf("Response code: %d\n", value);
-               switch (value) {
-               case 0:
-                       printf("  No error\n");
-                       break;
-               case 50:
-                       printf("  Login succeeded (Access ACCEPT)\n");
-                       break;
-               case 100:
-                       printf("  Login failed (Access REJECT)\n");
+       struct wispr_msg *msg = user_data;
+       int i;
+
+       if (msg->current_element == NULL)
+               return;
+
+       for (i = 0; wispr_element_map[i].str; i++) {
+               if (g_str_equal(wispr_element_map[i].str,
+                                       msg->current_element) == FALSE)
+                       continue;
+
+               switch (wispr_element_map[i].element) {
+               case WISPR_ELEMENT_NONE:
+               case WISPR_ELEMENT_ACCESS_PROCEDURE:
+               case WISPR_ELEMENT_ACCESS_LOCATION:
+               case WISPR_ELEMENT_LOCATION_NAME:
                        break;
-               case 102:
-                       printf("  RADIUS server error/timeout\n");
+               case WISPR_ELEMENT_LOGIN_URL:
+                       g_free(msg->login_url);
+                       msg->login_url = g_strdup(text);
                        break;
-               case 105:
-                       printf("  RADIUS server not enabled\n");
+               case WISPR_ELEMENT_ABORT_LOGIN_URL:
                        break;
-               case 150:
-                       printf("  Logoff succeeded\n");
+               case WISPR_ELEMENT_MESSAGE_TYPE:
+                       msg->message_type = atoi(text);
                        break;
-               case 151:
-                       printf("  Login aborted\n");
+               case WISPR_ELEMENT_RESPONSE_CODE:
+                       msg->response_code = atoi(text);
                        break;
-               case 200:
-                       printf("  Proxy detection/repeat operation\n");
-                       break;
-               case 201:
-                       printf("  Authentication pending\n");
-                       break;
-               case 255:
-                       printf("  Access gateway internal error\n");
+               case WISPR_ELEMENT_NEXT_URL:
+               case WISPR_ELEMENT_DELAY:
+               case WISPR_ELEMENT_REPLY_MESSAGE:
+               case WISPR_ELEMENT_LOGIN_RESULTS_URL:
+               case WISPR_ELEMENT_LOGOFF_URL:
                        break;
                }
-               break;
-       case WISPR_NEXT_URL:
-               printf("Next URL: %s\n", text);
-               break;
-       case WISPR_DELAY:
-               value = atoi(text);
-               printf("Delay: %d seconds\n", value);
-               break;
-       case WISPR_REPLY_MESSAGE:
-               printf("Reply message: %s\n", text);
-               break;
-       case WISPR_LOGIN_RESULTS_URL:
-               printf("Login results URL: %s\n", text);
-               break;
-       case WISPR_LOGOFF_URL:
-               printf("Logoff URL: %s\n", text);
-               break;
        }
 }
 
 static void error_handler(GMarkupParseContext *context,
                                        GError *error, gpointer user_data)
 {
-       printf("%s\n", error->message);
+       struct wispr_msg *msg = user_data;
+
+       msg->has_error = TRUE;
 }
 
 static const GMarkupParser wispr_parser = {
@@ -275,52 +243,95 @@ static const GMarkupParser wispr_parser = {
 
 static void parser_callback(const char *str, gpointer user_data)
 {
+       struct wispr_session *wispr = user_data;
        GMarkupParseContext *context;
        gboolean result;
 
        //printf("%s\n", str);
 
        context = g_markup_parse_context_new(&wispr_parser,
-                               G_MARKUP_TREAT_CDATA_AS_TEXT, NULL, NULL);
+                       G_MARKUP_TREAT_CDATA_AS_TEXT, &wispr->msg, NULL);
 
        result = g_markup_parse_context_parse(context, str, strlen(str), NULL);
-
-       result = g_markup_parse_context_end_parse(context, NULL);
+       if (result == TRUE)
+               result = g_markup_parse_context_end_parse(context, NULL);
 
        g_markup_parse_context_free(context);
 }
 
-static guint request_id;
-static GWebParser *request_parser;
+static gboolean wispr_input(const guint8 **data, gsize *length,
+                                               gpointer user_data)
+{
+       struct wispr_session *wispr = user_data;
+
+       g_free(wispr->formdata);
+       wispr->formdata = g_strdup_printf("button=Login&UserName=%s&"
+                       "Password=%s&FNAME=0&OriginatingServer=%s",
+                       wispr->username, wispr->password, wispr->originurl);
 
-static gboolean web_result(GWebResult *result, gpointer user_data)
+       *data = (guint8 *) wispr->formdata;
+       *length = strlen(wispr->formdata);
+
+       return FALSE;
+}
+
+static gboolean wispr_result(GWebResult *result, gpointer user_data)
 {
+       struct wispr_session *wispr = user_data;
        const guint8 *chunk;
        gsize length;
        guint16 status;
        gdouble elapsed;
 
-       status = g_web_result_get_status(result);
-       if (status == 200)
-               goto done;
-
        g_web_result_get_chunk(result, &chunk, &length);
 
        if (length > 0) {
                //printf("%s\n", (char *) chunk);
-               g_web_parser_feed_data(request_parser, chunk, length);
+               g_web_parser_feed_data(wispr->parser, chunk, length);
                return TRUE;
        }
 
-       g_web_parser_end_data(request_parser);
+       g_web_parser_end_data(wispr->parser);
+
+       status = g_web_result_get_status(result);
 
-done:
        g_print("status: %03u\n", status);
 
        elapsed = g_timer_elapsed(timer, NULL);
 
        g_print("elapse: %f seconds\n", elapsed);
 
+       if (wispr->msg.message_type < 0)
+               goto done;
+
+       printf("Message type: %s (%d)\n",
+                       message_type_to_string(wispr->msg.message_type),
+                                               wispr->msg.message_type);
+       printf("Response code: %s (%d)\n",
+                       response_code_to_string(wispr->msg.response_code),
+                                               wispr->msg.response_code);
+       if (wispr->msg.login_url != NULL)
+               printf("Login URL: %s\n", wispr->msg.login_url);
+       if (wispr->msg.logoff_url != NULL)
+               printf("Logoff URL: %s\n", wispr->msg.logoff_url);
+       printf("\n");
+
+       if (status == 302 && wispr->msg.message_type == 100) {
+               wispr->request = g_web_request_post(wispr->web,
+                                       wispr->msg.login_url,
+                                       "application/x-www-form-urlencoded",
+                                       wispr_input, wispr_result, wispr);
+
+               wispr_msg_init(&wispr->msg);
+
+               return FALSE;
+       } else if (status == 200 && wispr->msg.message_type == 120) {
+               int code = wispr->msg.response_code;
+               printf("Login process: %s\n",
+                                       code == 50 ? "SUCCESS" : "FAILURE");
+       }
+
+done:
        g_main_loop_quit(main_loop);
 
        return FALSE;
@@ -328,6 +339,8 @@ done:
 
 static gboolean option_debug = FALSE;
 static gchar *option_nameserver = NULL;
+static gchar *option_username = NULL;
+static gchar *option_password = NULL;
 static gchar *option_url = NULL;
 
 static GOptionEntry options[] = {
@@ -335,7 +348,11 @@ static GOptionEntry options[] = {
                                        "Enable debug output" },
        { "nameserver", 'n', 0, G_OPTION_ARG_STRING, &option_nameserver,
                                        "Specify nameserver", "ADDRESS" },
-       { "url", 'u', 0, G_OPTION_ARG_STRING, &option_url,
+       { "username", 'u', 0, G_OPTION_ARG_STRING, &option_username,
+                                       "Specify username", "USERNAME" },
+       { "password", 'p', 0, G_OPTION_ARG_STRING, &option_password,
+                                       "Specify password", "PASSWORD" },
+       { "url", 'U', 0, G_OPTION_ARG_STRING, &option_url,
                                        "Specify arbitrary request", "URL" },
        { NULL },
 };
@@ -345,7 +362,7 @@ int main(int argc, char *argv[])
        GOptionContext *context;
        GError *error = NULL;
        struct sigaction sa;
-       GWeb *web;
+       struct wispr_session wispr;
        int index = 0;
 
        context = g_option_context_new(NULL);
@@ -362,43 +379,46 @@ int main(int argc, char *argv[])
 
        g_option_context_free(context);
 
-       web = g_web_new(index);
-       if (web == NULL) {
+       memset(&wispr, 0, sizeof(wispr));
+       wispr_msg_init(&wispr.msg);
+
+       wispr.web = g_web_new(index);
+       if (wispr.web == NULL) {
                fprintf(stderr, "Failed to create web service\n");
                return 1;
        }
 
        if (option_debug == TRUE)
-               g_web_set_debug(web, web_debug, "WEB");
+               g_web_set_debug(wispr.web, web_debug, "WEB");
 
        main_loop = g_main_loop_new(NULL, FALSE);
 
        if (option_nameserver != NULL) {
-               g_web_add_nameserver(web, option_nameserver);
+               g_web_add_nameserver(wispr.web, option_nameserver);
                g_free(option_nameserver);
        }
 
-       g_web_set_accept(web, NULL);
-       g_web_set_user_agent(web, "SmartClient/%s wispr", VERSION);
-       g_web_set_close_connection(web, TRUE);
+       g_web_set_accept(wispr.web, NULL);
+       g_web_set_user_agent(wispr.web, "SmartClient/%s wispr", VERSION);
+       g_web_set_close_connection(wispr.web, TRUE);
 
        if (option_url == NULL)
                option_url = g_strdup(DEFAULT_URL);
 
+       wispr.username = option_username;
+       wispr.password = option_password;
+       wispr.originurl = option_url;
+
        timer = g_timer_new();
 
-       request_parser = g_web_parser_new("<WISPAccessGatewayParam",
+       wispr.parser = g_web_parser_new("<WISPAccessGatewayParam",
                                                "WISPAccessGatewayParam>",
-                                               parser_callback, NULL);
-
-       g_web_parser_ref(request_parser);
-       g_web_parser_unref(request_parser);
+                                               parser_callback, &wispr);
 
-       request_id = g_web_request_get(web, option_url, web_result, NULL);
-
-       g_free(option_url);
+       wispr.request = g_web_request_get(wispr.web, option_url,
+                                                       wispr_result, &wispr);
 
-       if (request_id == 0) {
+       if (wispr.request == 0) {
                fprintf(stderr, "Failed to start request\n");
                return 1;
        }
@@ -412,9 +432,17 @@ int main(int argc, char *argv[])
 
        g_timer_destroy(timer);
 
-       g_web_unref(web);
+       if (wispr.request > 0)
+               g_web_cancel_request(wispr.web, wispr.request);
+
+       g_web_parser_unref(wispr.parser);
+       g_web_unref(wispr.web);
 
        g_main_loop_unref(main_loop);
 
+       g_free(option_username);
+       g_free(option_password);
+       g_free(option_url);
+
        return 0;
 }