]>
Commit | Line | Data |
---|---|---|
d0b79d74 JM |
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" | |
b4b1b122 BG |
10 | #ifdef USE_WEBKIT2 |
11 | #include <webkit2/webkit2.h> | |
12 | #else /* USE_WEBKIT2 */ | |
d0b79d74 | 13 | #include <webkit/webkit.h> |
b4b1b122 | 14 | #endif /* USE_WEBKIT2 */ |
d0b79d74 JM |
15 | |
16 | #include "common.h" | |
17 | #include "browser.h" | |
18 | ||
19 | ||
20 | struct browser_context { | |
21 | GtkWidget *win; | |
c0c4685d | 22 | WebKitWebView *view; |
d0b79d74 JM |
23 | int success; |
24 | int progress; | |
25 | char *hover_link; | |
26 | char *title; | |
7de8bd50 JM |
27 | int gtk_main_started; |
28 | int quit_gtk_main; | |
d0b79d74 JM |
29 | }; |
30 | ||
31 | static void win_cb_destroy(GtkWidget *win, struct browser_context *ctx) | |
32 | { | |
33 | wpa_printf(MSG_DEBUG, "BROWSER:%s", __func__); | |
7de8bd50 JM |
34 | if (ctx->gtk_main_started) |
35 | gtk_main_quit(); | |
d0b79d74 JM |
36 | } |
37 | ||
38 | ||
39 | static 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 | ||
b4b1b122 | 61 | #ifdef USE_WEBKIT2 |
9ea9d18d JM |
62 | static void view_cb_notify_estimated_load_progress(WebKitWebView *view, |
63 | GParamSpec *pspec, | |
64 | struct browser_context *ctx) | |
65 | { | |
b4b1b122 | 66 | ctx->progress = 100 * webkit_web_view_get_estimated_load_progress(view); |
9ea9d18d JM |
67 | wpa_printf(MSG_DEBUG, "BROWSER:%s progress=%d", __func__, |
68 | ctx->progress); | |
69 | browser_update_title(ctx); | |
70 | } | |
b4b1b122 | 71 | #else /* USE_WEBKIT2 */ |
9ea9d18d JM |
72 | static void view_cb_notify_progress(WebKitWebView *view, GParamSpec *pspec, |
73 | struct browser_context *ctx) | |
74 | { | |
d0b79d74 JM |
75 | ctx->progress = 100 * webkit_web_view_get_progress(view); |
76 | wpa_printf(MSG_DEBUG, "BROWSER:%s progress=%d", __func__, | |
77 | ctx->progress); | |
78 | browser_update_title(ctx); | |
79 | } | |
9ea9d18d | 80 | #endif /* USE_WEBKIT2 */ |
d0b79d74 JM |
81 | |
82 | ||
ae07bc46 | 83 | #ifndef USE_WEBKIT2 |
d0b79d74 JM |
84 | static void view_cb_notify_load_status(WebKitWebView *view, GParamSpec *pspec, |
85 | struct browser_context *ctx) | |
86 | { | |
87 | int status = webkit_web_view_get_load_status(view); | |
88 | wpa_printf(MSG_DEBUG, "BROWSER:%s load-status=%d uri=%s", | |
89 | __func__, status, webkit_web_view_get_uri(view)); | |
7de8bd50 JM |
90 | if (ctx->quit_gtk_main) { |
91 | gtk_main_quit(); | |
92 | ctx->gtk_main_started = 0; | |
93 | } | |
d0b79d74 | 94 | } |
ae07bc46 | 95 | #endif /* USE_WEBKIT2 */ |
d0b79d74 JM |
96 | |
97 | ||
02ed737e JM |
98 | static void process_request_starting_uri(struct browser_context *ctx, |
99 | const char *uri) | |
d0b79d74 | 100 | { |
7de8bd50 JM |
101 | int quit = 0; |
102 | ||
d0b79d74 JM |
103 | if (g_str_has_prefix(uri, "osu://")) { |
104 | ctx->success = atoi(uri + 6); | |
7de8bd50 JM |
105 | quit = 1; |
106 | } else if (g_str_has_prefix(uri, "http://localhost:12345")) { | |
d0b79d74 JM |
107 | /* |
108 | * This is used as a special trigger to indicate that the | |
109 | * user exchange has been completed. | |
110 | */ | |
111 | ctx->success = 1; | |
7de8bd50 JM |
112 | quit = 1; |
113 | } | |
114 | ||
115 | if (quit) { | |
116 | if (ctx->gtk_main_started) { | |
117 | gtk_main_quit(); | |
118 | ctx->gtk_main_started = 0; | |
119 | } else { | |
120 | ctx->quit_gtk_main = 1; | |
121 | } | |
d0b79d74 JM |
122 | } |
123 | } | |
124 | ||
125 | ||
02ed737e JM |
126 | #ifdef USE_WEBKIT2 |
127 | static void view_cb_resource_load_starting(WebKitWebView *view, | |
128 | WebKitWebResource *res, | |
129 | WebKitURIRequest *req, | |
130 | struct browser_context *ctx) | |
131 | { | |
132 | const gchar *uri = webkit_uri_request_get_uri(req); | |
133 | ||
134 | wpa_printf(MSG_DEBUG, "BROWSER:%s uri=%s", __func__, uri); | |
135 | if (g_str_has_suffix(uri, "/favicon.ico")) | |
136 | webkit_uri_request_set_uri(req, "about:blank"); | |
137 | ||
138 | process_request_starting_uri(ctx, uri); | |
139 | } | |
140 | #else /* USE_WEBKIT2 */ | |
141 | static void view_cb_resource_request_starting(WebKitWebView *view, | |
142 | WebKitWebFrame *frame, | |
143 | WebKitWebResource *res, | |
144 | WebKitNetworkRequest *req, | |
145 | WebKitNetworkResponse *resp, | |
146 | struct browser_context *ctx) | |
147 | { | |
148 | const gchar *uri = webkit_network_request_get_uri(req); | |
149 | ||
150 | wpa_printf(MSG_DEBUG, "BROWSER:%s uri=%s", __func__, uri); | |
151 | if (g_str_has_suffix(uri, "/favicon.ico")) | |
152 | webkit_network_request_set_uri(req, "about:blank"); | |
153 | ||
154 | process_request_starting_uri(ctx, uri); | |
155 | } | |
156 | #endif /* USE_WEBKIT2 */ | |
157 | ||
158 | ||
d0b79d74 | 159 | static gboolean view_cb_mime_type_policy_decision( |
b4b1b122 BG |
160 | WebKitWebView *view, |
161 | #ifndef USE_WEBKIT2 | |
162 | WebKitWebFrame *frame, WebKitNetworkRequest *req, | |
d0b79d74 | 163 | gchar *mime, WebKitWebPolicyDecision *policy, |
b4b1b122 BG |
164 | #else /* USE_WEBKIT2 */ |
165 | WebKitPolicyDecision *policy, | |
166 | WebKitPolicyDecisionType type, | |
167 | #endif /* USE_WEBKIT2 */ | |
d0b79d74 JM |
168 | struct browser_context *ctx) |
169 | { | |
b4b1b122 BG |
170 | #ifdef USE_WEBKIT2 |
171 | wpa_printf(MSG_DEBUG, "BROWSER:%s type=%d", __func__, type); | |
172 | switch (type) { | |
173 | case WEBKIT_POLICY_DECISION_TYPE_RESPONSE: { | |
174 | /* This function makes webkit send a download signal for all | |
175 | * unknown mime types. */ | |
176 | WebKitResponsePolicyDecision *response; | |
177 | ||
178 | response = WEBKIT_RESPONSE_POLICY_DECISION(policy); | |
179 | if (!webkit_response_policy_decision_is_mime_type_supported( | |
180 | response)) { | |
181 | webkit_policy_decision_download(policy); | |
182 | return TRUE; | |
183 | } | |
184 | break; | |
185 | } | |
186 | default: | |
187 | break; | |
188 | } | |
189 | #else /* USE_WEBKIT2 */ | |
d0b79d74 JM |
190 | wpa_printf(MSG_DEBUG, "BROWSER:%s mime=%s", __func__, mime); |
191 | ||
192 | if (!webkit_web_view_can_show_mime_type(view, mime)) { | |
193 | webkit_web_policy_decision_download(policy); | |
194 | return TRUE; | |
195 | } | |
b4b1b122 | 196 | #endif /* USE_WEBKIT2 */ |
d0b79d74 JM |
197 | |
198 | return FALSE; | |
199 | } | |
200 | ||
201 | ||
b4b1b122 | 202 | #ifndef USE_WEBKIT2 |
d0b79d74 JM |
203 | static gboolean view_cb_download_requested(WebKitWebView *view, |
204 | WebKitDownload *dl, | |
205 | struct browser_context *ctx) | |
206 | { | |
207 | const gchar *uri; | |
208 | uri = webkit_download_get_uri(dl); | |
209 | wpa_printf(MSG_DEBUG, "BROWSER:%s uri=%s", __func__, uri); | |
210 | return FALSE; | |
211 | } | |
b4b1b122 | 212 | #endif /* USE_WEBKIT2 */ |
d0b79d74 JM |
213 | |
214 | ||
ffeafc08 JM |
215 | #ifdef USE_WEBKIT2 |
216 | static void view_cb_mouse_target_changed(WebKitWebView *view, | |
217 | WebKitHitTestResult *h, | |
218 | guint modifiers, | |
219 | struct browser_context *ctx) | |
220 | { | |
221 | WebKitHitTestResultContext hc = webkit_hit_test_result_get_context(h); | |
222 | const char *uri = NULL; | |
223 | ||
224 | if (hc & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK) | |
225 | uri = webkit_hit_test_result_get_link_uri(h); | |
226 | else if (hc & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE) | |
227 | uri = webkit_hit_test_result_get_image_uri(h); | |
228 | else if (hc & WEBKIT_HIT_TEST_RESULT_CONTEXT_MEDIA) | |
229 | uri = webkit_hit_test_result_get_media_uri(h); | |
230 | ||
231 | wpa_printf(MSG_DEBUG, "BROWSER:%s uri=%s", __func__, uri ? uri : "N/A"); | |
232 | os_free(ctx->hover_link); | |
233 | if (uri) | |
234 | ctx->hover_link = os_strdup(uri); | |
235 | else | |
236 | ctx->hover_link = NULL; | |
237 | ||
238 | browser_update_title(ctx); | |
239 | } | |
240 | #else /* USE_WEBKIT2 */ | |
d0b79d74 JM |
241 | static void view_cb_hovering_over_link(WebKitWebView *view, gchar *title, |
242 | gchar *uri, struct browser_context *ctx) | |
243 | { | |
244 | wpa_printf(MSG_DEBUG, "BROWSER:%s title=%s uri=%s", __func__, title, | |
245 | uri); | |
246 | os_free(ctx->hover_link); | |
247 | if (uri) | |
248 | ctx->hover_link = os_strdup(uri); | |
249 | else | |
250 | ctx->hover_link = NULL; | |
251 | ||
252 | browser_update_title(ctx); | |
253 | } | |
ffeafc08 | 254 | #endif /* USE_WEBKIT2 */ |
d0b79d74 JM |
255 | |
256 | ||
c0c4685d JM |
257 | #ifdef USE_WEBKIT2 |
258 | static void view_cb_notify_title(WebKitWebView *view, GParamSpec *ps, | |
259 | struct browser_context *ctx) | |
260 | { | |
261 | const char *title; | |
262 | ||
263 | title = webkit_web_view_get_title(ctx->view); | |
264 | wpa_printf(MSG_DEBUG, "BROWSER:%s title=%s", __func__, title); | |
265 | os_free(ctx->title); | |
266 | ctx->title = os_strdup(title); | |
267 | browser_update_title(ctx); | |
268 | } | |
269 | #else /* USE_WEBKIT2 */ | |
d0b79d74 JM |
270 | static void view_cb_title_changed(WebKitWebView *view, WebKitWebFrame *frame, |
271 | const char *title, | |
272 | struct browser_context *ctx) | |
273 | { | |
274 | wpa_printf(MSG_DEBUG, "BROWSER:%s title=%s", __func__, title); | |
275 | os_free(ctx->title); | |
276 | ctx->title = os_strdup(title); | |
277 | browser_update_title(ctx); | |
278 | } | |
b4b1b122 | 279 | #endif /* USE_WEBKIT2 */ |
d0b79d74 JM |
280 | |
281 | ||
61bf9819 | 282 | int hs20_web_browser(const char *url, int ignore_tls) |
d0b79d74 JM |
283 | { |
284 | GtkWidget *scroll; | |
d0b79d74 | 285 | WebKitWebView *view; |
b4b1b122 BG |
286 | #ifdef USE_WEBKIT2 |
287 | WebKitSettings *settings; | |
288 | #else /* USE_WEBKIT2 */ | |
d0b79d74 | 289 | WebKitWebSettings *settings; |
b4b1b122 BG |
290 | SoupSession *s; |
291 | #endif /* USE_WEBKIT2 */ | |
d0b79d74 JM |
292 | struct browser_context ctx; |
293 | ||
294 | memset(&ctx, 0, sizeof(ctx)); | |
295 | if (!gtk_init_check(NULL, NULL)) | |
296 | return -1; | |
297 | ||
b4b1b122 | 298 | #ifndef USE_WEBKIT2 |
d0b79d74 JM |
299 | s = webkit_get_default_session(); |
300 | g_object_set(G_OBJECT(s), "ssl-ca-file", | |
301 | "/etc/ssl/certs/ca-certificates.crt", NULL); | |
61bf9819 JM |
302 | if (ignore_tls) |
303 | g_object_set(G_OBJECT(s), "ssl-strict", FALSE, NULL); | |
b4b1b122 | 304 | #endif /* USE_WEBKIT2 */ |
d0b79d74 JM |
305 | |
306 | ctx.win = gtk_window_new(GTK_WINDOW_TOPLEVEL); | |
a139ddb6 | 307 | gtk_window_set_role(GTK_WINDOW(ctx.win), "Hotspot 2.0 client"); |
d0b79d74 JM |
308 | gtk_window_set_default_size(GTK_WINDOW(ctx.win), 800, 600); |
309 | ||
310 | scroll = gtk_scrolled_window_new(NULL, NULL); | |
311 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), | |
312 | GTK_POLICY_NEVER, GTK_POLICY_NEVER); | |
313 | ||
314 | g_signal_connect(G_OBJECT(ctx.win), "destroy", | |
315 | G_CALLBACK(win_cb_destroy), &ctx); | |
316 | ||
317 | view = WEBKIT_WEB_VIEW(webkit_web_view_new()); | |
c0c4685d | 318 | ctx.view = view; |
b4b1b122 | 319 | #ifdef USE_WEBKIT2 |
9ea9d18d JM |
320 | g_signal_connect(G_OBJECT(view), "notify::estimated-load-progress", |
321 | G_CALLBACK(view_cb_notify_estimated_load_progress), | |
322 | &ctx); | |
b4b1b122 | 323 | g_signal_connect(G_OBJECT(view), "resource-load-started", |
02ed737e | 324 | G_CALLBACK(view_cb_resource_load_starting), &ctx); |
b4b1b122 BG |
325 | g_signal_connect(G_OBJECT(view), "decide-policy", |
326 | G_CALLBACK(view_cb_mime_type_policy_decision), &ctx); | |
ffeafc08 JM |
327 | g_signal_connect(G_OBJECT(view), "mouse-target-changed", |
328 | G_CALLBACK(view_cb_mouse_target_changed), &ctx); | |
c0c4685d JM |
329 | g_signal_connect(G_OBJECT(view), "notify::title", |
330 | G_CALLBACK(view_cb_notify_title), &ctx); | |
b4b1b122 | 331 | #else /* USE_WEBKIT2 */ |
ae07bc46 JM |
332 | g_signal_connect(G_OBJECT(view), "notify::load-status", |
333 | G_CALLBACK(view_cb_notify_load_status), &ctx); | |
9ea9d18d JM |
334 | g_signal_connect(G_OBJECT(view), "notify::progress", |
335 | G_CALLBACK(view_cb_notify_progress), &ctx); | |
d0b79d74 JM |
336 | g_signal_connect(G_OBJECT(view), "resource-request-starting", |
337 | G_CALLBACK(view_cb_resource_request_starting), &ctx); | |
338 | g_signal_connect(G_OBJECT(view), "mime-type-policy-decision-requested", | |
339 | G_CALLBACK(view_cb_mime_type_policy_decision), &ctx); | |
340 | g_signal_connect(G_OBJECT(view), "download-requested", | |
341 | G_CALLBACK(view_cb_download_requested), &ctx); | |
ffeafc08 JM |
342 | g_signal_connect(G_OBJECT(view), "hovering-over-link", |
343 | G_CALLBACK(view_cb_hovering_over_link), &ctx); | |
d0b79d74 JM |
344 | g_signal_connect(G_OBJECT(view), "title-changed", |
345 | G_CALLBACK(view_cb_title_changed), &ctx); | |
b4b1b122 BG |
346 | #endif /* USE_WEBKIT2 */ |
347 | ||
d0b79d74 JM |
348 | gtk_container_add(GTK_CONTAINER(scroll), GTK_WIDGET(view)); |
349 | gtk_container_add(GTK_CONTAINER(ctx.win), GTK_WIDGET(scroll)); | |
350 | ||
351 | gtk_widget_grab_focus(GTK_WIDGET(view)); | |
352 | gtk_widget_show_all(ctx.win); | |
353 | ||
354 | settings = webkit_web_view_get_settings(view); | |
355 | g_object_set(G_OBJECT(settings), "user-agent", | |
356 | "Mozilla/5.0 (X11; U; Unix; en-US) " | |
357 | "AppleWebKit/537.15 (KHTML, like Gecko) " | |
358 | "hs20-client/1.0", NULL); | |
359 | g_object_set(G_OBJECT(settings), "auto-load-images", TRUE, NULL); | |
360 | ||
921ea496 | 361 | #ifdef USE_WEBKIT2 |
61bf9819 JM |
362 | if (ignore_tls) { |
363 | WebKitWebContext *wkctx; | |
364 | ||
365 | wkctx = webkit_web_context_get_default(); | |
366 | webkit_web_context_set_tls_errors_policy( | |
367 | wkctx, WEBKIT_TLS_ERRORS_POLICY_IGNORE); | |
368 | } | |
921ea496 BG |
369 | #endif /* USE_WEBKIT2 */ |
370 | ||
d0b79d74 JM |
371 | webkit_web_view_load_uri(view, url); |
372 | ||
7de8bd50 | 373 | ctx.gtk_main_started = 1; |
d0b79d74 JM |
374 | gtk_main(); |
375 | gtk_widget_destroy(ctx.win); | |
376 | while (gtk_events_pending()) | |
377 | gtk_main_iteration(); | |
378 | ||
379 | free(ctx.hover_link); | |
380 | free(ctx.title); | |
381 | return ctx.success; | |
382 | } |