blob: b5d5ba78f26475dd7d73727642d5301f8e82b92d [file] [log] [blame]
Dmitry Shmidtd5dc24e2014-03-12 14:22:04 -07001/*
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"
Hai Shalomfdcde762020-04-02 11:19:20 -070010#ifdef USE_WEBKIT2
11#include <webkit2/webkit2.h>
12#else /* USE_WEBKIT2 */
Dmitry Shmidtd5dc24e2014-03-12 14:22:04 -070013#include <webkit/webkit.h>
Hai Shalomfdcde762020-04-02 11:19:20 -070014#endif /* USE_WEBKIT2 */
Dmitry Shmidtd5dc24e2014-03-12 14:22:04 -070015
16#include "common.h"
17#include "browser.h"
18
19
20struct browser_context {
21 GtkWidget *win;
Hai Shalomfdcde762020-04-02 11:19:20 -070022 WebKitWebView *view;
Dmitry Shmidtd5dc24e2014-03-12 14:22:04 -070023 int success;
24 int progress;
25 char *hover_link;
26 char *title;
Hai Shalomfdcde762020-04-02 11:19:20 -070027 int gtk_main_started;
28 int quit_gtk_main;
Dmitry Shmidtd5dc24e2014-03-12 14:22:04 -070029};
30
31static void win_cb_destroy(GtkWidget *win, struct browser_context *ctx)
32{
33 wpa_printf(MSG_DEBUG, "BROWSER:%s", __func__);
Hai Shalomfdcde762020-04-02 11:19:20 -070034 if (ctx->gtk_main_started)
35 gtk_main_quit();
Dmitry Shmidtd5dc24e2014-03-12 14:22:04 -070036}
37
38
39static void browser_update_title(struct browser_context *ctx)
40{
41 char buf[100];
42
43 if (ctx->hover_link) {
44 gtk_window_set_title(GTK_WINDOW(ctx->win), ctx->hover_link);
45 return;
46 }
47
48 if (ctx->progress == 100) {
49 gtk_window_set_title(GTK_WINDOW(ctx->win),
50 ctx->title ? ctx->title :
51 "Hotspot 2.0 client");
52 return;
53 }
54
55 snprintf(buf, sizeof(buf), "[%d%%] %s", ctx->progress,
56 ctx->title ? ctx->title : "Hotspot 2.0 client");
57 gtk_window_set_title(GTK_WINDOW(ctx->win), buf);
58}
59
60
Hai Shalomfdcde762020-04-02 11:19:20 -070061static void process_request_starting_uri(struct browser_context *ctx,
62 const char *uri)
63{
64 int quit = 0;
65
66 if (g_str_has_prefix(uri, "osu://")) {
67 ctx->success = atoi(uri + 6);
68 quit = 1;
69 } else if (g_str_has_prefix(uri, "http://localhost:12345")) {
70 /*
71 * This is used as a special trigger to indicate that the
72 * user exchange has been completed.
73 */
74 ctx->success = 1;
75 quit = 1;
76 }
77
78 if (quit) {
79 if (ctx->gtk_main_started) {
80 gtk_main_quit();
81 ctx->gtk_main_started = 0;
82 } else {
83 ctx->quit_gtk_main = 1;
84 }
85 }
86}
87
88
89#ifdef USE_WEBKIT2
90
91static void view_cb_notify_estimated_load_progress(WebKitWebView *view,
92 GParamSpec *pspec,
93 struct browser_context *ctx)
94{
95 ctx->progress = 100 * webkit_web_view_get_estimated_load_progress(view);
96 wpa_printf(MSG_DEBUG, "BROWSER:%s progress=%d", __func__,
97 ctx->progress);
98 browser_update_title(ctx);
99}
100
101
102static void view_cb_resource_load_starting(WebKitWebView *view,
103 WebKitWebResource *res,
104 WebKitURIRequest *req,
105 struct browser_context *ctx)
106{
107 const gchar *uri = webkit_uri_request_get_uri(req);
108
109 wpa_printf(MSG_DEBUG, "BROWSER:%s uri=%s", __func__, uri);
110 process_request_starting_uri(ctx, uri);
111}
112
113
114static gboolean view_cb_decide_policy(WebKitWebView *view,
115 WebKitPolicyDecision *policy,
116 WebKitPolicyDecisionType type,
117 struct browser_context *ctx)
118{
119 wpa_printf(MSG_DEBUG, "BROWSER:%s type=%d", __func__, type);
120 switch (type) {
121 case WEBKIT_POLICY_DECISION_TYPE_RESPONSE: {
122 /* This function makes webkit send a download signal for all
123 * unknown mime types. */
124 WebKitResponsePolicyDecision *response;
125
126 response = WEBKIT_RESPONSE_POLICY_DECISION(policy);
127 if (!webkit_response_policy_decision_is_mime_type_supported(
128 response)) {
129 webkit_policy_decision_download(policy);
130 return TRUE;
131 }
132 break;
133 }
134 case WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION: {
135 WebKitNavigationPolicyDecision *d;
136 WebKitNavigationAction *a;
137 WebKitURIRequest *req;
138 const gchar *uri;
139
140 d = WEBKIT_NAVIGATION_POLICY_DECISION(policy);
141 a = webkit_navigation_policy_decision_get_navigation_action(d);
142 req = webkit_navigation_action_get_request(a);
143 uri = webkit_uri_request_get_uri(req);
144 wpa_printf(MSG_DEBUG, "BROWSER:%s navigation action: uri=%s",
145 __func__, uri);
146 process_request_starting_uri(ctx, uri);
147 break;
148 }
149 default:
150 break;
151 }
152
153 return FALSE;
154}
155
156
157static void view_cb_mouse_target_changed(WebKitWebView *view,
158 WebKitHitTestResult *h,
159 guint modifiers,
160 struct browser_context *ctx)
161{
162 WebKitHitTestResultContext hc = webkit_hit_test_result_get_context(h);
163 const char *uri = NULL;
164
165 if (hc & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK)
166 uri = webkit_hit_test_result_get_link_uri(h);
167 else if (hc & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE)
168 uri = webkit_hit_test_result_get_image_uri(h);
169 else if (hc & WEBKIT_HIT_TEST_RESULT_CONTEXT_MEDIA)
170 uri = webkit_hit_test_result_get_media_uri(h);
171
172 wpa_printf(MSG_DEBUG, "BROWSER:%s uri=%s", __func__, uri ? uri : "N/A");
173 os_free(ctx->hover_link);
174 if (uri)
175 ctx->hover_link = os_strdup(uri);
176 else
177 ctx->hover_link = NULL;
178
179 browser_update_title(ctx);
180}
181
182
183static void view_cb_notify_title(WebKitWebView *view, GParamSpec *ps,
184 struct browser_context *ctx)
185{
186 const char *title;
187
188 title = webkit_web_view_get_title(ctx->view);
189 wpa_printf(MSG_DEBUG, "BROWSER:%s title=%s", __func__, title);
190 os_free(ctx->title);
191 ctx->title = os_strdup(title);
192 browser_update_title(ctx);
193}
194
195#else /* USE_WEBKIT2 */
196
Dmitry Shmidtd5dc24e2014-03-12 14:22:04 -0700197static void view_cb_notify_progress(WebKitWebView *view, GParamSpec *pspec,
198 struct browser_context *ctx)
199{
200 ctx->progress = 100 * webkit_web_view_get_progress(view);
201 wpa_printf(MSG_DEBUG, "BROWSER:%s progress=%d", __func__,
202 ctx->progress);
203 browser_update_title(ctx);
204}
205
206
207static void view_cb_notify_load_status(WebKitWebView *view, GParamSpec *pspec,
208 struct browser_context *ctx)
209{
210 int status = webkit_web_view_get_load_status(view);
211 wpa_printf(MSG_DEBUG, "BROWSER:%s load-status=%d uri=%s",
212 __func__, status, webkit_web_view_get_uri(view));
Hai Shalomfdcde762020-04-02 11:19:20 -0700213 if (ctx->quit_gtk_main) {
214 gtk_main_quit();
215 ctx->gtk_main_started = 0;
216 }
Dmitry Shmidtd5dc24e2014-03-12 14:22:04 -0700217}
218
219
220static void view_cb_resource_request_starting(WebKitWebView *view,
221 WebKitWebFrame *frame,
222 WebKitWebResource *res,
223 WebKitNetworkRequest *req,
224 WebKitNetworkResponse *resp,
225 struct browser_context *ctx)
226{
227 const gchar *uri = webkit_network_request_get_uri(req);
Hai Shalomfdcde762020-04-02 11:19:20 -0700228
Dmitry Shmidtd5dc24e2014-03-12 14:22:04 -0700229 wpa_printf(MSG_DEBUG, "BROWSER:%s uri=%s", __func__, uri);
230 if (g_str_has_suffix(uri, "/favicon.ico"))
231 webkit_network_request_set_uri(req, "about:blank");
Hai Shalomfdcde762020-04-02 11:19:20 -0700232
233 process_request_starting_uri(ctx, uri);
Dmitry Shmidtd5dc24e2014-03-12 14:22:04 -0700234}
235
236
237static gboolean view_cb_mime_type_policy_decision(
238 WebKitWebView *view, WebKitWebFrame *frame, WebKitNetworkRequest *req,
239 gchar *mime, WebKitWebPolicyDecision *policy,
240 struct browser_context *ctx)
241{
242 wpa_printf(MSG_DEBUG, "BROWSER:%s mime=%s", __func__, mime);
243
244 if (!webkit_web_view_can_show_mime_type(view, mime)) {
245 webkit_web_policy_decision_download(policy);
246 return TRUE;
247 }
248
249 return FALSE;
250}
251
252
253static gboolean view_cb_download_requested(WebKitWebView *view,
254 WebKitDownload *dl,
255 struct browser_context *ctx)
256{
257 const gchar *uri;
258 uri = webkit_download_get_uri(dl);
259 wpa_printf(MSG_DEBUG, "BROWSER:%s uri=%s", __func__, uri);
260 return FALSE;
261}
262
263
264static void view_cb_hovering_over_link(WebKitWebView *view, gchar *title,
265 gchar *uri, struct browser_context *ctx)
266{
267 wpa_printf(MSG_DEBUG, "BROWSER:%s title=%s uri=%s", __func__, title,
268 uri);
269 os_free(ctx->hover_link);
270 if (uri)
271 ctx->hover_link = os_strdup(uri);
272 else
273 ctx->hover_link = NULL;
274
275 browser_update_title(ctx);
276}
277
278
279static void view_cb_title_changed(WebKitWebView *view, WebKitWebFrame *frame,
280 const char *title,
281 struct browser_context *ctx)
282{
283 wpa_printf(MSG_DEBUG, "BROWSER:%s title=%s", __func__, title);
284 os_free(ctx->title);
285 ctx->title = os_strdup(title);
286 browser_update_title(ctx);
287}
288
Hai Shalomfdcde762020-04-02 11:19:20 -0700289#endif /* USE_WEBKIT2 */
Dmitry Shmidtd5dc24e2014-03-12 14:22:04 -0700290
Hai Shalomfdcde762020-04-02 11:19:20 -0700291
292int hs20_web_browser(const char *url, int ignore_tls)
Dmitry Shmidtd5dc24e2014-03-12 14:22:04 -0700293{
294 GtkWidget *scroll;
Dmitry Shmidtd5dc24e2014-03-12 14:22:04 -0700295 WebKitWebView *view;
Hai Shalomfdcde762020-04-02 11:19:20 -0700296#ifdef USE_WEBKIT2
297 WebKitSettings *settings;
298#else /* USE_WEBKIT2 */
Dmitry Shmidtd5dc24e2014-03-12 14:22:04 -0700299 WebKitWebSettings *settings;
Hai Shalomfdcde762020-04-02 11:19:20 -0700300 SoupSession *s;
301#endif /* USE_WEBKIT2 */
Dmitry Shmidtd5dc24e2014-03-12 14:22:04 -0700302 struct browser_context ctx;
303
304 memset(&ctx, 0, sizeof(ctx));
305 if (!gtk_init_check(NULL, NULL))
306 return -1;
307
Hai Shalomfdcde762020-04-02 11:19:20 -0700308#ifndef USE_WEBKIT2
Dmitry Shmidtd5dc24e2014-03-12 14:22:04 -0700309 s = webkit_get_default_session();
310 g_object_set(G_OBJECT(s), "ssl-ca-file",
311 "/etc/ssl/certs/ca-certificates.crt", NULL);
Hai Shalomfdcde762020-04-02 11:19:20 -0700312 if (ignore_tls)
313 g_object_set(G_OBJECT(s), "ssl-strict", FALSE, NULL);
314#endif /* USE_WEBKIT2 */
Dmitry Shmidtd5dc24e2014-03-12 14:22:04 -0700315
316 ctx.win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Hai Shalom74f70d42019-02-11 14:42:39 -0800317 gtk_window_set_role(GTK_WINDOW(ctx.win), "Hotspot 2.0 client");
Dmitry Shmidtd5dc24e2014-03-12 14:22:04 -0700318 gtk_window_set_default_size(GTK_WINDOW(ctx.win), 800, 600);
319
320 scroll = gtk_scrolled_window_new(NULL, NULL);
321 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
322 GTK_POLICY_NEVER, GTK_POLICY_NEVER);
323
324 g_signal_connect(G_OBJECT(ctx.win), "destroy",
325 G_CALLBACK(win_cb_destroy), &ctx);
326
327 view = WEBKIT_WEB_VIEW(webkit_web_view_new());
Hai Shalomfdcde762020-04-02 11:19:20 -0700328 ctx.view = view;
329#ifdef USE_WEBKIT2
330 g_signal_connect(G_OBJECT(view), "notify::estimated-load-progress",
331 G_CALLBACK(view_cb_notify_estimated_load_progress),
332 &ctx);
333 g_signal_connect(G_OBJECT(view), "resource-load-started",
334 G_CALLBACK(view_cb_resource_load_starting), &ctx);
335 g_signal_connect(G_OBJECT(view), "decide-policy",
336 G_CALLBACK(view_cb_decide_policy), &ctx);
337 g_signal_connect(G_OBJECT(view), "mouse-target-changed",
338 G_CALLBACK(view_cb_mouse_target_changed), &ctx);
339 g_signal_connect(G_OBJECT(view), "notify::title",
340 G_CALLBACK(view_cb_notify_title), &ctx);
341#else /* USE_WEBKIT2 */
Dmitry Shmidtd5dc24e2014-03-12 14:22:04 -0700342 g_signal_connect(G_OBJECT(view), "notify::load-status",
343 G_CALLBACK(view_cb_notify_load_status), &ctx);
Hai Shalomfdcde762020-04-02 11:19:20 -0700344 g_signal_connect(G_OBJECT(view), "notify::progress",
345 G_CALLBACK(view_cb_notify_progress), &ctx);
Dmitry Shmidtd5dc24e2014-03-12 14:22:04 -0700346 g_signal_connect(G_OBJECT(view), "resource-request-starting",
347 G_CALLBACK(view_cb_resource_request_starting), &ctx);
348 g_signal_connect(G_OBJECT(view), "mime-type-policy-decision-requested",
349 G_CALLBACK(view_cb_mime_type_policy_decision), &ctx);
350 g_signal_connect(G_OBJECT(view), "download-requested",
351 G_CALLBACK(view_cb_download_requested), &ctx);
352 g_signal_connect(G_OBJECT(view), "hovering-over-link",
353 G_CALLBACK(view_cb_hovering_over_link), &ctx);
354 g_signal_connect(G_OBJECT(view), "title-changed",
355 G_CALLBACK(view_cb_title_changed), &ctx);
Hai Shalomfdcde762020-04-02 11:19:20 -0700356#endif /* USE_WEBKIT2 */
Dmitry Shmidtd5dc24e2014-03-12 14:22:04 -0700357
358 gtk_container_add(GTK_CONTAINER(scroll), GTK_WIDGET(view));
359 gtk_container_add(GTK_CONTAINER(ctx.win), GTK_WIDGET(scroll));
360
361 gtk_widget_grab_focus(GTK_WIDGET(view));
362 gtk_widget_show_all(ctx.win);
363
364 settings = webkit_web_view_get_settings(view);
365 g_object_set(G_OBJECT(settings), "user-agent",
366 "Mozilla/5.0 (X11; U; Unix; en-US) "
367 "AppleWebKit/537.15 (KHTML, like Gecko) "
368 "hs20-client/1.0", NULL);
369 g_object_set(G_OBJECT(settings), "auto-load-images", TRUE, NULL);
370
Hai Shalomfdcde762020-04-02 11:19:20 -0700371#ifdef USE_WEBKIT2
372 if (ignore_tls) {
Sunil Ravi036cec52023-03-29 11:35:17 -0700373#if WEBKIT_CHECK_VERSION(2, 32, 0)
374 WebKitWebContext *wkctx;
375 WebKitWebsiteDataManager *wkmgr;
376
377 wkctx = webkit_web_context_get_default();
378 wkmgr = webkit_web_context_get_website_data_manager(wkctx);
379 webkit_website_data_manager_set_tls_errors_policy(
380 wkmgr, WEBKIT_TLS_ERRORS_POLICY_IGNORE);
381#else
Hai Shalomfdcde762020-04-02 11:19:20 -0700382 WebKitWebContext *wkctx;
383
384 wkctx = webkit_web_context_get_default();
385 webkit_web_context_set_tls_errors_policy(
386 wkctx, WEBKIT_TLS_ERRORS_POLICY_IGNORE);
Sunil Ravi036cec52023-03-29 11:35:17 -0700387#endif
Hai Shalomfdcde762020-04-02 11:19:20 -0700388 }
389#endif /* USE_WEBKIT2 */
390
Dmitry Shmidtd5dc24e2014-03-12 14:22:04 -0700391 webkit_web_view_load_uri(view, url);
392
Hai Shalomfdcde762020-04-02 11:19:20 -0700393 ctx.gtk_main_started = 1;
Dmitry Shmidtd5dc24e2014-03-12 14:22:04 -0700394 gtk_main();
395 gtk_widget_destroy(ctx.win);
396 while (gtk_events_pending())
397 gtk_main_iteration();
398
399 free(ctx.hover_link);
400 free(ctx.title);
401 return ctx.success;
402}