summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar grunfink2025-05-28 07:48:47 +0200
committerGravatar grunfink2025-05-28 07:48:47 +0200
commit97ff66116b7633066375bbc6cc88be5b8587453b (patch)
tree9a9eb0540b1e06ec6f1e57f093949c095e1ec2f4
parentRenamed timeline_to_rss() to rss_from_timeline(). (diff)
downloadpenes-snac2-97ff66116b7633066375bbc6cc88be5b8587453b.tar.gz
penes-snac2-97ff66116b7633066375bbc6cc88be5b8587453b.tar.xz
penes-snac2-97ff66116b7633066375bbc6cc88be5b8587453b.zip
New file rss.c.
-rw-r--r--Makefile9
-rw-r--r--Makefile.NetBSD9
-rw-r--r--html.c97
-rw-r--r--rss.c105
-rw-r--r--snac.h5
5 files changed, 118 insertions, 107 deletions
diff --git a/Makefile b/Makefile
index 46fdb09..bb4029c 100644
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,7 @@ CFLAGS?=-g -Wall -Wextra -pedantic
5all: snac 5all: snac
6 6
7snac: snac.o main.o sandbox.o data.o http.o httpd.o webfinger.o \ 7snac: snac.o main.o sandbox.o data.o http.o httpd.o webfinger.o \
8 activitypub.o html.o utils.o format.o upgrade.o mastoapi.o 8 activitypub.o html.o utils.o format.o upgrade.o mastoapi.o rss.o
9 $(CC) $(CFLAGS) -L$(PREFIX)/lib *.o -lcurl -lcrypto $(LDFLAGS) -pthread -o $@ 9 $(CC) $(CFLAGS) -L$(PREFIX)/lib *.o -lcurl -lcrypto $(LDFLAGS) -pthread -o $@
10 10
11test: tests/smtp 11test: tests/smtp
@@ -48,12 +48,12 @@ update-po:
48 48
49activitypub.o: activitypub.c xs.h xs_json.h xs_curl.h xs_mime.h \ 49activitypub.o: activitypub.c xs.h xs_json.h xs_curl.h xs_mime.h \
50 xs_openssl.h xs_regex.h xs_time.h xs_set.h xs_match.h xs_unicode.h \ 50 xs_openssl.h xs_regex.h xs_time.h xs_set.h xs_match.h xs_unicode.h \
51 snac.h http_codes.h 51 xs_webmention.h snac.h http_codes.h
52data.o: data.c xs.h xs_hex.h xs_io.h xs_json.h xs_openssl.h xs_glob.h \ 52data.o: data.c xs.h xs_hex.h xs_io.h xs_json.h xs_openssl.h xs_glob.h \
53 xs_set.h xs_time.h xs_regex.h xs_match.h xs_unicode.h xs_random.h \ 53 xs_set.h xs_time.h xs_regex.h xs_match.h xs_unicode.h xs_random.h \
54 xs_po.h snac.h http_codes.h 54 xs_po.h snac.h http_codes.h
55format.o: format.c xs.h xs_regex.h xs_mime.h xs_html.h xs_json.h \ 55format.o: format.c xs.h xs_regex.h xs_mime.h xs_html.h xs_json.h \
56 xs_time.h xs_match.h snac.h http_codes.h 56 xs_time.h xs_match.h xs_unicode.h snac.h http_codes.h
57html.o: html.c xs.h xs_io.h xs_json.h xs_regex.h xs_set.h xs_openssl.h \ 57html.o: html.c xs.h xs_io.h xs_json.h xs_regex.h xs_set.h xs_openssl.h \
58 xs_time.h xs_mime.h xs_match.h xs_html.h xs_curl.h xs_unicode.h xs_url.h \ 58 xs_time.h xs_mime.h xs_match.h xs_html.h xs_curl.h xs_unicode.h xs_url.h \
59 xs_random.h snac.h http_codes.h 59 xs_random.h snac.h http_codes.h
@@ -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 \
66 snac.h http_codes.h 66 snac.h http_codes.h
67mastoapi.o: mastoapi.c xs.h xs_hex.h xs_openssl.h xs_json.h xs_io.h \ 67mastoapi.o: mastoapi.c xs.h xs_hex.h xs_openssl.h xs_json.h xs_io.h \
68 xs_time.h xs_glob.h xs_set.h xs_random.h xs_url.h xs_mime.h xs_match.h \ 68 xs_time.h xs_glob.h xs_set.h xs_random.h xs_url.h xs_mime.h xs_match.h \
69 snac.h http_codes.h 69 xs_unicode.h snac.h http_codes.h
70rss.o: rss.c xs.h xs_html.h xs_regex.h xs_time.h snac.h http_codes.h
70sandbox.o: sandbox.c xs.h snac.h http_codes.h 71sandbox.o: sandbox.c xs.h snac.h http_codes.h
71snac.o: snac.c xs.h xs_hex.h xs_io.h xs_unicode_tbl.h xs_unicode.h \ 72snac.o: snac.c xs.h xs_hex.h xs_io.h xs_unicode_tbl.h xs_unicode.h \
72 xs_json.h xs_curl.h xs_openssl.h xs_socket.h xs_unix_socket.h xs_url.h \ 73 xs_json.h xs_curl.h xs_openssl.h xs_socket.h xs_unix_socket.h xs_url.h \
diff --git a/Makefile.NetBSD b/Makefile.NetBSD
index ecf8205..a2bf83c 100644
--- a/Makefile.NetBSD
+++ b/Makefile.NetBSD
@@ -6,7 +6,7 @@ LDFLAGS=-lrt
6all: snac 6all: snac
7 7
8snac: snac.o main.o sandbox.o data.o http.o httpd.o webfinger.o \ 8snac: snac.o main.o sandbox.o data.o http.o httpd.o webfinger.o \
9 activitypub.o html.o utils.o format.o upgrade.o mastoapi.o 9 activitypub.o html.o utils.o format.o upgrade.o mastoapi.o rss.o
10 $(CC) $(CFLAGS) -L/usr/pkg/lib *.o -lcurl -lcrypto -pthread $(LDFLAGS) -Wl,-rpath,/usr/lib -Wl,-rpath,/usr/pkg/lib -o $@ 10 $(CC) $(CFLAGS) -L/usr/pkg/lib *.o -lcurl -lcrypto -pthread $(LDFLAGS) -Wl,-rpath,/usr/lib -Wl,-rpath,/usr/pkg/lib -o $@
11 11
12 12
@@ -37,12 +37,12 @@ uninstall:
37 37
38activitypub.o: activitypub.c xs.h xs_json.h xs_curl.h xs_mime.h \ 38activitypub.o: activitypub.c xs.h xs_json.h xs_curl.h xs_mime.h \
39 xs_openssl.h xs_regex.h xs_time.h xs_set.h xs_match.h xs_unicode.h \ 39 xs_openssl.h xs_regex.h xs_time.h xs_set.h xs_match.h xs_unicode.h \
40 snac.h http_codes.h 40 xs_webmention.h snac.h http_codes.h
41data.o: data.c xs.h xs_hex.h xs_io.h xs_json.h xs_openssl.h xs_glob.h \ 41data.o: data.c xs.h xs_hex.h xs_io.h xs_json.h xs_openssl.h xs_glob.h \
42 xs_set.h xs_time.h xs_regex.h xs_match.h xs_unicode.h xs_random.h \ 42 xs_set.h xs_time.h xs_regex.h xs_match.h xs_unicode.h xs_random.h \
43 xs_po.h snac.h http_codes.h 43 xs_po.h snac.h http_codes.h
44format.o: format.c xs.h xs_regex.h xs_mime.h xs_html.h xs_json.h \ 44format.o: format.c xs.h xs_regex.h xs_mime.h xs_html.h xs_json.h \
45 xs_time.h xs_match.h snac.h http_codes.h 45 xs_time.h xs_match.h xs_unicode.h snac.h http_codes.h
46html.o: html.c xs.h xs_io.h xs_json.h xs_regex.h xs_set.h xs_openssl.h \ 46html.o: html.c xs.h xs_io.h xs_json.h xs_regex.h xs_set.h xs_openssl.h \
47 xs_time.h xs_mime.h xs_match.h xs_html.h xs_curl.h xs_unicode.h xs_url.h \ 47 xs_time.h xs_mime.h xs_match.h xs_html.h xs_curl.h xs_unicode.h xs_url.h \
48 xs_random.h snac.h http_codes.h 48 xs_random.h snac.h http_codes.h
@@ -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 \
55 snac.h http_codes.h 55 snac.h http_codes.h
56mastoapi.o: mastoapi.c xs.h xs_hex.h xs_openssl.h xs_json.h xs_io.h \ 56mastoapi.o: mastoapi.c xs.h xs_hex.h xs_openssl.h xs_json.h xs_io.h \
57 xs_time.h xs_glob.h xs_set.h xs_random.h xs_url.h xs_mime.h xs_match.h \ 57 xs_time.h xs_glob.h xs_set.h xs_random.h xs_url.h xs_mime.h xs_match.h \
58 snac.h http_codes.h 58 xs_unicode.h snac.h http_codes.h
59rss.o: rss.c xs.h xs_html.h xs_regex.h xs_time.h snac.h http_codes.h
59sandbox.o: sandbox.c xs.h snac.h http_codes.h 60sandbox.o: sandbox.c xs.h snac.h http_codes.h
60snac.o: snac.c xs.h xs_hex.h xs_io.h xs_unicode_tbl.h xs_unicode.h \ 61snac.o: snac.c xs.h xs_hex.h xs_io.h xs_unicode_tbl.h xs_unicode.h \
61 xs_json.h xs_curl.h xs_openssl.h xs_socket.h xs_unix_socket.h xs_url.h \ 62 xs_json.h xs_curl.h xs_openssl.h xs_socket.h xs_unix_socket.h xs_url.h \
diff --git a/html.c b/html.c
index d3d5075..fadcfb0 100644
--- a/html.c
+++ b/html.c
@@ -5020,100 +5020,3 @@ int html_post_handler(const xs_dict *req, const char *q_path,
5020 5020
5021 return status; 5021 return status;
5022} 5022}
5023
5024
5025xs_str *rss_from_timeline(snac *user, const xs_list *timeline,
5026 const char *title, const char *link, const char *desc)
5027/* converts a timeline to rss */
5028{
5029 xs_html *rss = xs_html_tag("rss",
5030 xs_html_attr("xmlns:content", "http:/" "/purl.org/rss/1.0/modules/content/"),
5031 xs_html_attr("version", "2.0"),
5032 xs_html_attr("xmlns:atom", "http:/" "/www.w3.org/2005/Atom"));
5033
5034 xs_html *channel = xs_html_tag("channel",
5035 xs_html_tag("title",
5036 xs_html_text(title)),
5037 xs_html_tag("language",
5038 xs_html_text("en")),
5039 xs_html_tag("link",
5040 xs_html_text(link)),
5041 xs_html_sctag("atom:link",
5042 xs_html_attr("href", link),
5043 xs_html_attr("rel", "self"),
5044 xs_html_attr("type", "application/rss+xml")),
5045 xs_html_tag("generator",
5046 xs_html_text(USER_AGENT)),
5047 xs_html_tag("description",
5048 xs_html_text(desc)));
5049
5050 xs_html_add(rss, channel);
5051
5052 int cnt = 0;
5053 const char *v;
5054
5055 xs_list_foreach(timeline, v) {
5056 xs *msg = NULL;
5057
5058 if (user) {
5059 if (!valid_status(timeline_get_by_md5(user, v, &msg)))
5060 continue;
5061 }
5062 else {
5063 if (!valid_status(object_get_by_md5(v, &msg)))
5064 continue;
5065 }
5066
5067 const char *id = xs_dict_get(msg, "id");
5068 const char *content = xs_dict_get(msg, "content");
5069 const char *published = xs_dict_get(msg, "published");
5070
5071 if (user && !xs_startswith(id, user->actor))
5072 continue;
5073
5074 if (!id || !content || !published)
5075 continue;
5076
5077 /* create a title with the first line of the content */
5078 xs *title = xs_replace(content, "<br>", "\n");
5079 title = xs_regex_replace_i(title, "<[^>]+>", " ");
5080 title = xs_regex_replace_i(title, "&[^;]+;", " ");
5081 int i;
5082
5083 for (i = 0; title[i] && title[i] != '\n' && i < 50; i++);
5084
5085 if (title[i] != '\0') {
5086 title[i] = '\0';
5087 title = xs_str_cat(title, "...");
5088 }
5089
5090 title = xs_strip_i(title);
5091
5092 /* convert the date */
5093 time_t t = xs_parse_iso_date(published, 0);
5094 xs *rss_date = xs_str_utctime(t, "%a, %d %b %Y %T +0000");
5095
5096 /* if it's the first one, add it to the header */
5097 if (cnt == 0)
5098 xs_html_add(channel,
5099 xs_html_tag("lastBuildDate",
5100 xs_html_text(rss_date)));
5101
5102 xs_html_add(channel,
5103 xs_html_tag("item",
5104 xs_html_tag("title",
5105 xs_html_text(title)),
5106 xs_html_tag("link",
5107 xs_html_text(id)),
5108 xs_html_tag("guid",
5109 xs_html_text(id)),
5110 xs_html_tag("pubDate",
5111 xs_html_text(rss_date)),
5112 xs_html_tag("description",
5113 xs_html_text(content))));
5114
5115 cnt++;
5116 }
5117
5118 return xs_html_render_s(rss, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
5119}
diff --git a/rss.c b/rss.c
new file mode 100644
index 0000000..61620fa
--- /dev/null
+++ b/rss.c
@@ -0,0 +1,105 @@
1/* snac - A simple, minimalistic ActivityPub instance */
2/* copyright (c) 2025 grunfink et al. / MIT license */
3
4#include "xs.h"
5#include "xs_html.h"
6#include "xs_regex.h"
7#include "xs_time.h"
8
9#include "snac.h"
10
11xs_str *rss_from_timeline(snac *user, const xs_list *timeline,
12 const char *title, const char *link, const char *desc)
13/* converts a timeline to rss */
14{
15 xs_html *rss = xs_html_tag("rss",
16 xs_html_attr("xmlns:content", "http:/" "/purl.org/rss/1.0/modules/content/"),
17 xs_html_attr("version", "2.0"),
18 xs_html_attr("xmlns:atom", "http:/" "/www.w3.org/2005/Atom"));
19
20 xs_html *channel = xs_html_tag("channel",
21 xs_html_tag("title",
22 xs_html_text(title)),
23 xs_html_tag("language",
24 xs_html_text("en")),
25 xs_html_tag("link",
26 xs_html_text(link)),
27 xs_html_sctag("atom:link",
28 xs_html_attr("href", link),
29 xs_html_attr("rel", "self"),
30 xs_html_attr("type", "application/rss+xml")),
31 xs_html_tag("generator",
32 xs_html_text(USER_AGENT)),
33 xs_html_tag("description",
34 xs_html_text(desc)));
35
36 xs_html_add(rss, channel);
37
38 int cnt = 0;
39 const char *v;
40
41 xs_list_foreach(timeline, v) {
42 xs *msg = NULL;
43
44 if (user) {
45 if (!valid_status(timeline_get_by_md5(user, v, &msg)))
46 continue;
47 }
48 else {
49 if (!valid_status(object_get_by_md5(v, &msg)))
50 continue;
51 }
52
53 const char *id = xs_dict_get(msg, "id");
54 const char *content = xs_dict_get(msg, "content");
55 const char *published = xs_dict_get(msg, "published");
56
57 if (user && !xs_startswith(id, user->actor))
58 continue;
59
60 if (!id || !content || !published)
61 continue;
62
63 /* create a title with the first line of the content */
64 xs *title = xs_replace(content, "<br>", "\n");
65 title = xs_regex_replace_i(title, "<[^>]+>", " ");
66 title = xs_regex_replace_i(title, "&[^;]+;", " ");
67 int i;
68
69 for (i = 0; title[i] && title[i] != '\n' && i < 50; i++);
70
71 if (title[i] != '\0') {
72 title[i] = '\0';
73 title = xs_str_cat(title, "...");
74 }
75
76 title = xs_strip_i(title);
77
78 /* convert the date */
79 time_t t = xs_parse_iso_date(published, 0);
80 xs *rss_date = xs_str_utctime(t, "%a, %d %b %Y %T +0000");
81
82 /* if it's the first one, add it to the header */
83 if (cnt == 0)
84 xs_html_add(channel,
85 xs_html_tag("lastBuildDate",
86 xs_html_text(rss_date)));
87
88 xs_html_add(channel,
89 xs_html_tag("item",
90 xs_html_tag("title",
91 xs_html_text(title)),
92 xs_html_tag("link",
93 xs_html_text(id)),
94 xs_html_tag("guid",
95 xs_html_text(id)),
96 xs_html_tag("pubDate",
97 xs_html_text(rss_date)),
98 xs_html_tag("description",
99 xs_html_text(content))));
100
101 cnt++;
102 }
103
104 return xs_html_render_s(rss, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
105}
diff --git a/snac.h b/snac.h
index 9a5aca0..f95ba4d 100644
--- a/snac.h
+++ b/snac.h
@@ -395,8 +395,6 @@ int html_get_handler(const xs_dict *req, const char *q_path,
395int html_post_handler(const xs_dict *req, const char *q_path, 395int html_post_handler(const xs_dict *req, const char *q_path,
396 char *payload, int p_size, 396 char *payload, int p_size,
397 char **body, int *b_size, char **ctype); 397 char **body, int *b_size, char **ctype);
398xs_str *rss_from_timeline(snac *user, const xs_list *timeline,
399 const char *title, const char *link, const char *desc);
400 398
401int write_default_css(void); 399int write_default_css(void);
402int snac_init(const char *_basedir); 400int snac_init(const char *_basedir);
@@ -462,3 +460,6 @@ int badlogin_check(const char *user, const char *addr);
462void badlogin_inc(const char *user, const char *addr); 460void badlogin_inc(const char *user, const char *addr);
463 461
464const char *lang_str(const char *str, const snac *user); 462const char *lang_str(const char *str, const snac *user);
463
464xs_str *rss_from_timeline(snac *user, const xs_list *timeline,
465 const char *title, const char *link, const char *desc);