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 
win_cb_destroy(GtkWidget * win,struct browser_context * ctx)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 
browser_update_title(struct browser_context * ctx)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 
view_cb_notify_progress(WebKitWebView * view,GParamSpec * pspec,struct browser_context * ctx)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 
view_cb_notify_load_status(WebKitWebView * view,GParamSpec * pspec,struct browser_context * ctx)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 
view_cb_resource_request_starting(WebKitWebView * view,WebKitWebFrame * frame,WebKitWebResource * res,WebKitNetworkRequest * req,WebKitNetworkResponse * resp,struct browser_context * ctx)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 
view_cb_mime_type_policy_decision(WebKitWebView * view,WebKitWebFrame * frame,WebKitNetworkRequest * req,gchar * mime,WebKitWebPolicyDecision * policy,struct browser_context * ctx)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 
view_cb_download_requested(WebKitWebView * view,WebKitDownload * dl,struct browser_context * ctx)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 
view_cb_hovering_over_link(WebKitWebView * view,gchar * title,gchar * uri,struct browser_context * ctx)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 
view_cb_title_changed(WebKitWebView * view,WebKitWebFrame * frame,const char * title,struct browser_context * ctx)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 
hs20_web_browser(const char * url)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_role(GTK_WINDOW(ctx.win), "Hotspot 2.0 client");
170 	gtk_window_set_default_size(GTK_WINDOW(ctx.win), 800, 600);
171 
172 	scroll = gtk_scrolled_window_new(NULL, NULL);
173 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
174 				       GTK_POLICY_NEVER, GTK_POLICY_NEVER);
175 
176 	g_signal_connect(G_OBJECT(ctx.win), "destroy",
177 			 G_CALLBACK(win_cb_destroy), &ctx);
178 
179 	view = WEBKIT_WEB_VIEW(webkit_web_view_new());
180 	g_signal_connect(G_OBJECT(view), "notify::progress",
181 			 G_CALLBACK(view_cb_notify_progress), &ctx);
182 	g_signal_connect(G_OBJECT(view), "notify::load-status",
183 			 G_CALLBACK(view_cb_notify_load_status), &ctx);
184 	g_signal_connect(G_OBJECT(view), "resource-request-starting",
185 			 G_CALLBACK(view_cb_resource_request_starting), &ctx);
186 	g_signal_connect(G_OBJECT(view), "mime-type-policy-decision-requested",
187 			 G_CALLBACK(view_cb_mime_type_policy_decision), &ctx);
188 	g_signal_connect(G_OBJECT(view), "download-requested",
189 			 G_CALLBACK(view_cb_download_requested), &ctx);
190 	g_signal_connect(G_OBJECT(view), "hovering-over-link",
191 			 G_CALLBACK(view_cb_hovering_over_link), &ctx);
192 	g_signal_connect(G_OBJECT(view), "title-changed",
193 			 G_CALLBACK(view_cb_title_changed), &ctx);
194 
195 	gtk_container_add(GTK_CONTAINER(scroll), GTK_WIDGET(view));
196 	gtk_container_add(GTK_CONTAINER(ctx.win), GTK_WIDGET(scroll));
197 
198 	gtk_widget_grab_focus(GTK_WIDGET(view));
199 	gtk_widget_show_all(ctx.win);
200 
201 	settings = webkit_web_view_get_settings(view);
202 	g_object_set(G_OBJECT(settings), "user-agent",
203 		     "Mozilla/5.0 (X11; U; Unix; en-US) "
204 		     "AppleWebKit/537.15 (KHTML, like Gecko) "
205 		     "hs20-client/1.0", NULL);
206 	g_object_set(G_OBJECT(settings), "auto-load-images", TRUE, NULL);
207 
208 	webkit_web_view_load_uri(view, url);
209 
210 	gtk_main();
211 	gtk_widget_destroy(ctx.win);
212 	while (gtk_events_pending())
213 		gtk_main_iteration();
214 
215 	free(ctx.hover_link);
216 	free(ctx.title);
217 	return ctx.success;
218 }
219