diff options
| -rw-r--r-- | RELEASE_NOTES.md | 4 | ||||
| -rw-r--r-- | activitypub.c | 2 | ||||
| -rw-r--r-- | data/greeting.html | 13 | ||||
| -rw-r--r-- | data/server.json | 21 | ||||
| -rw-r--r-- | data/style.css | 27 | ||||
| -rw-r--r-- | html.c | 10 | ||||
| -rw-r--r-- | httpd.c | 8 | ||||
| -rw-r--r-- | mastoapi.c | 45 | ||||
| -rw-r--r-- | utils.c | 1 | ||||
| -rw-r--r-- | xs_formdata.h | 27 |
10 files changed, 148 insertions, 10 deletions
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 7d3f683..4614da6 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md | |||
| @@ -4,10 +4,12 @@ | |||
| 4 | 4 | ||
| 5 | Added support for Peertube videos. | 5 | Added support for Peertube videos. |
| 6 | 6 | ||
| 7 | Mastodon API: Added support for editing posts, fixed an error related to the edit date of a post. | 7 | Mastodon API: Added support for editing posts, fixed an error related to the edit date of a post, fixed some crashes. |
| 8 | 8 | ||
| 9 | Added a handshake emoji next to a user name if it's a mutual relation (follower and followed), because friendship is bliss. | 9 | Added a handshake emoji next to a user name if it's a mutual relation (follower and followed), because friendship is bliss. |
| 10 | 10 | ||
| 11 | Tweaked some retry timeout values for better behaviour in larger instances (thanks to me@mysmallinstance.homelinux.org for their help). | ||
| 12 | |||
| 11 | ## 2.45 | 13 | ## 2.45 |
| 12 | 14 | ||
| 13 | Fixed a collision in webfinger caching. This may affect federation with some software, so I recommend an upgrade. | 15 | Fixed a collision in webfinger caching. This may affect federation with some software, so I recommend an upgrade. |
diff --git a/activitypub.c b/activitypub.c index e680e33..698758c 100644 --- a/activitypub.c +++ b/activitypub.c | |||
| @@ -2220,7 +2220,7 @@ void process_queue_item(xs_dict *q_item) | |||
| 2220 | 2220 | ||
| 2221 | /* deliver (if previous error status was a timeout, try now longer) */ | 2221 | /* deliver (if previous error status was a timeout, try now longer) */ |
| 2222 | status = send_to_inbox_raw(keyid, seckey, inbox, msg, | 2222 | status = send_to_inbox_raw(keyid, seckey, inbox, msg, |
| 2223 | &payload, &p_size, p_status == 599 ? 20 : 3); | 2223 | &payload, &p_size, p_status == 599 ? 8 : 6); |
| 2224 | 2224 | ||
| 2225 | if (payload) { | 2225 | if (payload) { |
| 2226 | if (p_size > 64) { | 2226 | if (p_size > 64) { |
diff --git a/data/greeting.html b/data/greeting.html new file mode 100644 index 0000000..b664538 --- /dev/null +++ b/data/greeting.html | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | <!DOCTYPE html> | ||
| 2 | <html><head> | ||
| 3 | <meta name="viewport" content="width=device-width, initial-scale=1"/> | ||
| 4 | <title>Welcome to %host%</title> | ||
| 5 | <body style="margin: auto; max-width: 50em"> | ||
| 6 | <p><b>%host%</b> is a <a href="https://en.wikipedia.org/wiki/Fediverse">Fediverse</a> instance that uses the <a href="https://en.wikipedia.org/wiki/ActivityPub">ActivityPub</a> protocol. In other words, users at this host can communicate with people that use software like Mastodon, Pleroma, Friendica, etc. all around the world.</p> | ||
| 7 | <p>This server runs the <a href="https://comam.es/what-is-snac">snac</a> software and there is no automatic sign-up process.</p> | ||
| 8 | <p>The following users are part of this community:</p> | ||
| 9 | |||
| 10 | %userlist% | ||
| 11 | |||
| 12 | <p>This site is powered by <abbr title="Social Networks Are Crap">snac</abbr>.</p> | ||
| 13 | </body></html> | ||
diff --git a/data/server.json b/data/server.json new file mode 100644 index 0000000..901f377 --- /dev/null +++ b/data/server.json | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | { | ||
| 2 | "prefix": "", | ||
| 3 | "port": 8001, | ||
| 4 | "dbglevel": 0, | ||
| 5 | "queue_retry_minutes": 2, | ||
| 6 | "queue_retry_max": 10, | ||
| 7 | "cssurls": [ | ||
| 8 | "" | ||
| 9 | ], | ||
| 10 | "max_timeline_entries": 128, | ||
| 11 | "timeline_purge_days": 120, | ||
| 12 | "local_purge_days": 0, | ||
| 13 | "admin_account": "", | ||
| 14 | "title": "", | ||
| 15 | "short_description": "", | ||
| 16 | "fastcgi": false, | ||
| 17 | "layout": 2.7, | ||
| 18 | "address": "0.0.0.0", | ||
| 19 | "host": "snac.notnull.space", | ||
| 20 | "admin_email": "paul@notnull.space" | ||
| 21 | } \ No newline at end of file | ||
diff --git a/data/style.css b/data/style.css new file mode 100644 index 0000000..24be520 --- /dev/null +++ b/data/style.css | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | body { max-width: 48em; margin: auto; line-height: 1.5; padding: 0.8em; word-wrap: break-word; } | ||
| 2 | pre { overflow-x: scroll; } | ||
| 3 | .snac-embedded-video, img { max-width: 100% } | ||
| 4 | .snac-origin { font-size: 85% } | ||
| 5 | .snac-score { float: right; font-size: 85% } | ||
| 6 | .snac-top-user { text-align: center; padding-bottom: 2em } | ||
| 7 | .snac-top-user-name { font-size: 200% } | ||
| 8 | .snac-top-user-id { font-size: 150% } | ||
| 9 | .snac-avatar { float: left; height: 2.5em; padding: 0.25em } | ||
| 10 | .snac-author { font-size: 90%; text-decoration: none } | ||
| 11 | .snac-author-tag { font-size: 80% } | ||
| 12 | .snac-pubdate { color: #a0a0a0; font-size: 90% } | ||
| 13 | .snac-top-controls { padding-bottom: 1.5em } | ||
| 14 | .snac-post { border-top: 1px solid #a0a0a0; } | ||
| 15 | .snac-children { padding-left: 2em; border-left: 1px solid #a0a0a0; } | ||
| 16 | .snac-textarea { font-family: inherit; width: 100% } | ||
| 17 | .snac-history { border: 1px solid #606060; border-radius: 3px; margin: 2.5em 0; padding: 0 2em } | ||
| 18 | .snac-btn-mute { float: right; margin-left: 0.5em } | ||
| 19 | .snac-btn-unmute { float: right; margin-left: 0.5em } | ||
| 20 | .snac-btn-follow { float: right; margin-left: 0.5em } | ||
| 21 | .snac-btn-unfollow { float: right; margin-left: 0.5em } | ||
| 22 | .snac-btn-hide { float: right; margin-left: 0.5em } | ||
| 23 | .snac-btn-delete { float: right; margin-left: 0.5em } | ||
| 24 | .snac-btn-limit { float: right; margin-left: 0.5em } | ||
| 25 | .snac-btn-unlimit { float: right; margin-left: 0.5em } | ||
| 26 | .snac-footer { margin-top: 2em; font-size: 75% } | ||
| 27 | .snac-poll-result { margin-left: auto; margin-right: auto; } | ||
| @@ -427,7 +427,9 @@ static xs_html *html_base_head(void) | |||
| 427 | xs_html_attr("name", "generator"), | 427 | xs_html_attr("name", "generator"), |
| 428 | xs_html_attr("content", USER_AGENT))); | 428 | xs_html_attr("content", USER_AGENT))); |
| 429 | 429 | ||
| 430 | /* add server CSS */ | 430 | /* add server CSS and favicon */ |
| 431 | xs *f; | ||
| 432 | f = xs_fmt("%s/favicon.ico", srv_baseurl); | ||
| 431 | xs_list *p = xs_dict_get(srv_config, "cssurls"); | 433 | xs_list *p = xs_dict_get(srv_config, "cssurls"); |
| 432 | char *v; | 434 | char *v; |
| 433 | while (xs_list_iter(&p, &v)) { | 435 | while (xs_list_iter(&p, &v)) { |
| @@ -438,6 +440,12 @@ static xs_html *html_base_head(void) | |||
| 438 | xs_html_attr("href", v))); | 440 | xs_html_attr("href", v))); |
| 439 | } | 441 | } |
| 440 | 442 | ||
| 443 | xs_html_add(head, | ||
| 444 | xs_html_sctag("link", | ||
| 445 | xs_html_attr("rel", "icon"), | ||
| 446 | xs_html_attr("type", "image/x-icon"), | ||
| 447 | xs_html_attr("href", f))); | ||
| 448 | |||
| 441 | return head; | 449 | return head; |
| 442 | } | 450 | } |
| 443 | 451 | ||
| @@ -243,6 +243,14 @@ int server_get_handler(xs_dict *req, const char *q_path, | |||
| 243 | *body = xs_str_new("User-agent: *\n" | 243 | *body = xs_str_new("User-agent: *\n" |
| 244 | "Disallow: /\n"); | 244 | "Disallow: /\n"); |
| 245 | } | 245 | } |
| 246 | else | ||
| 247 | if (strcmp(q_path, "/.well-known/host-meta") == 0) { | ||
| 248 | status = 200; | ||
| 249 | *ctype = "application/xrd+xml"; | ||
| 250 | *body = xs_str_new("<XRD>" | ||
| 251 | "<Link rel=\"lrdd\" type=\"application/xrd+xml\" template=\"%s/.well-known/webfinger?resource={uri}\"/>" | ||
| 252 | "</XRD>"); | ||
| 253 | } | ||
| 246 | 254 | ||
| 247 | if (status != 0) | 255 | if (status != 0) |
| 248 | srv_debug(1, xs_fmt("server_get_handler serving '%s' %d", q_path, status)); | 256 | srv_debug(1, xs_fmt("server_get_handler serving '%s' %d", q_path, status)); |
| @@ -240,8 +240,10 @@ int oauth_post_handler(const xs_dict *req, const char *q_path, | |||
| 240 | char *i_ctype = xs_dict_get(req, "content-type"); | 240 | char *i_ctype = xs_dict_get(req, "content-type"); |
| 241 | xs *args = NULL; | 241 | xs *args = NULL; |
| 242 | 242 | ||
| 243 | if (i_ctype && xs_startswith(i_ctype, "application/json")) | 243 | if (i_ctype && xs_startswith(i_ctype, "application/json")) { |
| 244 | args = xs_json_loads(payload); | 244 | if (!xs_is_null(payload)) |
| 245 | args = xs_json_loads(payload); | ||
| 246 | } | ||
| 245 | else | 247 | else |
| 246 | if (i_ctype && xs_startswith(i_ctype, "application/x-www-form-urlencoded") && payload) { | 248 | if (i_ctype && xs_startswith(i_ctype, "application/x-www-form-urlencoded") && payload) { |
| 247 | xs *upl = xs_url_dec(payload); | 249 | xs *upl = xs_url_dec(payload); |
| @@ -250,6 +252,9 @@ int oauth_post_handler(const xs_dict *req, const char *q_path, | |||
| 250 | else | 252 | else |
| 251 | args = xs_dup(xs_dict_get(req, "p_vars")); | 253 | args = xs_dup(xs_dict_get(req, "p_vars")); |
| 252 | 254 | ||
| 255 | if (args == NULL) | ||
| 256 | return 400; | ||
| 257 | |||
| 253 | xs *cmd = xs_replace_n(q_path, "/oauth", "", 1); | 258 | xs *cmd = xs_replace_n(q_path, "/oauth", "", 1); |
| 254 | 259 | ||
| 255 | srv_debug(1, xs_fmt("oauth_post_handler %s", q_path)); | 260 | srv_debug(1, xs_fmt("oauth_post_handler %s", q_path)); |
| @@ -354,6 +359,12 @@ int oauth_post_handler(const xs_dict *req, const char *q_path, | |||
| 354 | } | 359 | } |
| 355 | } | 360 | } |
| 356 | 361 | ||
| 362 | /* no code? | ||
| 363 | I'm not sure of the impacts of this right now, but Subway Tooter does not | ||
| 364 | provide a code so one must be generated */ | ||
| 365 | if (xs_is_null(code)){ | ||
| 366 | code = random_str(); | ||
| 367 | } | ||
| 357 | if (gtype && code && cid && csec && ruri) { | 368 | if (gtype && code && cid && csec && ruri) { |
| 358 | xs *app = app_get(cid); | 369 | xs *app = app_get(cid); |
| 359 | 370 | ||
| @@ -1403,7 +1414,6 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 1403 | const char *type = xs_dict_get(msg, "type"); | 1414 | const char *type = xs_dict_get(msg, "type"); |
| 1404 | if (!xs_match(type, "Note|Question|Page|Article")) | 1415 | if (!xs_match(type, "Note|Question|Page|Article")) |
| 1405 | continue; | 1416 | continue; |
| 1406 | |||
| 1407 | const char *from = NULL; | 1417 | const char *from = NULL; |
| 1408 | if (strcmp(type, "Page") == 0) | 1418 | if (strcmp(type, "Page") == 0) |
| 1409 | from = xs_dict_get(msg, "audience"); | 1419 | from = xs_dict_get(msg, "audience"); |
| @@ -1617,6 +1627,15 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path, | |||
| 1617 | status = 200; | 1627 | status = 200; |
| 1618 | } | 1628 | } |
| 1619 | else | 1629 | else |
| 1630 | if (strcmp(cmd, "/v2/filters") == 0) { /** **/ | ||
| 1631 | /* snac will never have filters | ||
| 1632 | * but still, without a v2 endpoint a short delay is introduced | ||
| 1633 | * in some apps */ | ||
| 1634 | *body = xs_dup("[]"); | ||
| 1635 | *ctype = "application/json"; | ||
| 1636 | status = 200; | ||
| 1637 | } | ||
| 1638 | else | ||
| 1620 | if (strcmp(cmd, "/v1/favourites") == 0) { /** **/ | 1639 | if (strcmp(cmd, "/v1/favourites") == 0) { /** **/ |
| 1621 | /* snac will never support a list of favourites */ | 1640 | /* snac will never support a list of favourites */ |
| 1622 | *body = xs_dup("[]"); | 1641 | *body = xs_dup("[]"); |
| @@ -1981,8 +2000,18 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path, | |||
| 1981 | xs *args = NULL; | 2000 | xs *args = NULL; |
| 1982 | char *i_ctype = xs_dict_get(req, "content-type"); | 2001 | char *i_ctype = xs_dict_get(req, "content-type"); |
| 1983 | 2002 | ||
| 1984 | if (i_ctype && xs_startswith(i_ctype, "application/json")) | 2003 | if (i_ctype && xs_startswith(i_ctype, "application/json")) { |
| 1985 | args = xs_json_loads(payload); | 2004 | if (!xs_is_null(payload)) |
| 2005 | args = xs_json_loads(payload); | ||
| 2006 | } | ||
| 2007 | else if (i_ctype && xs_startswith(i_ctype, "application/x-www-form-urlencoded")) | ||
| 2008 | { | ||
| 2009 | // Some apps send form data instead of json so we should cater for those | ||
| 2010 | if (!xs_is_null(payload)) { | ||
| 2011 | xs *upl = xs_url_dec(payload); | ||
| 2012 | args = xs_url_vars(upl); | ||
| 2013 | } | ||
| 2014 | } | ||
| 1986 | else | 2015 | else |
| 1987 | args = xs_dup(xs_dict_get(req, "p_vars")); | 2016 | args = xs_dup(xs_dict_get(req, "p_vars")); |
| 1988 | 2017 | ||
| @@ -2504,8 +2533,10 @@ int mastoapi_put_handler(const xs_dict *req, const char *q_path, | |||
| 2504 | xs *args = NULL; | 2533 | xs *args = NULL; |
| 2505 | char *i_ctype = xs_dict_get(req, "content-type"); | 2534 | char *i_ctype = xs_dict_get(req, "content-type"); |
| 2506 | 2535 | ||
| 2507 | if (i_ctype && xs_startswith(i_ctype, "application/json")) | 2536 | if (i_ctype && xs_startswith(i_ctype, "application/json")) { |
| 2508 | args = xs_json_loads(payload); | 2537 | if (!xs_is_null(payload)) |
| 2538 | args = xs_json_loads(payload); | ||
| 2539 | } | ||
| 2509 | else | 2540 | else |
| 2510 | args = xs_dup(xs_dict_get(req, "p_vars")); | 2541 | args = xs_dup(xs_dict_get(req, "p_vars")); |
| 2511 | 2542 | ||
| @@ -81,6 +81,7 @@ static const char *greeting_html = | |||
| 81 | "<!DOCTYPE html>\n" | 81 | "<!DOCTYPE html>\n" |
| 82 | "<html><head>\n" | 82 | "<html><head>\n" |
| 83 | "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n" | 83 | "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n" |
| 84 | "<link rel=\"icon\" type=\"image/x-icon\" href=\"https://%host%/favicon.ico\"/>\n" | ||
| 84 | "<title>Welcome to %host%</title>\n" | 85 | "<title>Welcome to %host%</title>\n" |
| 85 | "<body style=\"margin: auto; max-width: 50em\">\n" | 86 | "<body style=\"margin: auto; max-width: 50em\">\n" |
| 86 | "%blurb%" | 87 | "%blurb%" |
diff --git a/xs_formdata.h b/xs_formdata.h new file mode 100644 index 0000000..213bd3e --- /dev/null +++ b/xs_formdata.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | /* copyright (c) 2022 - 2024 grunfink et al. / MIT license */ | ||
| 2 | #include "xs.h" | ||
| 3 | |||
| 4 | #ifndef _XS_FORMDATA_H | ||
| 5 | |||
| 6 | #define _XS_FORMDATA_H | ||
| 7 | |||
| 8 | xs_val *xs_formdata_loads(const xs_str *formdata); | ||
| 9 | |||
| 10 | #ifdef XS_IMPLEMENTATION | ||
| 11 | |||
| 12 | /** IMPLEMENTATION **/ | ||
| 13 | |||
| 14 | xs_val *xs_formdata_loads(const xs_str *formdata) | ||
| 15 | /* loads a string in formdata format and converts to a multiple data */ | ||
| 16 | { | ||
| 17 | xs_val *v = NULL; | ||
| 18 | xs_list *args = xs_split(formdata, "&"); | ||
| 19 | int i = 0; | ||
| 20 | while (){} | ||
| 21 | printf("args: %s\r\n", args); fflush(stdout); | ||
| 22 | printf("data: %s\r\n", formdata); fflush(stdout); | ||
| 23 | } | ||
| 24 | |||
| 25 | #endif /* XS_IMPLEMENTATION */ | ||
| 26 | |||
| 27 | #endif /* _XS_FORMDATA_H */ | ||