summaryrefslogtreecommitdiff
path: root/data.c
diff options
context:
space:
mode:
authorGravatar Louis Brauer2024-05-25 08:05:36 +0000
committerGravatar Louis Brauer2024-05-25 08:05:36 +0000
commit84a767dd0878013194ed7551b5ae6ef715e841a6 (patch)
tree9fb1b2b89e0bfbb4b8bf1e85d840c8653e646bb7 /data.c
parentPrevent some browsers from caching servers basic auth request (diff)
parentBackport from xs (fix regex.h compilation with tcc). (diff)
downloadsnac2-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.c586
1 files changed, 465 insertions, 121 deletions
diff --git a/data.c b/data.c
index 7dd7d19..edbc64f 100644
--- a/data.c
+++ b/data.c
@@ -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};
28int snac_upgrade(xs_str **error); 29int snac_upgrade(xs_str **error);
29 30
30 31
31int srv_open(char *basedir, int auto_upgrade) 32int 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
344int 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)
1060xs_str *timeline_fn_by_md5(snac *snac, const char *md5) 1079xs_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
1106int timeline_del(snac *snac, char *id) 1129int 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
1172xs_list *timeline_top_level(snac *snac, xs_list *list) 1197xs_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
1543int actor_add(const char *actor, xs_dict *msg) 1569int 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)
1664void tag_index(const char *id, const xs_dict *obj) 1690void 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
1709xs_list *tag_search(char *tag, int skip, int show) 1736xs_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
1752xs_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
1860xs_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
1877xs_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
1919void 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
1725static int _load_raw_file(const char *fn, xs_val **data, int *size, 1952static int _load_raw_file(const char *fn, xs_val **data, int *size,
@@ -1944,7 +2171,7 @@ void inbox_add(const char *inbox)
1944void inbox_add_by_actor(const xs_dict *actor) 2171void 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
1988xs_str *_instance_block_fn(const char *instance) 2215xs_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
2054int content_check(const char *file, const xs_dict *msg) 2282int 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
2317xs_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
2095xs_str *notify_check_time(snac *snac, int reset) 2432xs_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
2338void enqueue_output_raw(const char *keyid, const char *seckey, 2675void 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
2363void enqueue_output(snac *snac, xs_dict *msg, xs_str *inbox, int retries, int p_status) 2701void 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
2377void enqueue_output_by_actor(snac *snac, xs_dict *msg, const xs_str *actor, int retries) 2716void 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
2389void enqueue_email(xs_str *msg, int retries) 2729void 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
2461void enqueue_verify_links(snac *user) 2801void 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
2474void enqueue_actor_refresh(snac *user, const char *actor) 2816void 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
2489void enqueue_request_replies(snac *user, const char *id) 2829void 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
2990void srv_archive_qitem(char *prefix, xs_dict *q_item) 3334void 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);