commit 97ff661

grunfink  ·  2025-05-28 05:48:47 +0000 UTC
parent 859de58
New file rss.c.
5 files changed,  +118, -107
M html.c
A rss.c
M snac.h
+5, -4
 1@@ -5,7 +5,7 @@ CFLAGS?=-g -Wall -Wextra -pedantic
 2 all: snac
 3 
 4 snac: snac.o main.o sandbox.o data.o http.o httpd.o webfinger.o \
 5-    activitypub.o html.o utils.o format.o upgrade.o mastoapi.o
 6+    activitypub.o html.o utils.o format.o upgrade.o mastoapi.o rss.o
 7 	$(CC) $(CFLAGS) -L$(PREFIX)/lib *.o -lcurl -lcrypto $(LDFLAGS) -pthread -o $@
 8 
 9 test: tests/smtp
10@@ -48,12 +48,12 @@ update-po:
11 
12 activitypub.o: activitypub.c xs.h xs_json.h xs_curl.h xs_mime.h \
13  xs_openssl.h xs_regex.h xs_time.h xs_set.h xs_match.h xs_unicode.h \
14- snac.h http_codes.h
15+ xs_webmention.h snac.h http_codes.h
16 data.o: data.c xs.h xs_hex.h xs_io.h xs_json.h xs_openssl.h xs_glob.h \
17  xs_set.h xs_time.h xs_regex.h xs_match.h xs_unicode.h xs_random.h \
18  xs_po.h snac.h http_codes.h
19 format.o: format.c xs.h xs_regex.h xs_mime.h xs_html.h xs_json.h \
20- xs_time.h xs_match.h snac.h http_codes.h
21+ xs_time.h xs_match.h xs_unicode.h snac.h http_codes.h
22 html.o: html.c xs.h xs_io.h xs_json.h xs_regex.h xs_set.h xs_openssl.h \
23  xs_time.h xs_mime.h xs_match.h xs_html.h xs_curl.h xs_unicode.h xs_url.h \
24  xs_random.h snac.h http_codes.h
25@@ -66,7 +66,8 @@ main.o: main.c xs.h xs_io.h xs_json.h xs_time.h xs_openssl.h xs_match.h \
26  snac.h http_codes.h
27 mastoapi.o: mastoapi.c xs.h xs_hex.h xs_openssl.h xs_json.h xs_io.h \
28  xs_time.h xs_glob.h xs_set.h xs_random.h xs_url.h xs_mime.h xs_match.h \
29- snac.h http_codes.h
30+ xs_unicode.h snac.h http_codes.h
31+rss.o: rss.c xs.h xs_html.h xs_regex.h xs_time.h snac.h http_codes.h
32 sandbox.o: sandbox.c xs.h snac.h http_codes.h
33 snac.o: snac.c xs.h xs_hex.h xs_io.h xs_unicode_tbl.h xs_unicode.h \
34  xs_json.h xs_curl.h xs_openssl.h xs_socket.h xs_unix_socket.h xs_url.h \
+5, -4
 1@@ -6,7 +6,7 @@ LDFLAGS=-lrt
 2 all: snac
 3 
 4 snac: snac.o main.o sandbox.o data.o http.o httpd.o webfinger.o \
 5-    activitypub.o html.o utils.o format.o upgrade.o mastoapi.o
 6+    activitypub.o html.o utils.o format.o upgrade.o mastoapi.o rss.o
 7 	$(CC) $(CFLAGS) -L/usr/pkg/lib *.o -lcurl -lcrypto -pthread $(LDFLAGS) -Wl,-rpath,/usr/lib -Wl,-rpath,/usr/pkg/lib -o $@
 8 
 9 
10@@ -37,12 +37,12 @@ uninstall:
11 
12 activitypub.o: activitypub.c xs.h xs_json.h xs_curl.h xs_mime.h \
13  xs_openssl.h xs_regex.h xs_time.h xs_set.h xs_match.h xs_unicode.h \
14- snac.h http_codes.h
15+ xs_webmention.h snac.h http_codes.h
16 data.o: data.c xs.h xs_hex.h xs_io.h xs_json.h xs_openssl.h xs_glob.h \
17  xs_set.h xs_time.h xs_regex.h xs_match.h xs_unicode.h xs_random.h \
18  xs_po.h snac.h http_codes.h
19 format.o: format.c xs.h xs_regex.h xs_mime.h xs_html.h xs_json.h \
20- xs_time.h xs_match.h snac.h http_codes.h
21+ xs_time.h xs_match.h xs_unicode.h snac.h http_codes.h
22 html.o: html.c xs.h xs_io.h xs_json.h xs_regex.h xs_set.h xs_openssl.h \
23  xs_time.h xs_mime.h xs_match.h xs_html.h xs_curl.h xs_unicode.h xs_url.h \
24  xs_random.h snac.h http_codes.h
25@@ -55,7 +55,8 @@ main.o: main.c xs.h xs_io.h xs_json.h xs_time.h xs_openssl.h xs_match.h \
26  snac.h http_codes.h
27 mastoapi.o: mastoapi.c xs.h xs_hex.h xs_openssl.h xs_json.h xs_io.h \
28  xs_time.h xs_glob.h xs_set.h xs_random.h xs_url.h xs_mime.h xs_match.h \
29- snac.h http_codes.h
30+ xs_unicode.h snac.h http_codes.h
31+rss.o: rss.c xs.h xs_html.h xs_regex.h xs_time.h snac.h http_codes.h
32 sandbox.o: sandbox.c xs.h snac.h http_codes.h
33 snac.o: snac.c xs.h xs_hex.h xs_io.h xs_unicode_tbl.h xs_unicode.h \
34  xs_json.h xs_curl.h xs_openssl.h xs_socket.h xs_unix_socket.h xs_url.h \
M html.c
+0, -97
  1@@ -5020,100 +5020,3 @@ int html_post_handler(const xs_dict *req, const char *q_path,
  2 
  3     return status;
  4 }
  5-
  6-
  7-xs_str *rss_from_timeline(snac *user, const xs_list *timeline,
  8-                        const char *title, const char *link, const char *desc)
  9-/* converts a timeline to rss */
 10-{
 11-    xs_html *rss = xs_html_tag("rss",
 12-        xs_html_attr("xmlns:content", "http:/" "/purl.org/rss/1.0/modules/content/"),
 13-        xs_html_attr("version",       "2.0"),
 14-        xs_html_attr("xmlns:atom",    "http:/" "/www.w3.org/2005/Atom"));
 15-
 16-    xs_html *channel = xs_html_tag("channel",
 17-        xs_html_tag("title",
 18-            xs_html_text(title)),
 19-        xs_html_tag("language",
 20-            xs_html_text("en")),
 21-        xs_html_tag("link",
 22-            xs_html_text(link)),
 23-        xs_html_sctag("atom:link",
 24-            xs_html_attr("href", link),
 25-            xs_html_attr("rel", "self"),
 26-            xs_html_attr("type", "application/rss+xml")),
 27-        xs_html_tag("generator",
 28-            xs_html_text(USER_AGENT)),
 29-        xs_html_tag("description",
 30-            xs_html_text(desc)));
 31-
 32-    xs_html_add(rss, channel);
 33-
 34-    int cnt = 0;
 35-    const char *v;
 36-
 37-    xs_list_foreach(timeline, v) {
 38-        xs *msg = NULL;
 39-
 40-        if (user) {
 41-            if (!valid_status(timeline_get_by_md5(user, v, &msg)))
 42-                continue;
 43-        }
 44-        else {
 45-            if (!valid_status(object_get_by_md5(v, &msg)))
 46-                continue;
 47-        }
 48-
 49-        const char *id = xs_dict_get(msg, "id");
 50-        const char *content = xs_dict_get(msg, "content");
 51-        const char *published = xs_dict_get(msg, "published");
 52-
 53-        if (user && !xs_startswith(id, user->actor))
 54-            continue;
 55-
 56-        if (!id || !content || !published)
 57-            continue;
 58-
 59-        /* create a title with the first line of the content */
 60-        xs *title = xs_replace(content, "<br>", "\n");
 61-        title = xs_regex_replace_i(title, "<[^>]+>", " ");
 62-        title = xs_regex_replace_i(title, "&[^;]+;", " ");
 63-        int i;
 64-
 65-        for (i = 0; title[i] && title[i] != '\n' && i < 50; i++);
 66-
 67-        if (title[i] != '\0') {
 68-            title[i] = '\0';
 69-            title = xs_str_cat(title, "...");
 70-        }
 71-
 72-        title = xs_strip_i(title);
 73-
 74-        /* convert the date */
 75-        time_t t = xs_parse_iso_date(published, 0);
 76-        xs *rss_date = xs_str_utctime(t, "%a, %d %b %Y %T +0000");
 77-
 78-        /* if it's the first one, add it to the header */
 79-        if (cnt == 0)
 80-            xs_html_add(channel,
 81-                xs_html_tag("lastBuildDate",
 82-                    xs_html_text(rss_date)));
 83-
 84-        xs_html_add(channel,
 85-            xs_html_tag("item",
 86-                xs_html_tag("title",
 87-                    xs_html_text(title)),
 88-                xs_html_tag("link",
 89-                    xs_html_text(id)),
 90-                xs_html_tag("guid",
 91-                    xs_html_text(id)),
 92-                xs_html_tag("pubDate",
 93-                    xs_html_text(rss_date)),
 94-                xs_html_tag("description",
 95-                    xs_html_text(content))));
 96-
 97-        cnt++;
 98-    }
 99-
100-    return xs_html_render_s(rss, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
101-}
A rss.c
+105, -0
  1@@ -0,0 +1,105 @@
  2+/* snac - A simple, minimalistic ActivityPub instance */
  3+/* copyright (c) 2025 grunfink et al. / MIT license */
  4+
  5+#include "xs.h"
  6+#include "xs_html.h"
  7+#include "xs_regex.h"
  8+#include "xs_time.h"
  9+
 10+#include "snac.h"
 11+
 12+xs_str *rss_from_timeline(snac *user, const xs_list *timeline,
 13+                        const char *title, const char *link, const char *desc)
 14+/* converts a timeline to rss */
 15+{
 16+    xs_html *rss = xs_html_tag("rss",
 17+        xs_html_attr("xmlns:content", "http:/" "/purl.org/rss/1.0/modules/content/"),
 18+        xs_html_attr("version",       "2.0"),
 19+        xs_html_attr("xmlns:atom",    "http:/" "/www.w3.org/2005/Atom"));
 20+
 21+    xs_html *channel = xs_html_tag("channel",
 22+        xs_html_tag("title",
 23+            xs_html_text(title)),
 24+        xs_html_tag("language",
 25+            xs_html_text("en")),
 26+        xs_html_tag("link",
 27+            xs_html_text(link)),
 28+        xs_html_sctag("atom:link",
 29+            xs_html_attr("href", link),
 30+            xs_html_attr("rel", "self"),
 31+            xs_html_attr("type", "application/rss+xml")),
 32+        xs_html_tag("generator",
 33+            xs_html_text(USER_AGENT)),
 34+        xs_html_tag("description",
 35+            xs_html_text(desc)));
 36+
 37+    xs_html_add(rss, channel);
 38+
 39+    int cnt = 0;
 40+    const char *v;
 41+
 42+    xs_list_foreach(timeline, v) {
 43+        xs *msg = NULL;
 44+
 45+        if (user) {
 46+            if (!valid_status(timeline_get_by_md5(user, v, &msg)))
 47+                continue;
 48+        }
 49+        else {
 50+            if (!valid_status(object_get_by_md5(v, &msg)))
 51+                continue;
 52+        }
 53+
 54+        const char *id = xs_dict_get(msg, "id");
 55+        const char *content = xs_dict_get(msg, "content");
 56+        const char *published = xs_dict_get(msg, "published");
 57+
 58+        if (user && !xs_startswith(id, user->actor))
 59+            continue;
 60+
 61+        if (!id || !content || !published)
 62+            continue;
 63+
 64+        /* create a title with the first line of the content */
 65+        xs *title = xs_replace(content, "<br>", "\n");
 66+        title = xs_regex_replace_i(title, "<[^>]+>", " ");
 67+        title = xs_regex_replace_i(title, "&[^;]+;", " ");
 68+        int i;
 69+
 70+        for (i = 0; title[i] && title[i] != '\n' && i < 50; i++);
 71+
 72+        if (title[i] != '\0') {
 73+            title[i] = '\0';
 74+            title = xs_str_cat(title, "...");
 75+        }
 76+
 77+        title = xs_strip_i(title);
 78+
 79+        /* convert the date */
 80+        time_t t = xs_parse_iso_date(published, 0);
 81+        xs *rss_date = xs_str_utctime(t, "%a, %d %b %Y %T +0000");
 82+
 83+        /* if it's the first one, add it to the header */
 84+        if (cnt == 0)
 85+            xs_html_add(channel,
 86+                xs_html_tag("lastBuildDate",
 87+                    xs_html_text(rss_date)));
 88+
 89+        xs_html_add(channel,
 90+            xs_html_tag("item",
 91+                xs_html_tag("title",
 92+                    xs_html_text(title)),
 93+                xs_html_tag("link",
 94+                    xs_html_text(id)),
 95+                xs_html_tag("guid",
 96+                    xs_html_text(id)),
 97+                xs_html_tag("pubDate",
 98+                    xs_html_text(rss_date)),
 99+                xs_html_tag("description",
100+                    xs_html_text(content))));
101+
102+        cnt++;
103+    }
104+
105+    return xs_html_render_s(rss, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
106+}
M snac.h
+3, -2
 1@@ -395,8 +395,6 @@ int html_get_handler(const xs_dict *req, const char *q_path,
 2 int html_post_handler(const xs_dict *req, const char *q_path,
 3                       char *payload, int p_size,
 4                       char **body, int *b_size, char **ctype);
 5-xs_str *rss_from_timeline(snac *user, const xs_list *timeline,
 6-                        const char *title, const char *link, const char *desc);
 7 
 8 int write_default_css(void);
 9 int snac_init(const char *_basedir);
10@@ -462,3 +460,6 @@ int badlogin_check(const char *user, const char *addr);
11 void badlogin_inc(const char *user, const char *addr);
12 
13 const char *lang_str(const char *str, const snac *user);
14+
15+xs_str *rss_from_timeline(snac *user, const xs_list *timeline,
16+                        const char *title, const char *link, const char *desc);