diff options
Diffstat (limited to 'data.c')
| -rw-r--r-- | data.c | 778 |
1 files changed, 604 insertions, 174 deletions
| @@ -6,6 +6,7 @@ | |||
| 6 | #include "xs_json.h" | 6 | #include "xs_json.h" |
| 7 | #include "xs_openssl.h" | 7 | #include "xs_openssl.h" |
| 8 | #include "xs_glob.h" | 8 | #include "xs_glob.h" |
| 9 | #include "xs_set.h" | ||
| 9 | 10 | ||
| 10 | #include "snac.h" | 11 | #include "snac.h" |
| 11 | 12 | ||
| @@ -14,12 +15,12 @@ | |||
| 14 | #include <sys/file.h> | 15 | #include <sys/file.h> |
| 15 | #include <fcntl.h> | 16 | #include <fcntl.h> |
| 16 | 17 | ||
| 17 | double db_layout = 2.1; | 18 | double db_layout = 2.5; |
| 18 | 19 | ||
| 19 | 20 | ||
| 20 | int db_upgrade(d_char **error); | 21 | int db_upgrade(d_char **error); |
| 21 | 22 | ||
| 22 | int srv_open(char *basedir) | 23 | int srv_open(char *basedir, int auto_upgrade) |
| 23 | /* opens a server */ | 24 | /* opens a server */ |
| 24 | { | 25 | { |
| 25 | int ret = 0; | 26 | int ret = 0; |
| @@ -69,7 +70,14 @@ int srv_open(char *basedir) | |||
| 69 | error = xs_fmt("DEBUG level set to %d from environment", dbglevel); | 70 | error = xs_fmt("DEBUG level set to %d from environment", dbglevel); |
| 70 | } | 71 | } |
| 71 | 72 | ||
| 72 | ret = db_upgrade(&error); | 73 | if (auto_upgrade) |
| 74 | ret = db_upgrade(&error); | ||
| 75 | else { | ||
| 76 | if (xs_number_get(xs_dict_get(srv_config, "layout")) < db_layout) | ||
| 77 | error = xs_fmt("ERROR: disk layout changed - execute 'snac upgrade' first"); | ||
| 78 | else | ||
| 79 | ret = 1; | ||
| 80 | } | ||
| 73 | } | 81 | } |
| 74 | 82 | ||
| 75 | } | 83 | } |
| @@ -178,24 +186,215 @@ d_char *user_list(void) | |||
| 178 | } | 186 | } |
| 179 | 187 | ||
| 180 | 188 | ||
| 181 | double mtime(char *fn) | 189 | double mtime_nl(const char *fn, int *n_link) |
| 182 | /* returns the mtime of a file or directory, or 0.0 */ | 190 | /* returns the mtime and number of links of a file or directory, or 0.0 */ |
| 183 | { | 191 | { |
| 184 | struct stat st; | 192 | struct stat st; |
| 185 | double r = 0.0; | 193 | double r = 0.0; |
| 194 | int n = 0; | ||
| 186 | 195 | ||
| 187 | if (fn && stat(fn, &st) != -1) | 196 | if (fn && stat(fn, &st) != -1) { |
| 188 | r = (double)st.st_mtim.tv_sec; | 197 | r = (double) st.st_mtim.tv_sec; |
| 198 | n = st.st_nlink; | ||
| 199 | } | ||
| 200 | |||
| 201 | if (n_link) | ||
| 202 | *n_link = n; | ||
| 189 | 203 | ||
| 190 | return r; | 204 | return r; |
| 191 | } | 205 | } |
| 192 | 206 | ||
| 193 | 207 | ||
| 194 | /** object database 2.1+ **/ | 208 | /** database 2.1+ **/ |
| 209 | |||
| 210 | /** indexes **/ | ||
| 211 | |||
| 212 | int index_add_md5(const char *fn, const char *md5) | ||
| 213 | /* adds an md5 to an index */ | ||
| 214 | { | ||
| 215 | int status = 201; /* Created */ | ||
| 216 | FILE *f; | ||
| 217 | |||
| 218 | if ((f = fopen(fn, "a")) != NULL) { | ||
| 219 | flock(fileno(f), LOCK_EX); | ||
| 220 | |||
| 221 | /* ensure the position is at the end after getting the lock */ | ||
| 222 | fseek(f, 0, SEEK_END); | ||
| 223 | |||
| 224 | fprintf(f, "%s\n", md5); | ||
| 225 | fclose(f); | ||
| 226 | } | ||
| 227 | else | ||
| 228 | status = 500; | ||
| 229 | |||
| 230 | return status; | ||
| 231 | } | ||
| 232 | |||
| 233 | |||
| 234 | int index_add(const char *fn, const char *id) | ||
| 235 | /* adds an id to an index */ | ||
| 236 | { | ||
| 237 | xs *md5 = xs_md5_hex(id, strlen(id)); | ||
| 238 | return index_add_md5(fn, md5); | ||
| 239 | } | ||
| 240 | |||
| 241 | |||
| 242 | int index_del(const char *fn, const char *md5) | ||
| 243 | /* deletes an md5 from an index */ | ||
| 244 | { | ||
| 245 | int status = 404; | ||
| 246 | FILE *i, *o; | ||
| 247 | |||
| 248 | if ((i = fopen(fn, "r")) != NULL) { | ||
| 249 | flock(fileno(i), LOCK_EX); | ||
| 250 | |||
| 251 | xs *nfn = xs_fmt("%s.new", fn); | ||
| 252 | char line[256]; | ||
| 253 | |||
| 254 | if ((o = fopen(nfn, "w")) != NULL) { | ||
| 255 | while (fgets(line, sizeof(line), i) != NULL) { | ||
| 256 | line[32] = '\0'; | ||
| 257 | if (memcmp(line, md5, 32) != 0) | ||
| 258 | fprintf(o, "%s\n", line); | ||
| 259 | } | ||
| 260 | |||
| 261 | fclose(o); | ||
| 262 | |||
| 263 | xs *ofn = xs_fmt("%s.bak", fn); | ||
| 264 | |||
| 265 | link(fn, ofn); | ||
| 266 | rename(nfn, fn); | ||
| 267 | } | ||
| 268 | else | ||
| 269 | status = 500; | ||
| 270 | |||
| 271 | fclose(i); | ||
| 272 | } | ||
| 273 | else | ||
| 274 | status = 500; | ||
| 275 | |||
| 276 | return status; | ||
| 277 | } | ||
| 278 | |||
| 279 | |||
| 280 | int index_in_md5(const char *fn, const char *md5) | ||
| 281 | /* checks if the md5 is already in the index */ | ||
| 282 | { | ||
| 283 | FILE *f; | ||
| 284 | int ret = 0; | ||
| 285 | |||
| 286 | if ((f = fopen(fn, "r")) != NULL) { | ||
| 287 | flock(fileno(f), LOCK_SH); | ||
| 288 | |||
| 289 | char line[256]; | ||
| 290 | |||
| 291 | while (!ret && fgets(line, sizeof(line), f) != NULL) { | ||
| 292 | line[32] = '\0'; | ||
| 293 | |||
| 294 | if (strcmp(line, md5) == 0) | ||
| 295 | ret = 1; | ||
| 296 | } | ||
| 297 | |||
| 298 | fclose(f); | ||
| 299 | } | ||
| 300 | |||
| 301 | return ret; | ||
| 302 | } | ||
| 303 | |||
| 304 | |||
| 305 | int index_in(const char *fn, const char *id) | ||
| 306 | /* checks if the object id is already in the index */ | ||
| 307 | { | ||
| 308 | xs *md5 = xs_md5_hex(id, strlen(id)); | ||
| 309 | return index_in_md5(fn, md5); | ||
| 310 | } | ||
| 311 | |||
| 312 | |||
| 313 | int index_first(const char *fn, char *line, int size) | ||
| 314 | /* reads the first entry of an index */ | ||
| 315 | { | ||
| 316 | FILE *f; | ||
| 317 | int ret = 0; | ||
| 318 | |||
| 319 | if ((f = fopen(fn, "r")) != NULL) { | ||
| 320 | flock(fileno(f), LOCK_SH); | ||
| 321 | |||
| 322 | if (fgets(line, size, f) != NULL) { | ||
| 323 | line[32] = '\0'; | ||
| 324 | ret = 1; | ||
| 325 | } | ||
| 326 | |||
| 327 | fclose(f); | ||
| 328 | } | ||
| 329 | |||
| 330 | return ret; | ||
| 331 | } | ||
| 332 | |||
| 333 | |||
| 334 | d_char *index_list(const char *fn, int max) | ||
| 335 | /* returns an index as a list */ | ||
| 336 | { | ||
| 337 | d_char *list = NULL; | ||
| 338 | FILE *f; | ||
| 339 | int n = 0; | ||
| 340 | |||
| 341 | if ((f = fopen(fn, "r")) != NULL) { | ||
| 342 | flock(fileno(f), LOCK_SH); | ||
| 343 | |||
| 344 | char line[256]; | ||
| 345 | list = xs_list_new(); | ||
| 346 | |||
| 347 | while (n < max && fgets(line, sizeof(line), f) != NULL) { | ||
| 348 | line[32] = '\0'; | ||
| 349 | list = xs_list_append(list, line); | ||
| 350 | n++; | ||
| 351 | } | ||
| 352 | |||
| 353 | fclose(f); | ||
| 354 | } | ||
| 355 | |||
| 356 | return list; | ||
| 357 | } | ||
| 358 | |||
| 359 | |||
| 360 | d_char *index_list_desc(const char *fn, int max) | ||
| 361 | /* returns an index as a list, in reverse order */ | ||
| 362 | { | ||
| 363 | d_char *list = NULL; | ||
| 364 | FILE *f; | ||
| 365 | int n = 0; | ||
| 366 | |||
| 367 | if ((f = fopen(fn, "r")) != NULL) { | ||
| 368 | flock(fileno(f), LOCK_SH); | ||
| 369 | |||
| 370 | char line[256]; | ||
| 371 | list = xs_list_new(); | ||
| 372 | |||
| 373 | /* move to the end minus one entry */ | ||
| 374 | if (!fseek(f, 0, SEEK_END) && !fseek(f, -33, SEEK_CUR)) { | ||
| 375 | while (n < max && fgets(line, sizeof(line), f) != NULL) { | ||
| 376 | line[32] = '\0'; | ||
| 377 | list = xs_list_append(list, line); | ||
| 378 | n++; | ||
| 379 | |||
| 380 | /* move backwards 2 entries */ | ||
| 381 | if (fseek(f, -66, SEEK_CUR) == -1) | ||
| 382 | break; | ||
| 383 | } | ||
| 384 | } | ||
| 385 | |||
| 386 | fclose(f); | ||
| 387 | } | ||
| 388 | |||
| 389 | return list; | ||
| 390 | } | ||
| 391 | |||
| 392 | |||
| 393 | /** objects **/ | ||
| 195 | 394 | ||
| 196 | d_char *_object_fn_by_md5(const char *md5) | 395 | d_char *_object_fn_by_md5(const char *md5) |
| 197 | { | 396 | { |
| 198 | xs *bfn = xs_fmt("%s/object/%c%c/", srv_basedir, md5[0], md5[1]); | 397 | xs *bfn = xs_fmt("%s/object/%c%c", srv_basedir, md5[0], md5[1]); |
| 199 | 398 | ||
| 200 | mkdir(bfn, 0755); | 399 | mkdir(bfn, 0755); |
| 201 | 400 | ||
| @@ -203,19 +402,18 @@ d_char *_object_fn_by_md5(const char *md5) | |||
| 203 | } | 402 | } |
| 204 | 403 | ||
| 205 | 404 | ||
| 206 | d_char *_object_fn_by_id(const char *id) | 405 | d_char *_object_fn(const char *id) |
| 207 | { | 406 | { |
| 208 | xs *md5 = xs_md5_hex(id, strlen(id)); | 407 | xs *md5 = xs_md5_hex(id, strlen(id)); |
| 209 | |||
| 210 | return _object_fn_by_md5(md5); | 408 | return _object_fn_by_md5(md5); |
| 211 | } | 409 | } |
| 212 | 410 | ||
| 213 | 411 | ||
| 214 | int object_get(const char *id, d_char **obj, const char *type) | 412 | int object_get_by_md5(const char *md5, d_char **obj, const char *type) |
| 215 | /* returns a loaded object, optionally of the requested type */ | 413 | /* returns a stored object, optionally of the requested type */ |
| 216 | { | 414 | { |
| 217 | int status = 404; | 415 | int status = 404; |
| 218 | xs *fn = _object_fn_by_id(id); | 416 | xs *fn = _object_fn_by_md5(md5); |
| 219 | FILE *f; | 417 | FILE *f; |
| 220 | 418 | ||
| 221 | if ((f = fopen(fn, "r")) != NULL) { | 419 | if ((f = fopen(fn, "r")) != NULL) { |
| @@ -247,13 +445,27 @@ int object_get(const char *id, d_char **obj, const char *type) | |||
| 247 | } | 445 | } |
| 248 | 446 | ||
| 249 | 447 | ||
| 250 | int object_add(const char *id, d_char *obj) | 448 | int object_get(const char *id, d_char **obj, const char *type) |
| 449 | /* returns a stored object, optionally of the requested type */ | ||
| 450 | { | ||
| 451 | xs *md5 = xs_md5_hex(id, strlen(id)); | ||
| 452 | return object_get_by_md5(md5, obj, type); | ||
| 453 | } | ||
| 454 | |||
| 455 | |||
| 456 | int _object_add(const char *id, d_char *obj, int ow) | ||
| 251 | /* stores an object */ | 457 | /* stores an object */ |
| 252 | { | 458 | { |
| 253 | int status = 201; /* Created */ | 459 | int status = 201; /* Created */ |
| 254 | xs *fn = _object_fn_by_id(id); | 460 | xs *fn = _object_fn(id); |
| 255 | FILE *f; | 461 | FILE *f; |
| 256 | 462 | ||
| 463 | if (!ow && mtime(fn) > 0.0) { | ||
| 464 | /* object already here */ | ||
| 465 | srv_debug(0, xs_fmt("object_add object already here %s", id)); | ||
| 466 | return 204; /* No content */ | ||
| 467 | } | ||
| 468 | |||
| 257 | if ((f = fopen(fn, "w")) != NULL) { | 469 | if ((f = fopen(fn, "w")) != NULL) { |
| 258 | flock(fileno(f), LOCK_EX); | 470 | flock(fileno(f), LOCK_EX); |
| 259 | 471 | ||
| @@ -261,100 +473,241 @@ int object_add(const char *id, d_char *obj) | |||
| 261 | 473 | ||
| 262 | fwrite(j, strlen(j), 1, f); | 474 | fwrite(j, strlen(j), 1, f); |
| 263 | fclose(f); | 475 | fclose(f); |
| 476 | |||
| 477 | /* does this object has a parent? */ | ||
| 478 | char *in_reply_to = xs_dict_get(obj, "inReplyTo"); | ||
| 479 | |||
| 480 | if (!xs_is_null(in_reply_to) && *in_reply_to) { | ||
| 481 | /* update the children index of the parent */ | ||
| 482 | xs *c_idx = _object_fn(in_reply_to); | ||
| 483 | |||
| 484 | c_idx = xs_replace_i(c_idx, ".json", "_c.idx"); | ||
| 485 | index_add(c_idx, id); | ||
| 486 | |||
| 487 | srv_debug(0, xs_fmt("object_add added child %s to %s", id, c_idx)); | ||
| 488 | |||
| 489 | /* create a one-element index with the parent */ | ||
| 490 | xs *p_idx = xs_replace(fn, ".json", "_p.idx"); | ||
| 491 | index_add(p_idx, in_reply_to); | ||
| 492 | |||
| 493 | srv_debug(0, xs_fmt("object_add added parent %s to %s", in_reply_to, p_idx)); | ||
| 494 | } | ||
| 264 | } | 495 | } |
| 265 | else | 496 | else |
| 266 | status = 500; | 497 | status = 500; |
| 267 | 498 | ||
| 499 | srv_debug(0, xs_fmt("object_add %s %s %d", id, fn, status)); | ||
| 500 | |||
| 268 | return status; | 501 | return status; |
| 269 | } | 502 | } |
| 270 | 503 | ||
| 271 | 504 | ||
| 272 | d_char *_follower_fn(snac *snac, char *actor) | 505 | int object_add(const char *id, d_char *obj) |
| 506 | /* stores an object */ | ||
| 273 | { | 507 | { |
| 274 | xs *md5 = xs_md5_hex(actor, strlen(actor)); | 508 | return _object_add(id, obj, 0); |
| 275 | return xs_fmt("%s/followers/%s.json", snac->basedir, md5); | ||
| 276 | } | 509 | } |
| 277 | 510 | ||
| 278 | 511 | ||
| 279 | int follower_add(snac *snac, char *actor, char *msg) | 512 | int object_add_ow(const char *id, d_char *obj) |
| 280 | /* adds a follower */ | 513 | /* stores an object (overwriting allowed) */ |
| 281 | { | 514 | { |
| 282 | int ret = 201; /* created */ | 515 | return _object_add(id, obj, 1); |
| 283 | xs *fn = _follower_fn(snac, actor); | 516 | } |
| 284 | FILE *f; | ||
| 285 | 517 | ||
| 286 | if ((f = fopen(fn, "w")) != NULL) { | ||
| 287 | xs *j = xs_json_dumps_pp(msg, 4); | ||
| 288 | 518 | ||
| 289 | fwrite(j, 1, strlen(j), f); | 519 | int object_del_by_md5(const char *md5) |
| 290 | fclose(f); | 520 | /* deletes an object by its md5 */ |
| 521 | { | ||
| 522 | int status = 404; | ||
| 523 | xs *fn = _object_fn_by_md5(md5); | ||
| 524 | |||
| 525 | if (fn != NULL && unlink(fn) != -1) { | ||
| 526 | status = 200; | ||
| 527 | |||
| 528 | /* also delete associated indexes */ | ||
| 529 | xs *spec = xs_dup(fn); | ||
| 530 | spec = xs_replace_i(spec, ".json", "*.idx"); | ||
| 531 | xs *files = xs_glob(spec, 0, 0); | ||
| 532 | char *p, *v; | ||
| 533 | |||
| 534 | p = files; | ||
| 535 | while (xs_list_iter(&p, &v)) { | ||
| 536 | srv_debug(0, xs_fmt("object_del index %s", v)); | ||
| 537 | unlink(v); | ||
| 538 | } | ||
| 291 | } | 539 | } |
| 292 | else | ||
| 293 | ret = 500; | ||
| 294 | 540 | ||
| 295 | snac_debug(snac, 2, xs_fmt("follower_add %s %s", actor, fn)); | 541 | srv_debug(0, xs_fmt("object_del %s %d", fn, status)); |
| 542 | |||
| 543 | return status; | ||
| 544 | } | ||
| 545 | |||
| 546 | |||
| 547 | int object_del(const char *id) | ||
| 548 | /* deletes an object */ | ||
| 549 | { | ||
| 550 | xs *md5 = xs_md5_hex(id, strlen(id)); | ||
| 551 | return object_del_by_md5(md5); | ||
| 552 | } | ||
| 553 | |||
| 554 | |||
| 555 | int object_del_if_unref(const char *id) | ||
| 556 | /* deletes an object if its n_links < 2 */ | ||
| 557 | { | ||
| 558 | xs *fn = _object_fn(id); | ||
| 559 | int n_links; | ||
| 560 | int ret = 0; | ||
| 561 | |||
| 562 | if (mtime_nl(fn, &n_links) > 0.0 && n_links < 2) | ||
| 563 | ret = object_del(id); | ||
| 296 | 564 | ||
| 297 | return ret; | 565 | return ret; |
| 298 | } | 566 | } |
| 299 | 567 | ||
| 300 | 568 | ||
| 301 | int follower_del(snac *snac, char *actor) | 569 | d_char *object_children(const char *id) |
| 302 | /* deletes a follower */ | 570 | /* returns the list of an object's children */ |
| 571 | { | ||
| 572 | xs *fn = _object_fn(id); | ||
| 573 | |||
| 574 | fn = xs_replace_i(fn, ".json", "_c.idx"); | ||
| 575 | |||
| 576 | return index_list(fn, XS_ALL); | ||
| 577 | } | ||
| 578 | |||
| 579 | |||
| 580 | int object_admire(const char *id, const char *actor, int like) | ||
| 581 | /* actor likes or announces this object */ | ||
| 303 | { | 582 | { |
| 304 | int status = 200; | 583 | int status = 200; |
| 305 | xs *fn = _follower_fn(snac, actor); | 584 | xs *fn = _object_fn(id); |
| 306 | 585 | ||
| 307 | if (fn != NULL) | 586 | fn = xs_replace_i(fn, ".json", like ? "_l.idx" : "_a.idx"); |
| 308 | unlink(fn); | ||
| 309 | else | ||
| 310 | status = 404; | ||
| 311 | 587 | ||
| 312 | snac_debug(snac, 2, xs_fmt("follower_del %s %s", actor, fn)); | 588 | if (!index_in(fn, actor)) { |
| 589 | status = index_add(fn, actor); | ||
| 590 | |||
| 591 | srv_debug(0, xs_fmt("object_admire (%s) %s %s", like ? "Like" : "Announce", actor, fn)); | ||
| 592 | } | ||
| 313 | 593 | ||
| 314 | return status; | 594 | return status; |
| 315 | } | 595 | } |
| 316 | 596 | ||
| 317 | 597 | ||
| 318 | int follower_check(snac *snac, char *actor) | 598 | int _object_user_cache(snac *snac, const char *id, const char *cachedir, int del) |
| 319 | /* checks if someone is a follower */ | 599 | /* adds or deletes from a user cache */ |
| 320 | { | 600 | { |
| 321 | xs *fn = _follower_fn(snac, actor); | 601 | xs *ofn = _object_fn(id); |
| 602 | xs *l = xs_split(ofn, "/"); | ||
| 603 | xs *cfn = xs_fmt("%s/%s/%s", snac->basedir, cachedir, xs_list_get(l, -1)); | ||
| 604 | xs *idx = xs_fmt("%s/%s.idx", snac->basedir, cachedir); | ||
| 605 | int ret; | ||
| 606 | |||
| 607 | if (del) { | ||
| 608 | if ((ret = unlink(cfn)) != -1) | ||
| 609 | index_del(idx, id); | ||
| 610 | } | ||
| 611 | else { | ||
| 612 | index_add(idx, id); | ||
| 613 | ret = link(ofn, cfn); | ||
| 614 | } | ||
| 322 | 615 | ||
| 323 | return !!(mtime(fn) != 0.0); | 616 | return ret; |
| 617 | } | ||
| 618 | |||
| 619 | |||
| 620 | int object_user_cache_add(snac *snac, const char *id, const char *cachedir) | ||
| 621 | /* caches an object into a user cache */ | ||
| 622 | { | ||
| 623 | return _object_user_cache(snac, id, cachedir, 0); | ||
| 624 | } | ||
| 625 | |||
| 626 | |||
| 627 | int object_user_cache_del(snac *snac, const char *id, const char *cachedir) | ||
| 628 | /* deletes an object from a user cache */ | ||
| 629 | { | ||
| 630 | return _object_user_cache(snac, id, cachedir, 1); | ||
| 631 | } | ||
| 632 | |||
| 633 | |||
| 634 | int object_user_cache_in(snac *snac, const char *id, const char *cachedir) | ||
| 635 | /* checks if an object is stored in a cache */ | ||
| 636 | { | ||
| 637 | xs *md5 = xs_md5_hex(id, strlen(id)); | ||
| 638 | xs *cfn = xs_fmt("%s/%s/%s.json", snac->basedir, cachedir, md5); | ||
| 639 | |||
| 640 | return !!(mtime(cfn) != 0.0); | ||
| 641 | } | ||
| 642 | |||
| 643 | |||
| 644 | d_char *object_user_cache_list(snac *snac, const char *cachedir, int max) | ||
| 645 | /* returns the objects in a cache as a list */ | ||
| 646 | { | ||
| 647 | xs *idx = xs_fmt("%s/%s.idx", snac->basedir, cachedir); | ||
| 648 | return index_list(idx, max); | ||
| 649 | } | ||
| 650 | |||
| 651 | |||
| 652 | /** specialized functions **/ | ||
| 653 | |||
| 654 | /** followers **/ | ||
| 655 | |||
| 656 | int follower_add(snac *snac, const char *actor) | ||
| 657 | /* adds a follower */ | ||
| 658 | { | ||
| 659 | int ret = object_user_cache_add(snac, actor, "followers"); | ||
| 660 | |||
| 661 | snac_debug(snac, 2, xs_fmt("follower_add %s %s", actor)); | ||
| 662 | |||
| 663 | return ret == -1 ? 500 : 200; | ||
| 664 | } | ||
| 665 | |||
| 666 | |||
| 667 | int follower_del(snac *snac, const char *actor) | ||
| 668 | /* deletes a follower */ | ||
| 669 | { | ||
| 670 | int ret = object_user_cache_del(snac, actor, "followers"); | ||
| 671 | |||
| 672 | snac_debug(snac, 2, xs_fmt("follower_del %s %s", actor)); | ||
| 673 | |||
| 674 | return ret == -1 ? 404 : 200; | ||
| 675 | } | ||
| 676 | |||
| 677 | |||
| 678 | int follower_check(snac *snac, const char *actor) | ||
| 679 | /* checks if someone is a follower */ | ||
| 680 | { | ||
| 681 | return object_user_cache_in(snac, actor, "followers"); | ||
| 324 | } | 682 | } |
| 325 | 683 | ||
| 326 | 684 | ||
| 327 | d_char *follower_list(snac *snac) | 685 | d_char *follower_list(snac *snac) |
| 328 | /* returns the list of followers */ | 686 | /* returns the list of followers */ |
| 329 | { | 687 | { |
| 330 | xs *spec = xs_fmt("%s/followers/" "*.json", snac->basedir); | 688 | xs *list = object_user_cache_list(snac, "followers", XS_ALL); |
| 331 | xs *glist = xs_glob(spec, 0, 0); | 689 | d_char *fwers = xs_list_new(); |
| 332 | char *p, *v; | 690 | char *p, *v; |
| 333 | d_char *list = xs_list_new(); | ||
| 334 | 691 | ||
| 335 | /* iterate the list of files */ | 692 | /* resolve the list of md5 to be a list of actors */ |
| 336 | p = glist; | 693 | p = list; |
| 337 | while (xs_list_iter(&p, &v)) { | 694 | while (xs_list_iter(&p, &v)) { |
| 338 | FILE *f; | 695 | xs *a_obj = NULL; |
| 339 | |||
| 340 | /* load the follower data */ | ||
| 341 | if ((f = fopen(v, "r")) != NULL) { | ||
| 342 | xs *j = xs_readall(f); | ||
| 343 | fclose(f); | ||
| 344 | 696 | ||
| 345 | if (j != NULL) { | 697 | if (valid_status(object_get_by_md5(v, &a_obj, NULL))) { |
| 346 | xs *o = xs_json_loads(j); | 698 | char *actor = xs_dict_get(a_obj, "id"); |
| 347 | 699 | ||
| 348 | if (o != NULL) | 700 | if (!xs_is_null(actor)) |
| 349 | list = xs_list_append(list, o); | 701 | fwers = xs_list_append(fwers, actor); |
| 350 | } | ||
| 351 | } | 702 | } |
| 352 | } | 703 | } |
| 353 | 704 | ||
| 354 | return list; | 705 | return fwers; |
| 355 | } | 706 | } |
| 356 | 707 | ||
| 357 | 708 | ||
| 709 | /** timeline **/ | ||
| 710 | |||
| 358 | double timeline_mtime(snac *snac) | 711 | double timeline_mtime(snac *snac) |
| 359 | { | 712 | { |
| 360 | xs *fn = xs_fmt("%s/timeline", snac->basedir); | 713 | xs *fn = xs_fmt("%s/timeline", snac->basedir); |
| @@ -437,6 +790,13 @@ int timeline_del(snac *snac, char *id) | |||
| 437 | ret = 200; | 790 | ret = 200; |
| 438 | } | 791 | } |
| 439 | 792 | ||
| 793 | /* delete from the user's caches */ | ||
| 794 | object_user_cache_del(snac, id, "public"); | ||
| 795 | object_user_cache_del(snac, id, "private"); | ||
| 796 | |||
| 797 | /* try to delete the object if it's not used elsewhere */ | ||
| 798 | object_del_if_unref(id); | ||
| 799 | |||
| 440 | return ret; | 800 | return ret; |
| 441 | } | 801 | } |
| 442 | 802 | ||
| @@ -511,22 +871,11 @@ int _timeline_write(snac *snac, char *id, char *msg, char *parent, char *referre | |||
| 511 | 871 | ||
| 512 | if (pfn != NULL && (f = fopen(pfn, "r")) != NULL) { | 872 | if (pfn != NULL && (f = fopen(pfn, "r")) != NULL) { |
| 513 | xs *j; | 873 | xs *j; |
| 514 | char *v; | ||
| 515 | 874 | ||
| 516 | j = xs_readall(f); | 875 | j = xs_readall(f); |
| 517 | fclose(f); | 876 | fclose(f); |
| 518 | 877 | ||
| 519 | p_msg = xs_json_loads(j); | 878 | p_msg = xs_json_loads(j); |
| 520 | |||
| 521 | if ((v = xs_dict_get(p_msg, "_snac")) != NULL) { | ||
| 522 | /* is parent hidden? */ | ||
| 523 | if ((v = xs_dict_get(v, "hidden")) && xs_type(v) == XSTYPE_TRUE) { | ||
| 524 | snac_debug(snac, 1, | ||
| 525 | xs_fmt("_timeline_write dropping due to hidden parent %s (%s)", id, parent)); | ||
| 526 | |||
| 527 | return 0; | ||
| 528 | } | ||
| 529 | } | ||
| 530 | } | 879 | } |
| 531 | } | 880 | } |
| 532 | 881 | ||
| @@ -648,6 +997,16 @@ int _timeline_write(snac *snac, char *id, char *msg, char *parent, char *referre | |||
| 648 | } | 997 | } |
| 649 | 998 | ||
| 650 | 999 | ||
| 1000 | void timeline_update_indexes(snac *snac, const char *id) | ||
| 1001 | /* updates the indexes */ | ||
| 1002 | { | ||
| 1003 | object_user_cache_add(snac, id, "private"); | ||
| 1004 | |||
| 1005 | if (xs_startswith(id, snac->actor)) | ||
| 1006 | object_user_cache_add(snac, id, "public"); | ||
| 1007 | } | ||
| 1008 | |||
| 1009 | |||
| 651 | int timeline_add(snac *snac, char *id, char *o_msg, char *parent, char *referrer) | 1010 | int timeline_add(snac *snac, char *id, char *o_msg, char *parent, char *referrer) |
| 652 | /* adds a message to the timeline */ | 1011 | /* adds a message to the timeline */ |
| 653 | { | 1012 | { |
| @@ -680,13 +1039,61 @@ int timeline_add(snac *snac, char *id, char *o_msg, char *parent, char *referrer | |||
| 680 | 1039 | ||
| 681 | msg = xs_dict_set(msg, "_snac", md); | 1040 | msg = xs_dict_set(msg, "_snac", md); |
| 682 | 1041 | ||
| 683 | if ((ret = _timeline_write(snac, id, msg, parent, referrer))) | 1042 | if ((ret = _timeline_write(snac, id, msg, parent, referrer))) { |
| 684 | snac_debug(snac, 1, xs_fmt("timeline_add %s", id)); | 1043 | snac_debug(snac, 1, xs_fmt("timeline_add %s", id)); |
| 685 | 1044 | ||
| 1045 | object_add(id, o_msg); | ||
| 1046 | timeline_update_indexes(snac, id); | ||
| 1047 | } | ||
| 1048 | |||
| 686 | return ret; | 1049 | return ret; |
| 687 | } | 1050 | } |
| 688 | 1051 | ||
| 689 | 1052 | ||
| 1053 | d_char *timeline_top_level(snac *snac, d_char *list) | ||
| 1054 | /* returns the top level md5 entries from this index */ | ||
| 1055 | { | ||
| 1056 | d_char *tl = xs_list_new(); | ||
| 1057 | xs_set seen; | ||
| 1058 | char *p, *v; | ||
| 1059 | |||
| 1060 | xs_set_init(&seen); | ||
| 1061 | |||
| 1062 | p = list; | ||
| 1063 | while (xs_list_iter(&p, &v)) { | ||
| 1064 | char line[256] = ""; | ||
| 1065 | |||
| 1066 | strcpy(line, v); | ||
| 1067 | |||
| 1068 | for (;;) { | ||
| 1069 | char line2[256]; | ||
| 1070 | xs *fn = _object_fn_by_md5(line); | ||
| 1071 | fn = xs_replace_i(fn, ".json", "_p.idx"); | ||
| 1072 | |||
| 1073 | /* if it doesn't have a parent, use this */ | ||
| 1074 | if (index_first(fn, line2, sizeof(line2)) == 0) | ||
| 1075 | break; | ||
| 1076 | |||
| 1077 | xs *pfn = _object_fn_by_md5(line2); | ||
| 1078 | |||
| 1079 | /* well, there is a parent... but if it's not there, use this */ | ||
| 1080 | if (mtime(pfn) == 0.0) | ||
| 1081 | break; | ||
| 1082 | |||
| 1083 | /* it's here! try again with its own parent */ | ||
| 1084 | strcpy(line, line2); | ||
| 1085 | } | ||
| 1086 | |||
| 1087 | if (xs_set_add(&seen, line) == 1) | ||
| 1088 | tl = xs_list_append(tl, line); | ||
| 1089 | } | ||
| 1090 | |||
| 1091 | xs_set_free(&seen); | ||
| 1092 | |||
| 1093 | return tl; | ||
| 1094 | } | ||
| 1095 | |||
| 1096 | |||
| 690 | void timeline_admire(snac *snac, char *id, char *admirer, int like) | 1097 | void timeline_admire(snac *snac, char *id, char *admirer, int like) |
| 691 | /* updates a timeline entry with a new admiration */ | 1098 | /* updates a timeline entry with a new admiration */ |
| 692 | { | 1099 | { |
| @@ -734,52 +1141,16 @@ void timeline_admire(snac *snac, char *id, char *admirer, int like) | |||
| 734 | } | 1141 | } |
| 735 | else | 1142 | else |
| 736 | snac_log(snac, xs_fmt("timeline_admire ignored for unknown object %s", id)); | 1143 | snac_log(snac, xs_fmt("timeline_admire ignored for unknown object %s", id)); |
| 737 | } | ||
| 738 | |||
| 739 | |||
| 740 | int timeline_hide(snac *snac, char *id, int hide) | ||
| 741 | /* hides/unhides a timeline entry */ | ||
| 742 | { | ||
| 743 | int ret = 0; | ||
| 744 | xs *fn = _timeline_find_fn(snac, id); | ||
| 745 | FILE *f; | ||
| 746 | |||
| 747 | if (fn != NULL && (f = fopen(fn, "r")) != NULL) { | ||
| 748 | xs *s1 = xs_readall(f); | ||
| 749 | xs *msg = xs_json_loads(s1); | ||
| 750 | xs *meta = xs_dup(xs_dict_get(msg, "_snac")); | ||
| 751 | xs *hdn = xs_val_new(hide ? XSTYPE_TRUE : XSTYPE_FALSE); | ||
| 752 | char *p, *v; | ||
| 753 | |||
| 754 | fclose(f); | ||
| 755 | |||
| 756 | /* if it's already in this hidden state, we're done */ | ||
| 757 | if ((v = xs_dict_get(meta, "hidden")) && xs_type(v) == xs_type(hdn)) | ||
| 758 | return ret; | ||
| 759 | |||
| 760 | meta = xs_dict_set(meta, "hidden", hdn); | ||
| 761 | msg = xs_dict_set(msg, "_snac", meta); | ||
| 762 | |||
| 763 | if ((f = fopen(fn, "w")) != NULL) { | ||
| 764 | xs *j1 = xs_json_dumps_pp(msg, 4); | ||
| 765 | |||
| 766 | fwrite(j1, strlen(j1), 1, f); | ||
| 767 | fclose(f); | ||
| 768 | 1144 | ||
| 769 | snac_debug(snac, 1, xs_fmt("timeline_hide %d %s", hide, id)); | 1145 | object_admire(id, admirer, like); |
| 1146 | } | ||
| 770 | 1147 | ||
| 771 | /* now hide the children */ | ||
| 772 | p = xs_dict_get(meta, "children"); | ||
| 773 | while (xs_list_iter(&p, &v)) | ||
| 774 | timeline_hide(snac, v, hide); | ||
| 775 | 1148 | ||
| 776 | ret = 1; | 1149 | /** following **/ |
| 777 | } | ||
| 778 | } | ||
| 779 | |||
| 780 | return ret; | ||
| 781 | } | ||
| 782 | 1150 | ||
| 1151 | /* this needs special treatment and cannot use the object db as is, | ||
| 1152 | with a link to a cached author, because we need the Follow object | ||
| 1153 | in case we need to unfollow (Undo + original Follow) */ | ||
| 783 | 1154 | ||
| 784 | d_char *_following_fn(snac *snac, char *actor) | 1155 | d_char *_following_fn(snac *snac, char *actor) |
| 785 | { | 1156 | { |
| @@ -811,7 +1182,7 @@ int following_add(snac *snac, char *actor, char *msg) | |||
| 811 | 1182 | ||
| 812 | 1183 | ||
| 813 | int following_del(snac *snac, char *actor) | 1184 | int following_del(snac *snac, char *actor) |
| 814 | /* someone is no longer following us */ | 1185 | /* we're not following this actor any longer */ |
| 815 | { | 1186 | { |
| 816 | xs *fn = _following_fn(snac, actor); | 1187 | xs *fn = _following_fn(snac, actor); |
| 817 | 1188 | ||
| @@ -824,7 +1195,7 @@ int following_del(snac *snac, char *actor) | |||
| 824 | 1195 | ||
| 825 | 1196 | ||
| 826 | int following_check(snac *snac, char *actor) | 1197 | int following_check(snac *snac, char *actor) |
| 827 | /* checks if someone is following us */ | 1198 | /* checks if we are following this actor */ |
| 828 | { | 1199 | { |
| 829 | xs *fn = _following_fn(snac, actor); | 1200 | xs *fn = _following_fn(snac, actor); |
| 830 | 1201 | ||
| @@ -877,8 +1248,12 @@ d_char *following_list(snac *snac) | |||
| 877 | if (o != NULL) { | 1248 | if (o != NULL) { |
| 878 | char *type = xs_dict_get(o, "type"); | 1249 | char *type = xs_dict_get(o, "type"); |
| 879 | 1250 | ||
| 880 | if (!xs_is_null(type) && strcmp(type, "Accept") == 0) | 1251 | if (!xs_is_null(type) && strcmp(type, "Accept") == 0) { |
| 881 | list = xs_list_append(list, o); | 1252 | char *actor = xs_dict_get(o, "actor"); |
| 1253 | |||
| 1254 | if (!xs_is_null(actor)) | ||
| 1255 | list = xs_list_append(list, actor); | ||
| 1256 | } | ||
| 882 | } | 1257 | } |
| 883 | } | 1258 | } |
| 884 | } | 1259 | } |
| @@ -891,7 +1266,7 @@ d_char *following_list(snac *snac) | |||
| 891 | d_char *_muted_fn(snac *snac, char *actor) | 1266 | d_char *_muted_fn(snac *snac, char *actor) |
| 892 | { | 1267 | { |
| 893 | xs *md5 = xs_md5_hex(actor, strlen(actor)); | 1268 | xs *md5 = xs_md5_hex(actor, strlen(actor)); |
| 894 | return xs_fmt("%s/muted/%s.json", snac->basedir, md5); | 1269 | return xs_fmt("%s/muted/%s", snac->basedir, md5); |
| 895 | } | 1270 | } |
| 896 | 1271 | ||
| 897 | 1272 | ||
| @@ -930,65 +1305,89 @@ int is_muted(snac *snac, char *actor) | |||
| 930 | } | 1305 | } |
| 931 | 1306 | ||
| 932 | 1307 | ||
| 933 | d_char *_actor_fn(snac *snac, char *actor) | 1308 | d_char *_hidden_fn(snac *snac, const char *id) |
| 934 | /* returns the file name for an actor */ | ||
| 935 | { | 1309 | { |
| 936 | xs *md5 = xs_md5_hex(actor, strlen(actor)); | 1310 | xs *md5 = xs_md5_hex(id, strlen(id)); |
| 937 | return xs_fmt("%s/actors/%s.json", snac->basedir, md5); | 1311 | return xs_fmt("%s/hidden/%s", snac->basedir, md5); |
| 938 | } | 1312 | } |
| 939 | 1313 | ||
| 940 | 1314 | ||
| 941 | int actor_add(snac *snac, char *actor, char *msg) | 1315 | void hide(snac *snac, const char *id) |
| 942 | /* adds an actor */ | 1316 | /* hides a message tree */ |
| 943 | { | 1317 | { |
| 944 | int ret = 201; /* created */ | 1318 | xs *fn = _hidden_fn(snac, id); |
| 945 | xs *fn = _actor_fn(snac, actor); | ||
| 946 | FILE *f; | 1319 | FILE *f; |
| 947 | 1320 | ||
| 948 | if ((f = fopen(fn, "w")) != NULL) { | 1321 | if ((f = fopen(fn, "w")) != NULL) { |
| 949 | xs *j = xs_json_dumps_pp(msg, 4); | 1322 | fprintf(f, "%s\n", id); |
| 950 | |||
| 951 | fwrite(j, 1, strlen(j), f); | ||
| 952 | fclose(f); | 1323 | fclose(f); |
| 1324 | |||
| 1325 | snac_debug(snac, 2, xs_fmt("hidden %s %s", id, fn)); | ||
| 1326 | |||
| 1327 | /* hide all the children */ | ||
| 1328 | xs *chld = object_children(id); | ||
| 1329 | char *p, *v; | ||
| 1330 | |||
| 1331 | p = chld; | ||
| 1332 | while (xs_list_iter(&p, &v)) { | ||
| 1333 | xs *co = NULL; | ||
| 1334 | |||
| 1335 | /* resolve to get the id */ | ||
| 1336 | if (valid_status(object_get_by_md5(v, &co, NULL))) { | ||
| 1337 | if ((v = xs_dict_get(co, "id")) != NULL) | ||
| 1338 | hide(snac, v); | ||
| 1339 | } | ||
| 1340 | } | ||
| 953 | } | 1341 | } |
| 954 | else | 1342 | } |
| 955 | ret = 500; | ||
| 956 | 1343 | ||
| 957 | snac_debug(snac, 2, xs_fmt("actor_add %s %s", actor, fn)); | ||
| 958 | 1344 | ||
| 959 | // object_add(actor, msg); | 1345 | int is_hidden(snac *snac, const char *id) |
| 1346 | /* check is id is hidden */ | ||
| 1347 | { | ||
| 1348 | xs *fn = _hidden_fn(snac, id); | ||
| 960 | 1349 | ||
| 961 | return ret; | 1350 | return !!(mtime(fn) != 0.0); |
| 1351 | } | ||
| 1352 | |||
| 1353 | |||
| 1354 | int actor_add(snac *snac, const char *actor, d_char *msg) | ||
| 1355 | /* adds an actor */ | ||
| 1356 | { | ||
| 1357 | return object_add_ow(actor, msg); | ||
| 962 | } | 1358 | } |
| 963 | 1359 | ||
| 964 | 1360 | ||
| 965 | int actor_get(snac *snac, char *actor, d_char **data) | 1361 | int actor_get(snac *snac, const char *actor, d_char **data) |
| 966 | /* returns an already downloaded actor */ | 1362 | /* returns an already downloaded actor */ |
| 967 | { | 1363 | { |
| 968 | xs *fn = _actor_fn(snac, actor); | 1364 | int status = 200; |
| 969 | double t; | 1365 | char *d; |
| 970 | double max_time; | ||
| 971 | int status; | ||
| 972 | FILE *f; | ||
| 973 | 1366 | ||
| 974 | if (strcmp(actor, snac->actor) == 0) { | 1367 | if (strcmp(actor, snac->actor) == 0) { |
| 1368 | /* this actor */ | ||
| 975 | if (data) | 1369 | if (data) |
| 976 | *data = msg_actor(snac); | 1370 | *data = msg_actor(snac); |
| 977 | 1371 | ||
| 978 | return 200; | 1372 | return status; |
| 979 | } | 1373 | } |
| 980 | 1374 | ||
| 981 | t = mtime(fn); | 1375 | /* read the object */ |
| 1376 | if (!valid_status(status = object_get(actor, &d, NULL))) | ||
| 1377 | return status; | ||
| 982 | 1378 | ||
| 983 | /* no mtime? there is nothing here */ | 1379 | if (data) |
| 984 | if (t == 0.0) | 1380 | *data = d; |
| 985 | return 404; | 1381 | |
| 1382 | xs *fn = _object_fn(actor); | ||
| 1383 | double max_time; | ||
| 986 | 1384 | ||
| 987 | /* maximum time for the actor data to be considered stale */ | 1385 | /* maximum time for the actor data to be considered stale */ |
| 988 | max_time = 3600.0 * 36.0; | 1386 | max_time = 3600.0 * 36.0; |
| 989 | 1387 | ||
| 990 | if (t + max_time < (double) time(NULL)) { | 1388 | if (mtime(fn) + max_time < (double) time(NULL)) { |
| 991 | /* actor data exists but also stinks */ | 1389 | /* actor data exists but also stinks */ |
| 1390 | FILE *f; | ||
| 992 | 1391 | ||
| 993 | if ((f = fopen(fn, "a")) != NULL) { | 1392 | if ((f = fopen(fn, "a")) != NULL) { |
| 994 | /* write a blank at the end to 'touch' the file */ | 1393 | /* write a blank at the end to 'touch' the file */ |
| @@ -998,22 +1397,6 @@ int actor_get(snac *snac, char *actor, d_char **data) | |||
| 998 | 1397 | ||
| 999 | status = 205; /* "205: Reset Content" "110: Response Is Stale" */ | 1398 | status = 205; /* "205: Reset Content" "110: Response Is Stale" */ |
| 1000 | } | 1399 | } |
| 1001 | else { | ||
| 1002 | /* it's still valid */ | ||
| 1003 | status = 200; | ||
| 1004 | } | ||
| 1005 | |||
| 1006 | if (data) { | ||
| 1007 | if ((f = fopen(fn, "r")) != NULL) { | ||
| 1008 | xs *j = xs_readall(f); | ||
| 1009 | |||
| 1010 | fclose(f); | ||
| 1011 | |||
| 1012 | *data = xs_json_loads(j); | ||
| 1013 | } | ||
| 1014 | else | ||
| 1015 | status = 500; | ||
| 1016 | } | ||
| 1017 | 1400 | ||
| 1018 | return status; | 1401 | return status; |
| 1019 | } | 1402 | } |
| @@ -1033,7 +1416,7 @@ int static_get(snac *snac, const char *id, d_char **data, int *size) | |||
| 1033 | FILE *f; | 1416 | FILE *f; |
| 1034 | int status = 404; | 1417 | int status = 404; |
| 1035 | 1418 | ||
| 1036 | *size = 0xfffffff; | 1419 | *size = XS_ALL; |
| 1037 | 1420 | ||
| 1038 | if ((f = fopen(fn, "rb")) != NULL) { | 1421 | if ((f = fopen(fn, "rb")) != NULL) { |
| 1039 | *data = xs_read(f, size); | 1422 | *data = xs_read(f, size); |
| @@ -1121,6 +1504,8 @@ d_char *history_list(snac *snac) | |||
| 1121 | } | 1504 | } |
| 1122 | 1505 | ||
| 1123 | 1506 | ||
| 1507 | /** the queue **/ | ||
| 1508 | |||
| 1124 | static int _enqueue_put(char *fn, char *msg) | 1509 | static int _enqueue_put(char *fn, char *msg) |
| 1125 | /* writes safely to the queue */ | 1510 | /* writes safely to the queue */ |
| 1126 | { | 1511 | { |
| @@ -1267,38 +1652,81 @@ d_char *dequeue(snac *snac, char *fn) | |||
| 1267 | } | 1652 | } |
| 1268 | 1653 | ||
| 1269 | 1654 | ||
| 1655 | /** the purge **/ | ||
| 1656 | |||
| 1657 | static void _purge_file(const char *fn, time_t mt) | ||
| 1658 | /* purge fn if it's older than days */ | ||
| 1659 | { | ||
| 1660 | if (mtime(fn) < mt) { | ||
| 1661 | /* older than the minimum time: delete it */ | ||
| 1662 | unlink(fn); | ||
| 1663 | srv_debug(1, xs_fmt("purged %s", fn)); | ||
| 1664 | } | ||
| 1665 | } | ||
| 1666 | |||
| 1667 | |||
| 1270 | static void _purge_subdir(snac *snac, const char *subdir, int days) | 1668 | static void _purge_subdir(snac *snac, const char *subdir, int days) |
| 1271 | /* purges all files in subdir older than days */ | 1669 | /* purges all files in subdir older than days */ |
| 1272 | { | 1670 | { |
| 1273 | if (days) { | 1671 | if (days) { |
| 1274 | time_t mt = time(NULL) - days * 24 * 3600; | 1672 | time_t mt = time(NULL) - days * 24 * 3600; |
| 1275 | xs *spec = xs_fmt("%s/%s/" "*.json", snac->basedir, subdir); | 1673 | xs *spec = xs_fmt("%s/%s/" "*", snac->basedir, subdir); |
| 1276 | xs *list = xs_glob(spec, 0, 0); | 1674 | xs *list = xs_glob(spec, 0, 0); |
| 1277 | char *p, *v; | 1675 | char *p, *v; |
| 1278 | 1676 | ||
| 1279 | p = list; | 1677 | p = list; |
| 1280 | while (xs_list_iter(&p, &v)) { | 1678 | while (xs_list_iter(&p, &v)) |
| 1281 | if (mtime(v) < mt) { | 1679 | _purge_file(v, mt); |
| 1282 | /* older than the minimum time: delete it */ | 1680 | } |
| 1283 | unlink(v); | 1681 | } |
| 1284 | snac_debug(snac, 1, xs_fmt("purged %s", v)); | 1682 | |
| 1683 | |||
| 1684 | void purge_server(void) | ||
| 1685 | /* purge global server data */ | ||
| 1686 | { | ||
| 1687 | int tpd = xs_number_get(xs_dict_get(srv_config, "timeline_purge_days")); | ||
| 1688 | xs *spec = xs_fmt("%s/object/??", srv_basedir); | ||
| 1689 | xs *dirs = xs_glob(spec, 0, 0); | ||
| 1690 | char *p, *v; | ||
| 1691 | |||
| 1692 | time_t mt = time(NULL) - tpd * 24 * 3600; | ||
| 1693 | |||
| 1694 | p = dirs; | ||
| 1695 | while (xs_list_iter(&p, &v)) { | ||
| 1696 | xs *spec2 = xs_fmt("%s/" "*.json", v); | ||
| 1697 | xs *files = xs_glob(spec2, 0, 0); | ||
| 1698 | char *p2, *v2; | ||
| 1699 | |||
| 1700 | p2 = files; | ||
| 1701 | while (xs_list_iter(&p2, &v2)) { | ||
| 1702 | int n_link; | ||
| 1703 | |||
| 1704 | /* old and with no hard links? */ | ||
| 1705 | if (mtime_nl(v2, &n_link) < mt && n_link < 2) { | ||
| 1706 | xs *s1 = xs_replace(v2, ".json", ""); | ||
| 1707 | xs *l = xs_split(s1, "/"); | ||
| 1708 | char *md5 = xs_list_get(l, -1); | ||
| 1709 | |||
| 1710 | object_del_by_md5(md5); | ||
| 1285 | } | 1711 | } |
| 1286 | } | 1712 | } |
| 1287 | } | 1713 | } |
| 1288 | } | 1714 | } |
| 1289 | 1715 | ||
| 1290 | 1716 | ||
| 1291 | void purge(snac *snac) | 1717 | void purge_user(snac *snac) |
| 1292 | /* do the purge */ | 1718 | /* do the purge for this user */ |
| 1293 | { | 1719 | { |
| 1294 | int days; | 1720 | int days; |
| 1295 | 1721 | ||
| 1296 | days = xs_number_get(xs_dict_get(srv_config, "timeline_purge_days")); | 1722 | days = xs_number_get(xs_dict_get(srv_config, "timeline_purge_days")); |
| 1297 | _purge_subdir(snac, "timeline", days); | 1723 | _purge_subdir(snac, "timeline", days); |
| 1298 | _purge_subdir(snac, "actors", days); | 1724 | _purge_subdir(snac, "hidden", days); |
| 1725 | _purge_subdir(snac, "private", days); | ||
| 1299 | 1726 | ||
| 1300 | days = xs_number_get(xs_dict_get(srv_config, "local_purge_days")); | 1727 | days = xs_number_get(xs_dict_get(srv_config, "local_purge_days")); |
| 1301 | _purge_subdir(snac, "local", days); | 1728 | _purge_subdir(snac, "local", days); |
| 1729 | _purge_subdir(snac, "public", days); | ||
| 1302 | } | 1730 | } |
| 1303 | 1731 | ||
| 1304 | 1732 | ||
| @@ -1312,8 +1740,10 @@ void purge_all(void) | |||
| 1312 | p = list; | 1740 | p = list; |
| 1313 | while (xs_list_iter(&p, &uid)) { | 1741 | while (xs_list_iter(&p, &uid)) { |
| 1314 | if (user_open(&snac, uid)) { | 1742 | if (user_open(&snac, uid)) { |
| 1315 | purge(&snac); | 1743 | purge_user(&snac); |
| 1316 | user_free(&snac); | 1744 | user_free(&snac); |
| 1317 | } | 1745 | } |
| 1318 | } | 1746 | } |
| 1747 | |||
| 1748 | purge_server(); | ||
| 1319 | } | 1749 | } |