we_redirect.c
1/**
2 * Copyright (C) 2023 Chris Noxz
3 * Author(s): Chris Noxz <chris@noxz.tech>
4 *
5 * This program is free software: you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation, either version 3 of the License, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along with
16 * this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19#include <stdio.h>
20#include <glib.h>
21#include <webkit2/webkit-web-extension.h>
22
23#define RULE_FILE "redirect"
24
25GSList *redirect_patterns = NULL,
26 *redirect_destinations = NULL;
27gboolean verbose = FALSE;
28
29
30void
31redirect_load(void)
32{
33 GRegex *r = NULL; /* regex rule */
34 GError *e = NULL; /* errors */
35 GIOChannel *c = NULL; /* reading io channel */
36 gchar *p = NULL, /* path to redirect file */
37 *l = NULL, /* line buffer */
38 s; /* separator holder */
39 int i,
40 j,
41 d; /* destination index */
42
43 /* make it possible to debug redirect rules */
44 verbose = g_getenv(__NAME_UPPERCASE__"_VERBOSE") != NULL;
45
46 p = g_build_filename(g_get_user_config_dir(), __NAME__, RULE_FILE, NULL);
47 if ((c = g_io_channel_new_file(p, "r", NULL)) != NULL) {
48 while (g_io_channel_read_line(c, &l, NULL, NULL, NULL)
49 == G_IO_STATUS_NORMAL) {
50 g_strstrip(l);
51 /* ignore if separator is comment indicator, if not then parse as:
52 /pattern/replacement/ where '/' can be anything but '#' */
53 if ((s = l[0]) != '#') {
54 for (d = j = i = 0; l[i] && j <= 2; i++) {
55 if (d == 0 && j == 1)
56 d = i;
57 l[i] = l[i + 1];
58 if (l[i] == s) {
59 l[i] = '\0';
60 j++;
61 }
62 }
63
64 if (j == 2) { /* make sure to pass 2 null-terminated strings */
65 r = g_regex_new(
66 l,
67 G_REGEX_CASELESS | G_REGEX_OPTIMIZE,
68 G_REGEX_MATCH_PARTIAL,
69 &e
70 );
71 if (e != NULL) {
72 fprintf(
73 stderr,
74 __NAME__": Could not compile regex: %s\n",
75 l
76 );
77 g_error_free(e);
78 e = NULL;
79 } else {
80 redirect_patterns = g_slist_append(
81 redirect_patterns, r
82 );
83 redirect_destinations = g_slist_append(
84 redirect_destinations, g_strdup(l + d)
85 );
86 }
87 }
88 }
89 g_free(l);
90 }
91 g_io_channel_shutdown(c, FALSE, NULL);
92 }
93 g_free(p);
94}
95
96gboolean
97cb_web_page_send_request(WebKitWebPage *web_page, WebKitURIRequest *request,
98 WebKitURIResponse *redirected_response, gpointer data)
99{
100 GSList *p = redirect_patterns, /* pattern holder */
101 *d = redirect_destinations; /* destination holder */
102 const gchar *i; /* incoming uri */
103 gchar *o = NULL; /* outgoing uri */
104
105 (void)web_page;
106 (void)redirected_response;
107 (void)data;
108
109 i = webkit_uri_request_get_uri(request);
110
111 if (verbose)
112 fprintf(stderr, " [req]: %s\n", i);
113
114 /* go through every rule and apply accordingly */
115 while (p && d) {
116 /* find matching rule for incoming uri */
117 if (g_regex_match((GRegex *)(p->data), i, 0, NULL)) {
118 /* check if destination exists for rule */
119 if (((gchar*)d->data)[0])
120 o = g_regex_replace(
121 (GRegex *)(p->data),
122 i,
123 -1,
124 0,
125 ((gchar*)d->data),
126 G_REGEX_MATCH_DEFAULT,
127 NULL
128 );
129 if (verbose)
130 fprintf(stderr,
131 "redirect: %s %s -> %s\n",
132 g_regex_get_pattern(p->data),
133 i,
134 o ? o : "[blocked]"
135 );
136 /* if outgoing uri was constructed, then redirect... */
137 if (o) {
138 webkit_uri_request_set_uri(request, o);
139 g_free(o);
140 return FALSE;
141 /* ...else, block */
142 } else {
143 return TRUE;
144 }
145 }
146 p = g_slist_next(p);
147 d = g_slist_next(d);
148 }
149
150 return FALSE;
151}
152
153void
154cb_web_page_created(WebKitWebExtension *extension, WebKitWebPage *web_page,
155 gpointer data)
156{
157 (void)extension;
158 (void)data;
159
160 g_signal_connect_object(
161 web_page, "send-request", G_CALLBACK(cb_web_page_send_request), NULL, 0
162 );
163}
164
165G_MODULE_EXPORT void
166webkit_web_extension_initialize(WebKitWebExtension *extension)
167{
168 redirect_load();
169 g_signal_connect(
170 extension, "page-created", G_CALLBACK(cb_web_page_created), NULL
171 );
172}