chibipub

experimental activitypub node in C
git clone git://jb55.com/chibipub
Log | Files | Refs | README | LICENSE

post.c (6147B)


      1 
      2 #include "post.h"
      3 #include "env.h"
      4 #include "hex.h"
      5 #include "hash.h"
      6 #include "debug.h"
      7 #include "base64.h"
      8 
      9 #include <stdio.h>
     10 #include <time.h>
     11 #include <string.h>
     12 #include <assert.h>
     13 
     14 /*
     15 {
     16   "id": "https://bitcoinhackers.org/users/jb55/statuses/107067111309333161/activity",
     17   "type": "Create",
     18   "actor": "https://bitcoinhackers.org/users/jb55",
     19   "published": "2021-10-08T17:34:26Z",
     20   "to": [
     21     "https://www.w3.org/ns/activitystreams#Public"
     22   ],
     23   "cc": [
     24     "https://bitcoinhackers.org/users/jb55/followers"
     25   ],
     26   "object": {
     27     "id": "https://bitcoinhackers.org/users/jb55/statuses/107067111309333161",
     28     "type": "Note",
     29     "summary": null,
     30     "inReplyTo": "https://bitcoinhackers.org/users/jb55/statuses/107067094137302681",
     31     "published": "2021-10-08T17:34:26Z",
     32     "url": "https://bitcoinhackers.org/@jb55/107067111309333161",
     33     "attributedTo": "https://bitcoinhackers.org/users/jb55",
     34     "to": [
     35       "https://www.w3.org/ns/activitystreams#Public"
     36     ],
     37     "cc": [
     38       "https://bitcoinhackers.org/users/jb55/followers"
     39     ],
     40     "sensitive": false,
     41     "atomUri": "https://bitcoinhackers.org/users/jb55/statuses/107067111309333161",
     42     "inReplyToAtomUri": "https://bitcoinhackers.org/users/jb55/statuses/107067094137302681",
     43     "conversation": "tag:bitcoinhackers.org,2021-10-08:objectId=12204931:objectType=Conversation",
     44     "content": "<p>now this is retro computer gaming: &apos;70s Colossal Cave Adventure on &apos;60s teletype</p><p><a href=\"https://www.youtube.com/watch?v=Fr-HRPr4LxQ\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://www.</span><span class=\"ellipsis\">youtube.com/watch?v=Fr-HRPr4Lx</span><span class=\"invisible\">Q</span></a></p>",
     45     "contentMap": {
     46       "en": "<p>now this is retro computer gaming: &apos;70s Colossal Cave Adventure on &apos;60s teletype</p><p><a href=\"https://www.youtube.com/watch?v=Fr-HRPr4LxQ\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://www.</span><span class=\"ellipsis\">youtube.com/watch?v=Fr-HRPr4Lx</span><span class=\"invisible\">Q</span></a></p>"
     47     },
     48     "attachment": [],
     49     "tag": [],
     50     "replies": {
     51       "id": "https://bitcoinhackers.org/users/jb55/statuses/107067111309333161/replies",
     52       "type": "Collection",
     53       "first": {
     54         "type": "CollectionPage",
     55         "next": "https://bitcoinhackers.org/users/jb55/statuses/107067111309333161/replies?only_other_accounts=true&page=true",
     56         "partOf": "https://bitcoinhackers.org/users/jb55/statuses/107067111309333161/replies",
     57         "items": []
     58       }
     59     }
     60   }
     61 }
     62 */
     63 
     64 //static inline int writekv_(FILE *outbox, const char *key, const ch
     65 
     66 
     67 static int make_id(const char *activity, const char *dt, const char *content,
     68 		unsigned char buffer[32])
     69 {
     70 	debug("make_id: hashing '%s%s%s'", activity, dt, content);
     71 
     72 	blake3_hasher hasher;
     73 	blake3_hasher_init(&hasher);
     74 	blake3_hasher_update(&hasher, activity, strlen(activity));
     75 	blake3_hasher_update(&hasher, dt, strlen(dt));
     76 	blake3_hasher_update(&hasher, content, strlen(content));
     77 	blake3_hasher_finalize(&hasher, buffer, 32);
     78 
     79 	return 1;
     80 }
     81 
     82 #define writekv(key, fmt, ...) fprintf(outbox, "\"" key "\":" fmt, ## __VA_ARGS__)
     83 
     84 static void make_pubdate(char *buffer, int buflen)
     85 {
     86 	char scratch[128];
     87 	struct timespec dt;
     88 	struct tm utc;
     89 
     90 	clock_gettime(CLOCK_REALTIME, &dt);
     91 	gmtime_r(&dt.tv_sec, &utc);
     92 	strftime(scratch, sizeof(scratch)-1, "%F %T", &utc);
     93 	snprintf(buffer, buflen-1, "%s.%ldZ", scratch, dt.tv_nsec);
     94 }
     95 
     96 
     97 int create_post(struct chibipub *pub, struct post *post)
     98 {
     99 	char note_id[65];
    100 	char create_id[65];
    101 	char pubdate[128];
    102 	unsigned char rawid[32];
    103 	FILE *outbox;
    104 	int ok;
    105 
    106 	outbox = fopen("outbox.json", "a");
    107 	if (!outbox) return 0;
    108 
    109 	//base64_encode(rawid, sizeof(rawid), id, sizeof(id), &enclen);
    110 
    111 	// published date, includes nanos
    112 	make_pubdate(pubdate, sizeof(pubdate));
    113 
    114 	// note_id = H("note" + formatted_pubdate + content)
    115 	ok = make_id("note", pubdate, post->content, rawid);
    116 	assert(ok);
    117 
    118 	hex_bytes(rawid, sizeof(rawid), note_id, 64);
    119 	note_id[32] = '\0';
    120 
    121 	// create_id = H("create" + formatted_pubdate + note_id)
    122 	ok = make_id("create", pubdate, note_id, rawid);
    123 	assert(ok);
    124 
    125 	hex_bytes(rawid, sizeof(rawid), create_id, 64);
    126 	create_id[32] = '\0';
    127 
    128 	fprintf(outbox, "{");
    129 	// we can just use the first 16 bytes of our id as the shortid
    130 	writekv("id", "\"https://%s/obj/cr_%s\",", pub->hostname, create_id);
    131 	writekv("type", "\"Create\",");
    132 
    133 	writekv("published", "\"%s\",", pubdate);
    134 	writekv("to", "[\"https://www.w3.org/ns/activitystreams#Public\"],");
    135 	writekv("cc", "[\"https://%s/followers\"],", pub->hostname);
    136 	writekv("object", "{");
    137 
    138 	writekv("id", "\"https://%s/obj/note_%s\",", pub->hostname, note_id);
    139 	writekv("type", "\"Note\",");
    140 	writekv("summary", "null,");
    141 
    142 	if (post->in_reply_to)
    143 		writekv("inReplyTo", "\"%s\",", post->in_reply_to);
    144 
    145 	writekv("published", "\"%s\",", pubdate);
    146 	writekv("url", "\"https://%s/obj/note_%s\",", pub->hostname, note_id);
    147 
    148 	// TODO: multiuser
    149 	writekv("attributedTo", "\"https://%s\",", pub->hostname);
    150 	writekv("to", "[\"https://www.w3.org/ns/activitystreams#Public\"],");
    151 	writekv("cc", "[\"https://%s/followers\"],", pub->hostname);
    152 	writekv("sensitive", "false,");
    153 	writekv("atomUri", "\"https://%s/obj/note_%s\",", pub->hostname, note_id);
    154 	writekv("inReplyToAtomUri", "\"https://%s/obj/note_%s\",", pub->hostname, note_id);
    155 	writekv("content", "\"%s\"", post->content);
    156 
    157 	// TODO: attachments
    158 	//writekv("attachment", "[],");
    159 	// TODO: tags
    160 	//writekv("tag", "[],");
    161 	// TODO: replies
    162 	//writekv("replies", "{");
    163 	//
    164 	fprintf(outbox, "}");
    165 
    166 	/*
    167 "object": { 
    168     "replies": {
    169       "id": "https://bitcoinhackers.org/users/jb55/statuses/107067111309333161/replies",
    170       "type": "Collection",
    171       "first": {
    172         "type": "CollectionPage",
    173         "next": "https://bitcoinhackers.org/users/jb55/statuses/107067111309333161/replies?only_other_accounts=true&page=true",
    174         "partOf": "https://bitcoinhackers.org/users/jb55/statuses/107067111309333161/replies",
    175         "items": []
    176       }
    177     }
    178   }
    179   */
    180 
    181 	fprintf(outbox, "}\n");
    182 	
    183 	return 1;
    184 }