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 }