summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--Makefile.NetBSD2
-rw-r--r--TODO.md4
-rw-r--r--activitypub.c2
-rw-r--r--data.c16
-rw-r--r--html.c135
-rw-r--r--httpd.c5
-rw-r--r--snac.h6
-rw-r--r--xs_fcgi.h2
-rw-r--r--xs_httpd.h1
-rw-r--r--xs_url.h2
11 files changed, 142 insertions, 35 deletions
diff --git a/Makefile b/Makefile
index 6cfa070..6654fe2 100644
--- a/Makefile
+++ b/Makefile
@@ -42,7 +42,7 @@ data.o: data.c xs.h xs_hex.h xs_io.h xs_json.h xs_openssl.h xs_glob.h \
42format.o: format.c xs.h xs_regex.h xs_mime.h xs_html.h xs_json.h \ 42format.o: format.c xs.h xs_regex.h xs_mime.h xs_html.h xs_json.h \
43 xs_time.h snac.h http_codes.h 43 xs_time.h snac.h http_codes.h
44html.o: html.c xs.h xs_io.h xs_json.h xs_regex.h xs_set.h xs_openssl.h \ 44html.o: html.c xs.h xs_io.h xs_json.h xs_regex.h xs_set.h xs_openssl.h \
45 xs_time.h xs_mime.h xs_match.h xs_html.h snac.h http_codes.h 45 xs_time.h xs_mime.h xs_match.h xs_html.h xs_curl.h snac.h http_codes.h
46http.o: http.c xs.h xs_io.h xs_openssl.h xs_curl.h xs_time.h xs_json.h \ 46http.o: http.c xs.h xs_io.h xs_openssl.h xs_curl.h xs_time.h xs_json.h \
47 snac.h http_codes.h 47 snac.h http_codes.h
48httpd.o: httpd.c xs.h xs_io.h xs_json.h xs_socket.h xs_unix_socket.h \ 48httpd.o: httpd.c xs.h xs_io.h xs_json.h xs_socket.h xs_unix_socket.h \
diff --git a/Makefile.NetBSD b/Makefile.NetBSD
index 8883949..f4a2f55 100644
--- a/Makefile.NetBSD
+++ b/Makefile.NetBSD
@@ -44,7 +44,7 @@ data.o: data.c xs.h xs_hex.h xs_io.h xs_json.h xs_openssl.h xs_glob.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 snac.h http_codes.h 45 xs_time.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 snac.h http_codes.h 47 xs_time.h xs_mime.h xs_match.h xs_html.h xs_curl.h snac.h http_codes.h
48http.o: http.c xs.h xs_io.h xs_openssl.h xs_curl.h xs_time.h xs_json.h \ 48http.o: http.c xs.h xs_io.h xs_openssl.h xs_curl.h xs_time.h xs_json.h \
49 snac.h http_codes.h 49 snac.h http_codes.h
50httpd.o: httpd.c xs.h xs_io.h xs_json.h xs_socket.h xs_unix_socket.h \ 50httpd.o: httpd.c xs.h xs_io.h xs_json.h xs_socket.h xs_unix_socket.h \
diff --git a/TODO.md b/TODO.md
index 80623e4..2e57af8 100644
--- a/TODO.md
+++ b/TODO.md
@@ -348,4 +348,6 @@ Don't show image attachments which URLs are already in the post content (2.62, 2
348 348
349Add a user option to always collapse first level threads (2.62, 2024-10-28T14:50:42+0100). 349Add a user option to always collapse first level threads (2.62, 2024-10-28T14:50:42+0100).
350 350
351Add a 'disable_block_notifications' flag to server settings (2.62, 2024-10-30T16:58:16+0100). 351Add a `disable_block_notifications` flag to server settings (2.62, 2024-10-30T16:58:16+0100).
352
353The `strict_public_timelines` is broken, as it also applies to the private timeline (2.63, 2024-11-07T21:44:52+0100).
diff --git a/activitypub.c b/activitypub.c
index 96c3038..0b2fc6a 100644
--- a/activitypub.c
+++ b/activitypub.c
@@ -202,7 +202,7 @@ xs_list *get_attachments(const xs_dict *msg)
202 else 202 else
203 attach = xs_dup(p); 203 attach = xs_dup(p);
204 204
205 if (xs_type(attach) == XSTYPE_LIST) { 205 if (xs_type(attach) == XSTYPE_LIST && xs_list_len(attach) == 0) {
206 /* does the message have an image? */ 206 /* does the message have an image? */
207 const xs_dict *d = xs_dict_get(msg, "image"); 207 const xs_dict *d = xs_dict_get(msg, "image");
208 if (xs_type(d) == XSTYPE_DICT) { 208 if (xs_type(d) == XSTYPE_DICT) {
diff --git a/data.c b/data.c
index b3788e2..c119360 100644
--- a/data.c
+++ b/data.c
@@ -3650,3 +3650,19 @@ t_announcement *announcement(const double after)
3650 3650
3651 return NULL; 3651 return NULL;
3652} 3652}
3653
3654
3655xs_str *make_url(const char *href, const char *proxy)
3656/* makes an URL, possibly including proxying */
3657{
3658 xs_str *url = NULL;
3659
3660 if (proxy && !xs_startswith(href, srv_baseurl)) {
3661 xs *p = xs_str_cat(xs_dup(proxy), "/proxy/");
3662 url = xs_replace(href, "https:/" "/", p);
3663 }
3664 else
3665 url = xs_dup(href);
3666
3667 return url;
3668}
diff --git a/html.c b/html.c
index 810502e..3721f0e 100644
--- a/html.c
+++ b/html.c
@@ -11,6 +11,7 @@
11#include "xs_mime.h" 11#include "xs_mime.h"
12#include "xs_match.h" 12#include "xs_match.h"
13#include "xs_html.h" 13#include "xs_html.h"
14#include "xs_curl.h"
14 15
15#include "snac.h" 16#include "snac.h"
16 17
@@ -41,7 +42,7 @@ int login(snac *snac, const xs_dict *headers)
41} 42}
42 43
43 44
44xs_str *replace_shortnames(xs_str *s, const xs_list *tag, int ems) 45xs_str *replace_shortnames(xs_str *s, const xs_list *tag, int ems, const char *proxy)
45/* replaces all the :shortnames: with the emojis in tag */ 46/* replaces all the :shortnames: with the emojis in tag */
46{ 47{
47 if (!xs_is_null(tag)) { 48 if (!xs_is_null(tag)) {
@@ -69,9 +70,11 @@ xs_str *replace_shortnames(xs_str *s, const xs_list *tag, int ems)
69 70
70 if (n && i) { 71 if (n && i) {
71 const char *u = xs_dict_get(i, "url"); 72 const char *u = xs_dict_get(i, "url");
73 xs *url = make_url(u, proxy);
74
72 xs_html *img = xs_html_sctag("img", 75 xs_html *img = xs_html_sctag("img",
73 xs_html_attr("loading", "lazy"), 76 xs_html_attr("loading", "lazy"),
74 xs_html_attr("src", u), 77 xs_html_attr("src", url),
75 xs_html_attr("style", style)); 78 xs_html_attr("style", style));
76 79
77 xs *s1 = xs_html_render(img); 80 xs *s1 = xs_html_render(img);
@@ -85,7 +88,7 @@ xs_str *replace_shortnames(xs_str *s, const xs_list *tag, int ems)
85} 88}
86 89
87 90
88xs_str *actor_name(xs_dict *actor) 91xs_str *actor_name(xs_dict *actor, const char *proxy)
89/* gets the actor name */ 92/* gets the actor name */
90{ 93{
91 const char *v; 94 const char *v;
@@ -96,12 +99,12 @@ xs_str *actor_name(xs_dict *actor)
96 } 99 }
97 } 100 }
98 101
99 return replace_shortnames(xs_html_encode(v), xs_dict_get(actor, "tag"), 1); 102 return replace_shortnames(xs_html_encode(v), xs_dict_get(actor, "tag"), 1, proxy);
100} 103}
101 104
102 105
103xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date, 106xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date,
104 const char *udate, const char *url, int priv, int in_people) 107 const char *udate, const char *url, int priv, int in_people, const char *proxy)
105{ 108{
106 xs_html *actor_icon = xs_html_tag("p", NULL); 109 xs_html *actor_icon = xs_html_tag("p", NULL);
107 110
@@ -110,7 +113,7 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date,
110 int fwing = 0; 113 int fwing = 0;
111 int fwer = 0; 114 int fwer = 0;
112 115
113 xs *name = actor_name(actor); 116 xs *name = actor_name(actor, proxy);
114 117
115 /* get the avatar */ 118 /* get the avatar */
116 if ((v = xs_dict_get(actor, "icon")) != NULL) { 119 if ((v = xs_dict_get(actor, "icon")) != NULL) {
@@ -119,7 +122,7 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date,
119 v = xs_list_get(v, 0); 122 v = xs_list_get(v, 0);
120 123
121 if ((v = xs_dict_get(v, "url")) != NULL) 124 if ((v = xs_dict_get(v, "url")) != NULL)
122 avatar = xs_dup(v); 125 avatar = make_url(v, proxy);
123 } 126 }
124 127
125 if (avatar == NULL) 128 if (avatar == NULL)
@@ -244,7 +247,7 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date,
244} 247}
245 248
246 249
247xs_html *html_msg_icon(snac *user, const char *actor_id, const xs_dict *msg) 250xs_html *html_msg_icon(snac *user, const char *actor_id, const xs_dict *msg, const char *proxy)
248{ 251{
249 xs *actor = NULL; 252 xs *actor = NULL;
250 xs_html *actor_icon = NULL; 253 xs_html *actor_icon = NULL;
@@ -264,7 +267,7 @@ xs_html *html_msg_icon(snac *user, const char *actor_id, const xs_dict *msg)
264 date = xs_dict_get(msg, "published"); 267 date = xs_dict_get(msg, "published");
265 udate = xs_dict_get(msg, "updated"); 268 udate = xs_dict_get(msg, "updated");
266 269
267 actor_icon = html_actor_icon(user, actor, date, udate, url, priv, 0); 270 actor_icon = html_actor_icon(user, actor, date, udate, url, priv, 0, proxy);
268 } 271 }
269 272
270 return actor_icon; 273 return actor_icon;
@@ -681,6 +684,11 @@ xs_html *html_user_head(snac *user, const char *desc, const char *url)
681 684
682static xs_html *html_user_body(snac *user, int read_only) 685static xs_html *html_user_body(snac *user, int read_only)
683{ 686{
687 const char *proxy = NULL;
688
689 if (user && !read_only && xs_is_true(xs_dict_get(srv_config, "proxy_media")))
690 proxy = user->actor;
691
684 xs_html *body = xs_html_tag("body", NULL); 692 xs_html *body = xs_html_tag("body", NULL);
685 693
686 /* top nav */ 694 /* top nav */
@@ -826,7 +834,7 @@ static xs_html *html_user_body(snac *user, int read_only)
826 xs *bio1 = not_really_markdown(es1, NULL, &tags); 834 xs *bio1 = not_really_markdown(es1, NULL, &tags);
827 xs *bio2 = process_tags(user, bio1, &tags); 835 xs *bio2 = process_tags(user, bio1, &tags);
828 836
829 bio2 = replace_shortnames(bio2, tags, 2); 837 bio2 = replace_shortnames(bio2, tags, 2, proxy);
830 838
831 xs_html *top_user_bio = xs_html_tag("div", 839 xs_html *top_user_bio = xs_html_tag("div",
832 xs_html_attr("class", "p-note snac-top-user-bio"), 840 xs_html_attr("class", "p-note snac-top-user-bio"),
@@ -1464,6 +1472,10 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
1464 const char *v; 1472 const char *v;
1465 int has_title = 0; 1473 int has_title = 0;
1466 int collapse_threads = 0; 1474 int collapse_threads = 0;
1475 const char *proxy = NULL;
1476
1477 if (user && !read_only && xs_is_true(xs_dict_get(srv_config, "proxy_media")))
1478 proxy = user->actor;
1467 1479
1468 /* do not show non-public messages in the public timeline */ 1480 /* do not show non-public messages in the public timeline */
1469 if ((read_only || !user) && !is_msg_public(msg)) 1481 if ((read_only || !user) && !is_msg_public(msg))
@@ -1495,7 +1507,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
1495 xs_html_tag("div", 1507 xs_html_tag("div",
1496 xs_html_attr("class", "snac-origin"), 1508 xs_html_attr("class", "snac-origin"),
1497 xs_html_text(L("follows you"))), 1509 xs_html_text(L("follows you"))),
1498 html_msg_icon(read_only ? NULL : user, xs_dict_get(msg, "actor"), msg))); 1510 html_msg_icon(read_only ? NULL : user, xs_dict_get(msg, "actor"), msg, proxy)));
1499 } 1511 }
1500 else 1512 else
1501 if (!xs_match(type, POSTLIKE_OBJECT_TYPE)) { 1513 if (!xs_match(type, POSTLIKE_OBJECT_TYPE)) {
@@ -1625,7 +1637,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
1625 } 1637 }
1626 else 1638 else
1627 if (valid_status(object_get_by_md5(p, &actor_r))) { 1639 if (valid_status(object_get_by_md5(p, &actor_r))) {
1628 xs *name = actor_name(actor_r); 1640 xs *name = actor_name(actor_r, proxy);
1629 1641
1630 if (!xs_is_null(name)) { 1642 if (!xs_is_null(name)) {
1631 xs *href = NULL; 1643 xs *href = NULL;
@@ -1674,7 +1686,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
1674 } 1686 }
1675 1687
1676 xs_html_add(post_header, 1688 xs_html_add(post_header,
1677 html_msg_icon(read_only ? NULL : user, actor, msg)); 1689 html_msg_icon(read_only ? NULL : user, actor, msg, proxy));
1678 1690
1679 /** post content **/ 1691 /** post content **/
1680 1692
@@ -1763,7 +1775,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
1763 c = xs_str_cat(c, "<p>"); 1775 c = xs_str_cat(c, "<p>");
1764 1776
1765 /* replace the :shortnames: */ 1777 /* replace the :shortnames: */
1766 c = replace_shortnames(c, xs_dict_get(msg, "tag"), 2); 1778 c = replace_shortnames(c, xs_dict_get(msg, "tag"), 2, proxy);
1767 1779
1768 /* Peertube videos content is in markdown */ 1780 /* Peertube videos content is in markdown */
1769 const char *mtype = xs_dict_get(msg, "mediaType"); 1781 const char *mtype = xs_dict_get(msg, "mediaType");
@@ -1954,13 +1966,15 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
1954 const xs_dict *a; 1966 const xs_dict *a;
1955 while (xs_list_next(attach, &a, &c)) { 1967 while (xs_list_next(attach, &a, &c)) {
1956 const char *type = xs_dict_get(a, "type"); 1968 const char *type = xs_dict_get(a, "type");
1957 const char *href = xs_dict_get(a, "href"); 1969 const char *o_href = xs_dict_get(a, "href");
1958 const char *name = xs_dict_get(a, "name"); 1970 const char *name = xs_dict_get(a, "name");
1959 1971
1960 /* if this image is already in the post content, skip */ 1972 /* if this image is already in the post content, skip */
1961 if (content && xs_str_in(content, href) != -1) 1973 if (content && xs_str_in(content, o_href) != -1)
1962 continue; 1974 continue;
1963 1975
1976 xs *href = make_url(o_href, proxy);
1977
1964 if (xs_startswith(type, "image/") || strcmp(type, "Image") == 0) { 1978 if (xs_startswith(type, "image/") || strcmp(type, "Image") == 0) {
1965 xs_html_add(content_attachments, 1979 xs_html_add(content_attachments,
1966 xs_html_tag("a", 1980 xs_html_tag("a",
@@ -2007,7 +2021,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
2007 xs_html_add(content_attachments, 2021 xs_html_add(content_attachments,
2008 xs_html_tag("p", 2022 xs_html_tag("p",
2009 xs_html_tag("a", 2023 xs_html_tag("a",
2010 xs_html_attr("href", href), 2024 xs_html_attr("href", o_href),
2011 xs_html_text(href)))); 2025 xs_html_text(href))));
2012 2026
2013 /* do not generate an Alt... */ 2027 /* do not generate an Alt... */
@@ -2017,10 +2031,10 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
2017 xs_html_add(content_attachments, 2031 xs_html_add(content_attachments,
2018 xs_html_tag("p", 2032 xs_html_tag("p",
2019 xs_html_tag("a", 2033 xs_html_tag("a",
2020 xs_html_attr("href", href), 2034 xs_html_attr("href", o_href),
2021 xs_html_text(L("Attachment")), 2035 xs_html_text(L("Attachment")),
2022 xs_html_text(": "), 2036 xs_html_text(": "),
2023 xs_html_text(href)))); 2037 xs_html_text(o_href))));
2024 2038
2025 /* do not generate an Alt... */ 2039 /* do not generate an Alt... */
2026 name = NULL; 2040 name = NULL;
@@ -2168,7 +2182,7 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only,
2168 xs_list *p = (xs_list *)list; 2182 xs_list *p = (xs_list *)list;
2169 const char *v; 2183 const char *v;
2170 double t = ftime(); 2184 double t = ftime();
2171 int hide_children = xs_is_true(xs_dict_get(srv_config, "strict_public_timelines")); 2185 int hide_children = xs_is_true(xs_dict_get(srv_config, "strict_public_timelines")) && read_only;
2172 2186
2173 xs *desc = NULL; 2187 xs *desc = NULL;
2174 xs *alternate = NULL; 2188 xs *alternate = NULL;
@@ -2411,7 +2425,7 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only,
2411} 2425}
2412 2426
2413 2427
2414xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t) 2428xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t, const char *proxy)
2415{ 2429{
2416 xs_html *snac_posts; 2430 xs_html *snac_posts;
2417 xs_html *people = xs_html_tag("div", 2431 xs_html *people = xs_html_tag("div",
@@ -2437,7 +2451,8 @@ xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t)
2437 xs_html_attr("name", md5)), 2451 xs_html_attr("name", md5)),
2438 xs_html_tag("div", 2452 xs_html_tag("div",
2439 xs_html_attr("class", "snac-post-header"), 2453 xs_html_attr("class", "snac-post-header"),
2440 html_actor_icon(snac, actor, xs_dict_get(actor, "published"), NULL, NULL, 0, 1))); 2454 html_actor_icon(snac, actor, xs_dict_get(actor, "published"),
2455 NULL, NULL, 0, 1, proxy)));
2441 2456
2442 /* content (user bio) */ 2457 /* content (user bio) */
2443 const char *c = xs_dict_get(actor, "summary"); 2458 const char *c = xs_dict_get(actor, "summary");
@@ -2540,6 +2555,11 @@ xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t)
2540 2555
2541xs_str *html_people(snac *user) 2556xs_str *html_people(snac *user)
2542{ 2557{
2558 const char *proxy = NULL;
2559
2560 if (xs_is_true(xs_dict_get(srv_config, "proxy_media")))
2561 proxy = user->actor;
2562
2543 xs *wing = following_list(user); 2563 xs *wing = following_list(user);
2544 xs *wers = follower_list(user); 2564 xs *wers = follower_list(user);
2545 2565
@@ -2548,8 +2568,8 @@ xs_str *html_people(snac *user)
2548 xs_html_add(html_user_body(user, 0), 2568 xs_html_add(html_user_body(user, 0),
2549 xs_html_tag("div", 2569 xs_html_tag("div",
2550 xs_html_attr("class", "snac-posts"), 2570 xs_html_attr("class", "snac-posts"),
2551 html_people_list(user, wing, L("People you follow"), "i"), 2571 html_people_list(user, wing, L("People you follow"), "i", proxy),
2552 html_people_list(user, wers, L("People that follow you"), "e")), 2572 html_people_list(user, wers, L("People that follow you"), "e", proxy)),
2553 html_footer())); 2573 html_footer()));
2554 2574
2555 return xs_html_render_s(html, "<!DOCTYPE html>\n"); 2575 return xs_html_render_s(html, "<!DOCTYPE html>\n");
@@ -2558,6 +2578,11 @@ xs_str *html_people(snac *user)
2558 2578
2559xs_str *html_notifications(snac *user, int skip, int show) 2579xs_str *html_notifications(snac *user, int skip, int show)
2560{ 2580{
2581 const char *proxy = NULL;
2582
2583 if (xs_is_true(xs_dict_get(srv_config, "proxy_media")))
2584 proxy = user->actor;
2585
2561 xs *n_list = notify_list(user, skip, show); 2586 xs *n_list = notify_list(user, skip, show);
2562 xs *n_time = notify_check_time(user, 0); 2587 xs *n_time = notify_check_time(user, 0);
2563 2588
@@ -2616,7 +2641,7 @@ xs_str *html_notifications(snac *user, int skip, int show)
2616 if (!valid_status(actor_get(actor_id, &actor))) 2641 if (!valid_status(actor_get(actor_id, &actor)))
2617 continue; 2642 continue;
2618 2643
2619 xs *a_name = actor_name(actor); 2644 xs *a_name = actor_name(actor, proxy);
2620 const char *label = type; 2645 const char *label = type;
2621 2646
2622 if (strcmp(type, "Create") == 0) 2647 if (strcmp(type, "Create") == 0)
@@ -2658,7 +2683,7 @@ xs_str *html_notifications(snac *user, int skip, int show)
2658 xs_html_add(entry, 2683 xs_html_add(entry,
2659 xs_html_tag("div", 2684 xs_html_tag("div",
2660 xs_html_attr("class", "snac-post"), 2685 xs_html_attr("class", "snac-post"),
2661 html_actor_icon(user, actor, NULL, NULL, NULL, 0, 0))); 2686 html_actor_icon(user, actor, NULL, NULL, NULL, 0, 0, proxy)));
2662 } 2687 }
2663 else 2688 else
2664 if (strcmp(type, "Move") == 0) { 2689 if (strcmp(type, "Move") == 0) {
@@ -2672,7 +2697,7 @@ xs_str *html_notifications(snac *user, int skip, int show)
2672 xs_html_add(entry, 2697 xs_html_add(entry,
2673 xs_html_tag("div", 2698 xs_html_tag("div",
2674 xs_html_attr("class", "snac-post"), 2699 xs_html_attr("class", "snac-post"),
2675 html_actor_icon(user, old_actor, NULL, NULL, NULL, 0, 0))); 2700 html_actor_icon(user, old_actor, NULL, NULL, NULL, 0, 0, proxy)));
2676 } 2701 }
2677 } 2702 }
2678 } 2703 }
@@ -2753,7 +2778,8 @@ xs_str *html_notifications(snac *user, int skip, int show)
2753 2778
2754 2779
2755int html_get_handler(const xs_dict *req, const char *q_path, 2780int html_get_handler(const xs_dict *req, const char *q_path,
2756 char **body, int *b_size, char **ctype, xs_str **etag) 2781 char **body, int *b_size, char **ctype,
2782 xs_str **etag, xs_str **last_modified)
2757{ 2783{
2758 const char *accept = xs_dict_get(req, "accept"); 2784 const char *accept = xs_dict_get(req, "accept");
2759 int status = HTTP_STATUS_NOT_FOUND; 2785 int status = HTTP_STATUS_NOT_FOUND;
@@ -2762,6 +2788,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
2762 const char *p_path; 2788 const char *p_path;
2763 int cache = 1; 2789 int cache = 1;
2764 int save = 1; 2790 int save = 1;
2791 const char *proxy = NULL;
2765 const char *v; 2792 const char *v;
2766 2793
2767 xs *l = xs_split_n(q_path, "/", 2); 2794 xs *l = xs_split_n(q_path, "/", 2);
@@ -2788,6 +2815,9 @@ int html_get_handler(const xs_dict *req, const char *q_path,
2788 return HTTP_STATUS_NOT_FOUND; 2815 return HTTP_STATUS_NOT_FOUND;
2789 } 2816 }
2790 2817
2818 if (xs_is_true(xs_dict_get(srv_config, "proxy_media")))
2819 proxy = snac.actor;
2820
2791 /* return the RSS if requested by Accept header */ 2821 /* return the RSS if requested by Accept header */
2792 if (accept != NULL) { 2822 if (accept != NULL) {
2793 if (xs_str_in(accept, "text/xml") != -1 || 2823 if (xs_str_in(accept, "text/xml") != -1 ||
@@ -3164,6 +3194,55 @@ int html_get_handler(const xs_dict *req, const char *q_path,
3164 snac_debug(&snac, 1, xs_fmt("serving RSS")); 3194 snac_debug(&snac, 1, xs_fmt("serving RSS"));
3165 } 3195 }
3166 else 3196 else
3197 if (xs_startswith(p_path, "proxy/") && proxy) { /** remote media by proxy **/
3198 if (!login(&snac, req)) {
3199 *body = xs_dup(uid);
3200 status = HTTP_STATUS_UNAUTHORIZED;
3201 }
3202 else {
3203 /* pick the raw path (including optional ? arguments) */
3204 const char *raw_path = xs_dict_get(req, "raw_path");
3205
3206 /* skip to where the proxy/ string starts */
3207 raw_path += xs_str_in(raw_path, "proxy/");
3208
3209 xs *url = xs_replace(raw_path, "proxy/", "https:/" "/");
3210 xs *hdrs = xs_dict_new();
3211
3212 hdrs = xs_dict_append(hdrs, "user-agent", USER_AGENT);
3213
3214 const char *ims = xs_dict_get(req, "if-modified-since");
3215 const char *inm = xs_dict_get(req, "if-none-match");
3216
3217 if (ims) hdrs = xs_dict_append(hdrs, "if-modified-since", ims);
3218 if (inm) hdrs = xs_dict_append(hdrs, "if-none-match", inm);
3219
3220 xs *rsp = xs_http_request("GET", url, hdrs,
3221 NULL, 0, &status, body, b_size, 0);
3222
3223 if (valid_status(status)) {
3224 const char *ct = xs_dict_get(rsp, "content-type");
3225 const char *lm = xs_dict_get(rsp, "last-modified");
3226 const char *et = xs_dict_get(rsp, "etag");
3227
3228 if (lm) *last_modified = xs_dup(lm);
3229 if (et) *etag = xs_dup(et);
3230
3231 /* find the content-type in the static mime types,
3232 and return that value instead of ct, which will
3233 be destroyed when out of scope */
3234 for (int n = 0; xs_mime_types[n]; n += 2) {
3235 if (strcmp(ct, xs_mime_types[n + 1]) == 0) {
3236 *ctype = (char *)xs_mime_types[n + 1];
3237 break;
3238 }
3239 }
3240 }
3241
3242 snac_debug(&snac, 1, xs_fmt("Proxy for %s %d", url, status));
3243 }
3244 }
3245 else
3167 status = HTTP_STATUS_NOT_FOUND; 3246 status = HTTP_STATUS_NOT_FOUND;
3168 3247
3169 user_free(&snac); 3248 user_free(&snac);
diff --git a/httpd.c b/httpd.c
index 2fe9d6b..1613e1f 100644
--- a/httpd.c
+++ b/httpd.c
@@ -278,6 +278,7 @@ void httpd_connection(FILE *f)
278 xs *q_path = NULL; 278 xs *q_path = NULL;
279 xs *payload = NULL; 279 xs *payload = NULL;
280 xs *etag = NULL; 280 xs *etag = NULL;
281 xs *last_modified = NULL;
281 int p_size = 0; 282 int p_size = 0;
282 const char *p; 283 const char *p;
283 int fcgi_id; 284 int fcgi_id;
@@ -329,7 +330,7 @@ void httpd_connection(FILE *f)
329#endif /* NO_MASTODON_API */ 330#endif /* NO_MASTODON_API */
330 331
331 if (status == 0) 332 if (status == 0)
332 status = html_get_handler(req, q_path, &body, &b_size, &ctype, &etag); 333 status = html_get_handler(req, q_path, &body, &b_size, &ctype, &etag, &last_modified);
333 } 334 }
334 else 335 else
335 if (strcmp(method, "POST") == 0) { 336 if (strcmp(method, "POST") == 0) {
@@ -423,6 +424,8 @@ void httpd_connection(FILE *f)
423 424
424 if (!xs_is_null(etag)) 425 if (!xs_is_null(etag))
425 headers = xs_dict_append(headers, "etag", etag); 426 headers = xs_dict_append(headers, "etag", etag);
427 if (!xs_is_null(last_modified))
428 headers = xs_dict_append(headers, "last-modified", last_modified);
426 429
427 /* if there are any additional headers, add them */ 430 /* if there are any additional headers, add them */
428 const xs_dict *more_headers = xs_dict_get(srv_config, "http_headers"); 431 const xs_dict *more_headers = xs_dict_get(srv_config, "http_headers");
diff --git a/snac.h b/snac.h
index 27c3d68..aa53838 100644
--- a/snac.h
+++ b/snac.h
@@ -349,7 +349,9 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only,
349 const char *title, const char *page, int utl, const char *error); 349 const char *title, const char *page, int utl, const char *error);
350 350
351int html_get_handler(const xs_dict *req, const char *q_path, 351int html_get_handler(const xs_dict *req, const char *q_path,
352 char **body, int *b_size, char **ctype, xs_str **etag); 352 char **body, int *b_size, char **ctype,
353 xs_str **etag, xs_str **last_modified);
354
353int html_post_handler(const xs_dict *req, const char *q_path, 355int html_post_handler(const xs_dict *req, const char *q_path,
354 char *payload, int p_size, 356 char *payload, int p_size,
355 char **body, int *b_size, char **ctype); 357 char **body, int *b_size, char **ctype);
@@ -407,3 +409,5 @@ typedef struct {
407 char *text; 409 char *text;
408} t_announcement; 410} t_announcement;
409t_announcement *announcement(double after); 411t_announcement *announcement(double after);
412
413xs_str *make_url(const char *href, const char *proxy);
diff --git a/xs_fcgi.h b/xs_fcgi.h
index 6d3b030..c79121f 100644
--- a/xs_fcgi.h
+++ b/xs_fcgi.h
@@ -179,6 +179,8 @@ xs_dict *xs_fcgi_request(FILE *f, xs_str **payload, int *p_size, int *fcgi_id)
179 req = xs_dict_append(req, "method", v); 179 req = xs_dict_append(req, "method", v);
180 else 180 else
181 if (strcmp(k, "REQUEST_URI") == 0) { 181 if (strcmp(k, "REQUEST_URI") == 0) {
182 req = xs_dict_append(req, "raw_path", v);
183
182 xs *pnv = xs_split_n(v, "?", 1); 184 xs *pnv = xs_split_n(v, "?", 1);
183 185
184 /* store the path */ 186 /* store the path */
diff --git a/xs_httpd.h b/xs_httpd.h
index 02b8ac2..860ae05 100644
--- a/xs_httpd.h
+++ b/xs_httpd.h
@@ -32,6 +32,7 @@ xs_dict *xs_httpd_request(FILE *f, xs_str **payload, int *p_size)
32 xs_dict *req = xs_dict_new(); 32 xs_dict *req = xs_dict_new();
33 33
34 req = xs_dict_append(req, "method", xs_list_get(l2, 0)); 34 req = xs_dict_append(req, "method", xs_list_get(l2, 0));
35 req = xs_dict_append(req, "raw_path", xs_list_get(l2, 1));
35 req = xs_dict_append(req, "proto", xs_list_get(l2, 2)); 36 req = xs_dict_append(req, "proto", xs_list_get(l2, 2));
36 37
37 { 38 {
diff --git a/xs_url.h b/xs_url.h
index 1738634..bc4e87c 100644
--- a/xs_url.h
+++ b/xs_url.h
@@ -196,7 +196,7 @@ xs_dict *xs_multipart_form_data(const char *payload, int p_size, const char *hea
196 if (fn != NULL) { 196 if (fn != NULL) {
197 /* p_var value is a list */ 197 /* p_var value is a list */
198 /* if filename has no extension and content-type is image, attach extension to the filename */ 198 /* if filename has no extension and content-type is image, attach extension to the filename */
199 if (strchr(fn, '.') == NULL && xs_startswith(ct, "image/")) { 199 if (strchr(fn, '.') == NULL && ct && xs_startswith(ct, "image/")) {
200 char *ext = strchr(ct, '/'); 200 char *ext = strchr(ct, '/');
201 ext++; 201 ext++;
202 fn = xs_str_cat(xs_str_new(""), fn, ".", ext); 202 fn = xs_str_cat(xs_str_new(""), fn, ".", ext);