chibipub

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

ap_json.c (7602B)


      1 #include "ap_json.h"
      2 #include "inbox.h"
      3 #include "sha256/sha256.h"
      4 #include "base64.h"
      5 
      6 #include <time.h>
      7 #include <strings.h>
      8 
      9 
     10 static int handle_ap_string(struct json_handlers *h, struct json_strtok *str)
     11 {
     12 	struct ap_json *apjson = (struct ap_json*)h->data;
     13 	if (str->is_key && str->size == 8
     14 			&& !memcmp(str->text, "@context", str->size)) {
     15 		apjson->flags |= AP_IS_CONTEXT;
     16 	} else if (apjson->flags & AP_IS_CONTEXT) {
     17 		apjson->flags &= ~AP_IS_CONTEXT;
     18 	}
     19 
     20 	return handle_string(&apjson->compact_handler, str);
     21 }
     22 
     23 static int handle_key(struct ap_json *a, char *key)
     24 {
     25 	struct json_strtok str;
     26 	unsigned int *nop;
     27 	init_json_strtok(&str);
     28 
     29 	str.text = (unsigned char *)key;
     30 	str.size = strlen(key);
     31 	str.is_key = 1;
     32 	if (!handle_string(&a->compact_handler, &str)) {
     33 		note_error(&a->errs, "push @wsheaders oob");
     34 		return 0;
     35 	}
     36 
     37 	if (!handle_token(&a->compact_handler, ':', &nop)) {
     38 		return 0;
     39 	}
     40 
     41 	return 1;
     42 }
     43 
     44 static int handle_kv(struct ap_json *a, char *key, char *val, int escape_val)
     45 {
     46 	struct json_strtok str;
     47 	init_json_strtok(&str);
     48 
     49 	if (!handle_key(a, key)) {
     50 		return 0;
     51 	}
     52 
     53 	str.text = (unsigned char *)val;
     54 	str.size = strlen(val);
     55 	str.is_key = 0;
     56 	str.needs_escape = escape_val;
     57 	if (!handle_string(&a->compact_handler, &str)) {
     58 		note_error(&a->errs, "push @wsheaders oob");
     59 		return 0;
     60 	}
     61 
     62 	return 1;
     63 }
     64 
     65 static int handle_wssig(struct ap_json *a)
     66 {
     67 	struct json_strtok sig;
     68 	init_json_strtok(&sig);
     69 	unsigned int *nop;
     70 
     71 	if (!handle_key(a, "@wssig")) {
     72 		return 0;
     73 	}
     74 
     75 	sig.text = (unsigned char*)a->sig->b64_sig;
     76 	sig.size = a->sig->b64_len;
     77 	sig.is_key = 0;
     78 	if (!handle_string(&a->compact_handler, &sig)) {
     79 		note_error(&a->errs, "push @wssig val oob");
     80 		return 0;
     81 	}
     82 
     83 	if (!handle_token(&a->compact_handler, ',', &nop)) {
     84 		note_error(&a->errs, "push @ws_sig : tok oob");
     85 		return 0;
     86 	}
     87 
     88 	return 1;
     89 }
     90 
     91 static int handle_wsstamp(struct ap_json *a)
     92 {
     93 	unsigned int *nop;
     94 	struct json_strtok str;
     95 	init_json_strtok(&str);
     96 
     97 	if (!handle_key(a, "@wsstamp")) {
     98 		return 0;
     99 	}
    100 
    101 	if (!handle_number(&a->compact_handler, time(NULL))) {
    102 		note_error(&a->errs, "push @wsstamp oob number");
    103 		return 0;
    104 	}
    105 
    106 	if (!handle_token(&a->compact_handler, ',', &nop)) {
    107 		note_error(&a->errs, "push @ws_sig : tok oob");
    108 		return 0;
    109 	}
    110 
    111 	return 1;
    112 }
    113 
    114 static int handle_wskeyid(struct ap_json *a)
    115 {
    116 	unsigned int *nop;
    117 	struct json_strtok str;
    118 	init_json_strtok(&str);
    119 
    120 	if (!handle_kv(a, "@wskeyid", (char*)a->sig->key_id, 0)) {
    121 		return 0;
    122 	}
    123 
    124 	if (!handle_token(&a->compact_handler, ',', &nop)) {
    125 		note_error(&a->errs, "push @wskeyid , tok oob");
    126 		return 0;
    127 	}
    128 
    129 	return 1;
    130 }
    131 
    132 static inline int handle_header(struct ap_json *a, struct http_header *header)
    133 {
    134 	return handle_kv(a, header->name, header->value, 1);
    135 }
    136 
    137 struct sized_str {
    138 	unsigned char *text;
    139 	int size;
    140 };
    141 
    142 static int split_headers(unsigned char *headers, int header_len,
    143 		struct sized_str *strs, int max_strs, int *out)
    144 {
    145 	int n_strs = 0;
    146 	struct sized_str *str;
    147 	unsigned char *p, *pstart;
    148 
    149 	pstart = p = headers;
    150 	for (;; p++) {
    151 		if (n_strs == max_strs)
    152 			return 0;
    153 
    154 		if (*p == ' ' || *p == 0) {
    155 			str = &strs[n_strs++];
    156 			str->text = pstart;
    157 			str->size = p - pstart;
    158 			pstart = p+1;
    159 		}
    160 
    161 		if (*p == 0)
    162 			break;
    163 	}
    164 
    165 	*out = n_strs;
    166 	return 1;
    167 }
    168 
    169 static inline struct http_header *find_header(struct http_header *headers,
    170 		unsigned char *name, int name_len)
    171 {
    172 	struct http_header *header;
    173 
    174 	for (header = headers; header; header = header->next) {
    175 		if (name_len == strlen(header->name) &&
    176 		    !strncasecmp((const char *)name, header->name, name_len)) {
    177 			return header;
    178 		}
    179 	}
    180 
    181 	return NULL;
    182 }
    183 
    184 static int handle_wssigbuf(struct ap_json *a)
    185 {
    186 	struct sized_str strs[16] = {0};
    187 	int n_headers = 0, i;
    188 	struct http_header *header;
    189 	struct cursor *c = &((struct json_pusher *)a->compact_handler.data)->cur;
    190 	struct sized_str *str;
    191 
    192 	if (!handle_key(a, "@wssigbuf")) {
    193 		return 0;
    194 	}
    195 
    196 	if (!push_byte(c, '"')) {
    197 		return 0;
    198 	}
    199 
    200 	fprintf(stderr, "headers: %s\n", a->sig->headers);
    201 
    202 	if (!split_headers((unsigned char*)a->sig->headers,
    203 				strlen(a->sig->headers), strs,
    204 				sizeof(strs)/sizeof(strs[0]), &n_headers)) {
    205 		note_error(&a->errs, "split_headers overflow");
    206 		return 0;
    207 	}
    208 
    209 	static const char request_target[] = "(request-target)";
    210 
    211 	for (i = 0; i < n_headers; i++) {
    212 		str = &strs[i];
    213 		if (!push_escaped(c, str->text, str->size)) {
    214 			return 0;
    215 		}
    216 		if (!push_str(c, ": ")) {
    217 			return 0;
    218 		}
    219 
    220 		if (str->size == (sizeof(request_target)-1) &&
    221 		    !memcmp(request_target, str->text, str->size)) {
    222 			if (!push_str(c, "post /inbox")) {
    223 				return 0;
    224 			}
    225 		} else if ((header = find_header(a->req->headers, str->text, str->size))) {
    226 			if (!push_escaped(c, (unsigned char*)header->value,
    227 						strlen(header->value))) {
    228 				return 0;
    229 			}
    230 		} else {
    231 			note_error(&a->errs, "could not find header %.*s",
    232 					str->size, str->text);
    233 			return 0;
    234 		}
    235 
    236 		if (i != n_headers-1) {
    237 			push_str(c, "\\n");
    238 		}
    239 	}
    240 
    241 	if (!push_str(c, "\",")) {
    242 		return 0;
    243 	}
    244 
    245 	return 1;
    246 }
    247 
    248 static int handle_wsdigest(struct ap_json *a)
    249 {
    250 	struct json_pusher *p = (struct json_pusher *)a->compact_handler.data;
    251 	struct cursor *c = &p->cur;
    252 	size_t b64_len;
    253 	unsigned char hash[32];
    254 	unsigned char b64[128];
    255 	unsigned char *pos;
    256 
    257 	if (!handle_key(a, "@wsdigest")) {
    258 		note_error(&a->errs, "wsdigest key oob");
    259 		return 0;
    260 	}
    261 
    262 	if (!push_byte(c, '"')) {
    263 		return 0;
    264 	}
    265 
    266 	sha256_hash(hash, a->req->body, a->req->body_len);
    267 	// TODO: this might be broken... 
    268 	base64_encode(hash, 32, b64, sizeof(b64), &b64_len);
    269 
    270 	pos = c->p;
    271 
    272 	if (!(push_str(c, "SHA-256=") && push_data(c, b64, b64_len) )) {
    273 		return 0;
    274 	}
    275 
    276 	// quick sanity check
    277 	if ((b64_len+8) != (c->p - pos) || !memcmp(pos, b64, b64_len)) {
    278 		note_error(&a->errs, "bad digest");
    279 		return 0;
    280 	}
    281 
    282 	return push_str(c, "\",");
    283 }
    284 
    285 static int handle_ap_token(struct json_handlers *h, char token,
    286 			   unsigned int **len)
    287 {
    288 	struct ap_json *a = (struct ap_json*)h->data;
    289 	unsigned int *nop;
    290 
    291 	/* push some extra data after the start of the initial object */
    292 	if (token == '{' && !(a->flags & AP_HAD_START)) {
    293 		a->flags |= AP_HAD_START | AP_IS_START;
    294 
    295 		if (!handle_token(&a->compact_handler, '{', &nop)) {
    296 			note_error(&a->errs, "push @ws_sig { tok oob");
    297 			return 0;
    298 		}
    299 
    300 		if (!handle_wssig(a)) {
    301 			note_error(&a->errs, "push wssig");
    302 			return 0;
    303 		}
    304 
    305 		if (!handle_wsstamp(a)) {
    306 			note_error(&a->errs, "push wsstamp");
    307 			return 0;
    308 		}
    309 
    310 		if (!handle_wsdigest(a)) {
    311 			note_error(&a->errs, "push wsdigest");
    312 			return 0;
    313 		}
    314 
    315 		if (!handle_wskeyid(a)) {
    316 			note_error(&a->errs, "push wskeyid");
    317 			return 0;
    318 		}
    319 
    320 		if (!handle_wssigbuf(a)) {
    321 			note_error(&a->errs, "push wssigbuf");
    322 			return 0;
    323 		}
    324 
    325 		return 1;
    326 	}
    327 
    328 	return handle_token(&a->compact_handler, token, len);
    329 }
    330 
    331 void init_ap_json(struct ap_json *a, struct json_handlers *inner)
    332 {
    333 	memset(a, 0, sizeof(*a));
    334 	init_errors(&a->errs);
    335 	copy_json_handlers(inner, &a->compact_handler);
    336 }
    337 
    338 static int handle_ap_bool(struct json_handlers *h, int val)
    339 {
    340 	struct ap_json *a = (struct ap_json*)h->data;
    341 	return handle_bool(&a->compact_handler, val);
    342 }
    343 
    344 static int handle_ap_null(struct json_handlers *h)
    345 {
    346 	struct ap_json *a = (struct ap_json*)h->data;
    347 	return handle_null(&a->compact_handler);
    348 }
    349 
    350 static int handle_ap_number(struct json_handlers *h, unsigned int number)
    351 {
    352 	struct ap_json *a = (struct ap_json*)h->data;
    353 	return handle_number(&a->compact_handler, number);
    354 }
    355 
    356 void make_ap_json_pusher(struct json_handlers *h, struct ap_json *out)
    357 {
    358 	h->data = out;
    359 	h->string = handle_ap_string;
    360 	h->boolean = handle_ap_bool;
    361 	h->null = handle_ap_null;
    362 	h->number = handle_ap_number;
    363 	h->token = handle_ap_token;
    364 }
    365