commit bc5d0d4

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