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}