Home | History | Annotate | Download | only in utils
      1 /*
      2  * Hotspot 2.0 client - Web browser using WebKit
      3  * Copyright (c) 2013, Qualcomm Atheros, Inc.
      4  *
      5  * This software may be distributed under the terms of the BSD license.
      6  * See README for more details.
      7  */
      8 
      9 #include "includes.h"
     10 #include <webkit/webkit.h>
     11 
     12 #include "common.h"
     13 #include "browser.h"
     14 
     15 
     16 struct browser_context {
     17 	GtkWidget *win;
     18 	int success;
     19 	int progress;
     20 	char *hover_link;
     21 	char *title;
     22 };
     23 
     24 static void win_cb_destroy(GtkWidget *win, struct browser_context *ctx)
     25 {
     26 	wpa_printf(MSG_DEBUG, "BROWSER:%s", __func__);
     27 	gtk_main_quit();
     28 }
     29 
     30 
     31 static void browser_update_title(struct browser_context *ctx)
     32 {
     33 	char buf[100];
     34 
     35 	if (ctx->hover_link) {
     36 		gtk_window_set_title(GTK_WINDOW(ctx->win), ctx->hover_link);
     37 		return;
     38 	}
     39 
     40 	if (ctx->progress == 100) {
     41 		gtk_window_set_title(GTK_WINDOW(ctx->win),
     42 				     ctx->title ? ctx->title :
     43 				     "Hotspot 2.0 client");
     44 		return;
     45 	}
     46 
     47 	snprintf(buf, sizeof(buf), "[%d%%] %s", ctx->progress,
     48 		 ctx->title ? ctx->title : "Hotspot 2.0 client");
     49 	gtk_window_set_title(GTK_WINDOW(ctx->win), buf);
     50 }
     51 
     52 
     53 static void view_cb_notify_progress(WebKitWebView *view, GParamSpec *pspec,
     54 				    struct browser_context *ctx)
     55 {
     56 	ctx->progress = 100 * webkit_web_view_get_progress(view);
     57 	wpa_printf(MSG_DEBUG, "BROWSER:%s progress=%d", __func__,
     58 		   ctx->progress);
     59 	browser_update_title(ctx);
     60 }
     61 
     62 
     63 static void view_cb_notify_load_status(WebKitWebView *view, GParamSpec *pspec,
     64 				       struct browser_context *ctx)
     65 {
     66 	int status = webkit_web_view_get_load_status(view);
     67 	wpa_printf(MSG_DEBUG, "BROWSER:%s load-status=%d uri=%s",
     68 		   __func__, status, webkit_web_view_get_uri(view));
     69 }
     70 
     71 
     72 static void view_cb_resource_request_starting(WebKitWebView *view,
     73 					      WebKitWebFrame *frame,
     74 					      WebKitWebResource *res,
     75 					      WebKitNetworkRequest *req,
     76 					      WebKitNetworkResponse *resp,
     77 					      struct browser_context *ctx)
     78 {
     79 	const gchar *uri = webkit_network_request_get_uri(req);
     80 	wpa_printf(MSG_DEBUG, "BROWSER:%s uri=%s", __func__, uri);
     81 	if (g_str_has_suffix(uri, "/favicon.ico"))
     82 		webkit_network_request_set_uri(req, "about:blank");
     83 	if (g_str_has_prefix(uri, "osu://")) {
     84 		ctx->success = atoi(uri + 6);
     85 		gtk_main_quit();
     86 	}
     87 	if (g_str_has_prefix(uri, "http://localhost:12345")) {
     88 		/*
     89 		 * This is used as a special trigger to indicate that the
     90 		 * user exchange has been completed.
     91 		 */
     92 		ctx->success = 1;
     93 		gtk_main_quit();
     94 	}
     95 }
     96 
     97 
     98 static gboolean view_cb_mime_type_policy_decision(
     99 	WebKitWebView *view, WebKitWebFrame *frame, WebKitNetworkRequest *req,
    100 	gchar *mime, WebKitWebPolicyDecision *policy,
    101 	struct browser_context *ctx)
    102 {
    103 	wpa_printf(MSG_DEBUG, "BROWSER:%s mime=%s", __func__, mime);
    104 
    105 	if (!webkit_web_view_can_show_mime_type(view, mime)) {
    106 		webkit_web_policy_decision_download(policy);
    107 		return TRUE;
    108 	}
    109 
    110 	return FALSE;
    111 }
    112 
    113 
    114 static gboolean view_cb_download_requested(WebKitWebView *view,
    115 					   WebKitDownload *dl,
    116 					   struct browser_context *ctx)
    117 {
    118 	const gchar *uri;
    119 	uri = webkit_download_get_uri(dl);
    120 	wpa_printf(MSG_DEBUG, "BROWSER:%s uri=%s", __func__, uri);
    121 	return FALSE;
    122 }
    123 
    124 
    125 static void view_cb_hovering_over_link(WebKitWebView *view, gchar *title,
    126 				       gchar *uri, struct browser_context *ctx)
    127 {
    128 	wpa_printf(MSG_DEBUG, "BROWSER:%s title=%s uri=%s", __func__, title,
    129 		   uri);
    130 	os_free(ctx->hover_link);
    131 	if (uri)
    132 		ctx->hover_link = os_strdup(uri);
    133 	else
    134 		ctx->hover_link = NULL;
    135 
    136 	browser_update_title(ctx);
    137 }
    138 
    139 
    140 static void view_cb_title_changed(WebKitWebView *view, WebKitWebFrame *frame,
    141 				  const char *title,
    142 				  struct browser_context *ctx)
    143 {
    144 	wpa_printf(MSG_DEBUG, "BROWSER:%s title=%s", __func__, title);
    145 	os_free(ctx->title);
    146 	ctx->title = os_strdup(title);
    147 	browser_update_title(ctx);
    148 }
    149 
    150 
    151 int hs20_web_browser(const char *url)
    152 {
    153 	GtkWidget *scroll;
    154 	SoupSession *s;
    155 	WebKitWebView *view;
    156 	WebKitWebSettings *settings;
    157 	struct browser_context ctx;
    158 
    159 	memset(&ctx, 0, sizeof(ctx));
    160 	if (!gtk_init_check(NULL, NULL))
    161 		return -1;
    162 
    163 	s = webkit_get_default_session();
    164 	g_object_set(G_OBJECT(s), "ssl-ca-file",
    165 		     "/etc/ssl/certs/ca-certificates.crt", NULL);
    166 	g_object_set(G_OBJECT(s), "ssl-strict", FALSE, NULL);
    167 
    168 	ctx.win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    169 	gtk_window_set_wmclass(GTK_WINDOW(ctx.win), "Hotspot 2.0 client",
    170 			       "Hotspot 2.0 client");
    171 	gtk_window_set_default_size(GTK_WINDOW(ctx.win), 800, 600);
    172 
    173 	scroll = gtk_scrolled_window_new(NULL, NULL);
    174 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
    175 				       GTK_POLICY_NEVER, GTK_POLICY_NEVER);
    176 
    177 	g_signal_connect(G_OBJECT(ctx.win), "destroy",
    178 			 G_CALLBACK(win_cb_destroy), &ctx);
    179 
    180 	view = WEBKIT_WEB_VIEW(webkit_web_view_new());
    181 	g_signal_connect(G_OBJECT(view), "notify::progress",
    182 			 G_CALLBACK(view_cb_notify_progress), &ctx);
    183 	g_signal_connect(G_OBJECT(view), "notify::load-status",
    184 			 G_CALLBACK(view_cb_notify_load_status), &ctx);
    185 	g_signal_connect(G_OBJECT(view), "resource-request-starting",
    186 			 G_CALLBACK(view_cb_resource_request_starting), &ctx);
    187 	g_signal_connect(G_OBJECT(view), "mime-type-policy-decision-requested",
    188 			 G_CALLBACK(view_cb_mime_type_policy_decision), &ctx);
    189 	g_signal_connect(G_OBJECT(view), "download-requested",
    190 			 G_CALLBACK(view_cb_download_requested), &ctx);
    191 	g_signal_connect(G_OBJECT(view), "hovering-over-link",
    192 			 G_CALLBACK(view_cb_hovering_over_link), &ctx);
    193 	g_signal_connect(G_OBJECT(view), "title-changed",
    194 			 G_CALLBACK(view_cb_title_changed), &ctx);
    195 
    196 	gtk_container_add(GTK_CONTAINER(scroll), GTK_WIDGET(view));
    197 	gtk_container_add(GTK_CONTAINER(ctx.win), GTK_WIDGET(scroll));
    198 
    199 	gtk_widget_grab_focus(GTK_WIDGET(view));
    200 	gtk_widget_show_all(ctx.win);
    201 
    202 	settings = webkit_web_view_get_settings(view);
    203 	g_object_set(G_OBJECT(settings), "user-agent",
    204 		     "Mozilla/5.0 (X11; U; Unix; en-US) "
    205 		     "AppleWebKit/537.15 (KHTML, like Gecko) "
    206 		     "hs20-client/1.0", NULL);
    207 	g_object_set(G_OBJECT(settings), "auto-load-images", TRUE, NULL);
    208 
    209 	webkit_web_view_load_uri(view, url);
    210 
    211 	gtk_main();
    212 	gtk_widget_destroy(ctx.win);
    213 	while (gtk_events_pending())
    214 		gtk_main_iteration();
    215 
    216 	free(ctx.hover_link);
    217 	free(ctx.title);
    218 	return ctx.success;
    219 }
    220