snac2

Fork of https://codeberg.org/grunfink/snac2
git clone https://git.inz.fi/snac2
Log | Files | Refs | README | LICENSE

upgrade.c (10178B)


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