summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar default2022-12-02 19:14:59 +0100
committerGravatar default2022-12-02 19:14:59 +0100
commitc00e3afcdc8cf1503439a174621090b133ad329f (patch)
tree3738fb8d555a55878011cc70a33a05733e2322e8
parentUpdated RELEASE_NOTES. (diff)
downloadsnac2-c00e3afcdc8cf1503439a174621090b133ad329f.tar.gz
snac2-c00e3afcdc8cf1503439a174621090b133ad329f.tar.xz
snac2-c00e3afcdc8cf1503439a174621090b133ad329f.zip
Big data storage change.
-rw-r--r--activitypub.c6
-rw-r--r--data.c77
-rw-r--r--html.c142
-rw-r--r--main.c2
-rw-r--r--snac.h12
5 files changed, 131 insertions, 108 deletions
diff --git a/activitypub.c b/activitypub.c
index 7f63310..8e173c5 100644
--- a/activitypub.c
+++ b/activitypub.c
@@ -879,7 +879,7 @@ int process_message(snac *snac, char *msg, char *req)
879 if (xs_type(object) == XSTYPE_DICT) 879 if (xs_type(object) == XSTYPE_DICT)
880 object = xs_dict_get(object, "id"); 880 object = xs_dict_get(object, "id");
881 881
882 timeline_admire(snac, object, actor, 1); 882 timeline_admire(snac, msg, object, actor, 1);
883 snac_log(snac, xs_fmt("new 'Like' %s %s", actor, object)); 883 snac_log(snac, xs_fmt("new 'Like' %s %s", actor, object));
884 do_notify = 1; 884 do_notify = 1;
885 } 885 }
@@ -900,7 +900,7 @@ int process_message(snac *snac, char *msg, char *req)
900 xs *who_o = NULL; 900 xs *who_o = NULL;
901 901
902 if (valid_status(actor_request(snac, who, &who_o))) { 902 if (valid_status(actor_request(snac, who, &who_o))) {
903 timeline_admire(snac, object, actor, 0); 903 timeline_admire(snac, msg, object, actor, 0);
904 snac_log(snac, xs_fmt("new 'Announce' %s %s", actor, object)); 904 snac_log(snac, xs_fmt("new 'Announce' %s %s", actor, object));
905 do_notify = 1; 905 do_notify = 1;
906 } 906 }
@@ -1094,7 +1094,7 @@ int activitypub_get_handler(d_char *req, char *q_path,
1094 else 1094 else
1095 if (strcmp(p_path, "outbox") == 0) { 1095 if (strcmp(p_path, "outbox") == 0) {
1096 xs *id = xs_fmt("%s/outbox", snac.actor); 1096 xs *id = xs_fmt("%s/outbox", snac.actor);
1097 xs *elems = local_list(&snac, 20); 1097 xs *elems = timeline_list(&snac, "public", 20);
1098 xs *list = xs_list_new(); 1098 xs *list = xs_list_new();
1099 msg = msg_collection(&snac, id); 1099 msg = msg_collection(&snac, id);
1100 char *p, *v; 1100 char *p, *v;
diff --git a/data.c b/data.c
index 51ee2c7..e9f3d93 100644
--- a/data.c
+++ b/data.c
@@ -116,6 +116,7 @@ void user_free(snac *snac)
116 xs_free(snac->config); 116 xs_free(snac->config);
117 xs_free(snac->key); 117 xs_free(snac->key);
118 xs_free(snac->actor); 118 xs_free(snac->actor);
119 xs_free(snac->md5);
119} 120}
120 121
121 122
@@ -154,6 +155,7 @@ int user_open(snac *snac, char *uid)
154 155
155 if ((snac->key = xs_json_loads(key_data)) != NULL) { 156 if ((snac->key = xs_json_loads(key_data)) != NULL) {
156 snac->actor = xs_fmt("%s/%s", srv_baseurl, uid); 157 snac->actor = xs_fmt("%s/%s", srv_baseurl, uid);
158 snac->md5 = xs_md5_hex(snac->actor, strlen(snac->actor));
157 ret = 1; 159 ret = 1;
158 } 160 }
159 else 161 else
@@ -566,14 +568,31 @@ int object_del_if_unref(const char *id)
566} 568}
567 569
568 570
571d_char *_object_metadata(const char *id, const char *idxsfx)
572/* returns the content of a metadata index */
573{
574 xs *fn = _object_fn(id);
575 fn = xs_replace_i(fn, ".json", idxsfx);
576 return index_list(fn, XS_ALL);
577}
578
579
569d_char *object_children(const char *id) 580d_char *object_children(const char *id)
570/* returns the list of an object's children */ 581/* returns the list of an object's children */
571{ 582{
572 xs *fn = _object_fn(id); 583 return _object_metadata(id, "_c.idx");
584}
573 585
574 fn = xs_replace_i(fn, ".json", "_c.idx");
575 586
576 return index_list(fn, XS_ALL); 587d_char *object_likes(const char *id)
588{
589 return _object_metadata(id, "_l.idx");
590}
591
592
593d_char *object_announces(const char *id)
594{
595 return _object_metadata(id, "_a.idx");
577} 596}
578 597
579 598
@@ -818,35 +837,6 @@ d_char *timeline_get(snac *snac, char *fn)
818} 837}
819 838
820 839
821d_char *_timeline_list(snac *snac, char *directory, int max)
822/* returns a list of the timeline filenames */
823{
824 xs *spec = xs_fmt("%s/%s/" "*.json", snac->basedir, directory);
825 int c_max;
826
827 /* maximum number of items in the timeline */
828 c_max = xs_number_get(xs_dict_get(srv_config, "max_timeline_entries"));
829
830 /* never more timeline entries than the configured maximum */
831 if (max > c_max)
832 max = c_max;
833
834 return xs_glob_n(spec, 0, 1, max);
835}
836
837
838d_char *timeline_list(snac *snac, int max)
839{
840 return _timeline_list(snac, "timeline", max);
841}
842
843
844d_char *local_list(snac *snac, int max)
845{
846 return _timeline_list(snac, "local", max);
847}
848
849
850d_char *_timeline_new_fn(snac *snac, char *id) 840d_char *_timeline_new_fn(snac *snac, char *id)
851/* creates a new filename */ 841/* creates a new filename */
852{ 842{
@@ -1050,7 +1040,7 @@ int timeline_add(snac *snac, char *id, char *o_msg, char *parent, char *referrer
1050} 1040}
1051 1041
1052 1042
1053d_char *timeline_top_level(snac *snac, d_char *list) 1043d_char *timeline_top_level(d_char *list)
1054/* returns the top level md5 entries from this index */ 1044/* returns the top level md5 entries from this index */
1055{ 1045{
1056 d_char *tl = xs_list_new(); 1046 d_char *tl = xs_list_new();
@@ -1094,7 +1084,26 @@ d_char *timeline_top_level(snac *snac, d_char *list)
1094} 1084}
1095 1085
1096 1086
1097void timeline_admire(snac *snac, char *id, char *admirer, int like) 1087d_char *timeline_list(snac *snac, const char *idx_name, int max)
1088/* returns a timeline */
1089{
1090 int c_max;
1091
1092 /* maximum number of items in the timeline */
1093 c_max = xs_number_get(xs_dict_get(srv_config, "max_timeline_entries"));
1094
1095 /* never more timeline entries than the configured maximum */
1096 if (max > c_max)
1097 max = c_max;
1098
1099 xs *idx = xs_fmt("%s/%s.idx", snac->basedir, idx_name);
1100 xs *list = index_list_desc(idx, max);
1101
1102 return timeline_top_level(list);
1103}
1104
1105
1106void timeline_admire(snac *snac, char *o_msg, char *id, char *admirer, int like)
1098/* updates a timeline entry with a new admiration */ 1107/* updates a timeline entry with a new admiration */
1099{ 1108{
1100 xs *ofn = _timeline_find_fn(snac, id); 1109 xs *ofn = _timeline_find_fn(snac, id);
diff --git a/html.c b/html.c
index 63b54c3..4c1ca79 100644
--- a/html.c
+++ b/html.c
@@ -386,7 +386,8 @@ d_char *html_entry_controls(snac *snac, d_char *os, char *msg, int num)
386{ 386{
387 char *id = xs_dict_get(msg, "id"); 387 char *id = xs_dict_get(msg, "id");
388 char *actor = xs_dict_get(msg, "attributedTo"); 388 char *actor = xs_dict_get(msg, "attributedTo");
389 char *meta = xs_dict_get(msg, "_snac"); 389 xs *likes = object_likes(id);
390 xs *boosts = object_announces(id);
390 391
391 xs *s = xs_str_new(NULL); 392 xs *s = xs_str_new(NULL);
392 xs *md5 = xs_md5_hex(id, strlen(id)); 393 xs *md5 = xs_md5_hex(id, strlen(id));
@@ -407,20 +408,14 @@ d_char *html_entry_controls(snac *snac, d_char *os, char *msg, int num)
407 s = xs_str_cat(s, s1); 408 s = xs_str_cat(s, s1);
408 } 409 }
409 410
410 { 411 if (xs_list_in(likes, snac->md5) == -1) {
411 char *l; 412 /* not already liked; add button */
412 413 s = html_button(s, "like", L("Like"));
413 l = xs_dict_get(meta, "liked_by"); 414 }
414 if (xs_list_in(l, snac->actor) == -1) {
415 /* not already liked; add button */
416 s = html_button(s, "like", L("Like"));
417 }
418 415
419 l = xs_dict_get(meta, "announced_by"); 416 if (strcmp(actor, snac->actor) == 0 || xs_list_in(boosts, snac->md5) == -1) {
420 if (strcmp(actor, snac->actor) == 0 || xs_list_in(l, snac->actor) == -1) { 417 /* not already boosted or us; add button */
421 /* not already boosted or us; add button */ 418 s = html_button(s, "boost", L("Boost"));
422 s = html_button(s, "boost", L("Boost"));
423 }
424 } 419 }
425 420
426 if (strcmp(actor, snac->actor) != 0) { 421 if (strcmp(actor, snac->actor) != 0) {
@@ -477,23 +472,20 @@ d_char *html_entry_controls(snac *snac, d_char *os, char *msg, int num)
477} 472}
478 473
479 474
480d_char *html_entry(snac *snac, d_char *os, char *msg, xs_set *seen, int local, int level, int *num) 475d_char *html_entry(snac *snac, d_char *os, char *msg, int local, int level, int *num)
481{ 476{
482 char *id = xs_dict_get(msg, "id"); 477 char *id = xs_dict_get(msg, "id");
483 char *type = xs_dict_get(msg, "type"); 478 char *type = xs_dict_get(msg, "type");
484 char *meta = xs_dict_get(msg, "_snac");
485 char *actor; 479 char *actor;
486 int sensitive = 0; 480 int sensitive = 0;
487 char *v; 481 char *v;
482 xs *likes = NULL;
483 xs *boosts = NULL;
488 484
489 /* do not show non-public messages in the public timeline */ 485 /* do not show non-public messages in the public timeline */
490 if (local && !is_msg_public(snac, msg)) 486 if (local && !is_msg_public(snac, msg))
491 return os; 487 return os;
492 488
493 /* return if already seen */
494 if (xs_set_add(seen, id) == 0)
495 return os;
496
497 xs *s = xs_str_new(NULL); 489 xs *s = xs_str_new(NULL);
498 490
499 /* top wrap */ 491 /* top wrap */
@@ -522,6 +514,14 @@ d_char *html_entry(snac *snac, d_char *os, char *msg, xs_set *seen, int local, i
522 514
523 return xs_str_cat(os, s); 515 return xs_str_cat(os, s);
524 } 516 }
517 else
518 if (strcmp(type, "Note") != 0) {
519 s = xs_str_cat(s, "<div class=\"snac-post\">\n");
520
521 xs *s1 = xs_fmt("<p>%s</p>\n", type);
522
523 return xs_str_cat(os, s);
524 }
525 525
526 /* bring the main actor */ 526 /* bring the main actor */
527 if ((actor = xs_dict_get(msg, "attributedTo")) == NULL) 527 if ((actor = xs_dict_get(msg, "attributedTo")) == NULL)
@@ -536,14 +536,14 @@ d_char *html_entry(snac *snac, d_char *os, char *msg, xs_set *seen, int local, i
536 536
537 /* if this is our post, add the score */ 537 /* if this is our post, add the score */
538 if (xs_startswith(id, snac->actor)) { 538 if (xs_startswith(id, snac->actor)) {
539 int likes = xs_list_len(xs_dict_get(meta, "liked_by")); 539 likes = object_likes(id);
540 int boosts = xs_list_len(xs_dict_get(meta, "announced_by")); 540 boosts = object_announces(id);
541 541
542 /* alternate emojis: %d &#128077; %d &#128257; */ 542 /* alternate emojis: %d &#128077; %d &#128257; */
543 543
544 xs *s1 = xs_fmt( 544 xs *s1 = xs_fmt(
545 "<div class=\"snac-score\">%d &#9733; %d &#8634;</div>\n", 545 "<div class=\"snac-score\">%d &#9733; %d &#8634;</div>\n",
546 likes, boosts); 546 xs_list_len(likes), xs_list_len(boosts));
547 547
548 s = xs_str_cat(s, s1); 548 s = xs_str_cat(s, s1);
549 } 549 }
@@ -553,28 +553,46 @@ d_char *html_entry(snac *snac, d_char *os, char *msg, xs_set *seen, int local, i
553 553
554 s = xs_str_cat(s, "<div class=\"snac-post\">\n"); 554 s = xs_str_cat(s, "<div class=\"snac-post\">\n");
555 555
556 /* print the origin of the post, if any */ 556 if (boosts == NULL)
557 if (!xs_is_null(p = xs_dict_get(meta, "referrer"))) { 557 boosts = object_announces(id);
558
559 if (xs_list_len(boosts)) {
560 /* if somebody boosted this, show as origin */
561 p = xs_list_get(boosts, 0);
558 xs *actor_r = NULL; 562 xs *actor_r = NULL;
559 563
560 if (valid_status(actor_get(snac, p, &actor_r))) { 564 if (xs_list_in(boosts, snac->md5) != -1) {
565 /* we boosted this */
566 xs *s1 = xs_fmt(
567 "<div class=\"snac-origin\">"
568 "<a href=\"%s\">%s</a> %s</a></div>",
569 snac->actor, xs_dict_get(snac->config, "name"), L("boosted")
570 );
571
572 s = xs_str_cat(s, s1);
573 }
574 else
575 if (valid_status(object_get_by_md5(p, &actor_r, NULL))) {
561 char *name; 576 char *name;
562 577
563 if ((name = xs_dict_get(actor_r, "name")) == NULL) 578 if ((name = xs_dict_get(actor_r, "name")) == NULL)
564 name = xs_dict_get(actor_r, "preferredUsername"); 579 name = xs_dict_get(actor_r, "preferredUsername");
565 580
566 xs *s1 = xs_fmt( 581 if (!xs_is_null(name)) {
567 "<div class=\"snac-origin\">" 582 xs *s1 = xs_fmt(
568 "<a href=\"%s\">%s</a> %s</div>\n", 583 "<div class=\"snac-origin\">"
569 xs_dict_get(actor_r, "id"), 584 "<a href=\"%s\">%s</a> %s</div>\n",
570 name, 585 xs_dict_get(actor_r, "id"),
571 L("boosted") 586 name,
572 ); 587 L("boosted")
588 );
573 589
574 s = xs_str_cat(s, s1); 590 s = xs_str_cat(s, s1);
591 }
575 } 592 }
576 } 593 }
577 else 594
595#if 0
578 if (!xs_is_null((p = xs_dict_get(meta, "parent"))) && *p) { 596 if (!xs_is_null((p = xs_dict_get(meta, "parent"))) && *p) {
579 /* this may happen if any of the autor or the parent aren't here */ 597 /* this may happen if any of the autor or the parent aren't here */
580 xs *s1 = xs_fmt( 598 xs *s1 = xs_fmt(
@@ -586,18 +604,6 @@ d_char *html_entry(snac *snac, d_char *os, char *msg, xs_set *seen, int local, i
586 s = xs_str_cat(s, s1); 604 s = xs_str_cat(s, s1);
587 } 605 }
588 else 606 else
589 if (!xs_is_null((p = xs_dict_get(meta, "announced_by"))) &&
590 xs_list_in(p, snac->actor) != -1) {
591 /* we boosted this */
592 xs *s1 = xs_fmt(
593 "<div class=\"snac-origin\">"
594 "<a href=\"%s\">%s</a> %s</a></div>",
595 snac->actor, xs_dict_get(snac->config, "name"), L("boosted")
596 );
597
598 s = xs_str_cat(s, s1);
599 }
600 else
601 if (!xs_is_null((p = xs_dict_get(meta, "liked_by"))) && 607 if (!xs_is_null((p = xs_dict_get(meta, "liked_by"))) &&
602 xs_list_in(p, snac->actor) != -1) { 608 xs_list_in(p, snac->actor) != -1) {
603 /* we liked this */ 609 /* we liked this */
@@ -609,6 +615,7 @@ d_char *html_entry(snac *snac, d_char *os, char *msg, xs_set *seen, int local, i
609 615
610 s = xs_str_cat(s, s1); 616 s = xs_str_cat(s, s1);
611 } 617 }
618#endif
612 } 619 }
613 else 620 else
614 s = xs_str_cat(s, "<div class=\"snac-child\">\n"); 621 s = xs_str_cat(s, "<div class=\"snac-child\">\n");
@@ -717,12 +724,11 @@ d_char *html_entry(snac *snac, d_char *os, char *msg, xs_set *seen, int local, i
717 s = html_entry_controls(snac, s, msg, *num); 724 s = html_entry_controls(snac, s, msg, *num);
718 725
719 /** children **/ 726 /** children **/
720 727 xs *children = object_children(id);
721 char *children = xs_dict_get(meta, "children"); 728 int left = xs_list_len(children);
722 int left = xs_list_len(children);
723 729
724 if (left) { 730 if (left) {
725 char *id; 731 char *p, *cmd5;
726 732
727 if (level < 4) 733 if (level < 4)
728 s = xs_str_cat(s, "<div class=\"snac-children\">\n"); 734 s = xs_str_cat(s, "<div class=\"snac-children\">\n");
@@ -732,16 +738,18 @@ d_char *html_entry(snac *snac, d_char *os, char *msg, xs_set *seen, int local, i
732 if (left > 3) 738 if (left > 3)
733 s = xs_str_cat(s, "<details><summary>...</summary>\n"); 739 s = xs_str_cat(s, "<details><summary>...</summary>\n");
734 740
735 while (xs_list_iter(&children, &id)) { 741 p = children;
736 xs *chd = timeline_find(snac, id); 742 while (xs_list_iter(&p, &cmd5)) {
743 xs *chd = NULL;
744 object_get_by_md5(cmd5, &chd, NULL);
737 745
738 if (left == 3) 746 if (left == 3)
739 s = xs_str_cat(s, "</details>\n"); 747 s = xs_str_cat(s, "</details>\n");
740 748
741 if (chd != NULL) 749 if (chd != NULL)
742 s = html_entry(snac, s, chd, seen, local, level + 1, num); 750 s = html_entry(snac, s, chd, local, level + 1, num);
743 else 751 else
744 snac_debug(snac, 2, xs_fmt("cannot read from timeline child %s", id)); 752 snac_debug(snac, 2, xs_fmt("cannot read from timeline child %s", cmd5));
745 753
746 left--; 754 left--;
747 } 755 }
@@ -773,13 +781,10 @@ d_char *html_timeline(snac *snac, char *list, int local)
773/* returns the HTML for the timeline */ 781/* returns the HTML for the timeline */
774{ 782{
775 d_char *s = xs_str_new(NULL); 783 d_char *s = xs_str_new(NULL);
776 xs_set seen;
777 char *v; 784 char *v;
778 double t = ftime(); 785 double t = ftime();
779 int num = 0; 786 int num = 0;
780 787
781 xs_set_init(&seen);
782
783 s = html_user_header(snac, s, local); 788 s = html_user_header(snac, s, local);
784 789
785 if (!local) 790 if (!local)
@@ -789,9 +794,12 @@ d_char *html_timeline(snac *snac, char *list, int local)
789 s = xs_str_cat(s, "<div class=\"snac-posts\">\n"); 794 s = xs_str_cat(s, "<div class=\"snac-posts\">\n");
790 795
791 while (xs_list_iter(&list, &v)) { 796 while (xs_list_iter(&list, &v)) {
792 xs *msg = timeline_get(snac, v); 797 xs *msg = NULL;
798
799 if (!valid_status(object_get_by_md5(v, &msg, NULL)))
800 continue;
793 801
794 s = html_entry(snac, s, msg, &seen, local, 0, &num); 802 s = html_entry(snac, s, msg, local, 0, &num);
795 } 803 }
796 804
797 s = xs_str_cat(s, "</div>\n"); 805 s = xs_str_cat(s, "</div>\n");
@@ -830,8 +838,6 @@ d_char *html_timeline(snac *snac, char *list, int local)
830 838
831 s = xs_str_cat(s, "</body>\n</html>\n"); 839 s = xs_str_cat(s, "</body>\n</html>\n");
832 840
833 xs_set_free(&seen);
834
835 return s; 841 return s;
836} 842}
837 843
@@ -1007,7 +1013,7 @@ int html_get_handler(d_char *req, char *q_path, char **body, int *b_size, char *
1007 status = 200; 1013 status = 200;
1008 } 1014 }
1009 else { 1015 else {
1010 xs *list = local_list(&snac, XS_ALL); 1016 xs *list = timeline_list(&snac, "public", XS_ALL);
1011 1017
1012 *body = html_timeline(&snac, list, 1); 1018 *body = html_timeline(&snac, list, 1);
1013 *b_size = strlen(*body); 1019 *b_size = strlen(*body);
@@ -1033,7 +1039,9 @@ int html_get_handler(d_char *req, char *q_path, char **body, int *b_size, char *
1033 else { 1039 else {
1034 snac_debug(&snac, 1, xs_fmt("building timeline")); 1040 snac_debug(&snac, 1, xs_fmt("building timeline"));
1035 1041
1036 xs *list = timeline_list(&snac, XS_ALL); 1042 xs *list = timeline_list(&snac, "private", XS_ALL);
1043
1044 printf("--> %d\n", xs_list_len(list));
1037 1045
1038 *body = html_timeline(&snac, list, 0); 1046 *body = html_timeline(&snac, list, 0);
1039 *b_size = strlen(*body); 1047 *b_size = strlen(*body);
@@ -1098,7 +1106,7 @@ int html_get_handler(d_char *req, char *q_path, char **body, int *b_size, char *
1098 if (strcmp(p_path, ".rss") == 0) { 1106 if (strcmp(p_path, ".rss") == 0) {
1099 /* public timeline in RSS format */ 1107 /* public timeline in RSS format */
1100 d_char *rss; 1108 d_char *rss;
1101 xs *elems = local_list(&snac, 20); 1109 xs *elems = timeline_list(&snac, "public", 20);
1102 xs *bio = not_really_markdown(xs_dict_get(snac.config, "bio")); 1110 xs *bio = not_really_markdown(xs_dict_get(snac.config, "bio"));
1103 char *p, *v; 1111 char *p, *v;
1104 1112
@@ -1281,13 +1289,13 @@ int html_post_handler(d_char *req, char *q_path, d_char *payload, int p_size,
1281 if (strcmp(action, L("Like")) == 0) { 1289 if (strcmp(action, L("Like")) == 0) {
1282 xs *msg = msg_admiration(&snac, id, "Like"); 1290 xs *msg = msg_admiration(&snac, id, "Like");
1283 post(&snac, msg); 1291 post(&snac, msg);
1284 timeline_admire(&snac, id, snac.actor, 1); 1292 timeline_admire(&snac, msg, id, snac.actor, 1);
1285 } 1293 }
1286 else 1294 else
1287 if (strcmp(action, L("Boost")) == 0) { 1295 if (strcmp(action, L("Boost")) == 0) {
1288 xs *msg = msg_admiration(&snac, id, "Announce"); 1296 xs *msg = msg_admiration(&snac, id, "Announce");
1289 post(&snac, msg); 1297 post(&snac, msg);
1290 timeline_admire(&snac, id, snac.actor, 0); 1298 timeline_admire(&snac, msg, id, snac.actor, 0);
1291 } 1299 }
1292 else 1300 else
1293 if (strcmp(action, L("MUTE")) == 0) { 1301 if (strcmp(action, L("MUTE")) == 0) {
diff --git a/main.c b/main.c
index a109192..8fbdb88 100644
--- a/main.c
+++ b/main.c
@@ -167,7 +167,7 @@ int main(int argc, char *argv[])
167 167
168 xs *idx = xs_fmt("%s/private.idx", snac.basedir); 168 xs *idx = xs_fmt("%s/private.idx", snac.basedir);
169 xs *list = index_list_desc(idx, 256); 169 xs *list = index_list_desc(idx, 256);
170 xs *tl = timeline_top_level(&snac, list); 170 xs *tl = timeline_top_level(list);
171 171
172 xs *j = xs_json_dumps_pp(tl, 4); 172 xs *j = xs_json_dumps_pp(tl, 4);
173 printf("%s\n", j); 173 printf("%s\n", j);
diff --git a/snac.h b/snac.h
index b8b5215..88f335a 100644
--- a/snac.h
+++ b/snac.h
@@ -33,6 +33,7 @@ typedef struct _snac {
33 d_char *config; /* user configuration */ 33 d_char *config; /* user configuration */
34 d_char *key; /* keypair */ 34 d_char *key; /* keypair */
35 d_char *actor; /* actor url */ 35 d_char *actor; /* actor url */
36 d_char *md5; /* actor url md5 */
36} snac; 37} snac;
37 38
38int user_open(snac *snac, char *uid); 39int user_open(snac *snac, char *uid);
@@ -59,9 +60,14 @@ int index_first(const char *fn, char *buf, int size);
59d_char *index_list(const char *fn, int max); 60d_char *index_list(const char *fn, int max);
60d_char *index_list_desc(const char *fn, int max); 61d_char *index_list_desc(const char *fn, int max);
61 62
63int object_get_by_md5(const char *md5, d_char **obj, const char *type);
62int object_del(const char *id); 64int object_del(const char *id);
63int object_del_if_unref(const char *id); 65int object_del_if_unref(const char *id);
64 66
67d_char *object_children(const char *id);
68d_char *object_likes(const char *id);
69d_char *object_announces(const char *id);
70
65int follower_add(snac *snac, const char *actor); 71int follower_add(snac *snac, const char *actor);
66int follower_del(snac *snac, const char *actor); 72int follower_del(snac *snac, const char *actor);
67int follower_check(snac *snac, const char *actor); 73int follower_check(snac *snac, const char *actor);
@@ -73,11 +79,11 @@ d_char *_timeline_find_fn(snac *snac, char *id);
73d_char *timeline_find(snac *snac, char *id); 79d_char *timeline_find(snac *snac, char *id);
74int timeline_del(snac *snac, char *id); 80int timeline_del(snac *snac, char *id);
75d_char *timeline_get(snac *snac, char *fn); 81d_char *timeline_get(snac *snac, char *fn);
76d_char *timeline_list(snac *snac, int max); 82d_char *timeline_list(snac *snac, const char *idx_name, int max);
77int timeline_add(snac *snac, char *id, char *o_msg, char *parent, char *referrer); 83int timeline_add(snac *snac, char *id, char *o_msg, char *parent, char *referrer);
78void timeline_admire(snac *snac, char *id, char *admirer, int like); 84void timeline_admire(snac *snac, char *o_msg, char *id, char *admirer, int like);
79 85
80d_char *timeline_top_level(snac *snac, d_char *list); 86d_char *timeline_top_level(d_char *list);
81 87
82d_char *local_list(snac *snac, int max); 88d_char *local_list(snac *snac, int max);
83 89