2.53 upgrade.c
  1/* snac - A simple, minimalistic ActivityPub instance */
  2/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */
  3
  4#include "xs.h"
  5#include "xs_io.h"
  6#include "xs_json.h"
  7#include "xs_glob.h"
  8
  9#include "snac.h"
 10
 11#include <sys/stat.h>
 12
 13
 14int snac_upgrade(xs_str **error)
 15{
 16    int ret = 1;
 17    int changed = 0;
 18    double f = 0.0;
 19
 20    for (;;) {
 21        char *layout = xs_dict_get(srv_config, "layout");
 22        double nf;
 23
 24        f = nf = xs_number_get(layout);
 25
 26        if (!(f < disk_layout))
 27            break;
 28
 29        srv_log(xs_fmt("disk layout upgrade needed (%1.1lf < %1.1lf)", f, disk_layout));
 30
 31        if (f < 2.0) {
 32            *error = xs_fmt("ERROR: unsupported old disk layout %1.1lf\n", f);
 33            ret    = 0;
 34            break;
 35        }
 36        else
 37        if (f < 2.1) {
 38            xs *dir = xs_fmt("%s/object", srv_basedir);
 39            mkdirx(dir);
 40
 41            nf = 2.1;
 42        }
 43        else
 44        if (f < 2.2) {
 45            xs *users = user_list();
 46            char *p, *v;
 47
 48            p = users;
 49            while (xs_list_iter(&p, &v)) {
 50                snac snac;
 51
 52                if (user_open(&snac, v)) {
 53                    xs *spec = xs_fmt("%s/actors/" "*.json", snac.basedir);
 54                    xs *list = xs_glob(spec, 0, 0);
 55                    char *g, *fn;
 56
 57                    g = list;
 58                    while (xs_list_iter(&g, &fn)) {
 59                        xs *l   = xs_split(fn, "/");
 60                        char *b = xs_list_get(l, -1);
 61                        xs *dir = xs_fmt("%s/object/%c%c", srv_basedir, b[0], b[1]);
 62                        xs *nfn = xs_fmt("%s/%s", dir, b);
 63
 64                        mkdirx(dir);
 65                        rename(fn, nfn);
 66                    }
 67
 68                    xs *odir = xs_fmt("%s/actors", snac.basedir);
 69                    rmdir(odir);
 70
 71                    user_free(&snac);
 72                }
 73            }
 74
 75            nf = 2.2;
 76        }
 77        else
 78        if (f < 2.3) {
 79            xs *users = user_list();
 80            char *p, *v;
 81
 82            p = users;
 83            while (xs_list_iter(&p, &v)) {
 84                snac snac;
 85
 86                if (user_open(&snac, v)) {
 87                    char *p, *v;
 88                    xs *dir = xs_fmt("%s/hidden", snac.basedir);
 89
 90                    /* create the hidden directory */
 91                    mkdirx(dir);
 92
 93                    /* rename all muted files incorrectly named .json */
 94                    xs *spec = xs_fmt("%s/muted/" "*.json", snac.basedir);
 95                    xs *fns  = xs_glob(spec, 0, 0);
 96
 97                    p = fns;
 98                    while (xs_list_iter(&p, &v)) {
 99                        xs *nfn = xs_replace(v, ".json", "");
100                        rename(v, nfn);
101                    }
102
103                    user_free(&snac);
104                }
105            }
106
107            nf = 2.3;
108        }
109        else
110        if (f < 2.4) {
111            xs *users = user_list();
112            char *p, *v;
113
114            p = users;
115            while (xs_list_iter(&p, &v)) {
116                snac snac;
117
118                if (user_open(&snac, v)) {
119                    xs *dir = xs_fmt("%s/public", snac.basedir);
120                    mkdirx(dir);
121
122                    dir = xs_replace_i(dir, "public", "private");
123                    mkdirx(dir);
124
125                    user_free(&snac);
126                }
127            }
128
129            nf = 2.4;
130        }
131        else
132        if (f < 2.5) {
133            /* upgrade followers */
134            xs *users = user_list();
135            char *p, *v;
136
137            p = users;
138            while (xs_list_iter(&p, &v)) {
139                snac snac;
140
141                if (user_open(&snac, v)) {
142                    xs *spec = xs_fmt("%s/followers/" "*.json", snac.basedir);
143                    xs *dir  = xs_glob(spec, 0, 0);
144                    char *p, *v;
145
146                    p = dir;
147                    while (xs_list_iter(&p, &v)) {
148                        FILE *f;
149
150                        if ((f = fopen(v, "r")) != NULL) {
151                            xs *s = xs_readall(f);
152                            xs *o = xs_json_loads(s);
153                            fclose(f);
154
155                            char *type = xs_dict_get(o, "type");
156
157                            if (!xs_is_null(type) && strcmp(type, "Follow") == 0) {
158                                unlink(v);
159
160                                char *actor = xs_dict_get(o, "actor");
161
162                                if (!xs_is_null(actor))
163                                    follower_add(&snac, actor);
164                            }
165                        }
166                    }
167
168                    user_free(&snac);
169                }
170            }
171
172            nf = 2.5;
173        }
174        else
175        if (f < 2.6) {
176            /* upgrade local/ to public/ */
177            xs *users = user_list();
178            char *p, *v;
179
180            p = users;
181            while (xs_list_iter(&p, &v)) {
182                snac snac;
183
184                if (user_open(&snac, v)) {
185                    xs *spec = xs_fmt("%s/local/" "*.json", snac.basedir);
186                    xs *dir  = xs_glob(spec, 0, 0);
187                    char *p, *v;
188
189                    p = dir;
190                    while (xs_list_iter(&p, &v)) {
191                        FILE *f;
192
193                        if ((f = fopen(v, "r")) != NULL) {
194                            xs *s = xs_readall(f);
195                            xs *o = xs_json_loads(s);
196                            fclose(f);
197
198                            xs *meta = xs_dup(xs_dict_get(o, "_snac"));
199                            o = xs_dict_del(o, "_snac");
200
201                            char *id = xs_dict_get(o, "id");
202
203                            /* store object */
204                            object_add_ow(id, o);
205
206                            /* if it's from us, add to public */
207                            if (xs_startswith(id, snac.actor)) {
208                                char *p, *v;
209
210                                object_user_cache_add(&snac, id, "public");
211
212                                p = xs_dict_get(meta, "announced_by");
213                                while (xs_list_iter(&p, &v))
214                                    object_admire(id, v, 0);
215                                p = xs_dict_get(meta, "liked_by");
216                                while (xs_list_iter(&p, &v))
217                                    object_admire(id, v, 1);
218                            }
219
220                            unlink(v);
221                        }
222                    }
223
224                    xs *od = xs_fmt("%s/local", snac.basedir);
225                    rmdir(od);
226
227                    user_free(&snac);
228                }
229            }
230
231            nf = 2.6;
232        }
233        else
234        if (f < 2.7) {
235            /* upgrade timeline/ to private/ */
236            xs *users = user_list();
237            char *p, *v;
238
239            p = users;
240            while (xs_list_iter(&p, &v)) {
241                snac snac;
242
243                if (user_open(&snac, v)) {
244                    xs *spec = xs_fmt("%s/timeline/" "*.json", snac.basedir);
245                    xs *dir  = xs_glob(spec, 0, 0);
246                    char *p, *v;
247
248                    p = dir;
249                    while (xs_list_iter(&p, &v)) {
250                        FILE *f;
251
252                        if ((f = fopen(v, "r")) != NULL) {
253                            xs *s = xs_readall(f);
254                            xs *o = xs_json_loads(s);
255                            fclose(f);
256
257                            xs *meta = xs_dup(xs_dict_get(o, "_snac"));
258                            o = xs_dict_del(o, "_snac");
259
260                            char *id = xs_dict_get(o, "id");
261
262                            /* store object */
263                            object_add_ow(id, o);
264
265                            {
266                                char *p, *v;
267
268                                object_user_cache_add(&snac, id, "private");
269
270                                p = xs_dict_get(meta, "announced_by");
271                                while (xs_list_iter(&p, &v))
272                                    object_admire(id, v, 0);
273                                p = xs_dict_get(meta, "liked_by");
274                                while (xs_list_iter(&p, &v))
275                                    object_admire(id, v, 1);
276                            }
277
278                            unlink(v);
279                        }
280                    }
281
282                    xs *od = xs_fmt("%s/timeline", snac.basedir);
283                    rmdir(od);
284
285                    user_free(&snac);
286                }
287            }
288
289            nf = 2.7;
290        }
291
292        if (f < nf) {
293            f          = nf;
294            xs *nv     = xs_number_new(f);
295            srv_config = xs_dict_set(srv_config, "layout", nv);
296
297            srv_log(xs_fmt("disk layout upgraded to version %1.1lf", f));
298            changed++;
299        }
300        else
301            break;
302    }
303
304    if (f > disk_layout) {
305        *error = xs_fmt("ERROR: unknown future version %lf\n", f);
306        ret    = 0;
307    }
308
309    if (changed) {
310        /* upgrade the configuration file */
311        xs *fn = xs_fmt("%s/server.json", srv_basedir);
312        FILE *f;
313
314        if ((f = fopen(fn, "w")) != NULL) {
315            xs_json_dump(srv_config, 4, f);
316            fclose(f);
317
318            srv_log(xs_fmt("disk layout upgraded %s after %d changes", fn, changed));
319        }
320        else
321            ret = 0;
322    }
323
324    return ret;
325}