diff options
| -rw-r--r-- | Makefile | 9 | ||||
| -rw-r--r-- | Makefile.NetBSD | 9 | ||||
| -rw-r--r-- | html.c | 97 | ||||
| -rw-r--r-- | rss.c | 105 | ||||
| -rw-r--r-- | snac.h | 5 |
5 files changed, 118 insertions, 107 deletions
| @@ -5,7 +5,7 @@ CFLAGS?=-g -Wall -Wextra -pedantic | |||
| 5 | all: snac | 5 | all: snac |
| 6 | 6 | ||
| 7 | snac: snac.o main.o sandbox.o data.o http.o httpd.o webfinger.o \ | 7 | snac: 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 | ||
| 11 | test: tests/smtp | 11 | test: tests/smtp |
| @@ -48,12 +48,12 @@ update-po: | |||
| 48 | 48 | ||
| 49 | activitypub.o: activitypub.c xs.h xs_json.h xs_curl.h xs_mime.h \ | 49 | activitypub.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 |
| 52 | data.o: data.c xs.h xs_hex.h xs_io.h xs_json.h xs_openssl.h xs_glob.h \ | 52 | data.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 |
| 55 | format.o: format.c xs.h xs_regex.h xs_mime.h xs_html.h xs_json.h \ | 55 | format.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 |
| 57 | html.o: html.c xs.h xs_io.h xs_json.h xs_regex.h xs_set.h xs_openssl.h \ | 57 | html.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 |
| 67 | mastoapi.o: mastoapi.c xs.h xs_hex.h xs_openssl.h xs_json.h xs_io.h \ | 67 | mastoapi.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 |
| 70 | rss.o: rss.c xs.h xs_html.h xs_regex.h xs_time.h snac.h http_codes.h | ||
| 70 | sandbox.o: sandbox.c xs.h snac.h http_codes.h | 71 | sandbox.o: sandbox.c xs.h snac.h http_codes.h |
| 71 | snac.o: snac.c xs.h xs_hex.h xs_io.h xs_unicode_tbl.h xs_unicode.h \ | 72 | snac.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 | |||
| 6 | all: snac | 6 | all: snac |
| 7 | 7 | ||
| 8 | snac: snac.o main.o sandbox.o data.o http.o httpd.o webfinger.o \ | 8 | snac: 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 | ||
| 38 | activitypub.o: activitypub.c xs.h xs_json.h xs_curl.h xs_mime.h \ | 38 | activitypub.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 |
| 41 | data.o: data.c xs.h xs_hex.h xs_io.h xs_json.h xs_openssl.h xs_glob.h \ | 41 | data.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 |
| 44 | format.o: format.c xs.h xs_regex.h xs_mime.h xs_html.h xs_json.h \ | 44 | format.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 |
| 46 | html.o: html.c xs.h xs_io.h xs_json.h xs_regex.h xs_set.h xs_openssl.h \ | 46 | html.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 |
| 56 | mastoapi.o: mastoapi.c xs.h xs_hex.h xs_openssl.h xs_json.h xs_io.h \ | 56 | mastoapi.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 |
| 59 | rss.o: rss.c xs.h xs_html.h xs_regex.h xs_time.h snac.h http_codes.h | ||
| 59 | sandbox.o: sandbox.c xs.h snac.h http_codes.h | 60 | sandbox.o: sandbox.c xs.h snac.h http_codes.h |
| 60 | snac.o: snac.c xs.h xs_hex.h xs_io.h xs_unicode_tbl.h xs_unicode.h \ | 61 | snac.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 \ |
| @@ -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 | |||
| 5025 | xs_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 | } | ||
| @@ -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 | |||
| 11 | xs_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 | } | ||
| @@ -395,8 +395,6 @@ int html_get_handler(const xs_dict *req, const char *q_path, | |||
| 395 | int html_post_handler(const xs_dict *req, const char *q_path, | 395 | int 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); |
| 398 | xs_str *rss_from_timeline(snac *user, const xs_list *timeline, | ||
| 399 | const char *title, const char *link, const char *desc); | ||
| 400 | 398 | ||
| 401 | int write_default_css(void); | 399 | int write_default_css(void); |
| 402 | int snac_init(const char *_basedir); | 400 | int snac_init(const char *_basedir); |
| @@ -462,3 +460,6 @@ int badlogin_check(const char *user, const char *addr); | |||
| 462 | void badlogin_inc(const char *user, const char *addr); | 460 | void badlogin_inc(const char *user, const char *addr); |
| 463 | 461 | ||
| 464 | const char *lang_str(const char *str, const snac *user); | 462 | const char *lang_str(const char *str, const snac *user); |
| 463 | |||
| 464 | xs_str *rss_from_timeline(snac *user, const xs_list *timeline, | ||
| 465 | const char *title, const char *link, const char *desc); | ||