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 /* go through every rule and apply accordingly */
112 while (p && d) {
113 /* find matching rule for incoming uri */
114 if (g_regex_match((GRegex *)(p->data), i, 0, NULL)) {
115 /* check if destination exists for rule */
116 if (((gchar*)d->data)[0])
117 o = g_regex_replace(
118 (GRegex *)(p->data),
119 i,
120 -1,
121 0,
122 ((gchar*)d->data),
123 G_REGEX_MATCH_DEFAULT,
124 NULL
125 );
126 if (verbose)
127 fprintf(stderr,
128 "redirect: %s %s -> %s\n",
129 g_regex_get_pattern(p->data),
130 i,
131 o ? o : "[blocked]"
132 );
133 /* if outgoing uri was constructed, then redirect... */
134 if (o) {
135 webkit_uri_request_set_uri(request, o);
136 g_free(o);
137 return FALSE;
138 /* ...else, block */
139 } else {
140 return TRUE;
141 }
142 }
143 p = g_slist_next(p);
144 d = g_slist_next(d);
145 }
146
147 return FALSE;
148}
149
150void
151cb_web_page_created(WebKitWebExtension *extension, WebKitWebPage *web_page,
152 gpointer data)
153{
154 (void)extension;
155 (void)data;
156
157 g_signal_connect_object(
158 web_page, "send-request", G_CALLBACK(cb_web_page_send_request), NULL, 0
159 );
160}
161
162G_MODULE_EXPORT void
163webkit_web_extension_initialize(WebKitWebExtension *extension)
164{
165 redirect_load();
166 g_signal_connect(
167 extension, "page-created", G_CALLBACK(cb_web_page_created), NULL
168 );
169}