diff options
| author | 2024-12-05 22:53:40 +0100 | |
|---|---|---|
| committer | 2024-12-05 22:53:40 +0100 | |
| commit | bd74ffda5b25cc07e8d559815e027c8dab3b9d73 (patch) | |
| tree | 00725a5d5f53ed421b923de6fc1892c58815cc16 /activitypub.c | |
| parent | Makefile: enable static compilation with musl (diff) | |
| parent | Updated RELEASE_NOTES. (diff) | |
| download | snac2-bd74ffda5b25cc07e8d559815e027c8dab3b9d73.tar.gz snac2-bd74ffda5b25cc07e8d559815e027c8dab3b9d73.tar.xz snac2-bd74ffda5b25cc07e8d559815e027c8dab3b9d73.zip | |
Merge branch 'master' into build-with-musl
Diffstat (limited to 'activitypub.c')
| -rw-r--r-- | activitypub.c | 121 |
1 files changed, 96 insertions, 25 deletions
diff --git a/activitypub.c b/activitypub.c index 0b2fc6a..773df78 100644 --- a/activitypub.c +++ b/activitypub.c | |||
| @@ -183,6 +183,18 @@ const char *get_atto(const xs_dict *msg) | |||
| 183 | } | 183 | } |
| 184 | 184 | ||
| 185 | 185 | ||
| 186 | const char *get_in_reply_to(const xs_dict *msg) | ||
| 187 | /* gets the inReplyTo id */ | ||
| 188 | { | ||
| 189 | const xs_val *in_reply_to = xs_dict_get(msg, "inReplyTo"); | ||
| 190 | |||
| 191 | if (xs_type(in_reply_to) == XSTYPE_DICT) | ||
| 192 | in_reply_to = xs_dict_get(in_reply_to, "id"); | ||
| 193 | |||
| 194 | return in_reply_to; | ||
| 195 | } | ||
| 196 | |||
| 197 | |||
| 186 | xs_list *get_attachments(const xs_dict *msg) | 198 | xs_list *get_attachments(const xs_dict *msg) |
| 187 | /* unify the garbage fire that are the attachments */ | 199 | /* unify the garbage fire that are the attachments */ |
| 188 | { | 200 | { |
| @@ -373,7 +385,7 @@ int timeline_request(snac *snac, const char **id, xs_str **wrk, int level) | |||
| 373 | } | 385 | } |
| 374 | 386 | ||
| 375 | /* does it have an ancestor? */ | 387 | /* does it have an ancestor? */ |
| 376 | const char *in_reply_to = xs_dict_get(object, "inReplyTo"); | 388 | const char *in_reply_to = get_in_reply_to(object); |
| 377 | 389 | ||
| 378 | /* store */ | 390 | /* store */ |
| 379 | timeline_add(snac, nid, object); | 391 | timeline_add(snac, nid, object); |
| @@ -671,7 +683,7 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg) | |||
| 671 | return 3; | 683 | return 3; |
| 672 | 684 | ||
| 673 | /* is this message a reply to another? */ | 685 | /* is this message a reply to another? */ |
| 674 | const char *irt = xs_dict_get(msg, "inReplyTo"); | 686 | const char *irt = get_in_reply_to(msg); |
| 675 | if (!xs_is_null(irt)) { | 687 | if (!xs_is_null(irt)) { |
| 676 | xs *r_msg = NULL; | 688 | xs *r_msg = NULL; |
| 677 | 689 | ||
| @@ -724,7 +736,7 @@ xs_str *process_tags(snac *snac, const char *content, xs_list **tag) | |||
| 724 | /* use this same server */ | 736 | /* use this same server */ |
| 725 | def_srv = xs_dup(xs_dict_get(srv_config, "host")); | 737 | def_srv = xs_dup(xs_dict_get(srv_config, "host")); |
| 726 | 738 | ||
| 727 | split = xs_regex_split(content, "(@[A-Za-z0-9_]+(@[A-Za-z0-9\\.-]+)?|&#[0-9]+;|#[^[:punct:][:space:]]+)"); | 739 | split = xs_regex_split(content, "(@[A-Za-z0-9_]+(@[A-Za-z0-9\\.-]+)?|&#[0-9]+;|#(_|[^[:punct:][:space:]])+)"); |
| 728 | 740 | ||
| 729 | p = split; | 741 | p = split; |
| 730 | while (xs_list_iter(&p, &v)) { | 742 | while (xs_list_iter(&p, &v)) { |
| @@ -1026,15 +1038,14 @@ xs_dict *msg_base(snac *snac, const char *type, const char *id, | |||
| 1026 | } | 1038 | } |
| 1027 | 1039 | ||
| 1028 | 1040 | ||
| 1029 | xs_dict *msg_collection(snac *snac, const char *id) | 1041 | xs_dict *msg_collection(snac *snac, const char *id, int items) |
| 1030 | /* creates an empty OrderedCollection message */ | 1042 | /* creates an empty OrderedCollection message */ |
| 1031 | { | 1043 | { |
| 1032 | xs_dict *msg = msg_base(snac, "OrderedCollection", id, NULL, NULL, NULL); | 1044 | xs_dict *msg = msg_base(snac, "OrderedCollection", id, NULL, NULL, NULL); |
| 1033 | xs *ol = xs_list_new(); | 1045 | xs *n = xs_number_new(items); |
| 1034 | 1046 | ||
| 1035 | msg = xs_dict_append(msg, "attributedTo", snac->actor); | 1047 | msg = xs_dict_append(msg, "attributedTo", snac->actor); |
| 1036 | msg = xs_dict_append(msg, "orderedItems", ol); | 1048 | msg = xs_dict_append(msg, "totalItems", n); |
| 1037 | msg = xs_dict_append(msg, "totalItems", xs_stock(0)); | ||
| 1038 | 1049 | ||
| 1039 | return msg; | 1050 | return msg; |
| 1040 | } | 1051 | } |
| @@ -1206,7 +1217,30 @@ xs_dict *msg_actor(snac *snac) | |||
| 1206 | } | 1217 | } |
| 1207 | 1218 | ||
| 1208 | /* add the metadata as attachments of PropertyValue */ | 1219 | /* add the metadata as attachments of PropertyValue */ |
| 1209 | const xs_dict *metadata = xs_dict_get(snac->config, "metadata"); | 1220 | xs *metadata = NULL; |
| 1221 | const xs_dict *md = xs_dict_get(snac->config, "metadata"); | ||
| 1222 | |||
| 1223 | if (xs_type(md) == XSTYPE_DICT) | ||
| 1224 | metadata = xs_dup(md); | ||
| 1225 | else | ||
| 1226 | if (xs_type(md) == XSTYPE_STRING) { | ||
| 1227 | metadata = xs_dict_new(); | ||
| 1228 | xs *l = xs_split(md, "\n"); | ||
| 1229 | const char *ll; | ||
| 1230 | |||
| 1231 | xs_list_foreach(l, ll) { | ||
| 1232 | xs *kv = xs_split_n(ll, "=", 1); | ||
| 1233 | const char *k = xs_list_get(kv, 0); | ||
| 1234 | const char *v = xs_list_get(kv, 1); | ||
| 1235 | |||
| 1236 | if (k && v) { | ||
| 1237 | xs *kk = xs_strip_i(xs_dup(k)); | ||
| 1238 | xs *vv = xs_strip_i(xs_dup(v)); | ||
| 1239 | metadata = xs_dict_set(metadata, kk, vv); | ||
| 1240 | } | ||
| 1241 | } | ||
| 1242 | } | ||
| 1243 | |||
| 1210 | if (xs_type(metadata) == XSTYPE_DICT) { | 1244 | if (xs_type(metadata) == XSTYPE_DICT) { |
| 1211 | xs *attach = xs_list_new(); | 1245 | xs *attach = xs_list_new(); |
| 1212 | const xs_str *k; | 1246 | const xs_str *k; |
| @@ -1252,6 +1286,10 @@ xs_dict *msg_actor(snac *snac) | |||
| 1252 | msg = xs_dict_set(msg, "alsoKnownAs", loaka); | 1286 | msg = xs_dict_set(msg, "alsoKnownAs", loaka); |
| 1253 | } | 1287 | } |
| 1254 | 1288 | ||
| 1289 | const xs_val *manually = xs_dict_get(snac->config, "approve_followers"); | ||
| 1290 | msg = xs_dict_set(msg, "manuallyApprovesFollowers", | ||
| 1291 | xs_stock(xs_is_true(manually) ? XSTYPE_TRUE : XSTYPE_FALSE)); | ||
| 1292 | |||
| 1255 | return msg; | 1293 | return msg; |
| 1256 | } | 1294 | } |
| 1257 | 1295 | ||
| @@ -1888,22 +1926,31 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req) | |||
| 1888 | object_add(actor, actor_obj); | 1926 | object_add(actor, actor_obj); |
| 1889 | } | 1927 | } |
| 1890 | 1928 | ||
| 1891 | xs *f_msg = xs_dup(msg); | 1929 | if (xs_is_true(xs_dict_get(snac->config, "approve_followers"))) { |
| 1892 | xs *reply = msg_accept(snac, f_msg, actor); | 1930 | pending_add(snac, actor, msg); |
| 1893 | |||
| 1894 | post_message(snac, actor, reply); | ||
| 1895 | 1931 | ||
| 1896 | if (xs_is_null(xs_dict_get(f_msg, "published"))) { | 1932 | snac_log(snac, xs_fmt("new pending follower approval %s", actor)); |
| 1897 | /* add a date if it doesn't include one (Mastodon) */ | ||
| 1898 | xs *date = xs_str_utctime(0, ISO_DATE_SPEC); | ||
| 1899 | f_msg = xs_dict_set(f_msg, "published", date); | ||
| 1900 | } | 1933 | } |
| 1934 | else { | ||
| 1935 | /* automatic following */ | ||
| 1936 | xs *f_msg = xs_dup(msg); | ||
| 1937 | xs *reply = msg_accept(snac, f_msg, actor); | ||
| 1938 | |||
| 1939 | post_message(snac, actor, reply); | ||
| 1901 | 1940 | ||
| 1902 | timeline_add(snac, id, f_msg); | 1941 | if (xs_is_null(xs_dict_get(f_msg, "published"))) { |
| 1942 | /* add a date if it doesn't include one (Mastodon) */ | ||
| 1943 | xs *date = xs_str_utctime(0, ISO_DATE_SPEC); | ||
| 1944 | f_msg = xs_dict_set(f_msg, "published", date); | ||
| 1945 | } | ||
| 1946 | |||
| 1947 | timeline_add(snac, id, f_msg); | ||
| 1903 | 1948 | ||
| 1904 | follower_add(snac, actor); | 1949 | follower_add(snac, actor); |
| 1950 | |||
| 1951 | snac_log(snac, xs_fmt("new follower %s", actor)); | ||
| 1952 | } | ||
| 1905 | 1953 | ||
| 1906 | snac_log(snac, xs_fmt("new follower %s", actor)); | ||
| 1907 | do_notify = 1; | 1954 | do_notify = 1; |
| 1908 | } | 1955 | } |
| 1909 | else | 1956 | else |
| @@ -1925,6 +1972,11 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req) | |||
| 1925 | do_notify = 1; | 1972 | do_notify = 1; |
| 1926 | } | 1973 | } |
| 1927 | else | 1974 | else |
| 1975 | if (pending_check(snac, actor)) { | ||
| 1976 | pending_del(snac, actor); | ||
| 1977 | snac_log(snac, xs_fmt("cancelled pending follow from %s", actor)); | ||
| 1978 | } | ||
| 1979 | else | ||
| 1928 | snac_log(snac, xs_fmt("error deleting follower %s", actor)); | 1980 | snac_log(snac, xs_fmt("error deleting follower %s", actor)); |
| 1929 | } | 1981 | } |
| 1930 | } | 1982 | } |
| @@ -1957,7 +2009,7 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req) | |||
| 1957 | 2009 | ||
| 1958 | if (xs_match(utype, "Note|Article")) { /** **/ | 2010 | if (xs_match(utype, "Note|Article")) { /** **/ |
| 1959 | const char *id = xs_dict_get(object, "id"); | 2011 | const char *id = xs_dict_get(object, "id"); |
| 1960 | const char *in_reply_to = xs_dict_get(object, "inReplyTo"); | 2012 | const char *in_reply_to = get_in_reply_to(object); |
| 1961 | const char *atto = get_atto(object); | 2013 | const char *atto = get_atto(object); |
| 1962 | xs *wrk = NULL; | 2014 | xs *wrk = NULL; |
| 1963 | 2015 | ||
| @@ -2784,6 +2836,8 @@ int activitypub_get_handler(const xs_dict *req, const char *q_path, | |||
| 2784 | 2836 | ||
| 2785 | *ctype = "application/activity+json"; | 2837 | *ctype = "application/activity+json"; |
| 2786 | 2838 | ||
| 2839 | int show_contact_metrics = xs_is_true(xs_dict_get(snac.config, "show_contact_metrics")); | ||
| 2840 | |||
| 2787 | if (p_path == NULL) { | 2841 | if (p_path == NULL) { |
| 2788 | /* if there was no component after the user, it's an actor request */ | 2842 | /* if there was no component after the user, it's an actor request */ |
| 2789 | msg = msg_actor(&snac); | 2843 | msg = msg_actor(&snac); |
| @@ -2797,7 +2851,6 @@ int activitypub_get_handler(const xs_dict *req, const char *q_path, | |||
| 2797 | if (strcmp(p_path, "outbox") == 0 || strcmp(p_path, "featured") == 0) { | 2851 | if (strcmp(p_path, "outbox") == 0 || strcmp(p_path, "featured") == 0) { |
| 2798 | xs *id = xs_fmt("%s/%s", snac.actor, p_path); | 2852 | xs *id = xs_fmt("%s/%s", snac.actor, p_path); |
| 2799 | xs *list = xs_list_new(); | 2853 | xs *list = xs_list_new(); |
| 2800 | msg = msg_collection(&snac, id); | ||
| 2801 | const char *v; | 2854 | const char *v; |
| 2802 | int tc = 0; | 2855 | int tc = 0; |
| 2803 | 2856 | ||
| @@ -2819,14 +2872,32 @@ int activitypub_get_handler(const xs_dict *req, const char *q_path, | |||
| 2819 | } | 2872 | } |
| 2820 | 2873 | ||
| 2821 | /* replace the 'orderedItems' with the latest posts */ | 2874 | /* replace the 'orderedItems' with the latest posts */ |
| 2822 | xs *items = xs_number_new(xs_list_len(list)); | 2875 | msg = msg_collection(&snac, id, xs_list_len(list)); |
| 2823 | msg = xs_dict_set(msg, "orderedItems", list); | 2876 | msg = xs_dict_set(msg, "orderedItems", list); |
| 2824 | msg = xs_dict_set(msg, "totalItems", items); | ||
| 2825 | } | 2877 | } |
| 2826 | else | 2878 | else |
| 2827 | if (strcmp(p_path, "followers") == 0 || strcmp(p_path, "following") == 0) { | 2879 | if (strcmp(p_path, "followers") == 0) { |
| 2880 | int total = 0; | ||
| 2881 | |||
| 2882 | if (show_contact_metrics) { | ||
| 2883 | xs *l = follower_list(&snac); | ||
| 2884 | total = xs_list_len(l); | ||
| 2885 | } | ||
| 2886 | |||
| 2887 | xs *id = xs_fmt("%s/%s", snac.actor, p_path); | ||
| 2888 | msg = msg_collection(&snac, id, total); | ||
| 2889 | } | ||
| 2890 | else | ||
| 2891 | if (strcmp(p_path, "following") == 0) { | ||
| 2892 | int total = 0; | ||
| 2893 | |||
| 2894 | if (show_contact_metrics) { | ||
| 2895 | xs *l = following_list(&snac); | ||
| 2896 | total = xs_list_len(l); | ||
| 2897 | } | ||
| 2898 | |||
| 2828 | xs *id = xs_fmt("%s/%s", snac.actor, p_path); | 2899 | xs *id = xs_fmt("%s/%s", snac.actor, p_path); |
| 2829 | msg = msg_collection(&snac, id); | 2900 | msg = msg_collection(&snac, id, total); |
| 2830 | } | 2901 | } |
| 2831 | else | 2902 | else |
| 2832 | if (xs_startswith(p_path, "p/")) { | 2903 | if (xs_startswith(p_path, "p/")) { |