diff options
| author | 2024-05-25 08:05:36 +0000 | |
|---|---|---|
| committer | 2024-05-25 08:05:36 +0000 | |
| commit | 84a767dd0878013194ed7551b5ae6ef715e841a6 (patch) | |
| tree | 9fb1b2b89e0bfbb4b8bf1e85d840c8653e646bb7 /data.c | |
| parent | Prevent some browsers from caching servers basic auth request (diff) | |
| parent | Backport from xs (fix regex.h compilation with tcc). (diff) | |
| download | snac2-84a767dd0878013194ed7551b5ae6ef715e841a6.tar.gz snac2-84a767dd0878013194ed7551b5ae6ef715e841a6.tar.xz snac2-84a767dd0878013194ed7551b5ae6ef715e841a6.zip | |
Merge pull request 'master' (#1) from grunfink/snac2:master into master
Reviewed-on: https://codeberg.org/louis77/snac2/pulls/1
Diffstat (limited to 'data.c')
| -rw-r--r-- | data.c | 586 |
1 files changed, 465 insertions, 121 deletions
| @@ -10,6 +10,7 @@ | |||
| 10 | #include "xs_set.h" | 10 | #include "xs_set.h" |
| 11 | #include "xs_time.h" | 11 | #include "xs_time.h" |
| 12 | #include "xs_regex.h" | 12 | #include "xs_regex.h" |
| 13 | #include "xs_match.h" | ||
| 13 | 14 | ||
| 14 | #include "snac.h" | 15 | #include "snac.h" |
| 15 | 16 | ||
| @@ -28,7 +29,7 @@ pthread_mutex_t data_mutex = {0}; | |||
| 28 | int snac_upgrade(xs_str **error); | 29 | int snac_upgrade(xs_str **error); |
| 29 | 30 | ||
| 30 | 31 | ||
| 31 | int srv_open(char *basedir, int auto_upgrade) | 32 | int srv_open(const char *basedir, int auto_upgrade) |
| 32 | /* opens a server */ | 33 | /* opens a server */ |
| 33 | { | 34 | { |
| 34 | int ret = 0; | 35 | int ret = 0; |
| @@ -57,18 +58,20 @@ int srv_open(char *basedir, int auto_upgrade) | |||
| 57 | if (srv_config == NULL) | 58 | if (srv_config == NULL) |
| 58 | error = xs_fmt("ERROR: cannot parse '%s'", cfg_file); | 59 | error = xs_fmt("ERROR: cannot parse '%s'", cfg_file); |
| 59 | else { | 60 | else { |
| 60 | char *host; | 61 | const char *host; |
| 61 | char *prefix; | 62 | const char *prefix; |
| 62 | char *dbglvl; | 63 | const char *dbglvl; |
| 64 | const char *proto; | ||
| 63 | 65 | ||
| 64 | host = xs_dict_get(srv_config, "host"); | 66 | host = xs_dict_get(srv_config, "host"); |
| 65 | prefix = xs_dict_get(srv_config, "prefix"); | 67 | prefix = xs_dict_get(srv_config, "prefix"); |
| 66 | dbglvl = xs_dict_get(srv_config, "dbglevel"); | 68 | dbglvl = xs_dict_get(srv_config, "dbglevel"); |
| 69 | proto = xs_dict_get_def(srv_config, "protocol", "https"); | ||
| 67 | 70 | ||
| 68 | if (host == NULL || prefix == NULL) | 71 | if (host == NULL || prefix == NULL) |
| 69 | error = xs_str_new("ERROR: cannot get server data"); | 72 | error = xs_str_new("ERROR: cannot get server data"); |
| 70 | else { | 73 | else { |
| 71 | srv_baseurl = xs_fmt("https://%s%s", host, prefix); | 74 | srv_baseurl = xs_fmt("%s:/" "/%s%s", proto, host, prefix); |
| 72 | 75 | ||
| 73 | dbglevel = (int) xs_number_get(dbglvl); | 76 | dbglevel = (int) xs_number_get(dbglvl); |
| 74 | 77 | ||
| @@ -111,7 +114,7 @@ int srv_open(char *basedir, int auto_upgrade) | |||
| 111 | #endif | 114 | #endif |
| 112 | 115 | ||
| 113 | #ifdef __OpenBSD__ | 116 | #ifdef __OpenBSD__ |
| 114 | char *v = xs_dict_get(srv_config, "disable_openbsd_security"); | 117 | const char *v = xs_dict_get(srv_config, "disable_openbsd_security"); |
| 115 | 118 | ||
| 116 | if (v && xs_type(v) == XSTYPE_TRUE) { | 119 | if (v && xs_type(v) == XSTYPE_TRUE) { |
| 117 | srv_debug(1, xs_dup("OpenBSD security disabled by admin")); | 120 | srv_debug(1, xs_dup("OpenBSD security disabled by admin")); |
| @@ -190,7 +193,7 @@ int user_open(snac *user, const char *uid) | |||
| 190 | xs *lcuid = xs_tolower_i(xs_dup(uid)); | 193 | xs *lcuid = xs_tolower_i(xs_dup(uid)); |
| 191 | xs *ulist = user_list(); | 194 | xs *ulist = user_list(); |
| 192 | xs_list *p = ulist; | 195 | xs_list *p = ulist; |
| 193 | xs_str *v; | 196 | const xs_str *v; |
| 194 | 197 | ||
| 195 | while (xs_list_iter(&p, &v)) { | 198 | while (xs_list_iter(&p, &v)) { |
| 196 | xs *v2 = xs_tolower_i(xs_dup(v)); | 199 | xs *v2 = xs_tolower_i(xs_dup(v)); |
| @@ -286,7 +289,7 @@ int user_open_by_md5(snac *snac, const char *md5) | |||
| 286 | { | 289 | { |
| 287 | xs *ulist = user_list(); | 290 | xs *ulist = user_list(); |
| 288 | xs_list *p = ulist; | 291 | xs_list *p = ulist; |
| 289 | xs_str *v; | 292 | const xs_str *v; |
| 290 | 293 | ||
| 291 | while (xs_list_iter(&p, &v)) { | 294 | while (xs_list_iter(&p, &v)) { |
| 292 | user_open(snac, v); | 295 | user_open(snac, v); |
| @@ -338,6 +341,12 @@ double f_ctime(const char *fn) | |||
| 338 | } | 341 | } |
| 339 | 342 | ||
| 340 | 343 | ||
| 344 | int is_md5_hex(const char *md5) | ||
| 345 | { | ||
| 346 | return xs_is_hex(md5) && strlen(md5) == 32; | ||
| 347 | } | ||
| 348 | |||
| 349 | |||
| 341 | /** database 2.1+ **/ | 350 | /** database 2.1+ **/ |
| 342 | 351 | ||
| 343 | /** indexes **/ | 352 | /** indexes **/ |
| @@ -349,6 +358,11 @@ int index_add_md5(const char *fn, const char *md5) | |||
| 349 | int status = 201; /* Created */ | 358 | int status = 201; /* Created */ |
| 350 | FILE *f; | 359 | FILE *f; |
| 351 | 360 | ||
| 361 | if (!is_md5_hex(md5)) { | ||
| 362 | srv_log(xs_fmt("index_add_md5: bad md5 %s %s", fn, md5)); | ||
| 363 | return 400; | ||
| 364 | } | ||
| 365 | |||
| 352 | pthread_mutex_lock(&data_mutex); | 366 | pthread_mutex_lock(&data_mutex); |
| 353 | 367 | ||
| 354 | if ((f = fopen(fn, "a")) != NULL) { | 368 | if ((f = fopen(fn, "a")) != NULL) { |
| @@ -406,7 +420,7 @@ int index_del_md5(const char *fn, const char *md5) | |||
| 406 | fclose(f); | 420 | fclose(f); |
| 407 | } | 421 | } |
| 408 | else | 422 | else |
| 409 | status = 500; | 423 | status = 410; |
| 410 | 424 | ||
| 411 | pthread_mutex_unlock(&data_mutex); | 425 | pthread_mutex_unlock(&data_mutex); |
| 412 | 426 | ||
| @@ -604,7 +618,7 @@ static xs_str *_object_fn_by_md5(const char *md5, const char *func) | |||
| 604 | if (md5[0] == '-') | 618 | if (md5[0] == '-') |
| 605 | ok = 0; | 619 | ok = 0; |
| 606 | else | 620 | else |
| 607 | if (!xs_is_hex(md5) || strlen(md5) != 32) { | 621 | if (!is_md5_hex(md5)) { |
| 608 | srv_log(xs_fmt("_object_fn_by_md5() [from %s()]: bad md5 '%s'", func, md5)); | 622 | srv_log(xs_fmt("_object_fn_by_md5() [from %s()]: bad md5 '%s'", func, md5)); |
| 609 | ok = 0; | 623 | ok = 0; |
| 610 | } | 624 | } |
| @@ -696,7 +710,7 @@ int _object_add(const char *id, const xs_dict *obj, int ow) | |||
| 696 | fclose(f); | 710 | fclose(f); |
| 697 | 711 | ||
| 698 | /* does this object has a parent? */ | 712 | /* does this object has a parent? */ |
| 699 | char *in_reply_to = xs_dict_get(obj, "inReplyTo"); | 713 | const char *in_reply_to = xs_dict_get(obj, "inReplyTo"); |
| 700 | 714 | ||
| 701 | if (!xs_is_null(in_reply_to) && *in_reply_to) { | 715 | if (!xs_is_null(in_reply_to) && *in_reply_to) { |
| 702 | /* update the children index of the parent */ | 716 | /* update the children index of the parent */ |
| @@ -758,7 +772,8 @@ int object_del_by_md5(const char *md5) | |||
| 758 | xs *spec = xs_dup(fn); | 772 | xs *spec = xs_dup(fn); |
| 759 | spec = xs_replace_i(spec, ".json", "*.idx"); | 773 | spec = xs_replace_i(spec, ".json", "*.idx"); |
| 760 | xs *files = xs_glob(spec, 0, 0); | 774 | xs *files = xs_glob(spec, 0, 0); |
| 761 | char *p, *v; | 775 | char *p; |
| 776 | const char *v; | ||
| 762 | 777 | ||
| 763 | p = files; | 778 | p = files; |
| 764 | while (xs_list_iter(&p, &v)) { | 779 | while (xs_list_iter(&p, &v)) { |
| @@ -917,6 +932,9 @@ int object_unadmire(const char *id, const char *actor, int like) | |||
| 917 | 932 | ||
| 918 | status = index_del(fn, actor); | 933 | status = index_del(fn, actor); |
| 919 | 934 | ||
| 935 | if (valid_status(status)) | ||
| 936 | index_gc(fn); | ||
| 937 | |||
| 920 | srv_debug(0, | 938 | srv_debug(0, |
| 921 | xs_fmt("object_unadmire (%s) %s %s %d", like ? "Like" : "Announce", actor, fn, status)); | 939 | xs_fmt("object_unadmire (%s) %s %s %d", like ? "Like" : "Announce", actor, fn, status)); |
| 922 | 940 | ||
| @@ -1016,7 +1034,8 @@ xs_list *follower_list(snac *snac) | |||
| 1016 | { | 1034 | { |
| 1017 | xs *list = object_user_cache_list(snac, "followers", XS_ALL, 0); | 1035 | xs *list = object_user_cache_list(snac, "followers", XS_ALL, 0); |
| 1018 | xs_list *fwers = xs_list_new(); | 1036 | xs_list *fwers = xs_list_new(); |
| 1019 | char *p, *v; | 1037 | char *p; |
| 1038 | const char *v; | ||
| 1020 | 1039 | ||
| 1021 | /* resolve the list of md5 to be a list of actors */ | 1040 | /* resolve the list of md5 to be a list of actors */ |
| 1022 | p = list; | 1041 | p = list; |
| @@ -1060,14 +1079,18 @@ int timeline_touch(snac *snac) | |||
| 1060 | xs_str *timeline_fn_by_md5(snac *snac, const char *md5) | 1079 | xs_str *timeline_fn_by_md5(snac *snac, const char *md5) |
| 1061 | /* get the filename of an entry by md5 from any timeline */ | 1080 | /* get the filename of an entry by md5 from any timeline */ |
| 1062 | { | 1081 | { |
| 1063 | xs_str *fn = xs_fmt("%s/private/%s.json", snac->basedir, md5); | 1082 | xs_str *fn = NULL; |
| 1064 | 1083 | ||
| 1065 | if (mtime(fn) == 0.0) { | 1084 | if (xs_is_hex(md5) && strlen(md5) == 32) { |
| 1066 | fn = xs_free(fn); | 1085 | fn = xs_fmt("%s/private/%s.json", snac->basedir, md5); |
| 1067 | fn = xs_fmt("%s/public/%s.json", snac->basedir, md5); | ||
| 1068 | 1086 | ||
| 1069 | if (mtime(fn) == 0.0) | 1087 | if (mtime(fn) == 0.0) { |
| 1070 | fn = xs_free(fn); | 1088 | fn = xs_free(fn); |
| 1089 | fn = xs_fmt("%s/public/%s.json", snac->basedir, md5); | ||
| 1090 | |||
| 1091 | if (mtime(fn) == 0.0) | ||
| 1092 | fn = xs_free(fn); | ||
| 1093 | } | ||
| 1071 | } | 1094 | } |
| 1072 | 1095 | ||
| 1073 | return fn; | 1096 | return fn; |
| @@ -1103,7 +1126,7 @@ int timeline_get_by_md5(snac *snac, const char *md5, xs_dict **msg) | |||
| 1103 | } | 1126 | } |
| 1104 | 1127 | ||
| 1105 | 1128 | ||
| 1106 | int timeline_del(snac *snac, char *id) | 1129 | int timeline_del(snac *snac, const char *id) |
| 1107 | /* deletes a message from the timeline */ | 1130 | /* deletes a message from the timeline */ |
| 1108 | { | 1131 | { |
| 1109 | /* delete from the user's caches */ | 1132 | /* delete from the user's caches */ |
| @@ -1145,6 +1168,8 @@ int timeline_add(snac *snac, const char *id, const xs_dict *o_msg) | |||
| 1145 | 1168 | ||
| 1146 | tag_index(id, o_msg); | 1169 | tag_index(id, o_msg); |
| 1147 | 1170 | ||
| 1171 | list_distribute(snac, NULL, o_msg); | ||
| 1172 | |||
| 1148 | snac_debug(snac, 1, xs_fmt("timeline_add %s", id)); | 1173 | snac_debug(snac, 1, xs_fmt("timeline_add %s", id)); |
| 1149 | 1174 | ||
| 1150 | return ret; | 1175 | return ret; |
| @@ -1169,17 +1194,16 @@ int timeline_admire(snac *snac, const char *id, const char *admirer, int like) | |||
| 1169 | } | 1194 | } |
| 1170 | 1195 | ||
| 1171 | 1196 | ||
| 1172 | xs_list *timeline_top_level(snac *snac, xs_list *list) | 1197 | xs_list *timeline_top_level(snac *snac, const xs_list *list) |
| 1173 | /* returns the top level md5 entries from this index */ | 1198 | /* returns the top level md5 entries from this index */ |
| 1174 | { | 1199 | { |
| 1175 | xs_set seen; | 1200 | xs_set seen; |
| 1176 | xs_list *p; | 1201 | const xs_str *v; |
| 1177 | xs_str *v; | ||
| 1178 | 1202 | ||
| 1179 | xs_set_init(&seen); | 1203 | xs_set_init(&seen); |
| 1180 | 1204 | ||
| 1181 | p = list; | 1205 | int c = 0; |
| 1182 | while (xs_list_iter(&p, &v)) { | 1206 | while (xs_list_next(list, &v, &c)) { |
| 1183 | char line[256] = ""; | 1207 | char line[256] = ""; |
| 1184 | 1208 | ||
| 1185 | strncpy(line, v, sizeof(line)); | 1209 | strncpy(line, v, sizeof(line)); |
| @@ -1267,7 +1291,7 @@ int following_add(snac *snac, const char *actor, const xs_dict *msg) | |||
| 1267 | /* object already exists; if it's of type Accept, | 1291 | /* object already exists; if it's of type Accept, |
| 1268 | the actor is already being followed and confirmed, | 1292 | the actor is already being followed and confirmed, |
| 1269 | so do nothing */ | 1293 | so do nothing */ |
| 1270 | char *type = xs_dict_get(p_object, "type"); | 1294 | const char *type = xs_dict_get(p_object, "type"); |
| 1271 | 1295 | ||
| 1272 | if (!xs_is_null(type) && strcmp(type, "Accept") == 0) { | 1296 | if (!xs_is_null(type) && strcmp(type, "Accept") == 0) { |
| 1273 | snac_debug(snac, 1, xs_fmt("following_add actor already confirmed %s", actor)); | 1297 | snac_debug(snac, 1, xs_fmt("following_add actor already confirmed %s", actor)); |
| @@ -1345,7 +1369,7 @@ xs_list *following_list(snac *snac) | |||
| 1345 | xs *spec = xs_fmt("%s/following/" "*.json", snac->basedir); | 1369 | xs *spec = xs_fmt("%s/following/" "*.json", snac->basedir); |
| 1346 | xs *glist = xs_glob(spec, 0, 0); | 1370 | xs *glist = xs_glob(spec, 0, 0); |
| 1347 | xs_list *p; | 1371 | xs_list *p; |
| 1348 | xs_str *v; | 1372 | const xs_str *v; |
| 1349 | xs_list *list = xs_list_new(); | 1373 | xs_list *list = xs_list_new(); |
| 1350 | 1374 | ||
| 1351 | /* iterate the list of files */ | 1375 | /* iterate the list of files */ |
| @@ -1515,7 +1539,8 @@ void hide(snac *snac, const char *id) | |||
| 1515 | 1539 | ||
| 1516 | /* hide all the children */ | 1540 | /* hide all the children */ |
| 1517 | xs *chld = object_children(id); | 1541 | xs *chld = object_children(id); |
| 1518 | char *p, *v; | 1542 | char *p; |
| 1543 | const char *v; | ||
| 1519 | 1544 | ||
| 1520 | p = chld; | 1545 | p = chld; |
| 1521 | while (xs_list_iter(&p, &v)) { | 1546 | while (xs_list_iter(&p, &v)) { |
| @@ -1523,8 +1548,9 @@ void hide(snac *snac, const char *id) | |||
| 1523 | 1548 | ||
| 1524 | /* resolve to get the id */ | 1549 | /* resolve to get the id */ |
| 1525 | if (valid_status(object_get_by_md5(v, &co))) { | 1550 | if (valid_status(object_get_by_md5(v, &co))) { |
| 1526 | if ((v = xs_dict_get(co, "id")) != NULL) | 1551 | const char *id = xs_dict_get(co, "id"); |
| 1527 | hide(snac, v); | 1552 | if (id != NULL) |
| 1553 | hide(snac, id); | ||
| 1528 | } | 1554 | } |
| 1529 | } | 1555 | } |
| 1530 | } | 1556 | } |
| @@ -1540,7 +1566,7 @@ int is_hidden(snac *snac, const char *id) | |||
| 1540 | } | 1566 | } |
| 1541 | 1567 | ||
| 1542 | 1568 | ||
| 1543 | int actor_add(const char *actor, xs_dict *msg) | 1569 | int actor_add(const char *actor, const xs_dict *msg) |
| 1544 | /* adds an actor */ | 1570 | /* adds an actor */ |
| 1545 | { | 1571 | { |
| 1546 | return object_add_ow(actor, msg); | 1572 | return object_add_ow(actor, msg); |
| @@ -1609,7 +1635,7 @@ int actor_get_refresh(snac *user, const char *actor, xs_dict **data) | |||
| 1609 | int status = actor_get(actor, data); | 1635 | int status = actor_get(actor, data); |
| 1610 | 1636 | ||
| 1611 | if (status == 205 && user && !xs_startswith(actor, srv_baseurl)) | 1637 | if (status == 205 && user && !xs_startswith(actor, srv_baseurl)) |
| 1612 | enqueue_actor_refresh(user, actor); | 1638 | enqueue_actor_refresh(user, actor, 0); |
| 1613 | 1639 | ||
| 1614 | return status; | 1640 | return status; |
| 1615 | } | 1641 | } |
| @@ -1664,17 +1690,18 @@ int limited(snac *user, const char *id, int cmd) | |||
| 1664 | void tag_index(const char *id, const xs_dict *obj) | 1690 | void tag_index(const char *id, const xs_dict *obj) |
| 1665 | /* update the tag indexes for this object */ | 1691 | /* update the tag indexes for this object */ |
| 1666 | { | 1692 | { |
| 1667 | xs_list *tags = xs_dict_get(obj, "tag"); | 1693 | const xs_list *tags = xs_dict_get(obj, "tag"); |
| 1668 | 1694 | ||
| 1669 | if (is_msg_public(obj) && xs_type(tags) == XSTYPE_LIST && xs_list_len(tags) > 0) { | 1695 | if (is_msg_public(obj) && xs_type(tags) == XSTYPE_LIST && xs_list_len(tags) > 0) { |
| 1670 | xs *g_tag_dir = xs_fmt("%s/tag", srv_basedir); | 1696 | xs *g_tag_dir = xs_fmt("%s/tag", srv_basedir); |
| 1671 | 1697 | ||
| 1672 | mkdirx(g_tag_dir); | 1698 | mkdirx(g_tag_dir); |
| 1673 | 1699 | ||
| 1674 | xs_dict *v; | 1700 | const xs_dict *v; |
| 1675 | while (xs_list_iter(&tags, &v)) { | 1701 | int ct = 0; |
| 1676 | char *type = xs_dict_get(v, "type"); | 1702 | while (xs_list_next(tags, &v, &ct)) { |
| 1677 | char *name = xs_dict_get(v, "name"); | 1703 | const char *type = xs_dict_get(v, "type"); |
| 1704 | const char *name = xs_dict_get(v, "name"); | ||
| 1678 | 1705 | ||
| 1679 | if (!xs_is_null(type) && !xs_is_null(name) && strcmp(type, "Hashtag") == 0) { | 1706 | if (!xs_is_null(type) && !xs_is_null(name) && strcmp(type, "Hashtag") == 0) { |
| 1680 | while (*name == '#' || *name == '@') | 1707 | while (*name == '#' || *name == '@') |
| @@ -1683,7 +1710,7 @@ void tag_index(const char *id, const xs_dict *obj) | |||
| 1683 | if (*name == '\0') | 1710 | if (*name == '\0') |
| 1684 | continue; | 1711 | continue; |
| 1685 | 1712 | ||
| 1686 | name = xs_tolower_i(name); | 1713 | name = xs_tolower_i((xs_str *)name); |
| 1687 | 1714 | ||
| 1688 | xs *md5_tag = xs_md5_hex(name, strlen(name)); | 1715 | xs *md5_tag = xs_md5_hex(name, strlen(name)); |
| 1689 | xs *tag_dir = xs_fmt("%s/%c%c", g_tag_dir, md5_tag[0], md5_tag[1]); | 1716 | xs *tag_dir = xs_fmt("%s/%c%c", g_tag_dir, md5_tag[0], md5_tag[1]); |
| @@ -1706,7 +1733,7 @@ void tag_index(const char *id, const xs_dict *obj) | |||
| 1706 | } | 1733 | } |
| 1707 | 1734 | ||
| 1708 | 1735 | ||
| 1709 | xs_list *tag_search(char *tag, int skip, int show) | 1736 | xs_list *tag_search(const char *tag, int skip, int show) |
| 1710 | /* returns the list of posts tagged with tag */ | 1737 | /* returns the list of posts tagged with tag */ |
| 1711 | { | 1738 | { |
| 1712 | if (*tag == '#') | 1739 | if (*tag == '#') |
| @@ -1720,6 +1747,206 @@ xs_list *tag_search(char *tag, int skip, int show) | |||
| 1720 | } | 1747 | } |
| 1721 | 1748 | ||
| 1722 | 1749 | ||
| 1750 | /** lists **/ | ||
| 1751 | |||
| 1752 | xs_val *list_maint(snac *user, const char *list, int op) | ||
| 1753 | /* list maintenance */ | ||
| 1754 | { | ||
| 1755 | xs_val *l = NULL; | ||
| 1756 | |||
| 1757 | switch (op) { | ||
| 1758 | case 0: /** list of lists **/ | ||
| 1759 | { | ||
| 1760 | FILE *f; | ||
| 1761 | xs *spec = xs_fmt("%s/list/" "*.id", user->basedir); | ||
| 1762 | xs *ls = xs_glob(spec, 0, 0); | ||
| 1763 | int c = 0; | ||
| 1764 | const char *v; | ||
| 1765 | |||
| 1766 | l = xs_list_new(); | ||
| 1767 | |||
| 1768 | while (xs_list_next(ls, &v, &c)) { | ||
| 1769 | if ((f = fopen(v, "r")) != NULL) { | ||
| 1770 | xs *title = xs_readline(f); | ||
| 1771 | fclose(f); | ||
| 1772 | |||
| 1773 | title = xs_strip_i(title); | ||
| 1774 | |||
| 1775 | xs *v2 = xs_replace(v, ".id", ""); | ||
| 1776 | xs *l2 = xs_split(v2, "/"); | ||
| 1777 | |||
| 1778 | /* return [ list_id, list_title ] */ | ||
| 1779 | l = xs_list_append(l, xs_list_append(xs_list_new(), xs_list_get(l2, -1), title)); | ||
| 1780 | } | ||
| 1781 | } | ||
| 1782 | } | ||
| 1783 | |||
| 1784 | break; | ||
| 1785 | |||
| 1786 | case 1: /** create new list (list is the name) **/ | ||
| 1787 | { | ||
| 1788 | xs *lol = list_maint(user, NULL, 0); | ||
| 1789 | int c = 0; | ||
| 1790 | const xs_list *v; | ||
| 1791 | int add = 1; | ||
| 1792 | |||
| 1793 | /* check if this list name already exists */ | ||
| 1794 | while (xs_list_next(lol, &v, &c)) { | ||
| 1795 | if (strcmp(xs_list_get(v, 1), list) == 0) { | ||
| 1796 | add = 0; | ||
| 1797 | break; | ||
| 1798 | } | ||
| 1799 | } | ||
| 1800 | |||
| 1801 | if (add) { | ||
| 1802 | FILE *f; | ||
| 1803 | xs *dir = xs_fmt("%s/list/", user->basedir); | ||
| 1804 | xs *id = xs_fmt("%010x", time(NULL)); | ||
| 1805 | |||
| 1806 | mkdirx(dir); | ||
| 1807 | |||
| 1808 | xs *fn = xs_fmt("%s%s.id", dir, id); | ||
| 1809 | |||
| 1810 | if ((f = fopen(fn, "w")) != NULL) { | ||
| 1811 | fprintf(f, "%s\n", list); | ||
| 1812 | fclose(f); | ||
| 1813 | } | ||
| 1814 | |||
| 1815 | l = xs_stock(XSTYPE_TRUE); | ||
| 1816 | } | ||
| 1817 | else | ||
| 1818 | l = xs_stock(XSTYPE_FALSE); | ||
| 1819 | } | ||
| 1820 | |||
| 1821 | break; | ||
| 1822 | |||
| 1823 | case 2: /** delete list (list is the id) **/ | ||
| 1824 | { | ||
| 1825 | if (xs_is_hex(list)) { | ||
| 1826 | xs *fn = xs_fmt("%s/list/%s.id", user->basedir, list); | ||
| 1827 | unlink(fn); | ||
| 1828 | |||
| 1829 | fn = xs_replace_i(fn, ".id", ".lst"); | ||
| 1830 | unlink(fn); | ||
| 1831 | |||
| 1832 | fn = xs_replace_i(fn, ".lst", ".idx"); | ||
| 1833 | unlink(fn); | ||
| 1834 | |||
| 1835 | fn = xs_str_cat(fn, ".bak"); | ||
| 1836 | unlink(fn); | ||
| 1837 | } | ||
| 1838 | } | ||
| 1839 | |||
| 1840 | break; | ||
| 1841 | |||
| 1842 | case 3: /** get list name **/ | ||
| 1843 | if (xs_is_hex(list)) { | ||
| 1844 | FILE *f; | ||
| 1845 | xs *fn = xs_fmt("%s/list/%s.id", user->basedir, list); | ||
| 1846 | |||
| 1847 | if ((f = fopen(fn, "r")) != NULL) { | ||
| 1848 | l = xs_strip_i(xs_readline(f)); | ||
| 1849 | fclose(f); | ||
| 1850 | } | ||
| 1851 | } | ||
| 1852 | |||
| 1853 | break; | ||
| 1854 | } | ||
| 1855 | |||
| 1856 | return l; | ||
| 1857 | } | ||
| 1858 | |||
| 1859 | |||
| 1860 | xs_list *list_timeline(snac *user, const char *list, int skip, int show) | ||
| 1861 | /* returns the timeline of a list */ | ||
| 1862 | { | ||
| 1863 | xs_list *l = NULL; | ||
| 1864 | |||
| 1865 | if (!xs_is_hex(list)) | ||
| 1866 | return NULL; | ||
| 1867 | |||
| 1868 | xs *fn = xs_fmt("%s/list/%s.idx", user->basedir, list); | ||
| 1869 | |||
| 1870 | if (mtime(fn) > 0.0) | ||
| 1871 | l = index_list_desc(fn, skip, show); | ||
| 1872 | |||
| 1873 | return l; | ||
| 1874 | } | ||
| 1875 | |||
| 1876 | |||
| 1877 | xs_val *list_content(snac *user, const char *list, const char *actor_md5, int op) | ||
| 1878 | /* list content management */ | ||
| 1879 | { | ||
| 1880 | xs_val *l = NULL; | ||
| 1881 | |||
| 1882 | if (!xs_is_hex(list)) | ||
| 1883 | return NULL; | ||
| 1884 | |||
| 1885 | if (actor_md5 != NULL && !xs_is_hex(actor_md5)) | ||
| 1886 | return NULL; | ||
| 1887 | |||
| 1888 | xs *fn = xs_fmt("%s/list/%s.lst", user->basedir, list); | ||
| 1889 | |||
| 1890 | switch (op) { | ||
| 1891 | case 0: /** list content **/ | ||
| 1892 | l = index_list(fn, XS_ALL); | ||
| 1893 | |||
| 1894 | break; | ||
| 1895 | |||
| 1896 | case 1: /** append actor to list **/ | ||
| 1897 | if (actor_md5 != NULL) { | ||
| 1898 | if (!index_in(fn, actor_md5)) | ||
| 1899 | index_add_md5(fn, actor_md5); | ||
| 1900 | } | ||
| 1901 | |||
| 1902 | break; | ||
| 1903 | |||
| 1904 | case 2: /** delete actor from list **/ | ||
| 1905 | if (actor_md5 != NULL) | ||
| 1906 | index_del_md5(fn, actor_md5); | ||
| 1907 | |||
| 1908 | break; | ||
| 1909 | |||
| 1910 | default: | ||
| 1911 | srv_log(xs_fmt("ERROR: list_content: bad op %d", op)); | ||
| 1912 | break; | ||
| 1913 | } | ||
| 1914 | |||
| 1915 | return l; | ||
| 1916 | } | ||
| 1917 | |||
| 1918 | |||
| 1919 | void list_distribute(snac *user, const char *who, const xs_dict *post) | ||
| 1920 | /* distributes the post to all appropriate lists */ | ||
| 1921 | { | ||
| 1922 | const char *id = xs_dict_get(post, "id"); | ||
| 1923 | |||
| 1924 | /* if who is not set, use the attributedTo in the message */ | ||
| 1925 | if (xs_is_null(who)) | ||
| 1926 | who = get_atto(post); | ||
| 1927 | |||
| 1928 | if (xs_type(who) == XSTYPE_STRING && xs_type(id) == XSTYPE_STRING) { | ||
| 1929 | xs *a_md5 = xs_md5_hex(who, strlen(who)); | ||
| 1930 | xs *i_md5 = xs_md5_hex(id, strlen(id)); | ||
| 1931 | xs *spec = xs_fmt("%s/list/" "*.lst", user->basedir); | ||
| 1932 | xs *ls = xs_glob(spec, 0, 0); | ||
| 1933 | int c = 0; | ||
| 1934 | const char *v; | ||
| 1935 | |||
| 1936 | while (xs_list_next(ls, &v, &c)) { | ||
| 1937 | /* is the actor in this list? */ | ||
| 1938 | if (index_in_md5(v, a_md5)) { | ||
| 1939 | /* it is; add post md5 to its timeline */ | ||
| 1940 | xs *idx = xs_replace(v, ".lst", ".idx"); | ||
| 1941 | index_add_md5(idx, i_md5); | ||
| 1942 | |||
| 1943 | snac_debug(user, 1, xs_fmt("listed post %s in %s", id, idx)); | ||
| 1944 | } | ||
| 1945 | } | ||
| 1946 | } | ||
| 1947 | } | ||
| 1948 | |||
| 1949 | |||
| 1723 | /** static data **/ | 1950 | /** static data **/ |
| 1724 | 1951 | ||
| 1725 | static int _load_raw_file(const char *fn, xs_val **data, int *size, | 1952 | static int _load_raw_file(const char *fn, xs_val **data, int *size, |
| @@ -1944,7 +2171,7 @@ void inbox_add(const char *inbox) | |||
| 1944 | void inbox_add_by_actor(const xs_dict *actor) | 2171 | void inbox_add_by_actor(const xs_dict *actor) |
| 1945 | /* collects an actor's shared inbox, if it has one */ | 2172 | /* collects an actor's shared inbox, if it has one */ |
| 1946 | { | 2173 | { |
| 1947 | char *v; | 2174 | const char *v; |
| 1948 | 2175 | ||
| 1949 | if (!xs_is_null(v = xs_dict_get(actor, "endpoints")) && | 2176 | if (!xs_is_null(v = xs_dict_get(actor, "endpoints")) && |
| 1950 | !xs_is_null(v = xs_dict_get(v, "sharedInbox"))) { | 2177 | !xs_is_null(v = xs_dict_get(v, "sharedInbox"))) { |
| @@ -1962,7 +2189,7 @@ xs_list *inbox_list(void) | |||
| 1962 | xs *spec = xs_fmt("%s/inbox/" "*", srv_basedir); | 2189 | xs *spec = xs_fmt("%s/inbox/" "*", srv_basedir); |
| 1963 | xs *files = xs_glob(spec, 0, 0); | 2190 | xs *files = xs_glob(spec, 0, 0); |
| 1964 | xs_list *p = files; | 2191 | xs_list *p = files; |
| 1965 | xs_val *v; | 2192 | const xs_val *v; |
| 1966 | 2193 | ||
| 1967 | while (xs_list_iter(&p, &v)) { | 2194 | while (xs_list_iter(&p, &v)) { |
| 1968 | FILE *f; | 2195 | FILE *f; |
| @@ -1987,9 +2214,10 @@ xs_list *inbox_list(void) | |||
| 1987 | 2214 | ||
| 1988 | xs_str *_instance_block_fn(const char *instance) | 2215 | xs_str *_instance_block_fn(const char *instance) |
| 1989 | { | 2216 | { |
| 1990 | xs *s1 = xs_replace(instance, "https:/" "/", ""); | 2217 | xs *s = xs_replace(instance, "http:/" "/", ""); |
| 2218 | xs *s1 = xs_replace(s, "https:/" "/", ""); | ||
| 1991 | xs *l = xs_split(s1, "/"); | 2219 | xs *l = xs_split(s1, "/"); |
| 1992 | char *p = xs_list_get(l, 0); | 2220 | const char *p = xs_list_get(l, 0); |
| 1993 | xs *md5 = xs_md5_hex(p, strlen(p)); | 2221 | xs *md5 = xs_md5_hex(p, strlen(p)); |
| 1994 | 2222 | ||
| 1995 | return xs_fmt("%s/block/%s", srv_basedir, md5); | 2223 | return xs_fmt("%s/block/%s", srv_basedir, md5); |
| @@ -2049,20 +2277,20 @@ int instance_unblock(const char *instance) | |||
| 2049 | } | 2277 | } |
| 2050 | 2278 | ||
| 2051 | 2279 | ||
| 2052 | /** content filtering **/ | 2280 | /** operations by content **/ |
| 2053 | 2281 | ||
| 2054 | int content_check(const char *file, const xs_dict *msg) | 2282 | int content_match(const char *file, const xs_dict *msg) |
| 2055 | /* checks if a message's content matches any of the regexes in file */ | 2283 | /* checks if a message's content matches any of the regexes in file */ |
| 2056 | /* file format: one regex per line */ | 2284 | /* file format: one regex per line */ |
| 2057 | { | 2285 | { |
| 2058 | xs *fn = xs_fmt("%s/%s", srv_basedir, file); | 2286 | xs *fn = xs_fmt("%s/%s", srv_basedir, file); |
| 2059 | FILE *f; | 2287 | FILE *f; |
| 2060 | int r = 0; | 2288 | int r = 0; |
| 2061 | char *v = xs_dict_get(msg, "content"); | 2289 | const char *v = xs_dict_get(msg, "content"); |
| 2062 | 2290 | ||
| 2063 | if (xs_type(v) == XSTYPE_STRING && *v) { | 2291 | if (xs_type(v) == XSTYPE_STRING && *v) { |
| 2064 | if ((f = fopen(fn, "r")) != NULL) { | 2292 | if ((f = fopen(fn, "r")) != NULL) { |
| 2065 | srv_debug(1, xs_fmt("content_check: loading regexes from %s", fn)); | 2293 | srv_debug(1, xs_fmt("content_match: loading regexes from %s", fn)); |
| 2066 | 2294 | ||
| 2067 | /* massage content (strip HTML tags, etc.) */ | 2295 | /* massage content (strip HTML tags, etc.) */ |
| 2068 | xs *c = xs_regex_replace(v, "<[^>]+>", " "); | 2296 | xs *c = xs_regex_replace(v, "<[^>]+>", " "); |
| @@ -2072,13 +2300,9 @@ int content_check(const char *file, const xs_dict *msg) | |||
| 2072 | while (!r && !feof(f)) { | 2300 | while (!r && !feof(f)) { |
| 2073 | xs *rx = xs_strip_i(xs_readline(f)); | 2301 | xs *rx = xs_strip_i(xs_readline(f)); |
| 2074 | 2302 | ||
| 2075 | if (*rx) { | 2303 | if (*rx && xs_regex_match(c, rx)) { |
| 2076 | xs *l = xs_regex_select_n(c, rx, 1); | 2304 | srv_debug(1, xs_fmt("content_match: match for '%s'", rx)); |
| 2077 | 2305 | r = 1; | |
| 2078 | if (xs_list_len(l)) { | ||
| 2079 | srv_debug(1, xs_fmt("content_check: match for '%s'", rx)); | ||
| 2080 | r = 1; | ||
| 2081 | } | ||
| 2082 | } | 2306 | } |
| 2083 | } | 2307 | } |
| 2084 | 2308 | ||
| @@ -2090,6 +2314,119 @@ int content_check(const char *file, const xs_dict *msg) | |||
| 2090 | } | 2314 | } |
| 2091 | 2315 | ||
| 2092 | 2316 | ||
| 2317 | xs_list *content_search(snac *user, const char *regex, | ||
| 2318 | int priv, int skip, int show, int max_secs, int *timeout) | ||
| 2319 | /* returns a list of posts which content matches the regex */ | ||
| 2320 | { | ||
| 2321 | if (regex == NULL || *regex == '\0') | ||
| 2322 | return xs_list_new(); | ||
| 2323 | |||
| 2324 | xs *i_regex = xs_tolower_i(xs_dup(regex)); | ||
| 2325 | |||
| 2326 | xs_set seen; | ||
| 2327 | |||
| 2328 | xs_set_init(&seen); | ||
| 2329 | |||
| 2330 | if (max_secs == 0) | ||
| 2331 | max_secs = 3; | ||
| 2332 | |||
| 2333 | time_t t = time(NULL) + max_secs; | ||
| 2334 | *timeout = 0; | ||
| 2335 | |||
| 2336 | /* iterate all timelines simultaneously */ | ||
| 2337 | xs_list *tls[3] = {0}; | ||
| 2338 | const char *md5s[3] = {0}; | ||
| 2339 | int c[3] = {0}; | ||
| 2340 | |||
| 2341 | tls[0] = timeline_simple_list(user, "public", 0, XS_ALL); /* public */ | ||
| 2342 | tls[1] = timeline_instance_list(0, XS_ALL); /* instance */ | ||
| 2343 | tls[2] = priv ? timeline_simple_list(user, "private", 0, XS_ALL) : xs_list_new(); /* private or none */ | ||
| 2344 | |||
| 2345 | /* first positioning */ | ||
| 2346 | for (int n = 0; n < 3; n++) | ||
| 2347 | xs_list_next(tls[n], &md5s[n], &c[n]); | ||
| 2348 | |||
| 2349 | show += skip; | ||
| 2350 | |||
| 2351 | while (show > 0) { | ||
| 2352 | /* timeout? */ | ||
| 2353 | if (time(NULL) > t) { | ||
| 2354 | *timeout = 1; | ||
| 2355 | break; | ||
| 2356 | } | ||
| 2357 | |||
| 2358 | /* find the newest post */ | ||
| 2359 | int newest = -1; | ||
| 2360 | double mtime = 0.0; | ||
| 2361 | |||
| 2362 | for (int n = 0; n < 3; n++) { | ||
| 2363 | if (md5s[n] != NULL) { | ||
| 2364 | xs *fn = _object_fn_by_md5(md5s[n], "content_search"); | ||
| 2365 | double mt = mtime(fn); | ||
| 2366 | |||
| 2367 | if (mt > mtime) { | ||
| 2368 | newest = n; | ||
| 2369 | mtime = mt; | ||
| 2370 | } | ||
| 2371 | } | ||
| 2372 | } | ||
| 2373 | |||
| 2374 | if (newest == -1) | ||
| 2375 | break; | ||
| 2376 | |||
| 2377 | const char *md5 = md5s[newest]; | ||
| 2378 | |||
| 2379 | /* advance the chosen timeline */ | ||
| 2380 | if (!xs_list_next(tls[newest], &md5s[newest], &c[newest])) | ||
| 2381 | md5s[newest] = NULL; | ||
| 2382 | |||
| 2383 | xs *post = NULL; | ||
| 2384 | |||
| 2385 | if (!valid_status(object_get_by_md5(md5, &post))) | ||
| 2386 | continue; | ||
| 2387 | |||
| 2388 | if (!xs_match(xs_dict_get_def(post, "type", "-"), POSTLIKE_OBJECT_TYPE)) | ||
| 2389 | continue; | ||
| 2390 | |||
| 2391 | const char *id = xs_dict_get(post, "id"); | ||
| 2392 | |||
| 2393 | if (id == NULL || is_hidden(user, id)) | ||
| 2394 | continue; | ||
| 2395 | |||
| 2396 | const char *content = xs_dict_get(post, "content"); | ||
| 2397 | |||
| 2398 | if (xs_is_null(content)) | ||
| 2399 | continue; | ||
| 2400 | |||
| 2401 | /* strip HTML */ | ||
| 2402 | xs *c = xs_regex_replace(content, "<[^>]+>", " "); | ||
| 2403 | c = xs_regex_replace_i(c, " {2,}", " "); | ||
| 2404 | c = xs_tolower_i(c); | ||
| 2405 | |||
| 2406 | /* apply regex */ | ||
| 2407 | if (xs_regex_match(c, i_regex)) { | ||
| 2408 | if (xs_set_add(&seen, md5) == 1) | ||
| 2409 | show--; | ||
| 2410 | } | ||
| 2411 | } | ||
| 2412 | |||
| 2413 | xs_list *r = xs_set_result(&seen); | ||
| 2414 | |||
| 2415 | if (skip) { | ||
| 2416 | /* BAD */ | ||
| 2417 | while (skip--) { | ||
| 2418 | r = xs_list_del(r, 0); | ||
| 2419 | } | ||
| 2420 | } | ||
| 2421 | |||
| 2422 | xs_free(tls[0]); | ||
| 2423 | xs_free(tls[1]); | ||
| 2424 | xs_free(tls[2]); | ||
| 2425 | |||
| 2426 | return r; | ||
| 2427 | } | ||
| 2428 | |||
| 2429 | |||
| 2093 | /** notifications **/ | 2430 | /** notifications **/ |
| 2094 | 2431 | ||
| 2095 | xs_str *notify_check_time(snac *snac, int reset) | 2432 | xs_str *notify_check_time(snac *snac, int reset) |
| @@ -2203,7 +2540,7 @@ xs_list *notify_list(snac *snac, int skip, int show) | |||
| 2203 | xs *spec = xs_fmt("%s/notify/" "*.json", snac->basedir); | 2540 | xs *spec = xs_fmt("%s/notify/" "*.json", snac->basedir); |
| 2204 | xs *lst = xs_glob(spec, 1, 0); | 2541 | xs *lst = xs_glob(spec, 1, 0); |
| 2205 | xs_list *p = lst; | 2542 | xs_list *p = lst; |
| 2206 | char *v; | 2543 | const char *v; |
| 2207 | 2544 | ||
| 2208 | while (xs_list_iter(&p, &v)) { | 2545 | while (xs_list_iter(&p, &v)) { |
| 2209 | char *p = strrchr(v, '.'); | 2546 | char *p = strrchr(v, '.'); |
| @@ -2231,7 +2568,7 @@ int notify_new_num(snac *snac) | |||
| 2231 | int cnt = 0; | 2568 | int cnt = 0; |
| 2232 | 2569 | ||
| 2233 | xs_list *p = lst; | 2570 | xs_list *p = lst; |
| 2234 | xs_str *v; | 2571 | const xs_str *v; |
| 2235 | 2572 | ||
| 2236 | while (xs_list_iter(&p, &v)) { | 2573 | while (xs_list_iter(&p, &v)) { |
| 2237 | xs *id = xs_strip_i(xs_dup(v)); | 2574 | xs *id = xs_strip_i(xs_dup(v)); |
| @@ -2253,7 +2590,7 @@ void notify_clear(snac *snac) | |||
| 2253 | xs *spec = xs_fmt("%s/notify/" "*", snac->basedir); | 2590 | xs *spec = xs_fmt("%s/notify/" "*", snac->basedir); |
| 2254 | xs *lst = xs_glob(spec, 0, 0); | 2591 | xs *lst = xs_glob(spec, 0, 0); |
| 2255 | xs_list *p = lst; | 2592 | xs_list *p = lst; |
| 2256 | xs_str *v; | 2593 | const xs_str *v; |
| 2257 | 2594 | ||
| 2258 | while (xs_list_iter(&p, &v)) | 2595 | while (xs_list_iter(&p, &v)) |
| 2259 | unlink(v); | 2596 | unlink(v); |
| @@ -2309,7 +2646,7 @@ void enqueue_input(snac *snac, const xs_dict *msg, const xs_dict *req, int retri | |||
| 2309 | /* enqueues an input message */ | 2646 | /* enqueues an input message */ |
| 2310 | { | 2647 | { |
| 2311 | xs *qmsg = _new_qmsg("input", msg, retries); | 2648 | xs *qmsg = _new_qmsg("input", msg, retries); |
| 2312 | char *ntid = xs_dict_get(qmsg, "ntid"); | 2649 | const char *ntid = xs_dict_get(qmsg, "ntid"); |
| 2313 | xs *fn = xs_fmt("%s/queue/%s.json", snac->basedir, ntid); | 2650 | xs *fn = xs_fmt("%s/queue/%s.json", snac->basedir, ntid); |
| 2314 | 2651 | ||
| 2315 | qmsg = xs_dict_append(qmsg, "req", req); | 2652 | qmsg = xs_dict_append(qmsg, "req", req); |
| @@ -2324,7 +2661,7 @@ void enqueue_shared_input(const xs_dict *msg, const xs_dict *req, int retries) | |||
| 2324 | /* enqueues an input message from the shared input */ | 2661 | /* enqueues an input message from the shared input */ |
| 2325 | { | 2662 | { |
| 2326 | xs *qmsg = _new_qmsg("input", msg, retries); | 2663 | xs *qmsg = _new_qmsg("input", msg, retries); |
| 2327 | char *ntid = xs_dict_get(qmsg, "ntid"); | 2664 | const char *ntid = xs_dict_get(qmsg, "ntid"); |
| 2328 | xs *fn = xs_fmt("%s/queue/%s.json", srv_basedir, ntid); | 2665 | xs *fn = xs_fmt("%s/queue/%s.json", srv_basedir, ntid); |
| 2329 | 2666 | ||
| 2330 | qmsg = xs_dict_append(qmsg, "req", req); | 2667 | qmsg = xs_dict_append(qmsg, "req", req); |
| @@ -2336,11 +2673,12 @@ void enqueue_shared_input(const xs_dict *msg, const xs_dict *req, int retries) | |||
| 2336 | 2673 | ||
| 2337 | 2674 | ||
| 2338 | void enqueue_output_raw(const char *keyid, const char *seckey, | 2675 | void enqueue_output_raw(const char *keyid, const char *seckey, |
| 2339 | xs_dict *msg, xs_str *inbox, int retries, int p_status) | 2676 | const xs_dict *msg, const xs_str *inbox, |
| 2677 | int retries, int p_status) | ||
| 2340 | /* enqueues an output message to an inbox */ | 2678 | /* enqueues an output message to an inbox */ |
| 2341 | { | 2679 | { |
| 2342 | xs *qmsg = _new_qmsg("output", msg, retries); | 2680 | xs *qmsg = _new_qmsg("output", msg, retries); |
| 2343 | char *ntid = xs_dict_get(qmsg, "ntid"); | 2681 | const char *ntid = xs_dict_get(qmsg, "ntid"); |
| 2344 | xs *fn = xs_fmt("%s/queue/%s.json", srv_basedir, ntid); | 2682 | xs *fn = xs_fmt("%s/queue/%s.json", srv_basedir, ntid); |
| 2345 | 2683 | ||
| 2346 | xs *ns = xs_number_new(p_status); | 2684 | xs *ns = xs_number_new(p_status); |
| @@ -2360,7 +2698,8 @@ void enqueue_output_raw(const char *keyid, const char *seckey, | |||
| 2360 | } | 2698 | } |
| 2361 | 2699 | ||
| 2362 | 2700 | ||
| 2363 | void enqueue_output(snac *snac, xs_dict *msg, xs_str *inbox, int retries, int p_status) | 2701 | void enqueue_output(snac *snac, const xs_dict *msg, |
| 2702 | const xs_str *inbox, int retries, int p_status) | ||
| 2364 | /* enqueues an output message to an inbox */ | 2703 | /* enqueues an output message to an inbox */ |
| 2365 | { | 2704 | { |
| 2366 | if (xs_startswith(inbox, snac->actor)) { | 2705 | if (xs_startswith(inbox, snac->actor)) { |
| @@ -2368,13 +2707,14 @@ void enqueue_output(snac *snac, xs_dict *msg, xs_str *inbox, int retries, int p_ | |||
| 2368 | return; | 2707 | return; |
| 2369 | } | 2708 | } |
| 2370 | 2709 | ||
| 2371 | char *seckey = xs_dict_get(snac->key, "secret"); | 2710 | const char *seckey = xs_dict_get(snac->key, "secret"); |
| 2372 | 2711 | ||
| 2373 | enqueue_output_raw(snac->actor, seckey, msg, inbox, retries, p_status); | 2712 | enqueue_output_raw(snac->actor, seckey, msg, inbox, retries, p_status); |
| 2374 | } | 2713 | } |
| 2375 | 2714 | ||
| 2376 | 2715 | ||
| 2377 | void enqueue_output_by_actor(snac *snac, xs_dict *msg, const xs_str *actor, int retries) | 2716 | void enqueue_output_by_actor(snac *snac, const xs_dict *msg, |
| 2717 | const xs_str *actor, int retries) | ||
| 2378 | /* enqueues an output message for an actor */ | 2718 | /* enqueues an output message for an actor */ |
| 2379 | { | 2719 | { |
| 2380 | xs *inbox = get_actor_inbox(actor); | 2720 | xs *inbox = get_actor_inbox(actor); |
| @@ -2386,11 +2726,11 @@ void enqueue_output_by_actor(snac *snac, xs_dict *msg, const xs_str *actor, int | |||
| 2386 | } | 2726 | } |
| 2387 | 2727 | ||
| 2388 | 2728 | ||
| 2389 | void enqueue_email(xs_str *msg, int retries) | 2729 | void enqueue_email(const xs_str *msg, int retries) |
| 2390 | /* enqueues an email message to be sent */ | 2730 | /* enqueues an email message to be sent */ |
| 2391 | { | 2731 | { |
| 2392 | xs *qmsg = _new_qmsg("email", msg, retries); | 2732 | xs *qmsg = _new_qmsg("email", msg, retries); |
| 2393 | char *ntid = xs_dict_get(qmsg, "ntid"); | 2733 | const char *ntid = xs_dict_get(qmsg, "ntid"); |
| 2394 | xs *fn = xs_fmt("%s/queue/%s.json", srv_basedir, ntid); | 2734 | xs *fn = xs_fmt("%s/queue/%s.json", srv_basedir, ntid); |
| 2395 | 2735 | ||
| 2396 | qmsg = _enqueue_put(fn, qmsg); | 2736 | qmsg = _enqueue_put(fn, qmsg); |
| @@ -2403,7 +2743,7 @@ void enqueue_telegram(const xs_str *msg, const char *bot, const char *chat_id) | |||
| 2403 | /* enqueues a message to be sent via Telegram */ | 2743 | /* enqueues a message to be sent via Telegram */ |
| 2404 | { | 2744 | { |
| 2405 | xs *qmsg = _new_qmsg("telegram", msg, 0); | 2745 | xs *qmsg = _new_qmsg("telegram", msg, 0); |
| 2406 | char *ntid = xs_dict_get(qmsg, "ntid"); | 2746 | const char *ntid = xs_dict_get(qmsg, "ntid"); |
| 2407 | xs *fn = xs_fmt("%s/queue/%s.json", srv_basedir, ntid); | 2747 | xs *fn = xs_fmt("%s/queue/%s.json", srv_basedir, ntid); |
| 2408 | 2748 | ||
| 2409 | qmsg = xs_dict_append(qmsg, "bot", bot); | 2749 | qmsg = xs_dict_append(qmsg, "bot", bot); |
| @@ -2418,7 +2758,7 @@ void enqueue_ntfy(const xs_str *msg, const char *ntfy_server, const char *ntfy_t | |||
| 2418 | /* enqueues a message to be sent via ntfy */ | 2758 | /* enqueues a message to be sent via ntfy */ |
| 2419 | { | 2759 | { |
| 2420 | xs *qmsg = _new_qmsg("ntfy", msg, 0); | 2760 | xs *qmsg = _new_qmsg("ntfy", msg, 0); |
| 2421 | char *ntid = xs_dict_get(qmsg, "ntid"); | 2761 | const char *ntid = xs_dict_get(qmsg, "ntid"); |
| 2422 | xs *fn = xs_fmt("%s/queue/%s.json", srv_basedir, ntid); | 2762 | xs *fn = xs_fmt("%s/queue/%s.json", srv_basedir, ntid); |
| 2423 | 2763 | ||
| 2424 | qmsg = xs_dict_append(qmsg, "ntfy_server", ntfy_server); | 2764 | qmsg = xs_dict_append(qmsg, "ntfy_server", ntfy_server); |
| @@ -2434,7 +2774,7 @@ void enqueue_message(snac *snac, const xs_dict *msg) | |||
| 2434 | /* enqueues an output message */ | 2774 | /* enqueues an output message */ |
| 2435 | { | 2775 | { |
| 2436 | xs *qmsg = _new_qmsg("message", msg, 0); | 2776 | xs *qmsg = _new_qmsg("message", msg, 0); |
| 2437 | char *ntid = xs_dict_get(qmsg, "ntid"); | 2777 | const char *ntid = xs_dict_get(qmsg, "ntid"); |
| 2438 | xs *fn = xs_fmt("%s/queue/%s.json", snac->basedir, ntid); | 2778 | xs *fn = xs_fmt("%s/queue/%s.json", snac->basedir, ntid); |
| 2439 | 2779 | ||
| 2440 | qmsg = _enqueue_put(fn, qmsg); | 2780 | qmsg = _enqueue_put(fn, qmsg); |
| @@ -2458,67 +2798,47 @@ void enqueue_close_question(snac *user, const char *id, int end_secs) | |||
| 2458 | } | 2798 | } |
| 2459 | 2799 | ||
| 2460 | 2800 | ||
| 2461 | void enqueue_verify_links(snac *user) | 2801 | void enqueue_object_request(snac *user, const char *id, int forward_secs) |
| 2462 | /* enqueues a link verification */ | 2802 | /* enqueues the request of an object in the future */ |
| 2463 | { | 2803 | { |
| 2464 | xs *qmsg = _new_qmsg("verify_links", "", 0); | 2804 | xs *qmsg = _new_qmsg("object_request", id, 0); |
| 2465 | char *ntid = xs_dict_get(qmsg, "ntid"); | 2805 | xs *ntid = tid(forward_secs); |
| 2466 | xs *fn = xs_fmt("%s/queue/%s.json", user->basedir, ntid); | 2806 | xs *fn = xs_fmt("%s/queue/%s.json", user->basedir, ntid); |
| 2807 | |||
| 2808 | qmsg = xs_dict_set(qmsg, "ntid", ntid); | ||
| 2467 | 2809 | ||
| 2468 | qmsg = _enqueue_put(fn, qmsg); | 2810 | qmsg = _enqueue_put(fn, qmsg); |
| 2469 | 2811 | ||
| 2470 | snac_debug(user, 1, xs_fmt("enqueue_verify_links %s", user->actor)); | 2812 | snac_debug(user, 0, xs_fmt("enqueue_object_request %s %d", id, forward_secs)); |
| 2471 | } | 2813 | } |
| 2472 | 2814 | ||
| 2473 | 2815 | ||
| 2474 | void enqueue_actor_refresh(snac *user, const char *actor) | 2816 | void enqueue_verify_links(snac *user) |
| 2475 | /* enqueues an actor refresh */ | 2817 | /* enqueues a link verification */ |
| 2476 | { | 2818 | { |
| 2477 | xs *qmsg = _new_qmsg("actor_refresh", "", 0); | 2819 | xs *qmsg = _new_qmsg("verify_links", "", 0); |
| 2478 | char *ntid = xs_dict_get(qmsg, "ntid"); | 2820 | const char *ntid = xs_dict_get(qmsg, "ntid"); |
| 2479 | xs *fn = xs_fmt("%s/queue/%s.json", user->basedir, ntid); | 2821 | xs *fn = xs_fmt("%s/queue/%s.json", user->basedir, ntid); |
| 2480 | 2822 | ||
| 2481 | qmsg = xs_dict_append(qmsg, "actor", actor); | ||
| 2482 | |||
| 2483 | qmsg = _enqueue_put(fn, qmsg); | 2823 | qmsg = _enqueue_put(fn, qmsg); |
| 2484 | 2824 | ||
| 2485 | snac_debug(user, 1, xs_fmt("enqueue_actor_refresh %s", actor)); | 2825 | snac_debug(user, 1, xs_fmt("enqueue_verify_links %s", user->actor)); |
| 2486 | } | 2826 | } |
| 2487 | 2827 | ||
| 2488 | 2828 | ||
| 2489 | void enqueue_request_replies(snac *user, const char *id) | 2829 | void enqueue_actor_refresh(snac *user, const char *actor, int forward_secs) |
| 2490 | /* enqueues a request for the replies of a message */ | 2830 | /* enqueues an actor refresh */ |
| 2491 | { | 2831 | { |
| 2492 | /* test first if this precise request is already in the queue */ | 2832 | xs *qmsg = _new_qmsg("actor_refresh", "", 0); |
| 2493 | xs *queue = user_queue(user); | 2833 | xs *ntid = tid(forward_secs); |
| 2494 | xs_list *p = queue; | ||
| 2495 | xs_str *v; | ||
| 2496 | |||
| 2497 | while (xs_list_iter(&p, &v)) { | ||
| 2498 | xs *q_item = queue_get(v); | ||
| 2499 | |||
| 2500 | if (q_item != NULL) { | ||
| 2501 | const char *type = xs_dict_get(q_item, "type"); | ||
| 2502 | const char *msg = xs_dict_get(q_item, "message"); | ||
| 2503 | |||
| 2504 | if (type && msg && strcmp(type, "request_replies") == 0 && strcmp(msg, id) == 0) { | ||
| 2505 | /* don't requeue */ | ||
| 2506 | snac_debug(user, 1, xs_fmt("enqueue_request_replies already here %s", id)); | ||
| 2507 | return; | ||
| 2508 | } | ||
| 2509 | } | ||
| 2510 | } | ||
| 2511 | |||
| 2512 | /* not there; enqueue the request with a small delay */ | ||
| 2513 | xs *qmsg = _new_qmsg("request_replies", id, 0); | ||
| 2514 | xs *ntid = tid(10); | ||
| 2515 | xs *fn = xs_fmt("%s/queue/%s.json", user->basedir, ntid); | 2834 | xs *fn = xs_fmt("%s/queue/%s.json", user->basedir, ntid); |
| 2516 | 2835 | ||
| 2517 | qmsg = xs_dict_set(qmsg, "ntid", ntid); | 2836 | qmsg = xs_dict_set(qmsg, "ntid", ntid); |
| 2837 | qmsg = xs_dict_append(qmsg, "actor", actor); | ||
| 2518 | 2838 | ||
| 2519 | qmsg = _enqueue_put(fn, qmsg); | 2839 | qmsg = _enqueue_put(fn, qmsg); |
| 2520 | 2840 | ||
| 2521 | snac_debug(user, 2, xs_fmt("enqueue_request_replies %s", id)); | 2841 | snac_debug(user, 1, xs_fmt("enqueue_actor_refresh %s", actor)); |
| 2522 | } | 2842 | } |
| 2523 | 2843 | ||
| 2524 | 2844 | ||
| @@ -2528,14 +2848,14 @@ int was_question_voted(snac *user, const char *id) | |||
| 2528 | xs *children = object_children(id); | 2848 | xs *children = object_children(id); |
| 2529 | int voted = 0; | 2849 | int voted = 0; |
| 2530 | xs_list *p; | 2850 | xs_list *p; |
| 2531 | xs_str *md5; | 2851 | const xs_str *md5; |
| 2532 | 2852 | ||
| 2533 | p = children; | 2853 | p = children; |
| 2534 | while (xs_list_iter(&p, &md5)) { | 2854 | while (xs_list_iter(&p, &md5)) { |
| 2535 | xs *obj = NULL; | 2855 | xs *obj = NULL; |
| 2536 | 2856 | ||
| 2537 | if (valid_status(object_get_by_md5(md5, &obj))) { | 2857 | if (valid_status(object_get_by_md5(md5, &obj))) { |
| 2538 | char *atto = get_atto(obj); | 2858 | const char *atto = get_atto(obj); |
| 2539 | if (atto && strcmp(atto, user->actor) == 0 && | 2859 | if (atto && strcmp(atto, user->actor) == 0 && |
| 2540 | !xs_is_null(xs_dict_get(obj, "name"))) { | 2860 | !xs_is_null(xs_dict_get(obj, "name"))) { |
| 2541 | voted = 1; | 2861 | voted = 1; |
| @@ -2555,7 +2875,7 @@ xs_list *user_queue(snac *snac) | |||
| 2555 | xs_list *list = xs_list_new(); | 2875 | xs_list *list = xs_list_new(); |
| 2556 | time_t t = time(NULL); | 2876 | time_t t = time(NULL); |
| 2557 | xs_list *p; | 2877 | xs_list *p; |
| 2558 | xs_val *v; | 2878 | const xs_val *v; |
| 2559 | 2879 | ||
| 2560 | xs *fns = xs_glob(spec, 0, 0); | 2880 | xs *fns = xs_glob(spec, 0, 0); |
| 2561 | 2881 | ||
| @@ -2584,7 +2904,7 @@ xs_list *queue(void) | |||
| 2584 | xs_list *list = xs_list_new(); | 2904 | xs_list *list = xs_list_new(); |
| 2585 | time_t t = time(NULL); | 2905 | time_t t = time(NULL); |
| 2586 | xs_list *p; | 2906 | xs_list *p; |
| 2587 | xs_val *v; | 2907 | const xs_val *v; |
| 2588 | 2908 | ||
| 2589 | xs *fns = xs_glob(spec, 0, 0); | 2909 | xs *fns = xs_glob(spec, 0, 0); |
| 2590 | 2910 | ||
| @@ -2660,7 +2980,7 @@ static void _purge_dir(const char *dir, int days) | |||
| 2660 | xs *spec = xs_fmt("%s/" "*", dir); | 2980 | xs *spec = xs_fmt("%s/" "*", dir); |
| 2661 | xs *list = xs_glob(spec, 0, 0); | 2981 | xs *list = xs_glob(spec, 0, 0); |
| 2662 | xs_list *p; | 2982 | xs_list *p; |
| 2663 | xs_str *v; | 2983 | const xs_str *v; |
| 2664 | 2984 | ||
| 2665 | p = list; | 2985 | p = list; |
| 2666 | while (xs_list_iter(&p, &v)) | 2986 | while (xs_list_iter(&p, &v)) |
| @@ -2686,7 +3006,7 @@ void purge_server(void) | |||
| 2686 | xs *spec = xs_fmt("%s/object/??", srv_basedir); | 3006 | xs *spec = xs_fmt("%s/object/??", srv_basedir); |
| 2687 | xs *dirs = xs_glob(spec, 0, 0); | 3007 | xs *dirs = xs_glob(spec, 0, 0); |
| 2688 | xs_list *p; | 3008 | xs_list *p; |
| 2689 | xs_str *v; | 3009 | const xs_str *v; |
| 2690 | int cnt = 0; | 3010 | int cnt = 0; |
| 2691 | int icnt = 0; | 3011 | int icnt = 0; |
| 2692 | 3012 | ||
| @@ -2695,7 +3015,7 @@ void purge_server(void) | |||
| 2695 | p = dirs; | 3015 | p = dirs; |
| 2696 | while (xs_list_iter(&p, &v)) { | 3016 | while (xs_list_iter(&p, &v)) { |
| 2697 | xs_list *p2; | 3017 | xs_list *p2; |
| 2698 | xs_str *v2; | 3018 | const xs_str *v2; |
| 2699 | 3019 | ||
| 2700 | { | 3020 | { |
| 2701 | xs *spec2 = xs_fmt("%s/" "*.json", v); | 3021 | xs *spec2 = xs_fmt("%s/" "*.json", v); |
| @@ -2709,7 +3029,7 @@ void purge_server(void) | |||
| 2709 | if (mtime_nl(v2, &n_link) < mt && n_link < 2) { | 3029 | if (mtime_nl(v2, &n_link) < mt && n_link < 2) { |
| 2710 | xs *s1 = xs_replace(v2, ".json", ""); | 3030 | xs *s1 = xs_replace(v2, ".json", ""); |
| 2711 | xs *l = xs_split(s1, "/"); | 3031 | xs *l = xs_split(s1, "/"); |
| 2712 | char *md5 = xs_list_get(l, -1); | 3032 | const char *md5 = xs_list_get(l, -1); |
| 2713 | 3033 | ||
| 2714 | object_del_by_md5(md5); | 3034 | object_del_by_md5(md5); |
| 2715 | cnt++; | 3035 | cnt++; |
| @@ -2743,6 +3063,16 @@ void purge_server(void) | |||
| 2743 | } | 3063 | } |
| 2744 | } | 3064 | } |
| 2745 | } | 3065 | } |
| 3066 | |||
| 3067 | /* delete index backups */ | ||
| 3068 | xs *specb = xs_fmt("%s/" "*.bak", v); | ||
| 3069 | xs *bakfs = xs_glob(specb, 0, 0); | ||
| 3070 | |||
| 3071 | p2 = bakfs; | ||
| 3072 | while (xs_list_iter(&p2, &v2)) { | ||
| 3073 | unlink(v2); | ||
| 3074 | srv_debug(1, xs_fmt("purged %s", v2)); | ||
| 3075 | } | ||
| 2746 | } | 3076 | } |
| 2747 | } | 3077 | } |
| 2748 | 3078 | ||
| @@ -2764,7 +3094,7 @@ void purge_server(void) | |||
| 2764 | xs *spec2 = xs_fmt("%s/" "*.idx", v); | 3094 | xs *spec2 = xs_fmt("%s/" "*.idx", v); |
| 2765 | xs *files = xs_glob(spec2, 0, 0); | 3095 | xs *files = xs_glob(spec2, 0, 0); |
| 2766 | xs_list *p2; | 3096 | xs_list *p2; |
| 2767 | xs_str *v2; | 3097 | const xs_str *v2; |
| 2768 | 3098 | ||
| 2769 | p2 = files; | 3099 | p2 = files; |
| 2770 | while (xs_list_iter(&p2, &v2)) { | 3100 | while (xs_list_iter(&p2, &v2)) { |
| @@ -2791,7 +3121,7 @@ void purge_user(snac *snac) | |||
| 2791 | /* do the purge for this user */ | 3121 | /* do the purge for this user */ |
| 2792 | { | 3122 | { |
| 2793 | int priv_days, pub_days, user_days = 0; | 3123 | int priv_days, pub_days, user_days = 0; |
| 2794 | char *v; | 3124 | const char *v; |
| 2795 | int n; | 3125 | int n; |
| 2796 | 3126 | ||
| 2797 | priv_days = xs_number_get(xs_dict_get(srv_config, "timeline_purge_days")); | 3127 | priv_days = xs_number_get(xs_dict_get(srv_config, "timeline_purge_days")); |
| @@ -2823,6 +3153,19 @@ void purge_user(snac *snac) | |||
| 2823 | srv_debug(1, xs_fmt("purge: %s %d", idx, gc)); | 3153 | srv_debug(1, xs_fmt("purge: %s %d", idx, gc)); |
| 2824 | } | 3154 | } |
| 2825 | 3155 | ||
| 3156 | /* purge lists */ | ||
| 3157 | { | ||
| 3158 | xs *spec = xs_fmt("%s/list/" "*.idx", snac->basedir); | ||
| 3159 | xs *lol = xs_glob(spec, 0, 0); | ||
| 3160 | int c = 0; | ||
| 3161 | const char *v; | ||
| 3162 | |||
| 3163 | while (xs_list_next(lol, &v, &c)) { | ||
| 3164 | int gc = index_gc(v); | ||
| 3165 | srv_debug(1, xs_fmt("purge: %s %d", v, gc)); | ||
| 3166 | } | ||
| 3167 | } | ||
| 3168 | |||
| 2826 | /* unrelated to purging, but it's a janitorial process, so what the hell */ | 3169 | /* unrelated to purging, but it's a janitorial process, so what the hell */ |
| 2827 | verify_links(snac); | 3170 | verify_links(snac); |
| 2828 | } | 3171 | } |
| @@ -2833,7 +3176,8 @@ void purge_all(void) | |||
| 2833 | { | 3176 | { |
| 2834 | snac snac; | 3177 | snac snac; |
| 2835 | xs *list = user_list(); | 3178 | xs *list = user_list(); |
| 2836 | char *p, *uid; | 3179 | char *p; |
| 3180 | const char *uid; | ||
| 2837 | 3181 | ||
| 2838 | p = list; | 3182 | p = list; |
| 2839 | while (xs_list_iter(&p, &uid)) { | 3183 | while (xs_list_iter(&p, &uid)) { |
| @@ -2887,7 +3231,7 @@ void srv_archive(const char *direction, const char *url, xs_dict *req, | |||
| 2887 | if (p_size && payload) { | 3231 | if (p_size && payload) { |
| 2888 | xs *payload_fn = NULL; | 3232 | xs *payload_fn = NULL; |
| 2889 | xs *payload_fn_raw = NULL; | 3233 | xs *payload_fn_raw = NULL; |
| 2890 | char *v = xs_dict_get(req, "content-type"); | 3234 | const char *v = xs_dict_get(req, "content-type"); |
| 2891 | 3235 | ||
| 2892 | if (v && xs_str_in(v, "json") != -1) { | 3236 | if (v && xs_str_in(v, "json") != -1) { |
| 2893 | payload_fn = xs_fmt("%s/payload.json", dir); | 3237 | payload_fn = xs_fmt("%s/payload.json", dir); |
| @@ -2918,7 +3262,7 @@ void srv_archive(const char *direction, const char *url, xs_dict *req, | |||
| 2918 | 3262 | ||
| 2919 | if (b_size && body) { | 3263 | if (b_size && body) { |
| 2920 | xs *body_fn = NULL; | 3264 | xs *body_fn = NULL; |
| 2921 | char *v = xs_dict_get(headers, "content-type"); | 3265 | const char *v = xs_dict_get(headers, "content-type"); |
| 2922 | 3266 | ||
| 2923 | if (v && xs_str_in(v, "json") != -1) { | 3267 | if (v && xs_str_in(v, "json") != -1) { |
| 2924 | body_fn = xs_fmt("%s/body.json", dir); | 3268 | body_fn = xs_fmt("%s/body.json", dir); |
| @@ -2987,7 +3331,7 @@ void srv_archive_error(const char *prefix, const xs_str *err, | |||
| 2987 | } | 3331 | } |
| 2988 | 3332 | ||
| 2989 | 3333 | ||
| 2990 | void srv_archive_qitem(char *prefix, xs_dict *q_item) | 3334 | void srv_archive_qitem(const char *prefix, xs_dict *q_item) |
| 2991 | /* archives a q_item in the error folder */ | 3335 | /* archives a q_item in the error folder */ |
| 2992 | { | 3336 | { |
| 2993 | xs *ntid = tid(0); | 3337 | xs *ntid = tid(0); |