summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile7
-rw-r--r--format.c16
-rw-r--r--html.c11
-rw-r--r--snac.c1
-rw-r--r--snac.h3
-rw-r--r--xs.h19
-rw-r--r--xs_html.h240
-rw-r--r--xs_version.h2
8 files changed, 273 insertions, 26 deletions
diff --git a/Makefile b/Makefile
index 0880b3d..9374792 100644
--- a/Makefile
+++ b/Makefile
@@ -37,9 +37,9 @@ activitypub.o: activitypub.c xs.h xs_json.h xs_curl.h xs_mime.h \
37 xs_openssl.h xs_regex.h xs_time.h xs_set.h xs_match.h snac.h 37 xs_openssl.h xs_regex.h xs_time.h xs_set.h xs_match.h snac.h
38data.o: data.c xs.h xs_hex.h xs_io.h xs_json.h xs_openssl.h xs_glob.h \ 38data.o: data.c xs.h xs_hex.h xs_io.h xs_json.h xs_openssl.h xs_glob.h \
39 xs_set.h xs_time.h snac.h 39 xs_set.h xs_time.h snac.h
40format.o: format.c xs.h xs_regex.h xs_mime.h snac.h 40format.o: format.c xs.h xs_regex.h xs_mime.h xs_html.h snac.h
41html.o: html.c xs.h xs_io.h xs_json.h xs_regex.h xs_set.h xs_openssl.h \ 41html.o: html.c xs.h xs_io.h xs_json.h xs_regex.h xs_set.h xs_openssl.h \
42 xs_time.h xs_mime.h xs_match.h snac.h 42 xs_time.h xs_mime.h xs_match.h xs_html.h snac.h
43http.o: http.c xs.h xs_io.h xs_openssl.h xs_curl.h xs_time.h xs_json.h \ 43http.o: http.c xs.h xs_io.h xs_openssl.h xs_curl.h xs_time.h xs_json.h \
44 snac.h 44 snac.h
45httpd.o: httpd.c xs.h xs_io.h xs_json.h xs_socket.h xs_httpd.h xs_mime.h \ 45httpd.o: httpd.c xs.h xs_io.h xs_json.h xs_socket.h xs_httpd.h xs_mime.h \
@@ -50,7 +50,8 @@ mastoapi.o: mastoapi.c xs.h xs_hex.h xs_openssl.h xs_json.h xs_io.h \
50 snac.h 50 snac.h
51snac.o: snac.c xs.h xs_hex.h xs_io.h xs_unicode.h xs_json.h xs_curl.h \ 51snac.o: snac.c xs.h xs_hex.h xs_io.h xs_unicode.h xs_json.h xs_curl.h \
52 xs_openssl.h xs_socket.h xs_url.h xs_httpd.h xs_mime.h xs_regex.h \ 52 xs_openssl.h xs_socket.h xs_url.h xs_httpd.h xs_mime.h xs_regex.h \
53 xs_set.h xs_time.h xs_glob.h xs_random.h xs_match.h xs_fcgi.h snac.h 53 xs_set.h xs_time.h xs_glob.h xs_random.h xs_match.h xs_fcgi.h xs_html.h \
54 snac.h
54upgrade.o: upgrade.c xs.h xs_io.h xs_json.h xs_glob.h snac.h 55upgrade.o: upgrade.c xs.h xs_io.h xs_json.h xs_glob.h snac.h
55utils.o: utils.c xs.h xs_io.h xs_json.h xs_time.h xs_openssl.h \ 56utils.o: utils.c xs.h xs_io.h xs_json.h xs_time.h xs_openssl.h \
56 xs_random.h xs_glob.h snac.h 57 xs_random.h xs_glob.h snac.h
diff --git a/format.c b/format.c
index cfe2294..019260c 100644
--- a/format.c
+++ b/format.c
@@ -4,6 +4,7 @@
4#include "xs.h" 4#include "xs.h"
5#include "xs_regex.h" 5#include "xs_regex.h"
6#include "xs_mime.h" 6#include "xs_mime.h"
7#include "xs_html.h"
7 8
8#include "snac.h" 9#include "snac.h"
9 10
@@ -260,23 +261,10 @@ xs_str *sanitize(const char *content)
260} 261}
261 262
262 263
263xs_str *encode_html_strict(const char *str)
264/* escapes html characters */
265{
266 xs_str *encoded = xs_replace(str, "&", "&");
267 encoded = xs_replace_i(encoded, "<", "&lt;");
268 encoded = xs_replace_i(encoded, ">", "&gt;");
269 encoded = xs_replace_i(encoded, "\"", "&#34;");
270 encoded = xs_replace_i(encoded, "'", "&#39;");
271
272 return encoded;
273}
274
275
276xs_str *encode_html(const char *str) 264xs_str *encode_html(const char *str)
277/* escapes html characters */ 265/* escapes html characters */
278{ 266{
279 xs_str *encoded = encode_html_strict(str); 267 xs_str *encoded = xs_html_encode((char *)str);
280 268
281 /* Restore only <br>. Probably safe. Let's hope nothing goes wrong with this. */ 269 /* Restore only <br>. Probably safe. Let's hope nothing goes wrong with this. */
282 encoded = xs_replace_i(encoded, "&lt;br&gt;", "<br>"); 270 encoded = xs_replace_i(encoded, "&lt;br&gt;", "<br>");
diff --git a/html.c b/html.c
index 50dc25b..4673130 100644
--- a/html.c
+++ b/html.c
@@ -10,6 +10,7 @@
10#include "xs_time.h" 10#include "xs_time.h"
11#include "xs_mime.h" 11#include "xs_mime.h"
12#include "xs_match.h" 12#include "xs_match.h"
13#include "xs_html.h"
13 14
14#include "snac.h" 15#include "snac.h"
15 16
@@ -2137,10 +2138,10 @@ int html_get_handler(const xs_dict *req, const char *q_path,
2137 xs *bio = not_really_markdown(xs_dict_get(snac.config, "bio"), NULL); 2138 xs *bio = not_really_markdown(xs_dict_get(snac.config, "bio"), NULL);
2138 char *p, *v; 2139 char *p, *v;
2139 2140
2140 xs *es1 = encode_html_strict(xs_dict_get(snac.config, "name")); 2141 xs *es1 = xs_html_encode(xs_dict_get(snac.config, "name"));
2141 xs *es2 = encode_html_strict(snac.uid); 2142 xs *es2 = xs_html_encode(snac.uid);
2142 xs *es3 = encode_html_strict(xs_dict_get(srv_config, "host")); 2143 xs *es3 = xs_html_encode(xs_dict_get(srv_config, "host"));
2143 xs *es4 = encode_html_strict(bio); 2144 xs *es4 = xs_html_encode(bio);
2144 rss = xs_fmt( 2145 rss = xs_fmt(
2145 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" 2146 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
2146 "<rss version=\"0.91\">\n" 2147 "<rss version=\"0.91\">\n"
@@ -2168,7 +2169,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
2168 if (!xs_startswith(id, snac.actor)) 2169 if (!xs_startswith(id, snac.actor))
2169 continue; 2170 continue;
2170 2171
2171 xs *content = encode_html_strict(xs_dict_get(msg, "content")); 2172 xs *content = xs_html_encode(xs_dict_get(msg, "content"));
2172 2173
2173 // We SHOULD only use sanitized one for description. 2174 // We SHOULD only use sanitized one for description.
2174 // So, only encode for feed title, while the description just keep it sanitized as is. 2175 // So, only encode for feed title, while the description just keep it sanitized as is.
diff --git a/snac.c b/snac.c
index 6394608..6ce7741 100644
--- a/snac.c
+++ b/snac.c
@@ -21,6 +21,7 @@
21#include "xs_random.h" 21#include "xs_random.h"
22#include "xs_match.h" 22#include "xs_match.h"
23#include "xs_fcgi.h" 23#include "xs_fcgi.h"
24#include "xs_html.h"
24 25
25#include "snac.h" 26#include "snac.h"
26 27
diff --git a/snac.h b/snac.h
index 3101fbb..cb240d8 100644
--- a/snac.h
+++ b/snac.h
@@ -1,7 +1,7 @@
1/* snac - A simple, minimalistic ActivityPub instance */ 1/* snac - A simple, minimalistic ActivityPub instance */
2/* copyright (c) 2022 - 2023 grunfink et al. / MIT license */ 2/* copyright (c) 2022 - 2023 grunfink et al. / MIT license */
3 3
4#define VERSION "2.43" 4#define VERSION "2.44-dev"
5 5
6#define USER_AGENT "snac/" VERSION 6#define USER_AGENT "snac/" VERSION
7 7
@@ -266,7 +266,6 @@ int activitypub_post_handler(const xs_dict *req, const char *q_path,
266 266
267xs_str *not_really_markdown(const char *content, xs_list **attach); 267xs_str *not_really_markdown(const char *content, xs_list **attach);
268xs_str *sanitize(const char *content); 268xs_str *sanitize(const char *content);
269xs_str *encode_html_strict(const char *str);
270xs_str *encode_html(const char *str); 269xs_str *encode_html(const char *str);
271 270
272xs_str *html_timeline(snac *user, const xs_list *list, int local, 271xs_str *html_timeline(snac *user, const xs_list *list, int local,
diff --git a/xs.h b/xs.h
index 63715ac..d7d9169 100644
--- a/xs.h
+++ b/xs.h
@@ -62,7 +62,8 @@ xs_str *xs_str_new(const char *str);
62xs_str *xs_str_new_sz(const char *mem, int sz); 62xs_str *xs_str_new_sz(const char *mem, int sz);
63xs_str *xs_str_wrap_i(const char *prefix, xs_str *str, const char *suffix); 63xs_str *xs_str_wrap_i(const char *prefix, xs_str *str, const char *suffix);
64#define xs_str_prepend_i(str, prefix) xs_str_wrap_i(prefix, str, NULL) 64#define xs_str_prepend_i(str, prefix) xs_str_wrap_i(prefix, str, NULL)
65#define xs_str_cat(str, suffix) xs_str_wrap_i(NULL, str, suffix) 65xs_str *_xs_str_cat(xs_str *str, const char *strs[]);
66#define xs_str_cat(str, ...) _xs_str_cat(str, (const char *[]){ __VA_ARGS__, NULL })
66xs_str *xs_replace_in(xs_str *str, const char *sfrom, const char *sto, int times); 67xs_str *xs_replace_in(xs_str *str, const char *sfrom, const char *sto, int times);
67#define xs_replace_i(str, sfrom, sto) xs_replace_in(str, sfrom, sto, XS_ALL) 68#define xs_replace_i(str, sfrom, sto) xs_replace_in(str, sfrom, sto, XS_ALL)
68#define xs_replace(str, sfrom, sto) xs_replace_in(xs_dup(str), sfrom, sto, XS_ALL) 69#define xs_replace(str, sfrom, sto) xs_replace_in(xs_dup(str), sfrom, sto, XS_ALL)
@@ -451,6 +452,22 @@ xs_str *xs_str_wrap_i(const char *prefix, xs_str *str, const char *suffix)
451} 452}
452 453
453 454
455xs_str *_xs_str_cat(xs_str *str, const char *strs[])
456/* concatenates all strings after str */
457{
458 int o = strlen(str);
459
460 while (*strs) {
461 int sz = strlen(*strs);
462 str = xs_insert_m(str, o, *strs, sz);
463 o += sz;
464 strs++;
465 }
466
467 return str;
468}
469
470
454xs_str *xs_replace_in(xs_str *str, const char *sfrom, const char *sto, int times) 471xs_str *xs_replace_in(xs_str *str, const char *sfrom, const char *sto, int times)
455/* replaces inline all sfrom with sto */ 472/* replaces inline all sfrom with sto */
456{ 473{
diff --git a/xs_html.h b/xs_html.h
new file mode 100644
index 0000000..744df5b
--- /dev/null
+++ b/xs_html.h
@@ -0,0 +1,240 @@
1/* copyright (c) 2022 - 2023 grunfink et al. / MIT license */
2
3#ifndef _XS_HTML_H
4
5#define _XS_HTML_H
6
7typedef struct xs_html xs_html;
8
9xs_str *xs_html_encode(char *str);
10
11xs_html *xs_html_attr(char *key, char *value);
12xs_html *xs_html_text(char *content);
13xs_html *xs_html_raw(char *content);
14
15xs_html *xs_html_add(xs_html *tag, xs_html *data);
16
17xs_html *_xs_html_tag(char *tag, xs_html *var[]);
18#define xs_html_tag(tag, ...) _xs_html_tag(tag, (xs_html *[]) { __VA_ARGS__, NULL })
19xs_html *_xs_html_sctag(char *tag, xs_html *var[]);
20#define xs_html_sctag(tag, ...) _xs_html_sctag(tag, (xs_html *[]) { __VA_ARGS__, NULL })
21xs_str *_xs_html_render(xs_html *h, xs_str *s);
22#define xs_html_render(h) _xs_html_render(h, xs_str_new(NULL))
23
24#ifdef XS_IMPLEMENTATION
25
26typedef enum {
27 XS_HTML_TAG,
28 XS_HTML_SCTAG,
29 XS_HTML_ATTR,
30 XS_HTML_TEXT
31} xs_html_type;
32
33struct xs_html {
34 xs_html_type type;
35 xs_str *content;
36 xs_html *f_attr;
37 xs_html *l_attr;
38 xs_html *f_tag;
39 xs_html *l_tag;
40 xs_html *next;
41};
42
43xs_str *xs_html_encode(char *str)
44/* encodes str using HTML entities */
45{
46 xs_str *s = xs_str_new(NULL);
47 int o = 0;
48 char *e = str + strlen(str);
49
50 for (;;) {
51 char *ec = "<>\"'&"; /* characters to escape */
52 char *q = e;
53 int z;
54
55 /* find the nearest happening of a char */
56 while (*ec) {
57 char *m = memchr(str, *ec++, q - str);
58 if (m)
59 q = m;
60 }
61
62 /* copy string to here */
63 z = q - str;
64 s = xs_insert_m(s, o, str, z);
65 o += z;
66
67 /* if q points to the end, nothing more to do */
68 if (q == e)
69 break;
70
71 /* insert the escaped char */
72 char tmp[8];
73 snprintf(tmp, sizeof(tmp), "&#%d;", *q);
74
75 z = strlen(tmp);
76 s = xs_insert_m(s, o, tmp, z);
77 o += z;
78
79 str = q + 1;
80 }
81
82 return s;
83}
84
85
86#define XS_HTML_NEW() memset(xs_realloc(NULL, sizeof(xs_html)), '\0', sizeof(xs_html))
87
88xs_html *xs_html_attr(char *key, char *value)
89/* creates an HTML block with an attribute */
90{
91 xs_html *a = XS_HTML_NEW();
92
93 a->type = XS_HTML_ATTR;
94
95 if (value) {
96 xs *ev = xs_html_encode(value);
97 a->content = xs_fmt("%s=\"%s\"", key, ev);
98 }
99 else
100 a->content = xs_dup(key);
101
102 return a;
103}
104
105
106xs_html *xs_html_text(char *content)
107/* creates an HTML block of text, escaping it previously */
108{
109 xs_html *a = XS_HTML_NEW();
110
111 a->type = XS_HTML_TEXT;
112 a->content = xs_html_encode(content);
113
114 return a;
115}
116
117
118xs_html *xs_html_raw(char *content)
119/* creates an HTML block without escaping (for pre-formatted HTML, comments, etc) */
120{
121 xs_html *a = XS_HTML_NEW();
122
123 a->type = XS_HTML_TEXT;
124 a->content = xs_dup(content);
125
126 return a;
127}
128
129
130xs_html *xs_html_add(xs_html *tag, xs_html *data)
131/* add data (attrs, tags or text) to a tag */
132{
133 xs_html **first;
134 xs_html **last;
135
136 if (data->type == XS_HTML_ATTR) {
137 first = &tag->f_attr;
138 last = &tag->l_attr;
139 }
140 else {
141 first = &tag->f_tag;
142 last = &tag->l_tag;
143 }
144
145 if (*first == NULL)
146 *first = data;
147
148 if (*last != NULL)
149 (*last)->next = data;
150
151 *last = data;
152
153 return tag;
154}
155
156
157static xs_html *_xs_html_tag_t(xs_html_type type, char *tag, xs_html *var[])
158/* creates a tag with a variable list of attributes and subtags */
159{
160 xs_html *a = XS_HTML_NEW();
161
162 a->type = type;
163 a->content = xs_dup(tag);
164
165 while (*var)
166 xs_html_add(a, *var++);
167
168 return a;
169}
170
171
172xs_html *_xs_html_tag(char *tag, xs_html *var[])
173{
174 return _xs_html_tag_t(XS_HTML_TAG, tag, var);
175}
176
177
178xs_html *_xs_html_sctag(char *tag, xs_html *var[])
179{
180 return _xs_html_tag_t(XS_HTML_SCTAG, tag, var);
181}
182
183
184xs_str *_xs_html_render(xs_html *h, xs_str *s)
185/* renders the tag and its subtags */
186{
187 xs_html *st;
188
189 switch (h->type) {
190 case XS_HTML_TAG:
191 case XS_HTML_SCTAG:
192 s = xs_str_cat(s, "<", h->content);
193
194 /* render the attributes */
195 st = h->f_attr;
196 while (st) {
197 xs_html *nst = st->next;
198 s = _xs_html_render(st, s);
199 st = nst;
200 }
201
202 if (h->type == XS_HTML_SCTAG) {
203 /* self-closing tags should not have subtags */
204 s = xs_str_cat(s, "/>");
205 }
206 else {
207 s = xs_str_cat(s, ">");
208
209 /* render the subtags */
210 st = h->f_tag;
211 while (st) {
212 xs_html *nst = st->next;
213 s = _xs_html_render(st, s);
214 st = nst;
215 }
216
217 s = xs_str_cat(s, "</", h->content, ">");
218 }
219
220 break;
221
222 case XS_HTML_ATTR:
223 s = xs_str_cat(s, " ", h->content);
224 break;
225
226 case XS_HTML_TEXT:
227 s = xs_str_cat(s, h->content);
228 break;
229 }
230
231 xs_free(h->content);
232 xs_free(h);
233
234 return s;
235}
236
237
238#endif /* XS_IMPLEMENTATION */
239
240#endif /* _XS_HTML_H */
diff --git a/xs_version.h b/xs_version.h
index e5a5e49..b9b734b 100644
--- a/xs_version.h
+++ b/xs_version.h
@@ -1 +1 @@
/* 416f5ffa99ecd4a3ec25d273b986d3d99dc92d22 */ /* 63beb583926bb5dfec89e1d694172cc887614460 2023-11-19T19:51:05+01:00 */