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