diff options
| author | 2025-07-16 20:21:20 +0200 | |
|---|---|---|
| committer | 2025-07-16 20:21:20 +0200 | |
| commit | 3554a73aa50526631961efcca78c6c8eb2a16911 (patch) | |
| tree | fc4c21a65337ab8ecb4fb00aa551ee21997faf6f /html.c | |
| parent | po/de_DE.po aktualisiert (diff) | |
| parent | Updated RELEASE_NOTES. (diff) | |
| download | penes-snac2-3554a73aa50526631961efcca78c6c8eb2a16911.tar.gz penes-snac2-3554a73aa50526631961efcca78c6c8eb2a16911.tar.xz penes-snac2-3554a73aa50526631961efcca78c6c8eb2a16911.zip | |
Merge pull request 'master' (#9) from grunfink/snac2:master into master
Reviewed-on: https://codeberg.org/zen/snac2/pulls/9
Diffstat (limited to 'html.c')
| -rw-r--r-- | html.c | 224 |
1 files changed, 94 insertions, 130 deletions
| @@ -636,13 +636,18 @@ static xs_html *html_base_head(void) | |||
| 636 | xs_html_attr("content", USER_AGENT))); | 636 | xs_html_attr("content", USER_AGENT))); |
| 637 | 637 | ||
| 638 | /* add server CSS and favicon */ | 638 | /* add server CSS and favicon */ |
| 639 | xs *f; | 639 | xs *f = NULL; |
| 640 | f = xs_fmt("%s/favicon.ico", srv_baseurl); | 640 | const char *favicon = xs_dict_get(srv_config, "favicon_url"); |
| 641 | |||
| 642 | if (xs_is_string(favicon)) | ||
| 643 | f = xs_dup(favicon); | ||
| 644 | else | ||
| 645 | f = xs_fmt("%s/favicon.ico", srv_baseurl); | ||
| 646 | |||
| 641 | const xs_list *p = xs_dict_get(srv_config, "cssurls"); | 647 | const xs_list *p = xs_dict_get(srv_config, "cssurls"); |
| 642 | const char *v; | 648 | const char *v; |
| 643 | int c = 0; | ||
| 644 | 649 | ||
| 645 | while (xs_list_next(p, &v, &c)) { | 650 | xs_list_foreach(p, v) { |
| 646 | xs_html_add(head, | 651 | xs_html_add(head, |
| 647 | xs_html_sctag("link", | 652 | xs_html_sctag("link", |
| 648 | xs_html_attr("rel", "stylesheet"), | 653 | xs_html_attr("rel", "stylesheet"), |
| @@ -863,6 +868,14 @@ xs_html *html_user_head(snac *user, const char *desc, const char *url) | |||
| 863 | xs_html_attr("type", "application/activity+json"), | 868 | xs_html_attr("type", "application/activity+json"), |
| 864 | xs_html_attr("href", url ? url : user->actor))); | 869 | xs_html_attr("href", url ? url : user->actor))); |
| 865 | 870 | ||
| 871 | /* webmention hook */ | ||
| 872 | xs *wbh = xs_fmt("%s/webmention-hook", srv_baseurl); | ||
| 873 | |||
| 874 | xs_html_add(head, | ||
| 875 | xs_html_sctag("link", | ||
| 876 | xs_html_attr("rel", "webmention"), | ||
| 877 | xs_html_attr("href", wbh))); | ||
| 878 | |||
| 866 | return head; | 879 | return head; |
| 867 | } | 880 | } |
| 868 | 881 | ||
| @@ -1077,10 +1090,17 @@ static xs_html *html_user_body(snac *user, int read_only) | |||
| 1077 | while (xs_dict_next(metadata, &k, &v, &c)) { | 1090 | while (xs_dict_next(metadata, &k, &v, &c)) { |
| 1078 | xs_html *value; | 1091 | xs_html *value; |
| 1079 | 1092 | ||
| 1080 | if (xs_startswith(v, "https:/") || xs_startswith(v, "http:/")) { | 1093 | if (xs_startswith(v, "https:/") || xs_startswith(v, "http:/") || *v == '@') { |
| 1081 | /* is this link validated? */ | 1094 | /* is this link validated? */ |
| 1082 | xs *verified_link = NULL; | 1095 | xs *verified_link = NULL; |
| 1083 | const xs_number *val_time = xs_dict_get(val_links, v); | 1096 | const xs_number *val_time = xs_dict_get(val_links, v); |
| 1097 | const char *url = NULL; | ||
| 1098 | |||
| 1099 | if (xs_is_string(val_time)) { | ||
| 1100 | /* resolve again, as it may be an account handle */ | ||
| 1101 | url = val_time; | ||
| 1102 | val_time = xs_dict_get(val_links, val_time); | ||
| 1103 | } | ||
| 1084 | 1104 | ||
| 1085 | if (xs_type(val_time) == XSTYPE_NUMBER) { | 1105 | if (xs_type(val_time) == XSTYPE_NUMBER) { |
| 1086 | time_t t = xs_number_get(val_time); | 1106 | time_t t = xs_number_get(val_time); |
| @@ -1098,13 +1118,13 @@ static xs_html *html_user_body(snac *user, int read_only) | |||
| 1098 | xs_html_tag("a", | 1118 | xs_html_tag("a", |
| 1099 | xs_html_attr("rel", "me"), | 1119 | xs_html_attr("rel", "me"), |
| 1100 | xs_html_attr("target", "_blank"), | 1120 | xs_html_attr("target", "_blank"), |
| 1101 | xs_html_attr("href", v), | 1121 | xs_html_attr("href", url ? url : v), |
| 1102 | xs_html_text(v))); | 1122 | xs_html_text(v))); |
| 1103 | } | 1123 | } |
| 1104 | else { | 1124 | else { |
| 1105 | value = xs_html_tag("a", | 1125 | value = xs_html_tag("a", |
| 1106 | xs_html_attr("rel", "me"), | 1126 | xs_html_attr("rel", "me"), |
| 1107 | xs_html_attr("href", v), | 1127 | xs_html_attr("href", url ? url : v), |
| 1108 | xs_html_text(v)); | 1128 | xs_html_text(v)); |
| 1109 | } | 1129 | } |
| 1110 | } | 1130 | } |
| @@ -1290,6 +1310,7 @@ xs_html *html_top_controls(snac *user) | |||
| 1290 | const xs_val *show_foll = xs_dict_get(user->config, "show_contact_metrics"); | 1310 | const xs_val *show_foll = xs_dict_get(user->config, "show_contact_metrics"); |
| 1291 | const char *latitude = xs_dict_get_def(user->config, "latitude", ""); | 1311 | const char *latitude = xs_dict_get_def(user->config, "latitude", ""); |
| 1292 | const char *longitude = xs_dict_get_def(user->config, "longitude", ""); | 1312 | const char *longitude = xs_dict_get_def(user->config, "longitude", ""); |
| 1313 | const char *webhook = xs_dict_get_def(user->config, "notify_webhook", ""); | ||
| 1293 | 1314 | ||
| 1294 | xs *metadata = NULL; | 1315 | xs *metadata = NULL; |
| 1295 | const xs_dict *md = xs_dict_get(user->config, "metadata"); | 1316 | const xs_dict *md = xs_dict_get(user->config, "metadata"); |
| @@ -1452,6 +1473,14 @@ xs_html *html_top_controls(snac *user) | |||
| 1452 | xs_html_attr("value", ntfy_token), | 1473 | xs_html_attr("value", ntfy_token), |
| 1453 | xs_html_attr("placeholder", L("ntfy token - if needed")))), | 1474 | xs_html_attr("placeholder", L("ntfy token - if needed")))), |
| 1454 | xs_html_tag("p", | 1475 | xs_html_tag("p", |
| 1476 | xs_html_text(L("Notify webhook:")), | ||
| 1477 | xs_html_sctag("br", NULL), | ||
| 1478 | xs_html_sctag("input", | ||
| 1479 | xs_html_attr("type", "url"), | ||
| 1480 | xs_html_attr("name", "notify_webhook"), | ||
| 1481 | xs_html_attr("value", webhook), | ||
| 1482 | xs_html_attr("placeholder", L("http://example.com/webhook")))), | ||
| 1483 | xs_html_tag("p", | ||
| 1455 | xs_html_text(L("Maximum days to keep posts (0: server settings):")), | 1484 | xs_html_text(L("Maximum days to keep posts (0: server settings):")), |
| 1456 | xs_html_sctag("br", NULL), | 1485 | xs_html_sctag("br", NULL), |
| 1457 | xs_html_sctag("input", | 1486 | xs_html_sctag("input", |
| @@ -1601,7 +1630,8 @@ xs_html *html_top_controls(snac *user) | |||
| 1601 | xs_html_attr("name", "followed_hashtags"), | 1630 | xs_html_attr("name", "followed_hashtags"), |
| 1602 | xs_html_attr("cols", "40"), | 1631 | xs_html_attr("cols", "40"), |
| 1603 | xs_html_attr("rows", "4"), | 1632 | xs_html_attr("rows", "4"), |
| 1604 | xs_html_attr("placeholder", "#cats\n#windowfriday\n#classicalmusic"), | 1633 | xs_html_attr("placeholder", "#cats\n#windowfriday\n#classicalmusic\nhttps:/" |
| 1634 | "/mastodon.social/tags/dogs"), | ||
| 1605 | xs_html_text(followed_hashtags)), | 1635 | xs_html_text(followed_hashtags)), |
| 1606 | 1636 | ||
| 1607 | xs_html_tag("br", NULL), | 1637 | xs_html_tag("br", NULL), |
| @@ -1978,8 +2008,13 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, | |||
| 1978 | } | 2008 | } |
| 1979 | 2009 | ||
| 1980 | if ((user == NULL || strcmp(actor, user->actor) != 0) | 2010 | if ((user == NULL || strcmp(actor, user->actor) != 0) |
| 1981 | && !valid_status(actor_get(actor, NULL))) | 2011 | && !valid_status(actor_get(actor, NULL))) { |
| 2012 | |||
| 2013 | if (user) | ||
| 2014 | enqueue_actor_refresh(user, actor, 0); | ||
| 2015 | |||
| 1982 | return NULL; | 2016 | return NULL; |
| 2017 | } | ||
| 1983 | 2018 | ||
| 1984 | /** html_entry top tag **/ | 2019 | /** html_entry top tag **/ |
| 1985 | xs_html *entry_top = xs_html_tag("div", NULL); | 2020 | xs_html *entry_top = xs_html_tag("div", NULL); |
| @@ -2115,9 +2150,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, | |||
| 2115 | const char *parent = get_in_reply_to(msg); | 2150 | const char *parent = get_in_reply_to(msg); |
| 2116 | 2151 | ||
| 2117 | if (!xs_is_null(parent) && *parent) { | 2152 | if (!xs_is_null(parent) && *parent) { |
| 2118 | xs *md5 = xs_md5_hex(parent, strlen(parent)); | 2153 | if (!timeline_here(user, parent)) { |
| 2119 | |||
| 2120 | if (!timeline_here(user, md5)) { | ||
| 2121 | xs_html_add(post_header, | 2154 | xs_html_add(post_header, |
| 2122 | xs_html_tag("div", | 2155 | xs_html_tag("div", |
| 2123 | xs_html_attr("class", "snac-origin"), | 2156 | xs_html_attr("class", "snac-origin"), |
| @@ -2419,6 +2452,9 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, | |||
| 2419 | const char *o_href = xs_dict_get(a, "href"); | 2452 | const char *o_href = xs_dict_get(a, "href"); |
| 2420 | const char *name = xs_dict_get(a, "name"); | 2453 | const char *name = xs_dict_get(a, "name"); |
| 2421 | 2454 | ||
| 2455 | if (!xs_is_string(type) || !xs_is_string(o_href)) | ||
| 2456 | continue; | ||
| 2457 | |||
| 2422 | /* if this URL is already in the post content, skip */ | 2458 | /* if this URL is already in the post content, skip */ |
| 2423 | if (content && xs_str_in(content, o_href) != -1) | 2459 | if (content && xs_str_in(content, o_href) != -1) |
| 2424 | continue; | 2460 | continue; |
| @@ -3284,12 +3320,12 @@ xs_str *html_people(snac *user) | |||
| 3284 | 3320 | ||
| 3285 | xs *wing = following_list(user); | 3321 | xs *wing = following_list(user); |
| 3286 | xs *wers = follower_list(user); | 3322 | xs *wers = follower_list(user); |
| 3323 | xs *pending = pending_list(user); | ||
| 3287 | 3324 | ||
| 3288 | xs_html *lists = xs_html_tag("div", | 3325 | xs_html *lists = xs_html_tag("div", |
| 3289 | xs_html_attr("class", "snac-posts")); | 3326 | xs_html_attr("class", "snac-posts")); |
| 3290 | 3327 | ||
| 3291 | if (xs_is_true(xs_dict_get(user->config, "approve_followers"))) { | 3328 | if (xs_list_len(pending) || xs_is_true(xs_dict_get(user->config, "approve_followers"))) { |
| 3292 | xs *pending = pending_list(user); | ||
| 3293 | xs_html_add(lists, | 3329 | xs_html_add(lists, |
| 3294 | html_people_list(user, pending, L("Pending follow confirmations"), "p", proxy)); | 3330 | html_people_list(user, pending, L("Pending follow confirmations"), "p", proxy)); |
| 3295 | } | 3331 | } |
| @@ -3384,13 +3420,15 @@ xs_str *html_notifications(snac *user, int skip, int show) | |||
| 3384 | 3420 | ||
| 3385 | const char *actor_id = xs_dict_get(noti, "actor"); | 3421 | const char *actor_id = xs_dict_get(noti, "actor"); |
| 3386 | xs *actor = NULL; | 3422 | xs *actor = NULL; |
| 3423 | xs *a_name = NULL; | ||
| 3387 | 3424 | ||
| 3388 | if (!valid_status(actor_get(actor_id, &actor))) | 3425 | if (valid_status(actor_get(actor_id, &actor))) |
| 3389 | continue; | 3426 | a_name = actor_name(actor, proxy); |
| 3427 | else | ||
| 3428 | a_name = xs_dup(actor_id); | ||
| 3390 | 3429 | ||
| 3391 | xs *a_name = actor_name(actor, proxy); | 3430 | xs *label_sanitized = sanitize(type); |
| 3392 | xs *label_sanatized = sanitize(type); | 3431 | const char *label = label_sanitized; |
| 3393 | const char *label = label_sanatized; | ||
| 3394 | 3432 | ||
| 3395 | if (strcmp(type, "Create") == 0) | 3433 | if (strcmp(type, "Create") == 0) |
| 3396 | label = L("Mention"); | 3434 | label = L("Mention"); |
| @@ -3462,10 +3500,11 @@ xs_str *html_notifications(snac *user, int skip, int show) | |||
| 3462 | html_label); | 3500 | html_label); |
| 3463 | 3501 | ||
| 3464 | if (strcmp(type, "Follow") == 0 || strcmp(utype, "Follow") == 0 || strcmp(type, "Block") == 0) { | 3502 | if (strcmp(type, "Follow") == 0 || strcmp(utype, "Follow") == 0 || strcmp(type, "Block") == 0) { |
| 3465 | xs_html_add(entry, | 3503 | if (actor) |
| 3466 | xs_html_tag("div", | 3504 | xs_html_add(entry, |
| 3467 | xs_html_attr("class", "snac-post"), | 3505 | xs_html_tag("div", |
| 3468 | html_actor_icon(user, actor, NULL, NULL, NULL, 0, 0, proxy, NULL, NULL))); | 3506 | xs_html_attr("class", "snac-post"), |
| 3507 | html_actor_icon(user, actor, NULL, NULL, NULL, 0, 0, proxy, NULL, NULL))); | ||
| 3469 | } | 3508 | } |
| 3470 | else | 3509 | else |
| 3471 | if (strcmp(type, "Move") == 0) { | 3510 | if (strcmp(type, "Move") == 0) { |
| @@ -3498,6 +3537,13 @@ xs_str *html_notifications(snac *user, int skip, int show) | |||
| 3498 | xs_html_text(L("Context")))), | 3537 | xs_html_text(L("Context")))), |
| 3499 | h); | 3538 | h); |
| 3500 | } | 3539 | } |
| 3540 | else | ||
| 3541 | xs_html_add(entry, | ||
| 3542 | xs_html_tag("p", | ||
| 3543 | xs_html_text(L("Location: ")), | ||
| 3544 | xs_html_tag("a", | ||
| 3545 | xs_html_attr("href", id), | ||
| 3546 | xs_html_text(id)))); | ||
| 3501 | } | 3547 | } |
| 3502 | 3548 | ||
| 3503 | if (strcmp(v, n_time) > 0) { | 3549 | if (strcmp(v, n_time) > 0) { |
| @@ -3748,7 +3794,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, | |||
| 3748 | /* may by an actor; try a webfinger */ | 3794 | /* may by an actor; try a webfinger */ |
| 3749 | xs *actor_obj = NULL; | 3795 | xs *actor_obj = NULL; |
| 3750 | 3796 | ||
| 3751 | if (valid_status(webfinger_request(q, &actor_obj, &url_acct))) { | 3797 | if (valid_status(webfinger_request(q, &actor_obj, &url_acct)) && xs_is_string(url_acct)) { |
| 3752 | /* it's an actor; do the dirty trick of changing q to the account name */ | 3798 | /* it's an actor; do the dirty trick of changing q to the account name */ |
| 3753 | q = url_acct; | 3799 | q = url_acct; |
| 3754 | } | 3800 | } |
| @@ -3773,12 +3819,18 @@ int html_get_handler(const xs_dict *req, const char *q_path, | |||
| 3773 | q = url_acct; | 3819 | q = url_acct; |
| 3774 | 3820 | ||
| 3775 | /* add the post to the timeline */ | 3821 | /* add the post to the timeline */ |
| 3776 | xs *md5 = xs_md5_hex(q, strlen(q)); | 3822 | if (!timeline_here(&snac, q)) |
| 3777 | |||
| 3778 | if (!timeline_here(&snac, md5)) | ||
| 3779 | timeline_add(&snac, q, object); | 3823 | timeline_add(&snac, q, object); |
| 3780 | } | 3824 | } |
| 3781 | } | 3825 | } |
| 3826 | else { | ||
| 3827 | /* retry webfinger, this time with the 'official' id */ | ||
| 3828 | const char *id = xs_dict_get(object, "id"); | ||
| 3829 | |||
| 3830 | if (xs_is_string(id) && valid_status(webfinger_request(id, &actor_obj, &url_acct)) && | ||
| 3831 | xs_is_string(url_acct)) | ||
| 3832 | q = url_acct; | ||
| 3833 | } | ||
| 3782 | } | 3834 | } |
| 3783 | } | 3835 | } |
| 3784 | 3836 | ||
| @@ -3917,7 +3969,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, | |||
| 3917 | xs *l = xs_split(p_path, "/"); | 3969 | xs *l = xs_split(p_path, "/"); |
| 3918 | const char *md5 = xs_list_get(l, -1); | 3970 | const char *md5 = xs_list_get(l, -1); |
| 3919 | 3971 | ||
| 3920 | if (md5 && *md5 && timeline_here(&snac, md5)) { | 3972 | if (md5 && *md5 && timeline_here_by_md5(&snac, md5)) { |
| 3921 | xs *list0 = xs_list_append(xs_list_new(), md5); | 3973 | xs *list0 = xs_list_append(xs_list_new(), md5); |
| 3922 | xs *list = timeline_top_level(&snac, list0); | 3974 | xs *list = timeline_top_level(&snac, list0); |
| 3923 | 3975 | ||
| @@ -4127,7 +4179,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, | |||
| 4127 | xs_dict_get(srv_config, "host")); | 4179 | xs_dict_get(srv_config, "host")); |
| 4128 | xs *rss_link = xs_fmt("%s.rss", snac.actor); | 4180 | xs *rss_link = xs_fmt("%s.rss", snac.actor); |
| 4129 | 4181 | ||
| 4130 | *body = timeline_to_rss(&snac, elems, rss_title, rss_link, bio); | 4182 | *body = rss_from_timeline(&snac, elems, rss_title, rss_link, bio); |
| 4131 | *b_size = strlen(*body); | 4183 | *b_size = strlen(*body); |
| 4132 | *ctype = "application/rss+xml; charset=utf-8"; | 4184 | *ctype = "application/rss+xml; charset=utf-8"; |
| 4133 | status = HTTP_STATUS_OK; | 4185 | status = HTTP_STATUS_OK; |
| @@ -4365,7 +4417,7 @@ int html_post_handler(const xs_dict *req, const char *q_path, | |||
| 4365 | xs_rnd_buf(rnd, sizeof(rnd)); | 4417 | xs_rnd_buf(rnd, sizeof(rnd)); |
| 4366 | 4418 | ||
| 4367 | const char *ext = strrchr(fn, '.'); | 4419 | const char *ext = strrchr(fn, '.'); |
| 4368 | xs *hash = xs_md5_hex(rnd, strlen(rnd)); | 4420 | xs *hash = xs_md5_hex(rnd, sizeof(rnd)); |
| 4369 | xs *id = xs_fmt("post-%s%s", hash, ext ? ext : ""); | 4421 | xs *id = xs_fmt("post-%s%s", hash, ext ? ext : ""); |
| 4370 | xs *url = xs_fmt("%s/s/%s", snac.actor, id); | 4422 | xs *url = xs_fmt("%s/s/%s", snac.actor, id); |
| 4371 | int fo = xs_number_get(xs_list_get(attach_file, 1)); | 4423 | int fo = xs_number_get(xs_list_get(attach_file, 1)); |
| @@ -4825,6 +4877,8 @@ int html_post_handler(const xs_dict *req, const char *q_path, | |||
| 4825 | snac.config = xs_dict_set(snac.config, "latitude", xs_dict_get_def(p_vars, "latitude", "")); | 4877 | snac.config = xs_dict_set(snac.config, "latitude", xs_dict_get_def(p_vars, "latitude", "")); |
| 4826 | snac.config = xs_dict_set(snac.config, "longitude", xs_dict_get_def(p_vars, "longitude", "")); | 4878 | snac.config = xs_dict_set(snac.config, "longitude", xs_dict_get_def(p_vars, "longitude", "")); |
| 4827 | 4879 | ||
| 4880 | snac.config = xs_dict_set(snac.config, "notify_webhook", xs_dict_get_def(p_vars, "notify_webhook", "")); | ||
| 4881 | |||
| 4828 | if ((v = xs_dict_get(p_vars, "metadata")) != NULL) | 4882 | if ((v = xs_dict_get(p_vars, "metadata")) != NULL) |
| 4829 | snac.config = xs_dict_set(snac.config, "metadata", v); | 4883 | snac.config = xs_dict_set(snac.config, "metadata", v); |
| 4830 | 4884 | ||
| @@ -4960,9 +5014,16 @@ int html_post_handler(const xs_dict *req, const char *q_path, | |||
| 4960 | if (*s1 == '\0') | 5014 | if (*s1 == '\0') |
| 4961 | continue; | 5015 | continue; |
| 4962 | 5016 | ||
| 4963 | xs *s2 = xs_utf8_to_lower(s1); | 5017 | xs *s2 = NULL; |
| 4964 | if (*s2 != '#') | 5018 | |
| 4965 | s2 = xs_str_prepend_i(s2, "#"); | 5019 | if (xs_startswith(s1, "https:/")) |
| 5020 | s2 = xs_dup(s1); | ||
| 5021 | else { | ||
| 5022 | s2 = xs_utf8_to_lower(s1); | ||
| 5023 | |||
| 5024 | if (*s2 != '#') | ||
| 5025 | s2 = xs_str_prepend_i(s2, "#"); | ||
| 5026 | } | ||
| 4966 | 5027 | ||
| 4967 | new_hashtags = xs_list_append(new_hashtags, s2); | 5028 | new_hashtags = xs_list_append(new_hashtags, s2); |
| 4968 | } | 5029 | } |
| @@ -5024,100 +5085,3 @@ int html_post_handler(const xs_dict *req, const char *q_path, | |||
| 5024 | 5085 | ||
| 5025 | return status; | 5086 | return status; |
| 5026 | } | 5087 | } |
| 5027 | |||
| 5028 | |||
| 5029 | xs_str *timeline_to_rss(snac *user, const xs_list *timeline, | ||
| 5030 | const char *title, const char *link, const char *desc) | ||
| 5031 | /* converts a timeline to rss */ | ||
| 5032 | { | ||
| 5033 | xs_html *rss = xs_html_tag("rss", | ||
| 5034 | xs_html_attr("xmlns:content", "http:/" "/purl.org/rss/1.0/modules/content/"), | ||
| 5035 | xs_html_attr("version", "2.0"), | ||
| 5036 | xs_html_attr("xmlns:atom", "http:/" "/www.w3.org/2005/Atom")); | ||
| 5037 | |||
| 5038 | xs_html *channel = xs_html_tag("channel", | ||
| 5039 | xs_html_tag("title", | ||
| 5040 | xs_html_text(title)), | ||
| 5041 | xs_html_tag("language", | ||
| 5042 | xs_html_text("en")), | ||
| 5043 | xs_html_tag("link", | ||
| 5044 | xs_html_text(link)), | ||
| 5045 | xs_html_sctag("atom:link", | ||
| 5046 | xs_html_attr("href", link), | ||
| 5047 | xs_html_attr("rel", "self"), | ||
| 5048 | xs_html_attr("type", "application/rss+xml")), | ||
| 5049 | xs_html_tag("generator", | ||
| 5050 | xs_html_text(USER_AGENT)), | ||
| 5051 | xs_html_tag("description", | ||
| 5052 | xs_html_text(desc))); | ||
| 5053 | |||
| 5054 | xs_html_add(rss, channel); | ||
| 5055 | |||
| 5056 | int cnt = 0; | ||
| 5057 | const char *v; | ||
| 5058 | |||
| 5059 | xs_list_foreach(timeline, v) { | ||
| 5060 | xs *msg = NULL; | ||
| 5061 | |||
| 5062 | if (user) { | ||
| 5063 | if (!valid_status(timeline_get_by_md5(user, v, &msg))) | ||
| 5064 | continue; | ||
| 5065 | } | ||
| 5066 | else { | ||
| 5067 | if (!valid_status(object_get_by_md5(v, &msg))) | ||
| 5068 | continue; | ||
| 5069 | } | ||
| 5070 | |||
| 5071 | const char *id = xs_dict_get(msg, "id"); | ||
| 5072 | const char *content = xs_dict_get(msg, "content"); | ||
| 5073 | const char *published = xs_dict_get(msg, "published"); | ||
| 5074 | |||
| 5075 | if (user && !xs_startswith(id, user->actor)) | ||
| 5076 | continue; | ||
| 5077 | |||
| 5078 | if (!id || !content || !published) | ||
| 5079 | continue; | ||
| 5080 | |||
| 5081 | /* create a title with the first line of the content */ | ||
| 5082 | xs *title = xs_replace(content, "<br>", "\n"); | ||
| 5083 | title = xs_regex_replace_i(title, "<[^>]+>", " "); | ||
| 5084 | title = xs_regex_replace_i(title, "&[^;]+;", " "); | ||
| 5085 | int i; | ||
| 5086 | |||
| 5087 | for (i = 0; title[i] && title[i] != '\n' && i < 50; i++); | ||
| 5088 | |||
| 5089 | if (title[i] != '\0') { | ||
| 5090 | title[i] = '\0'; | ||
| 5091 | title = xs_str_cat(title, "..."); | ||
| 5092 | } | ||
| 5093 | |||
| 5094 | title = xs_strip_i(title); | ||
| 5095 | |||
| 5096 | /* convert the date */ | ||
| 5097 | time_t t = xs_parse_iso_date(published, 0); | ||
| 5098 | xs *rss_date = xs_str_utctime(t, "%a, %d %b %Y %T +0000"); | ||
| 5099 | |||
| 5100 | /* if it's the first one, add it to the header */ | ||
| 5101 | if (cnt == 0) | ||
| 5102 | xs_html_add(channel, | ||
| 5103 | xs_html_tag("lastBuildDate", | ||
| 5104 | xs_html_text(rss_date))); | ||
| 5105 | |||
| 5106 | xs_html_add(channel, | ||
| 5107 | xs_html_tag("item", | ||
| 5108 | xs_html_tag("title", | ||
| 5109 | xs_html_text(title)), | ||
| 5110 | xs_html_tag("link", | ||
| 5111 | xs_html_text(id)), | ||
| 5112 | xs_html_tag("guid", | ||
| 5113 | xs_html_text(id)), | ||
| 5114 | xs_html_tag("pubDate", | ||
| 5115 | xs_html_text(rss_date)), | ||
| 5116 | xs_html_tag("description", | ||
| 5117 | xs_html_text(content)))); | ||
| 5118 | |||
| 5119 | cnt++; | ||
| 5120 | } | ||
| 5121 | |||
| 5122 | return xs_html_render_s(rss, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); | ||
| 5123 | } | ||