diff options
| -rw-r--r-- | activitypub.c | 6 | ||||
| -rw-r--r-- | data.c | 69 | ||||
| -rw-r--r-- | httpd.c | 2 | ||||
| -rw-r--r-- | main.c | 6 | ||||
| -rw-r--r-- | snac.h | 3 |
5 files changed, 86 insertions, 0 deletions
diff --git a/activitypub.c b/activitypub.c index d699fee..5fb60ba 100644 --- a/activitypub.c +++ b/activitypub.c @@ -3377,6 +3377,12 @@ void process_queue_item(xs_dict *q_item) rss_poll_hashtags(); } else + if (strcmp(type, "fsck") == 0) { + srv_log(xs_fmt("started deferred data integrity check")); + data_fsck(); + srv_log(xs_fmt("finished deferred data integrity check")); + } + else srv_log(xs_fmt("unexpected q_item type '%s'", type)); } @@ -3605,6 +3605,17 @@ void enqueue_collect_outbox(snac *user, const char *actor_id) } +void enqueue_fsck(void) +/* enqueues an fsck */ +{ + xs *qmsg = _new_qmsg("fsck", "", 0); + const char *ntid = xs_dict_get(qmsg, "ntid"); + xs *fn = xs_fmt("%s/queue/%s.json", srv_basedir, ntid); + + qmsg = _enqueue_put(fn, qmsg); +} + + int was_question_voted(snac *user, const char *id) /* returns true if the user voted in this poll */ { @@ -4362,3 +4373,61 @@ const char *lang_str(const char *str, const snac *user) return n_str; } + + +/** integrity checks **/ + +void data_fsck(void) +{ + xs *list = user_list(); + const char *uid; + + xs_list_foreach(list, uid) { + snac user; + + if (!user_open(&user, uid)) + continue; + + { + /* iterate all private posts and check that non-public posts + from this user are also linked into the public directory, + to avoid the don't-fucking-delete-my-own-private-posts purge bug */ + + xs *priv_spec = xs_fmt("%s/private/""*.json", user.basedir); + xs *posts = xs_glob(priv_spec, 0, 0); + const char *priv_fn; + + xs_list_foreach(posts, priv_fn) { + xs *pub_fn = xs_replace(priv_fn, "/private/", "/public/"); + + /* already there? look no more */ + if (mtime(pub_fn)) + continue; + + /* read the post */ + FILE *f; + if ((f = fopen(priv_fn, "r")) == NULL) + continue; + + xs *post = xs_json_load(f); + fclose(f); + + if (!xs_is_dict(post)) + continue; + + const char *attr_to = get_atto(post); + + if (!xs_is_string(attr_to) || strcmp(attr_to, user.actor) != 0) { + /* not from this user */ + continue; + } + + /* link */ + snac_debug(&user, 1, xs_fmt("fsck: fixed missing link %s", xs_dict_get(post, "id"))); + link(priv_fn, pub_fn); + } + } + + user_free(&user); + } +} @@ -805,6 +805,8 @@ static void *background_thread(void *arg) srv_log(xs_fmt("background thread started")); + enqueue_fsck(); + while (p_state->srv_running) { int cnt = 0; @@ -34,6 +34,7 @@ int usage(const char *cmd) "httpd {basedir} Starts the HTTPD daemon\n" "purge {basedir} Purges old data\n" "state {basedir} Prints server state\n" + "fsck {basedir} Performs a non-destructive data integrity check\n" "webfinger {basedir} {account} Queries about an account (@user@host or actor url)\n" "queue {basedir} {uid} Processes a user queue\n" "follow {basedir} {uid} {actor} Follows an actor\n" @@ -200,6 +201,11 @@ int main(int argc, char *argv[]) return 0; } + if (strcmp(cmd, "fsck") == 0) { /** **/ + data_fsck(); + return 0; + } + if ((user = GET_ARGV()) == NULL) return usage(cmd); @@ -299,6 +299,7 @@ void enqueue_webmention(const xs_dict *msg); void enqueue_notify_webhook(snac *user, const xs_dict *noti, int retries); void enqueue_collect_replies(snac *user, const char *post); void enqueue_collect_outbox(snac *user, const char *actor_id); +void enqueue_fsck(void); int was_question_voted(snac *user, const char *id); @@ -477,3 +478,5 @@ xs_str *rss_from_timeline(snac *user, const xs_list *timeline, const char *title, const char *link, const char *desc); void rss_to_timeline(snac *user, const char *url); void rss_poll_hashtags(void); + +void data_fsck(void); |