summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--activitypub.c283
-rw-r--r--data.c130
-rw-r--r--html.c278
-rw-r--r--http.c27
-rw-r--r--httpd.c16
-rw-r--r--main.c2
-rw-r--r--mastoapi.c109
-rw-r--r--snac.h45
-rw-r--r--upgrade.c40
-rw-r--r--utils.c2
-rw-r--r--webfinger.c23
-rw-r--r--xs.h110
-rw-r--r--xs_curl.h2
-rw-r--r--xs_html.h34
-rw-r--r--xs_httpd.h5
-rw-r--r--xs_json.h9
-rw-r--r--xs_set.h2
-rw-r--r--xs_unicode.h6
-rw-r--r--xs_url.h4
-rw-r--r--xs_version.h2
20 files changed, 549 insertions, 580 deletions
diff --git a/activitypub.c b/activitypub.c
index 231715b..f4b4eac 100644
--- a/activitypub.c
+++ b/activitypub.c
@@ -67,7 +67,7 @@ int activitypub_request(snac *user, const char *url, xs_dict **data)
67 xs *response = NULL; 67 xs *response = NULL;
68 xs *payload = NULL; 68 xs *payload = NULL;
69 int p_size; 69 int p_size;
70 char *ctype; 70 const char *ctype;
71 71
72 *data = NULL; 72 *data = NULL;
73 73
@@ -154,20 +154,21 @@ int actor_request(snac *user, const char *actor, xs_dict **data)
154} 154}
155 155
156 156
157char *get_atto(const xs_dict *msg) 157const char *get_atto(const xs_dict *msg)
158/* gets the attributedTo field (an actor) */ 158/* gets the attributedTo field (an actor) */
159{ 159{
160 char *actor = xs_dict_get(msg, "attributedTo"); 160 const xs_val *actor = xs_dict_get(msg, "attributedTo");
161 161
162 /* if the actor is a list of objects (like on Peertube videos), pick the Person */ 162 /* if the actor is a list of objects (like on Peertube videos), pick the Person */
163 if (xs_type(actor) == XSTYPE_LIST) { 163 if (xs_type(actor) == XSTYPE_LIST) {
164 xs_list *p = actor; 164 const xs_list *p = actor;
165 int c = 0;
165 xs_dict *v; 166 xs_dict *v;
166 actor = NULL; 167 actor = NULL;
167 168
168 while (actor == NULL && xs_list_iter(&p, &v)) { 169 while (actor == NULL && xs_list_next(p, &v, &c)) {
169 if (xs_type(v) == XSTYPE_DICT) { 170 if (xs_type(v) == XSTYPE_DICT) {
170 char *type = xs_dict_get(v, "type"); 171 const char *type = xs_dict_get(v, "type");
171 if (xs_type(type) == XSTYPE_STRING && strcmp(type, "Person") == 0) { 172 if (xs_type(type) == XSTYPE_STRING && strcmp(type, "Person") == 0) {
172 actor = xs_dict_get(v, "id"); 173 actor = xs_dict_get(v, "id");
173 174
@@ -186,7 +187,7 @@ xs_list *get_attachments(const xs_dict *msg)
186/* unify the garbage fire that are the attachments */ 187/* unify the garbage fire that are the attachments */
187{ 188{
188 xs_list *l = xs_list_new(); 189 xs_list *l = xs_list_new();
189 xs_list *p; 190 const xs_list *p;
190 191
191 /* try first the attachments list */ 192 /* try first the attachments list */
192 if (!xs_is_null(p = xs_dict_get(msg, "attachment"))) { 193 if (!xs_is_null(p = xs_dict_get(msg, "attachment"))) {
@@ -203,23 +204,24 @@ xs_list *get_attachments(const xs_dict *msg)
203 204
204 if (xs_type(attach) == XSTYPE_LIST) { 205 if (xs_type(attach) == XSTYPE_LIST) {
205 /* does the message have an image? */ 206 /* does the message have an image? */
206 if (xs_type(v = xs_dict_get(msg, "image")) == XSTYPE_DICT) { 207 const xs_dict *d = xs_dict_get(msg, "image");
208 if (xs_type(d) == XSTYPE_DICT) {
207 /* add it to the attachment list */ 209 /* add it to the attachment list */
208 attach = xs_list_append(attach, v); 210 attach = xs_list_append(attach, d);
209 } 211 }
210 } 212 }
211 213
212 /* now iterate the list */ 214 /* now iterate the list */
213 p = attach; 215 int c = 0;
214 while (xs_list_iter(&p, &v)) { 216 while (xs_list_next(attach, &v, &c)) {
215 char *type = xs_dict_get(v, "mediaType"); 217 const char *type = xs_dict_get(v, "mediaType");
216 if (xs_is_null(type)) 218 if (xs_is_null(type))
217 type = xs_dict_get(v, "type"); 219 type = xs_dict_get(v, "type");
218 220
219 if (xs_is_null(type)) 221 if (xs_is_null(type))
220 continue; 222 continue;
221 223
222 char *href = xs_dict_get(v, "url"); 224 const char *href = xs_dict_get(v, "url");
223 if (xs_is_null(href)) 225 if (xs_is_null(href))
224 href = xs_dict_get(v, "href"); 226 href = xs_dict_get(v, "href");
225 if (xs_is_null(href)) 227 if (xs_is_null(href))
@@ -233,7 +235,7 @@ xs_list *get_attachments(const xs_dict *msg)
233 type = mt; 235 type = mt;
234 } 236 }
235 237
236 char *name = xs_dict_get(v, "name"); 238 const char *name = xs_dict_get(v, "name");
237 if (xs_is_null(name)) 239 if (xs_is_null(name))
238 name = xs_dict_get(msg, "name"); 240 name = xs_dict_get(msg, "name");
239 if (xs_is_null(name)) 241 if (xs_is_null(name))
@@ -252,29 +254,31 @@ xs_list *get_attachments(const xs_dict *msg)
252 p = xs_dict_get(msg, "url"); 254 p = xs_dict_get(msg, "url");
253 255
254 if (xs_type(p) == XSTYPE_LIST) { 256 if (xs_type(p) == XSTYPE_LIST) {
255 char *href = NULL; 257 const char *href = NULL;
256 char *type = NULL; 258 const char *type = NULL;
259 int c = 0;
257 xs_val *v; 260 xs_val *v;
258 261
259 while (href == NULL && xs_list_iter(&p, &v)) { 262 while (href == NULL && xs_list_next(p, &v, &c)) {
260 if (xs_type(v) == XSTYPE_DICT) { 263 if (xs_type(v) == XSTYPE_DICT) {
261 char *mtype = xs_dict_get(v, "type"); 264 const char *mtype = xs_dict_get(v, "type");
262 265
263 if (xs_type(mtype) == XSTYPE_STRING && strcmp(mtype, "Link") == 0) { 266 if (xs_type(mtype) == XSTYPE_STRING && strcmp(mtype, "Link") == 0) {
264 mtype = xs_dict_get(v, "mediaType"); 267 mtype = xs_dict_get(v, "mediaType");
265 xs_list *tag = xs_dict_get(v, "tag"); 268 const xs_list *tag = xs_dict_get(v, "tag");
266 269
267 if (xs_type(mtype) == XSTYPE_STRING && 270 if (xs_type(mtype) == XSTYPE_STRING &&
268 strcmp(mtype, "application/x-mpegURL") == 0 && 271 strcmp(mtype, "application/x-mpegURL") == 0 &&
269 xs_type(tag) == XSTYPE_LIST) { 272 xs_type(tag) == XSTYPE_LIST) {
270 /* now iterate the tag list, looking for a video URL */ 273 /* now iterate the tag list, looking for a video URL */
271 xs_dict *d; 274 xs_dict *d;
275 int c = 0;
272 276
273 while (href == NULL && xs_list_iter(&tag, &d)) { 277 while (href == NULL && xs_list_next(tag, &d, &c)) {
274 if (xs_type(d) == XSTYPE_DICT) { 278 if (xs_type(d) == XSTYPE_DICT) {
275 if (xs_type(mtype = xs_dict_get(d, "mediaType")) == XSTYPE_STRING && 279 if (xs_type(mtype = xs_dict_get(d, "mediaType")) == XSTYPE_STRING &&
276 xs_startswith(mtype, "video/")) { 280 xs_startswith(mtype, "video/")) {
277 char *h = xs_dict_get(d, "href"); 281 const char *h = xs_dict_get(d, "href");
278 282
279 /* this is probably it */ 283 /* this is probably it */
280 if (xs_type(h) == XSTYPE_STRING) { 284 if (xs_type(h) == XSTYPE_STRING) {
@@ -303,7 +307,7 @@ xs_list *get_attachments(const xs_dict *msg)
303} 307}
304 308
305 309
306int timeline_request(snac *snac, char **id, xs_str **wrk, int level) 310int timeline_request(snac *snac, const char **id, xs_str **wrk, int level)
307/* ensures that an entry and its ancestors are in the timeline */ 311/* ensures that an entry and its ancestors are in the timeline */
308{ 312{
309 int status = 0; 313 int status = 0;
@@ -323,7 +327,7 @@ int timeline_request(snac *snac, char **id, xs_str **wrk, int level)
323 status = activitypub_request(snac, *id, &msg); 327 status = activitypub_request(snac, *id, &msg);
324 328
325 if (valid_status(status)) { 329 if (valid_status(status)) {
326 xs_dict *object = msg; 330 const xs_dict *object = msg;
327 const char *type = xs_dict_get(object, "type"); 331 const char *type = xs_dict_get(object, "type");
328 332
329 /* get the id again from the object, as it may be different */ 333 /* get the id again from the object, as it may be different */
@@ -369,7 +373,7 @@ int timeline_request(snac *snac, char **id, xs_str **wrk, int level)
369 } 373 }
370 374
371 /* does it have an ancestor? */ 375 /* does it have an ancestor? */
372 char *in_reply_to = xs_dict_get(object, "inReplyTo"); 376 const char *in_reply_to = xs_dict_get(object, "inReplyTo");
373 377
374 /* store */ 378 /* store */
375 timeline_add(snac, nid, object); 379 timeline_add(snac, nid, object);
@@ -381,83 +385,12 @@ int timeline_request(snac *snac, char **id, xs_str **wrk, int level)
381 } 385 }
382 } 386 }
383 } 387 }
384
385 enqueue_request_replies(snac, *id);
386 } 388 }
387 389
388 return status; 390 return status;
389} 391}
390 392
391 393
392void timeline_request_replies(snac *user, const char *id)
393/* requests all replies of a message */
394/* FIXME: experimental -- needs more testing */
395{
396 /* FIXME: TEMPORARILY DISABLED */
397 /* Reason: I've found that many of the posts in the 'replies' Collection
398 do not have an inReplyTo field (why??? aren't they 'replies'???).
399 For this reason, these requested objects are not stored as children
400 of the original post and they are shown as out-of-context, top level posts.
401 This process is disabled until I find an elegant way of providing a parent
402 for these 'stray' children. */
403 return;
404
405 xs *msg = NULL;
406
407 if (!valid_status(object_get(id, &msg)))
408 return;
409
410 /* does it have a replies collection? */
411 const xs_dict *replies = xs_dict_get(msg, "replies");
412
413 if (!xs_is_null(replies)) {
414 const char *type = xs_dict_get(replies, "type");
415 const char *first = xs_dict_get(replies, "first");
416
417 if (!xs_is_null(type) && !xs_is_null(first) && strcmp(type, "Collection") == 0) {
418 const char *next = xs_dict_get(first, "next");
419
420 if (!xs_is_null(next)) {
421 xs *rpls = NULL;
422 int status = activitypub_request(user, next, &rpls);
423
424 /* request the Collection of replies */
425 if (valid_status(status)) {
426 xs_list *items = xs_dict_get(rpls, "items");
427
428 if (xs_type(items) == XSTYPE_LIST) {
429 xs_val *v;
430
431 /* request them all */
432 while (xs_list_iter(&items, &v)) {
433 if (xs_type(v) == XSTYPE_DICT) {
434 /* not an id, but the object itself (!) */
435 const char *c_id = xs_dict_get(v, "id");
436
437 if (!xs_is_null(id)) {
438 snac_debug(user, 0, xs_fmt("embedded reply %s", c_id));
439
440 object_add(c_id, v);
441
442 /* get its own children */
443 timeline_request_replies(user, v);
444 }
445 }
446 else {
447 snac_debug(user, 0, xs_fmt("request reply %s", v));
448 timeline_request(user, &v, NULL, 0);
449 }
450 }
451 }
452 }
453 else
454 snac_debug(user, 0, xs_fmt("replies request error %s %d", next, status));
455 }
456 }
457 }
458}
459
460
461int send_to_inbox_raw(const char *keyid, const char *seckey, 394int send_to_inbox_raw(const char *keyid, const char *seckey,
462 const xs_str *inbox, const xs_dict *msg, 395 const xs_str *inbox, const xs_dict *msg,
463 xs_val **payload, int *p_size, int timeout) 396 xs_val **payload, int *p_size, int timeout)
@@ -480,7 +413,7 @@ int send_to_inbox(snac *snac, const xs_str *inbox, const xs_dict *msg,
480 xs_val **payload, int *p_size, int timeout) 413 xs_val **payload, int *p_size, int timeout)
481/* sends a message to an Inbox */ 414/* sends a message to an Inbox */
482{ 415{
483 char *seckey = xs_dict_get(snac->key, "secret"); 416 const char *seckey = xs_dict_get(snac->key, "secret");
484 417
485 return send_to_inbox_raw(snac->actor, seckey, inbox, msg, payload, p_size, timeout); 418 return send_to_inbox_raw(snac->actor, seckey, inbox, msg, payload, p_size, timeout);
486} 419}
@@ -490,7 +423,7 @@ xs_str *get_actor_inbox(const char *actor)
490/* gets an actor's inbox */ 423/* gets an actor's inbox */
491{ 424{
492 xs *data = NULL; 425 xs *data = NULL;
493 char *v = NULL; 426 const char *v = NULL;
494 427
495 if (valid_status(actor_request(NULL, actor, &data))) { 428 if (valid_status(actor_request(NULL, actor, &data))) {
496 /* try first endpoints/sharedInbox */ 429 /* try first endpoints/sharedInbox */
@@ -539,16 +472,16 @@ void post_message(snac *snac, const char *actor, const xs_dict *msg)
539xs_list *recipient_list(snac *snac, const xs_dict *msg, int expand_public) 472xs_list *recipient_list(snac *snac, const xs_dict *msg, int expand_public)
540/* returns the list of recipients for a message */ 473/* returns the list of recipients for a message */
541{ 474{
542 char *to = xs_dict_get(msg, "to"); 475 const xs_val *to = xs_dict_get(msg, "to");
543 char *cc = xs_dict_get(msg, "cc"); 476 const xs_val *cc = xs_dict_get(msg, "cc");
544 xs_set rcpts; 477 xs_set rcpts;
545 int n; 478 int n;
546 479
547 xs_set_init(&rcpts); 480 xs_set_init(&rcpts);
548 481
549 char *lists[] = { to, cc, NULL }; 482 const xs_list *lists[] = { to, cc, NULL };
550 for (n = 0; lists[n]; n++) { 483 for (n = 0; lists[n]; n++) {
551 char *l = lists[n]; 484 xs_list *l = (xs_list *)lists[n];
552 char *v; 485 char *v;
553 xs *tl = NULL; 486 xs *tl = NULL;
554 487
@@ -671,13 +604,13 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg)
671 604
672 /* if it's a Follow, it must be explicitly for us */ 605 /* if it's a Follow, it must be explicitly for us */
673 if (xs_match(type, "Follow")) { 606 if (xs_match(type, "Follow")) {
674 char *object = xs_dict_get(c_msg, "object"); 607 const char *object = xs_dict_get(c_msg, "object");
675 return !xs_is_null(object) && strcmp(snac->actor, object) == 0; 608 return !xs_is_null(object) && strcmp(snac->actor, object) == 0;
676 } 609 }
677 610
678 /* only accept Ping directed to us */ 611 /* only accept Ping directed to us */
679 if (xs_match(type, "Ping")) { 612 if (xs_match(type, "Ping")) {
680 char *dest = xs_dict_get(c_msg, "to"); 613 const char *dest = xs_dict_get(c_msg, "to");
681 return !xs_is_null(dest) && strcmp(snac->actor, dest) == 0; 614 return !xs_is_null(dest) && strcmp(snac->actor, dest) == 0;
682 } 615 }
683 616
@@ -692,7 +625,7 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg)
692 if (pub_msg && following_check(snac, actor)) 625 if (pub_msg && following_check(snac, actor))
693 return 1; 626 return 1;
694 627
695 xs_dict *msg = xs_dict_get(c_msg, "object"); 628 const xs_dict *msg = xs_dict_get(c_msg, "object");
696 xs *rcpts = recipient_list(snac, msg, 0); 629 xs *rcpts = recipient_list(snac, msg, 0);
697 xs_list *p = rcpts; 630 xs_list *p = rcpts;
698 xs_str *v; 631 xs_str *v;
@@ -704,8 +637,9 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg)
704 xs *actor_obj = NULL; 637 xs *actor_obj = NULL;
705 638
706 if (valid_status(object_get(actor, &actor_obj))) { 639 if (valid_status(object_get(actor, &actor_obj))) {
707 if ((v = xs_dict_get(actor_obj, "followers"))) 640 const xs_val *fw = xs_dict_get(actor_obj, "followers");
708 actor_followers = xs_dup(v); 641 if (fw)
642 actor_followers = xs_dup(fw);
709 } 643 }
710 } 644 }
711 645
@@ -728,13 +662,13 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg)
728 } 662 }
729 663
730 /* accept if it's by someone we follow */ 664 /* accept if it's by someone we follow */
731 char *atto = get_atto(msg); 665 const char *atto = get_atto(msg);
732 666
733 if (pub_msg && !xs_is_null(atto) && following_check(snac, atto)) 667 if (pub_msg && !xs_is_null(atto) && following_check(snac, atto))
734 return 3; 668 return 3;
735 669
736 /* is this message a reply to another? */ 670 /* is this message a reply to another? */
737 char *irt = xs_dict_get(msg, "inReplyTo"); 671 const char *irt = xs_dict_get(msg, "inReplyTo");
738 if (!xs_is_null(irt)) { 672 if (!xs_is_null(irt)) {
739 xs *r_msg = NULL; 673 xs *r_msg = NULL;
740 674
@@ -987,8 +921,8 @@ void notify(snac *snac, const char *type, const char *utype, const char *actor,
987 921
988 /* telegram */ 922 /* telegram */
989 923
990 char *bot = xs_dict_get(snac->config, "telegram_bot"); 924 const char *bot = xs_dict_get(snac->config, "telegram_bot");
991 char *chat_id = xs_dict_get(snac->config, "telegram_chat_id"); 925 const char *chat_id = xs_dict_get(snac->config, "telegram_chat_id");
992 926
993 if (!xs_is_null(bot) && !xs_is_null(chat_id) && *bot && *chat_id) 927 if (!xs_is_null(bot) && !xs_is_null(chat_id) && *bot && *chat_id)
994 enqueue_telegram(body, bot, chat_id); 928 enqueue_telegram(body, bot, chat_id);
@@ -1001,8 +935,8 @@ void notify(snac *snac, const char *type, const char *utype, const char *actor,
1001 objid = actor; 935 objid = actor;
1002 936
1003 /* ntfy */ 937 /* ntfy */
1004 char *ntfy_server = xs_dict_get(snac->config, "ntfy_server"); 938 const char *ntfy_server = xs_dict_get(snac->config, "ntfy_server");
1005 char *ntfy_token = xs_dict_get(snac->config, "ntfy_token"); 939 const char *ntfy_token = xs_dict_get(snac->config, "ntfy_token");
1006 940
1007 if (!xs_is_null(ntfy_server) && *ntfy_server) 941 if (!xs_is_null(ntfy_server) && *ntfy_server)
1008 enqueue_ntfy(body, ntfy_server, ntfy_token); 942 enqueue_ntfy(body, ntfy_server, ntfy_token);
@@ -1088,7 +1022,7 @@ xs_dict *msg_base(snac *snac, const char *type, const char *id,
1088} 1022}
1089 1023
1090 1024
1091xs_dict *msg_collection(snac *snac, char *id) 1025xs_dict *msg_collection(snac *snac, const char *id)
1092/* creates an empty OrderedCollection message */ 1026/* creates an empty OrderedCollection message */
1093{ 1027{
1094 xs_dict *msg = msg_base(snac, "OrderedCollection", id, NULL, NULL, NULL); 1028 xs_dict *msg = msg_base(snac, "OrderedCollection", id, NULL, NULL, NULL);
@@ -1102,7 +1036,7 @@ xs_dict *msg_collection(snac *snac, char *id)
1102} 1036}
1103 1037
1104 1038
1105xs_dict *msg_accept(snac *snac, char *object, char *to) 1039xs_dict *msg_accept(snac *snac, const xs_val *object, const char *to)
1106/* creates an Accept message (as a response to a Follow) */ 1040/* creates an Accept message (as a response to a Follow) */
1107{ 1041{
1108 xs_dict *msg = msg_base(snac, "Accept", "@dummy", snac->actor, NULL, object); 1042 xs_dict *msg = msg_base(snac, "Accept", "@dummy", snac->actor, NULL, object);
@@ -1113,12 +1047,12 @@ xs_dict *msg_accept(snac *snac, char *object, char *to)
1113} 1047}
1114 1048
1115 1049
1116xs_dict *msg_update(snac *snac, xs_dict *object) 1050xs_dict *msg_update(snac *snac, const xs_dict *object)
1117/* creates an Update message */ 1051/* creates an Update message */
1118{ 1052{
1119 xs_dict *msg = msg_base(snac, "Update", "@object", snac->actor, "@now", object); 1053 xs_dict *msg = msg_base(snac, "Update", "@object", snac->actor, "@now", object);
1120 1054
1121 char *type = xs_dict_get(object, "type"); 1055 const char *type = xs_dict_get(object, "type");
1122 1056
1123 if (strcmp(type, "Note") == 0) { 1057 if (strcmp(type, "Note") == 0) {
1124 msg = xs_dict_append(msg, "to", xs_dict_get(object, "to")); 1058 msg = xs_dict_append(msg, "to", xs_dict_get(object, "to"));
@@ -1141,7 +1075,7 @@ xs_dict *msg_update(snac *snac, xs_dict *object)
1141} 1075}
1142 1076
1143 1077
1144xs_dict *msg_admiration(snac *snac, char *object, char *type) 1078xs_dict *msg_admiration(snac *snac, const char *object, const char *type)
1145/* creates a Like or Announce message */ 1079/* creates a Like or Announce message */
1146{ 1080{
1147 xs *a_msg = NULL; 1081 xs *a_msg = NULL;
@@ -1172,7 +1106,7 @@ xs_dict *msg_admiration(snac *snac, char *object, char *type)
1172} 1106}
1173 1107
1174 1108
1175xs_dict *msg_repulsion(snac *user, char *id, char *type) 1109xs_dict *msg_repulsion(snac *user, const char *id, const char *type)
1176/* creates an Undo + admiration message */ 1110/* creates an Undo + admiration message */
1177{ 1111{
1178 xs *a_msg = NULL; 1112 xs *a_msg = NULL;
@@ -1210,7 +1144,7 @@ xs_dict *msg_actor(snac *snac)
1210 xs *kid = NULL; 1144 xs *kid = NULL;
1211 xs *f_bio = NULL; 1145 xs *f_bio = NULL;
1212 xs_dict *msg = msg_base(snac, "Person", snac->actor, NULL, NULL, NULL); 1146 xs_dict *msg = msg_base(snac, "Person", snac->actor, NULL, NULL, NULL);
1213 char *p; 1147 const char *p;
1214 int n; 1148 int n;
1215 1149
1216 /* change the @context (is this really necessary?) */ 1150 /* change the @context (is this really necessary?) */
@@ -1268,7 +1202,7 @@ xs_dict *msg_actor(snac *snac)
1268 } 1202 }
1269 1203
1270 /* add the metadata as attachments of PropertyValue */ 1204 /* add the metadata as attachments of PropertyValue */
1271 xs_dict *metadata = xs_dict_get(snac->config, "metadata"); 1205 const xs_dict *metadata = xs_dict_get(snac->config, "metadata");
1272 if (xs_type(metadata) == XSTYPE_DICT) { 1206 if (xs_type(metadata) == XSTYPE_DICT) {
1273 xs *attach = xs_list_new(); 1207 xs *attach = xs_list_new();
1274 xs_str *k; 1208 xs_str *k;
@@ -1314,7 +1248,7 @@ xs_dict *msg_create(snac *snac, const xs_dict *object)
1314/* creates a 'Create' message */ 1248/* creates a 'Create' message */
1315{ 1249{
1316 xs_dict *msg = msg_base(snac, "Create", "@wrapper", snac->actor, NULL, object); 1250 xs_dict *msg = msg_base(snac, "Create", "@wrapper", snac->actor, NULL, object);
1317 xs_val *v; 1251 const xs_val *v;
1318 1252
1319 if ((v = get_atto(object))) 1253 if ((v = get_atto(object)))
1320 msg = xs_dict_append(msg, "attributedTo", v); 1254 msg = xs_dict_append(msg, "attributedTo", v);
@@ -1331,7 +1265,7 @@ xs_dict *msg_create(snac *snac, const xs_dict *object)
1331} 1265}
1332 1266
1333 1267
1334xs_dict *msg_undo(snac *snac, char *object) 1268xs_dict *msg_undo(snac *snac, const xs_val *object)
1335/* creates an 'Undo' message */ 1269/* creates an 'Undo' message */
1336{ 1270{
1337 xs_dict *msg = msg_base(snac, "Undo", "@object", snac->actor, "@now", object); 1271 xs_dict *msg = msg_base(snac, "Undo", "@object", snac->actor, "@now", object);
@@ -1344,7 +1278,7 @@ xs_dict *msg_undo(snac *snac, char *object)
1344} 1278}
1345 1279
1346 1280
1347xs_dict *msg_delete(snac *snac, char *id) 1281xs_dict *msg_delete(snac *snac, const char *id)
1348/* creates a 'Delete' + 'Tombstone' for a local entry */ 1282/* creates a 'Delete' + 'Tombstone' for a local entry */
1349{ 1283{
1350 xs *tomb = xs_dict_new(); 1284 xs *tomb = xs_dict_new();
@@ -1386,7 +1320,7 @@ xs_dict *msg_follow(snac *snac, const char *q)
1386 1320
1387 if (valid_status(status)) { 1321 if (valid_status(status)) {
1388 /* check if the actor is an alias */ 1322 /* check if the actor is an alias */
1389 char *r_actor = xs_dict_get(actor_o, "id"); 1323 const char *r_actor = xs_dict_get(actor_o, "id");
1390 1324
1391 if (r_actor && strcmp(actor, r_actor) != 0) { 1325 if (r_actor && strcmp(actor, r_actor) != 0) {
1392 snac_log(snac, xs_fmt("actor to follow is an alias %s -> %s", actor, r_actor)); 1326 snac_log(snac, xs_fmt("actor to follow is an alias %s -> %s", actor, r_actor));
@@ -1402,7 +1336,7 @@ xs_dict *msg_follow(snac *snac, const char *q)
1402 1336
1403 1337
1404xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts, 1338xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts,
1405 xs_str *in_reply_to, xs_list *attach, int priv) 1339 const xs_str *in_reply_to, const xs_list *attach, int priv)
1406/* creates a 'Note' message */ 1340/* creates a 'Note' message */
1407{ 1341{
1408 xs *ntid = tid(0); 1342 xs *ntid = tid(0);
@@ -1442,7 +1376,7 @@ xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts,
1442 1376
1443 if (valid_status(object_get(in_reply_to, &p_msg))) { 1377 if (valid_status(object_get(in_reply_to, &p_msg))) {
1444 /* add this author as recipient */ 1378 /* add this author as recipient */
1445 char *a, *v; 1379 const char *a, *v;
1446 1380
1447 if ((a = get_atto(p_msg)) && xs_list_in(to, a) == -1) 1381 if ((a = get_atto(p_msg)) && xs_list_in(to, a) == -1)
1448 to = xs_list_append(to, a); 1382 to = xs_list_append(to, a);
@@ -1453,7 +1387,7 @@ xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts,
1453 xs *actor_o = NULL; 1387 xs *actor_o = NULL;
1454 1388
1455 if (xs_list_len(l) > 3 && valid_status(object_get(a, &actor_o))) { 1389 if (xs_list_len(l) > 3 && valid_status(object_get(a, &actor_o))) {
1456 char *uname = xs_dict_get(actor_o, "preferredUsername"); 1390 const char *uname = xs_dict_get(actor_o, "preferredUsername");
1457 1391
1458 if (!xs_is_null(uname) && *uname) { 1392 if (!xs_is_null(uname) && *uname) {
1459 xs *handle = xs_fmt("@%s@%s", uname, xs_list_get(l, 2)); 1393 xs *handle = xs_fmt("@%s@%s", uname, xs_list_get(l, 2));
@@ -1492,7 +1426,8 @@ xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts,
1492 1426
1493 /* create the attachment list, if there are any */ 1427 /* create the attachment list, if there are any */
1494 if (!xs_is_null(attach)) { 1428 if (!xs_is_null(attach)) {
1495 while (xs_list_iter(&attach, &v)) { 1429 int c = 0;
1430 while (xs_list_next(attach, &v, &c)) {
1496 xs *d = xs_dict_new(); 1431 xs *d = xs_dict_new();
1497 const char *url = xs_list_get(v, 0); 1432 const char *url = xs_list_get(v, 0);
1498 const char *alt = xs_list_get(v, 1); 1433 const char *alt = xs_list_get(v, 1);
@@ -1515,7 +1450,7 @@ xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts,
1515 p = tag; 1450 p = tag;
1516 while (xs_list_iter(&p, &v)) { 1451 while (xs_list_iter(&p, &v)) {
1517 if (xs_type(v) == XSTYPE_DICT) { 1452 if (xs_type(v) == XSTYPE_DICT) {
1518 char *t; 1453 const char *t;
1519 1454
1520 if (!xs_is_null(t = xs_dict_get(v, "type")) && strcmp(t, "Mention") == 0) { 1455 if (!xs_is_null(t = xs_dict_get(v, "type")) && strcmp(t, "Mention") == 0) {
1521 if (!xs_is_null(t = xs_dict_get(v, "href"))) 1456 if (!xs_is_null(t = xs_dict_get(v, "href")))
@@ -1639,7 +1574,7 @@ int update_question(snac *user, const char *id)
1639 xs *msg = NULL; 1574 xs *msg = NULL;
1640 xs *rcnt = xs_dict_new(); 1575 xs *rcnt = xs_dict_new();
1641 xs *lopts = xs_list_new(); 1576 xs *lopts = xs_list_new();
1642 xs_list *opts; 1577 const xs_list *opts;
1643 xs_list *p; 1578 xs_list *p;
1644 xs_val *v; 1579 xs_val *v;
1645 1580
@@ -1657,8 +1592,8 @@ int update_question(snac *user, const char *id)
1657 return -3; 1592 return -3;
1658 1593
1659 /* fill the initial count */ 1594 /* fill the initial count */
1660 p = opts; 1595 int c = 0;
1661 while (xs_list_iter(&p, &v)) { 1596 while (xs_list_next(opts, &v, &c)) {
1662 const char *name = xs_dict_get(v, "name"); 1597 const char *name = xs_dict_get(v, "name");
1663 if (name) { 1598 if (name) {
1664 lopts = xs_list_append(lopts, name); 1599 lopts = xs_list_append(lopts, name);
@@ -1764,13 +1699,13 @@ int update_question(snac *user, const char *id)
1764 1699
1765/** queues **/ 1700/** queues **/
1766 1701
1767int process_input_message(snac *snac, xs_dict *msg, xs_dict *req) 1702int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req)
1768/* processes an ActivityPub message from the input queue */ 1703/* processes an ActivityPub message from the input queue */
1769/* return values: -1, fatal error; 0, transient error, retry; 1704/* return values: -1, fatal error; 0, transient error, retry;
1770 1, processed and done; 2, propagate to users (only when no user is set) */ 1705 1, processed and done; 2, propagate to users (only when no user is set) */
1771{ 1706{
1772 char *actor = xs_dict_get(msg, "actor"); 1707 const char *actor = xs_dict_get(msg, "actor");
1773 char *type = xs_dict_get(msg, "type"); 1708 const char *type = xs_dict_get(msg, "type");
1774 xs *actor_o = NULL; 1709 xs *actor_o = NULL;
1775 int a_status; 1710 int a_status;
1776 int do_notify = 0; 1711 int do_notify = 0;
@@ -1790,7 +1725,7 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
1790 return -1; 1725 return -1;
1791 } 1726 }
1792 1727
1793 char *object, *utype; 1728 const char *object, *utype;
1794 1729
1795 object = xs_dict_get(msg, "object"); 1730 object = xs_dict_get(msg, "object");
1796 if (object != NULL && xs_type(object) == XSTYPE_DICT) 1731 if (object != NULL && xs_type(object) == XSTYPE_DICT)
@@ -1813,7 +1748,7 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
1813 } 1748 }
1814 1749
1815 /* also discard if the object to be deleted is not here */ 1750 /* also discard if the object to be deleted is not here */
1816 char *obj_id = object; 1751 const char *obj_id = object;
1817 if (xs_type(obj_id) == XSTYPE_DICT) 1752 if (xs_type(obj_id) == XSTYPE_DICT)
1818 obj_id = xs_dict_get(obj_id, "id"); 1753 obj_id = xs_dict_get(obj_id, "id");
1819 1754
@@ -1885,7 +1820,7 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
1885 int min_account_age = xs_number_get(xs_dict_get(srv_config, "min_account_age")); 1820 int min_account_age = xs_number_get(xs_dict_get(srv_config, "min_account_age"));
1886 1821
1887 if (min_account_age > 0) { 1822 if (min_account_age > 0) {
1888 char *actor_date = xs_dict_get(actor_o, "published"); 1823 const char *actor_date = xs_dict_get(actor_o, "published");
1889 if (!xs_is_null(actor_date)) { 1824 if (!xs_is_null(actor_date)) {
1890 time_t actor_t = xs_parse_iso_date(actor_date, 0); 1825 time_t actor_t = xs_parse_iso_date(actor_date, 0);
1891 1826
@@ -1945,7 +1880,7 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
1945 } 1880 }
1946 else 1881 else
1947 if (strcmp(type, "Undo") == 0) { /** **/ 1882 if (strcmp(type, "Undo") == 0) { /** **/
1948 char *id = xs_dict_get(object, "object"); 1883 const char *id = xs_dict_get(object, "object");
1949 1884
1950 if (xs_type(object) != XSTYPE_DICT) 1885 if (xs_type(object) != XSTYPE_DICT)
1951 utype = "Follow"; 1886 utype = "Follow";
@@ -1990,9 +1925,9 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
1990 } 1925 }
1991 1926
1992 if (xs_match(utype, "Note|Article")) { /** **/ 1927 if (xs_match(utype, "Note|Article")) { /** **/
1993 char *id = xs_dict_get(object, "id"); 1928 const char *id = xs_dict_get(object, "id");
1994 char *in_reply_to = xs_dict_get(object, "inReplyTo"); 1929 const char *in_reply_to = xs_dict_get(object, "inReplyTo");
1995 char *atto = get_atto(object); 1930 const char *atto = get_atto(object);
1996 xs *wrk = NULL; 1931 xs *wrk = NULL;
1997 1932
1998 if (xs_is_null(id)) 1933 if (xs_is_null(id))
@@ -2029,14 +1964,14 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
2029 } 1964 }
2030 else 1965 else
2031 if (strcmp(utype, "Question") == 0) { /** **/ 1966 if (strcmp(utype, "Question") == 0) { /** **/
2032 char *id = xs_dict_get(object, "id"); 1967 const char *id = xs_dict_get(object, "id");
2033 1968
2034 if (timeline_add(snac, id, object)) 1969 if (timeline_add(snac, id, object))
2035 snac_log(snac, xs_fmt("new 'Question' %s %s", actor, id)); 1970 snac_log(snac, xs_fmt("new 'Question' %s %s", actor, id));
2036 } 1971 }
2037 else 1972 else
2038 if (strcmp(utype, "Video") == 0) { /** **/ 1973 if (strcmp(utype, "Video") == 0) { /** **/
2039 char *id = xs_dict_get(object, "id"); 1974 const char *id = xs_dict_get(object, "id");
2040 1975
2041 if (timeline_add(snac, id, object)) 1976 if (timeline_add(snac, id, object))
2042 snac_log(snac, xs_fmt("new 'Video' %s %s", actor, id)); 1977 snac_log(snac, xs_fmt("new 'Video' %s %s", actor, id));
@@ -2212,7 +2147,7 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
2212} 2147}
2213 2148
2214 2149
2215int send_email(char *msg) 2150int send_email(const char *msg)
2216/* invoke sendmail with email headers and body in msg */ 2151/* invoke sendmail with email headers and body in msg */
2217{ 2152{
2218 FILE *f; 2153 FILE *f;
@@ -2244,14 +2179,14 @@ int send_email(char *msg)
2244void process_user_queue_item(snac *snac, xs_dict *q_item) 2179void process_user_queue_item(snac *snac, xs_dict *q_item)
2245/* processes an item from the user queue */ 2180/* processes an item from the user queue */
2246{ 2181{
2247 char *type; 2182 const char *type;
2248 int queue_retry_max = xs_number_get(xs_dict_get(srv_config, "queue_retry_max")); 2183 int queue_retry_max = xs_number_get(xs_dict_get(srv_config, "queue_retry_max"));
2249 2184
2250 if ((type = xs_dict_get(q_item, "type")) == NULL) 2185 if ((type = xs_dict_get(q_item, "type")) == NULL)
2251 type = "output"; 2186 type = "output";
2252 2187
2253 if (strcmp(type, "message") == 0) { 2188 if (strcmp(type, "message") == 0) {
2254 xs_dict *msg = xs_dict_get(q_item, "message"); 2189 const xs_dict *msg = xs_dict_get(q_item, "message");
2255 xs *rcpts = recipient_list(snac, msg, 1); 2190 xs *rcpts = recipient_list(snac, msg, 1);
2256 xs_set inboxes; 2191 xs_set inboxes;
2257 xs_list *p; 2192 xs_list *p;
@@ -2292,8 +2227,8 @@ void process_user_queue_item(snac *snac, xs_dict *q_item)
2292 else 2227 else
2293 if (strcmp(type, "input") == 0) { 2228 if (strcmp(type, "input") == 0) {
2294 /* process the message */ 2229 /* process the message */
2295 xs_dict *msg = xs_dict_get(q_item, "message"); 2230 const xs_dict *msg = xs_dict_get(q_item, "message");
2296 xs_dict *req = xs_dict_get(q_item, "req"); 2231 const xs_dict *req = xs_dict_get(q_item, "req");
2297 int retries = xs_number_get(xs_dict_get(q_item, "retries")); 2232 int retries = xs_number_get(xs_dict_get(q_item, "retries"));
2298 2233
2299 if (xs_is_null(msg)) 2234 if (xs_is_null(msg))
@@ -2320,13 +2255,6 @@ void process_user_queue_item(snac *snac, xs_dict *q_item)
2320 update_question(snac, id); 2255 update_question(snac, id);
2321 } 2256 }
2322 else 2257 else
2323 if (strcmp(type, "request_replies") == 0) {
2324 const char *id = xs_dict_get(q_item, "message");
2325
2326 if (!xs_is_null(id))
2327 timeline_request_replies(snac, id);
2328 }
2329 else
2330 if (strcmp(type, "object_request") == 0) { 2258 if (strcmp(type, "object_request") == 0) {
2331 const char *id = xs_dict_get(q_item, "message"); 2259 const char *id = xs_dict_get(q_item, "message");
2332 2260
@@ -2395,15 +2323,15 @@ int process_user_queue(snac *snac)
2395void process_queue_item(xs_dict *q_item) 2323void process_queue_item(xs_dict *q_item)
2396/* processes an item from the global queue */ 2324/* processes an item from the global queue */
2397{ 2325{
2398 char *type = xs_dict_get(q_item, "type"); 2326 const char *type = xs_dict_get(q_item, "type");
2399 int queue_retry_max = xs_number_get(xs_dict_get(srv_config, "queue_retry_max")); 2327 int queue_retry_max = xs_number_get(xs_dict_get(srv_config, "queue_retry_max"));
2400 2328
2401 if (strcmp(type, "output") == 0) { 2329 if (strcmp(type, "output") == 0) {
2402 int status; 2330 int status;
2403 xs_str *inbox = xs_dict_get(q_item, "inbox"); 2331 const xs_str *inbox = xs_dict_get(q_item, "inbox");
2404 xs_str *keyid = xs_dict_get(q_item, "keyid"); 2332 const xs_str *keyid = xs_dict_get(q_item, "keyid");
2405 xs_str *seckey = xs_dict_get(q_item, "seckey"); 2333 const xs_str *seckey = xs_dict_get(q_item, "seckey");
2406 xs_dict *msg = xs_dict_get(q_item, "message"); 2334 const xs_dict *msg = xs_dict_get(q_item, "message");
2407 int retries = xs_number_get(xs_dict_get(q_item, "retries")); 2335 int retries = xs_number_get(xs_dict_get(q_item, "retries"));
2408 int p_status = xs_number_get(xs_dict_get(q_item, "p_status")); 2336 int p_status = xs_number_get(xs_dict_get(q_item, "p_status"));
2409 xs *payload = NULL; 2337 xs *payload = NULL;
@@ -2475,7 +2403,7 @@ void process_queue_item(xs_dict *q_item)
2475 else 2403 else
2476 if (strcmp(type, "email") == 0) { 2404 if (strcmp(type, "email") == 0) {
2477 /* send this email */ 2405 /* send this email */
2478 xs_str *msg = xs_dict_get(q_item, "message"); 2406 const xs_str *msg = xs_dict_get(q_item, "message");
2479 int retries = xs_number_get(xs_dict_get(q_item, "retries")); 2407 int retries = xs_number_get(xs_dict_get(q_item, "retries"));
2480 2408
2481 if (!send_email(msg)) 2409 if (!send_email(msg))
@@ -2497,8 +2425,8 @@ void process_queue_item(xs_dict *q_item)
2497 else 2425 else
2498 if (strcmp(type, "telegram") == 0) { 2426 if (strcmp(type, "telegram") == 0) {
2499 /* send this via telegram */ 2427 /* send this via telegram */
2500 char *bot = xs_dict_get(q_item, "bot"); 2428 const char *bot = xs_dict_get(q_item, "bot");
2501 char *msg = xs_dict_get(q_item, "message"); 2429 const char *msg = xs_dict_get(q_item, "message");
2502 xs *chat_id = xs_dup(xs_dict_get(q_item, "chat_id")); 2430 xs *chat_id = xs_dup(xs_dict_get(q_item, "chat_id"));
2503 int status = 0; 2431 int status = 0;
2504 2432
@@ -2521,9 +2449,9 @@ void process_queue_item(xs_dict *q_item)
2521 else 2449 else
2522 if (strcmp(type, "ntfy") == 0) { 2450 if (strcmp(type, "ntfy") == 0) {
2523 /* send this via ntfy */ 2451 /* send this via ntfy */
2524 char *ntfy_server = xs_dict_get(q_item, "ntfy_server"); 2452 const char *ntfy_server = xs_dict_get(q_item, "ntfy_server");
2525 char *msg = xs_dict_get(q_item, "message"); 2453 const char *msg = xs_dict_get(q_item, "message");
2526 char *ntfy_token = xs_dict_get(q_item, "ntfy_token"); 2454 const char *ntfy_token = xs_dict_get(q_item, "ntfy_token");
2527 int status = 0; 2455 int status = 0;
2528 2456
2529 xs *url = xs_fmt("%s", ntfy_server); 2457 xs *url = xs_fmt("%s", ntfy_server);
@@ -2552,8 +2480,8 @@ void process_queue_item(xs_dict *q_item)
2552 } 2480 }
2553 else 2481 else
2554 if (strcmp(type, "input") == 0) { 2482 if (strcmp(type, "input") == 0) {
2555 xs_dict *msg = xs_dict_get(q_item, "message"); 2483 const xs_dict *msg = xs_dict_get(q_item, "message");
2556 xs_dict *req = xs_dict_get(q_item, "req"); 2484 const xs_dict *req = xs_dict_get(q_item, "req");
2557 int retries = xs_number_get(xs_dict_get(q_item, "retries")); 2485 int retries = xs_number_get(xs_dict_get(q_item, "retries"));
2558 2486
2559 /* do some instance-level checks */ 2487 /* do some instance-level checks */
@@ -2572,7 +2500,7 @@ void process_queue_item(xs_dict *q_item)
2572 else 2500 else
2573 if (r == 2) { 2501 if (r == 2) {
2574 /* redistribute the input message to all users */ 2502 /* redistribute the input message to all users */
2575 char *ntid = xs_dict_get(q_item, "ntid"); 2503 const char *ntid = xs_dict_get(q_item, "ntid");
2576 xs *tmpfn = xs_fmt("%s/tmp/%s.json", srv_basedir, ntid); 2504 xs *tmpfn = xs_fmt("%s/tmp/%s.json", srv_basedir, ntid);
2577 FILE *f; 2505 FILE *f;
2578 2506
@@ -2647,7 +2575,7 @@ int activitypub_get_handler(const xs_dict *req, const char *q_path,
2647 char **body, int *b_size, char **ctype) 2575 char **body, int *b_size, char **ctype)
2648{ 2576{
2649 int status = 200; 2577 int status = 200;
2650 char *accept = xs_dict_get(req, "accept"); 2578 const char *accept = xs_dict_get(req, "accept");
2651 snac snac; 2579 snac snac;
2652 xs *msg = NULL; 2580 xs *msg = NULL;
2653 2581
@@ -2659,7 +2587,8 @@ int activitypub_get_handler(const xs_dict *req, const char *q_path,
2659 return 0; 2587 return 0;
2660 2588
2661 xs *l = xs_split_n(q_path, "/", 2); 2589 xs *l = xs_split_n(q_path, "/", 2);
2662 char *uid, *p_path; 2590 const char *uid;
2591 const char *p_path;
2663 2592
2664 uid = xs_list_get(l, 1); 2593 uid = xs_list_get(l, 1);
2665 if (!user_open(&snac, uid)) { 2594 if (!user_open(&snac, uid)) {
@@ -2677,7 +2606,7 @@ int activitypub_get_handler(const xs_dict *req, const char *q_path,
2677 msg = msg_actor(&snac); 2606 msg = msg_actor(&snac);
2678 *ctype = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""; 2607 *ctype = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"";
2679 2608
2680 char *ua = xs_dict_get(req, "user-agent"); 2609 const char *ua = xs_dict_get(req, "user-agent");
2681 2610
2682 snac_debug(&snac, 0, xs_fmt("serving actor [%s]", ua ? ua : "No UA")); 2611 snac_debug(&snac, 0, xs_fmt("serving actor [%s]", ua ? ua : "No UA"));
2683 } 2612 }
@@ -2694,8 +2623,8 @@ int activitypub_get_handler(const xs_dict *req, const char *q_path,
2694 xs *i = NULL; 2623 xs *i = NULL;
2695 2624
2696 if (valid_status(object_get_by_md5(v, &i))) { 2625 if (valid_status(object_get_by_md5(v, &i))) {
2697 char *type = xs_dict_get(i, "type"); 2626 const char *type = xs_dict_get(i, "type");
2698 char *id = xs_dict_get(i, "id"); 2627 const char *id = xs_dict_get(i, "id");
2699 2628
2700 if (type && id && strcmp(type, "Note") == 0 && xs_startswith(id, snac.actor)) { 2629 if (type && id && strcmp(type, "Note") == 0 && xs_startswith(id, snac.actor)) {
2701 xs *c_msg = msg_create(&snac, i); 2630 xs *c_msg = msg_create(&snac, i);
@@ -2748,9 +2677,9 @@ int activitypub_post_handler(const xs_dict *req, const char *q_path,
2748 (void)b_size; 2677 (void)b_size;
2749 2678
2750 int status = 202; /* accepted */ 2679 int status = 202; /* accepted */
2751 char *i_ctype = xs_dict_get(req, "content-type"); 2680 const char *i_ctype = xs_dict_get(req, "content-type");
2752 snac snac; 2681 snac snac;
2753 char *v; 2682 const char *v;
2754 2683
2755 if (i_ctype == NULL) { 2684 if (i_ctype == NULL) {
2756 *body = xs_str_new("no content-type"); 2685 *body = xs_str_new("no content-type");
diff --git a/data.c b/data.c
index 0cceefd..3377f3e 100644
--- a/data.c
+++ b/data.c
@@ -29,7 +29,7 @@ pthread_mutex_t data_mutex = {0};
29int snac_upgrade(xs_str **error); 29int snac_upgrade(xs_str **error);
30 30
31 31
32int srv_open(char *basedir, int auto_upgrade) 32int srv_open(const char *basedir, int auto_upgrade)
33/* opens a server */ 33/* opens a server */
34{ 34{
35 int ret = 0; 35 int ret = 0;
@@ -58,10 +58,10 @@ int srv_open(char *basedir, int auto_upgrade)
58 if (srv_config == NULL) 58 if (srv_config == NULL)
59 error = xs_fmt("ERROR: cannot parse '%s'", cfg_file); 59 error = xs_fmt("ERROR: cannot parse '%s'", cfg_file);
60 else { 60 else {
61 char *host; 61 const char *host;
62 char *prefix; 62 const char *prefix;
63 char *dbglvl; 63 const char *dbglvl;
64 char *proto; 64 const char *proto;
65 65
66 host = xs_dict_get(srv_config, "host"); 66 host = xs_dict_get(srv_config, "host");
67 prefix = xs_dict_get(srv_config, "prefix"); 67 prefix = xs_dict_get(srv_config, "prefix");
@@ -710,7 +710,7 @@ int _object_add(const char *id, const xs_dict *obj, int ow)
710 fclose(f); 710 fclose(f);
711 711
712 /* does this object has a parent? */ 712 /* does this object has a parent? */
713 char *in_reply_to = xs_dict_get(obj, "inReplyTo"); 713 const char *in_reply_to = xs_dict_get(obj, "inReplyTo");
714 714
715 if (!xs_is_null(in_reply_to) && *in_reply_to) { 715 if (!xs_is_null(in_reply_to) && *in_reply_to) {
716 /* update the children index of the parent */ 716 /* update the children index of the parent */
@@ -1124,7 +1124,7 @@ int timeline_get_by_md5(snac *snac, const char *md5, xs_dict **msg)
1124} 1124}
1125 1125
1126 1126
1127int timeline_del(snac *snac, char *id) 1127int timeline_del(snac *snac, const char *id)
1128/* deletes a message from the timeline */ 1128/* deletes a message from the timeline */
1129{ 1129{
1130 /* delete from the user's caches */ 1130 /* delete from the user's caches */
@@ -1192,17 +1192,16 @@ int timeline_admire(snac *snac, const char *id, const char *admirer, int like)
1192} 1192}
1193 1193
1194 1194
1195xs_list *timeline_top_level(snac *snac, xs_list *list) 1195xs_list *timeline_top_level(snac *snac, const xs_list *list)
1196/* returns the top level md5 entries from this index */ 1196/* returns the top level md5 entries from this index */
1197{ 1197{
1198 xs_set seen; 1198 xs_set seen;
1199 xs_list *p;
1200 xs_str *v; 1199 xs_str *v;
1201 1200
1202 xs_set_init(&seen); 1201 xs_set_init(&seen);
1203 1202
1204 p = list; 1203 int c = 0;
1205 while (xs_list_iter(&p, &v)) { 1204 while (xs_list_next(list, &v, &c)) {
1206 char line[256] = ""; 1205 char line[256] = "";
1207 1206
1208 strncpy(line, v, sizeof(line)); 1207 strncpy(line, v, sizeof(line));
@@ -1290,7 +1289,7 @@ int following_add(snac *snac, const char *actor, const xs_dict *msg)
1290 /* object already exists; if it's of type Accept, 1289 /* object already exists; if it's of type Accept,
1291 the actor is already being followed and confirmed, 1290 the actor is already being followed and confirmed,
1292 so do nothing */ 1291 so do nothing */
1293 char *type = xs_dict_get(p_object, "type"); 1292 const char *type = xs_dict_get(p_object, "type");
1294 1293
1295 if (!xs_is_null(type) && strcmp(type, "Accept") == 0) { 1294 if (!xs_is_null(type) && strcmp(type, "Accept") == 0) {
1296 snac_debug(snac, 1, xs_fmt("following_add actor already confirmed %s", actor)); 1295 snac_debug(snac, 1, xs_fmt("following_add actor already confirmed %s", actor));
@@ -1546,8 +1545,9 @@ void hide(snac *snac, const char *id)
1546 1545
1547 /* resolve to get the id */ 1546 /* resolve to get the id */
1548 if (valid_status(object_get_by_md5(v, &co))) { 1547 if (valid_status(object_get_by_md5(v, &co))) {
1549 if ((v = xs_dict_get(co, "id")) != NULL) 1548 const char *id = xs_dict_get(co, "id");
1550 hide(snac, v); 1549 if (id != NULL)
1550 hide(snac, id);
1551 } 1551 }
1552 } 1552 }
1553 } 1553 }
@@ -1563,7 +1563,7 @@ int is_hidden(snac *snac, const char *id)
1563} 1563}
1564 1564
1565 1565
1566int actor_add(const char *actor, xs_dict *msg) 1566int actor_add(const char *actor, const xs_dict *msg)
1567/* adds an actor */ 1567/* adds an actor */
1568{ 1568{
1569 return object_add_ow(actor, msg); 1569 return object_add_ow(actor, msg);
@@ -1687,7 +1687,7 @@ int limited(snac *user, const char *id, int cmd)
1687void tag_index(const char *id, const xs_dict *obj) 1687void tag_index(const char *id, const xs_dict *obj)
1688/* update the tag indexes for this object */ 1688/* update the tag indexes for this object */
1689{ 1689{
1690 xs_list *tags = xs_dict_get(obj, "tag"); 1690 const xs_list *tags = xs_dict_get(obj, "tag");
1691 1691
1692 if (is_msg_public(obj) && xs_type(tags) == XSTYPE_LIST && xs_list_len(tags) > 0) { 1692 if (is_msg_public(obj) && xs_type(tags) == XSTYPE_LIST && xs_list_len(tags) > 0) {
1693 xs *g_tag_dir = xs_fmt("%s/tag", srv_basedir); 1693 xs *g_tag_dir = xs_fmt("%s/tag", srv_basedir);
@@ -1695,9 +1695,10 @@ void tag_index(const char *id, const xs_dict *obj)
1695 mkdirx(g_tag_dir); 1695 mkdirx(g_tag_dir);
1696 1696
1697 xs_dict *v; 1697 xs_dict *v;
1698 while (xs_list_iter(&tags, &v)) { 1698 int ct = 0;
1699 char *type = xs_dict_get(v, "type"); 1699 while (xs_list_next(tags, &v, &ct)) {
1700 char *name = xs_dict_get(v, "name"); 1700 const char *type = xs_dict_get(v, "type");
1701 const char *name = xs_dict_get(v, "name");
1701 1702
1702 if (!xs_is_null(type) && !xs_is_null(name) && strcmp(type, "Hashtag") == 0) { 1703 if (!xs_is_null(type) && !xs_is_null(name) && strcmp(type, "Hashtag") == 0) {
1703 while (*name == '#' || *name == '@') 1704 while (*name == '#' || *name == '@')
@@ -1706,7 +1707,7 @@ void tag_index(const char *id, const xs_dict *obj)
1706 if (*name == '\0') 1707 if (*name == '\0')
1707 continue; 1708 continue;
1708 1709
1709 name = xs_tolower_i(name); 1710 name = xs_tolower_i((xs_str *)name);
1710 1711
1711 xs *md5_tag = xs_md5_hex(name, strlen(name)); 1712 xs *md5_tag = xs_md5_hex(name, strlen(name));
1712 xs *tag_dir = xs_fmt("%s/%c%c", g_tag_dir, md5_tag[0], md5_tag[1]); 1713 xs *tag_dir = xs_fmt("%s/%c%c", g_tag_dir, md5_tag[0], md5_tag[1]);
@@ -1729,7 +1730,7 @@ void tag_index(const char *id, const xs_dict *obj)
1729} 1730}
1730 1731
1731 1732
1732xs_list *tag_search(char *tag, int skip, int show) 1733xs_list *tag_search(const char *tag, int skip, int show)
1733/* returns the list of posts tagged with tag */ 1734/* returns the list of posts tagged with tag */
1734{ 1735{
1735 if (*tag == '#') 1736 if (*tag == '#')
@@ -1912,7 +1913,7 @@ xs_val *list_content(snac *user, const char *list, const char *actor_md5, int op
1912void list_distribute(snac *user, const char *who, const xs_dict *post) 1913void list_distribute(snac *user, const char *who, const xs_dict *post)
1913/* distributes the post to all appropriate lists */ 1914/* distributes the post to all appropriate lists */
1914{ 1915{
1915 char *id = xs_dict_get(post, "id"); 1916 const char *id = xs_dict_get(post, "id");
1916 1917
1917 /* if who is not set, use the attributedTo in the message */ 1918 /* if who is not set, use the attributedTo in the message */
1918 if (xs_is_null(who)) 1919 if (xs_is_null(who))
@@ -2164,7 +2165,7 @@ void inbox_add(const char *inbox)
2164void inbox_add_by_actor(const xs_dict *actor) 2165void inbox_add_by_actor(const xs_dict *actor)
2165/* collects an actor's shared inbox, if it has one */ 2166/* collects an actor's shared inbox, if it has one */
2166{ 2167{
2167 char *v; 2168 const char *v;
2168 2169
2169 if (!xs_is_null(v = xs_dict_get(actor, "endpoints")) && 2170 if (!xs_is_null(v = xs_dict_get(actor, "endpoints")) &&
2170 !xs_is_null(v = xs_dict_get(v, "sharedInbox"))) { 2171 !xs_is_null(v = xs_dict_get(v, "sharedInbox"))) {
@@ -2210,7 +2211,7 @@ xs_str *_instance_block_fn(const char *instance)
2210 xs *s = xs_replace(instance, "http:/" "/", ""); 2211 xs *s = xs_replace(instance, "http:/" "/", "");
2211 xs *s1 = xs_replace(s, "https:/" "/", ""); 2212 xs *s1 = xs_replace(s, "https:/" "/", "");
2212 xs *l = xs_split(s1, "/"); 2213 xs *l = xs_split(s1, "/");
2213 char *p = xs_list_get(l, 0); 2214 const char *p = xs_list_get(l, 0);
2214 xs *md5 = xs_md5_hex(p, strlen(p)); 2215 xs *md5 = xs_md5_hex(p, strlen(p));
2215 2216
2216 return xs_fmt("%s/block/%s", srv_basedir, md5); 2217 return xs_fmt("%s/block/%s", srv_basedir, md5);
@@ -2279,7 +2280,7 @@ int content_match(const char *file, const xs_dict *msg)
2279 xs *fn = xs_fmt("%s/%s", srv_basedir, file); 2280 xs *fn = xs_fmt("%s/%s", srv_basedir, file);
2280 FILE *f; 2281 FILE *f;
2281 int r = 0; 2282 int r = 0;
2282 char *v = xs_dict_get(msg, "content"); 2283 const char *v = xs_dict_get(msg, "content");
2283 2284
2284 if (xs_type(v) == XSTYPE_STRING && *v) { 2285 if (xs_type(v) == XSTYPE_STRING && *v) {
2285 if ((f = fopen(fn, "r")) != NULL) { 2286 if ((f = fopen(fn, "r")) != NULL) {
@@ -2386,7 +2387,7 @@ xs_list *content_search(snac *user, const char *regex,
2386 if (id == NULL || is_hidden(user, id)) 2387 if (id == NULL || is_hidden(user, id))
2387 continue; 2388 continue;
2388 2389
2389 char *content = xs_dict_get(post, "content"); 2390 const char *content = xs_dict_get(post, "content");
2390 2391
2391 if (xs_is_null(content)) 2392 if (xs_is_null(content))
2392 continue; 2393 continue;
@@ -2639,7 +2640,7 @@ void enqueue_input(snac *snac, const xs_dict *msg, const xs_dict *req, int retri
2639/* enqueues an input message */ 2640/* enqueues an input message */
2640{ 2641{
2641 xs *qmsg = _new_qmsg("input", msg, retries); 2642 xs *qmsg = _new_qmsg("input", msg, retries);
2642 char *ntid = xs_dict_get(qmsg, "ntid"); 2643 const char *ntid = xs_dict_get(qmsg, "ntid");
2643 xs *fn = xs_fmt("%s/queue/%s.json", snac->basedir, ntid); 2644 xs *fn = xs_fmt("%s/queue/%s.json", snac->basedir, ntid);
2644 2645
2645 qmsg = xs_dict_append(qmsg, "req", req); 2646 qmsg = xs_dict_append(qmsg, "req", req);
@@ -2654,7 +2655,7 @@ void enqueue_shared_input(const xs_dict *msg, const xs_dict *req, int retries)
2654/* enqueues an input message from the shared input */ 2655/* enqueues an input message from the shared input */
2655{ 2656{
2656 xs *qmsg = _new_qmsg("input", msg, retries); 2657 xs *qmsg = _new_qmsg("input", msg, retries);
2657 char *ntid = xs_dict_get(qmsg, "ntid"); 2658 const char *ntid = xs_dict_get(qmsg, "ntid");
2658 xs *fn = xs_fmt("%s/queue/%s.json", srv_basedir, ntid); 2659 xs *fn = xs_fmt("%s/queue/%s.json", srv_basedir, ntid);
2659 2660
2660 qmsg = xs_dict_append(qmsg, "req", req); 2661 qmsg = xs_dict_append(qmsg, "req", req);
@@ -2666,11 +2667,12 @@ void enqueue_shared_input(const xs_dict *msg, const xs_dict *req, int retries)
2666 2667
2667 2668
2668void enqueue_output_raw(const char *keyid, const char *seckey, 2669void enqueue_output_raw(const char *keyid, const char *seckey,
2669 xs_dict *msg, xs_str *inbox, int retries, int p_status) 2670 const xs_dict *msg, const xs_str *inbox,
2671 int retries, int p_status)
2670/* enqueues an output message to an inbox */ 2672/* enqueues an output message to an inbox */
2671{ 2673{
2672 xs *qmsg = _new_qmsg("output", msg, retries); 2674 xs *qmsg = _new_qmsg("output", msg, retries);
2673 char *ntid = xs_dict_get(qmsg, "ntid"); 2675 const char *ntid = xs_dict_get(qmsg, "ntid");
2674 xs *fn = xs_fmt("%s/queue/%s.json", srv_basedir, ntid); 2676 xs *fn = xs_fmt("%s/queue/%s.json", srv_basedir, ntid);
2675 2677
2676 xs *ns = xs_number_new(p_status); 2678 xs *ns = xs_number_new(p_status);
@@ -2690,7 +2692,8 @@ void enqueue_output_raw(const char *keyid, const char *seckey,
2690} 2692}
2691 2693
2692 2694
2693void enqueue_output(snac *snac, xs_dict *msg, xs_str *inbox, int retries, int p_status) 2695void enqueue_output(snac *snac, const xs_dict *msg,
2696 const xs_str *inbox, int retries, int p_status)
2694/* enqueues an output message to an inbox */ 2697/* enqueues an output message to an inbox */
2695{ 2698{
2696 if (xs_startswith(inbox, snac->actor)) { 2699 if (xs_startswith(inbox, snac->actor)) {
@@ -2698,13 +2701,14 @@ void enqueue_output(snac *snac, xs_dict *msg, xs_str *inbox, int retries, int p_
2698 return; 2701 return;
2699 } 2702 }
2700 2703
2701 char *seckey = xs_dict_get(snac->key, "secret"); 2704 const char *seckey = xs_dict_get(snac->key, "secret");
2702 2705
2703 enqueue_output_raw(snac->actor, seckey, msg, inbox, retries, p_status); 2706 enqueue_output_raw(snac->actor, seckey, msg, inbox, retries, p_status);
2704} 2707}
2705 2708
2706 2709
2707void enqueue_output_by_actor(snac *snac, xs_dict *msg, const xs_str *actor, int retries) 2710void enqueue_output_by_actor(snac *snac, const xs_dict *msg,
2711 const xs_str *actor, int retries)
2708/* enqueues an output message for an actor */ 2712/* enqueues an output message for an actor */
2709{ 2713{
2710 xs *inbox = get_actor_inbox(actor); 2714 xs *inbox = get_actor_inbox(actor);
@@ -2716,11 +2720,11 @@ void enqueue_output_by_actor(snac *snac, xs_dict *msg, const xs_str *actor, int
2716} 2720}
2717 2721
2718 2722
2719void enqueue_email(xs_str *msg, int retries) 2723void enqueue_email(const xs_str *msg, int retries)
2720/* enqueues an email message to be sent */ 2724/* enqueues an email message to be sent */
2721{ 2725{
2722 xs *qmsg = _new_qmsg("email", msg, retries); 2726 xs *qmsg = _new_qmsg("email", msg, retries);
2723 char *ntid = xs_dict_get(qmsg, "ntid"); 2727 const char *ntid = xs_dict_get(qmsg, "ntid");
2724 xs *fn = xs_fmt("%s/queue/%s.json", srv_basedir, ntid); 2728 xs *fn = xs_fmt("%s/queue/%s.json", srv_basedir, ntid);
2725 2729
2726 qmsg = _enqueue_put(fn, qmsg); 2730 qmsg = _enqueue_put(fn, qmsg);
@@ -2733,7 +2737,7 @@ void enqueue_telegram(const xs_str *msg, const char *bot, const char *chat_id)
2733/* enqueues a message to be sent via Telegram */ 2737/* enqueues a message to be sent via Telegram */
2734{ 2738{
2735 xs *qmsg = _new_qmsg("telegram", msg, 0); 2739 xs *qmsg = _new_qmsg("telegram", msg, 0);
2736 char *ntid = xs_dict_get(qmsg, "ntid"); 2740 const char *ntid = xs_dict_get(qmsg, "ntid");
2737 xs *fn = xs_fmt("%s/queue/%s.json", srv_basedir, ntid); 2741 xs *fn = xs_fmt("%s/queue/%s.json", srv_basedir, ntid);
2738 2742
2739 qmsg = xs_dict_append(qmsg, "bot", bot); 2743 qmsg = xs_dict_append(qmsg, "bot", bot);
@@ -2748,7 +2752,7 @@ void enqueue_ntfy(const xs_str *msg, const char *ntfy_server, const char *ntfy_t
2748/* enqueues a message to be sent via ntfy */ 2752/* enqueues a message to be sent via ntfy */
2749{ 2753{
2750 xs *qmsg = _new_qmsg("ntfy", msg, 0); 2754 xs *qmsg = _new_qmsg("ntfy", msg, 0);
2751 char *ntid = xs_dict_get(qmsg, "ntid"); 2755 const char *ntid = xs_dict_get(qmsg, "ntid");
2752 xs *fn = xs_fmt("%s/queue/%s.json", srv_basedir, ntid); 2756 xs *fn = xs_fmt("%s/queue/%s.json", srv_basedir, ntid);
2753 2757
2754 qmsg = xs_dict_append(qmsg, "ntfy_server", ntfy_server); 2758 qmsg = xs_dict_append(qmsg, "ntfy_server", ntfy_server);
@@ -2764,7 +2768,7 @@ void enqueue_message(snac *snac, const xs_dict *msg)
2764/* enqueues an output message */ 2768/* enqueues an output message */
2765{ 2769{
2766 xs *qmsg = _new_qmsg("message", msg, 0); 2770 xs *qmsg = _new_qmsg("message", msg, 0);
2767 char *ntid = xs_dict_get(qmsg, "ntid"); 2771 const char *ntid = xs_dict_get(qmsg, "ntid");
2768 xs *fn = xs_fmt("%s/queue/%s.json", snac->basedir, ntid); 2772 xs *fn = xs_fmt("%s/queue/%s.json", snac->basedir, ntid);
2769 2773
2770 qmsg = _enqueue_put(fn, qmsg); 2774 qmsg = _enqueue_put(fn, qmsg);
@@ -2807,7 +2811,7 @@ void enqueue_verify_links(snac *user)
2807/* enqueues a link verification */ 2811/* enqueues a link verification */
2808{ 2812{
2809 xs *qmsg = _new_qmsg("verify_links", "", 0); 2813 xs *qmsg = _new_qmsg("verify_links", "", 0);
2810 char *ntid = xs_dict_get(qmsg, "ntid"); 2814 const char *ntid = xs_dict_get(qmsg, "ntid");
2811 xs *fn = xs_fmt("%s/queue/%s.json", user->basedir, ntid); 2815 xs *fn = xs_fmt("%s/queue/%s.json", user->basedir, ntid);
2812 2816
2813 qmsg = _enqueue_put(fn, qmsg); 2817 qmsg = _enqueue_put(fn, qmsg);
@@ -2832,42 +2836,6 @@ void enqueue_actor_refresh(snac *user, const char *actor, int forward_secs)
2832} 2836}
2833 2837
2834 2838
2835void enqueue_request_replies(snac *user, const char *id)
2836/* enqueues a request for the replies of a message */
2837{
2838 /* test first if this precise request is already in the queue */
2839 xs *queue = user_queue(user);
2840 xs_list *p = queue;
2841 xs_str *v;
2842
2843 while (xs_list_iter(&p, &v)) {
2844 xs *q_item = queue_get(v);
2845
2846 if (q_item != NULL) {
2847 const char *type = xs_dict_get(q_item, "type");
2848 const char *msg = xs_dict_get(q_item, "message");
2849
2850 if (type && msg && strcmp(type, "request_replies") == 0 && strcmp(msg, id) == 0) {
2851 /* don't requeue */
2852 snac_debug(user, 1, xs_fmt("enqueue_request_replies already here %s", id));
2853 return;
2854 }
2855 }
2856 }
2857
2858 /* not there; enqueue the request with a small delay */
2859 xs *qmsg = _new_qmsg("request_replies", id, 0);
2860 xs *ntid = tid(10);
2861 xs *fn = xs_fmt("%s/queue/%s.json", user->basedir, ntid);
2862
2863 qmsg = xs_dict_set(qmsg, "ntid", ntid);
2864
2865 qmsg = _enqueue_put(fn, qmsg);
2866
2867 snac_debug(user, 2, xs_fmt("enqueue_request_replies %s", id));
2868}
2869
2870
2871int was_question_voted(snac *user, const char *id) 2839int was_question_voted(snac *user, const char *id)
2872/* returns true if the user voted in this poll */ 2840/* returns true if the user voted in this poll */
2873{ 2841{
@@ -2881,7 +2849,7 @@ int was_question_voted(snac *user, const char *id)
2881 xs *obj = NULL; 2849 xs *obj = NULL;
2882 2850
2883 if (valid_status(object_get_by_md5(md5, &obj))) { 2851 if (valid_status(object_get_by_md5(md5, &obj))) {
2884 char *atto = get_atto(obj); 2852 const char *atto = get_atto(obj);
2885 if (atto && strcmp(atto, user->actor) == 0 && 2853 if (atto && strcmp(atto, user->actor) == 0 &&
2886 !xs_is_null(xs_dict_get(obj, "name"))) { 2854 !xs_is_null(xs_dict_get(obj, "name"))) {
2887 voted = 1; 2855 voted = 1;
@@ -3055,7 +3023,7 @@ void purge_server(void)
3055 if (mtime_nl(v2, &n_link) < mt && n_link < 2) { 3023 if (mtime_nl(v2, &n_link) < mt && n_link < 2) {
3056 xs *s1 = xs_replace(v2, ".json", ""); 3024 xs *s1 = xs_replace(v2, ".json", "");
3057 xs *l = xs_split(s1, "/"); 3025 xs *l = xs_split(s1, "/");
3058 char *md5 = xs_list_get(l, -1); 3026 const char *md5 = xs_list_get(l, -1);
3059 3027
3060 object_del_by_md5(md5); 3028 object_del_by_md5(md5);
3061 cnt++; 3029 cnt++;
@@ -3147,7 +3115,7 @@ void purge_user(snac *snac)
3147/* do the purge for this user */ 3115/* do the purge for this user */
3148{ 3116{
3149 int priv_days, pub_days, user_days = 0; 3117 int priv_days, pub_days, user_days = 0;
3150 char *v; 3118 const char *v;
3151 int n; 3119 int n;
3152 3120
3153 priv_days = xs_number_get(xs_dict_get(srv_config, "timeline_purge_days")); 3121 priv_days = xs_number_get(xs_dict_get(srv_config, "timeline_purge_days"));
@@ -3256,7 +3224,7 @@ void srv_archive(const char *direction, const char *url, xs_dict *req,
3256 if (p_size && payload) { 3224 if (p_size && payload) {
3257 xs *payload_fn = NULL; 3225 xs *payload_fn = NULL;
3258 xs *payload_fn_raw = NULL; 3226 xs *payload_fn_raw = NULL;
3259 char *v = xs_dict_get(req, "content-type"); 3227 const char *v = xs_dict_get(req, "content-type");
3260 3228
3261 if (v && xs_str_in(v, "json") != -1) { 3229 if (v && xs_str_in(v, "json") != -1) {
3262 payload_fn = xs_fmt("%s/payload.json", dir); 3230 payload_fn = xs_fmt("%s/payload.json", dir);
@@ -3287,7 +3255,7 @@ void srv_archive(const char *direction, const char *url, xs_dict *req,
3287 3255
3288 if (b_size && body) { 3256 if (b_size && body) {
3289 xs *body_fn = NULL; 3257 xs *body_fn = NULL;
3290 char *v = xs_dict_get(headers, "content-type"); 3258 const char *v = xs_dict_get(headers, "content-type");
3291 3259
3292 if (v && xs_str_in(v, "json") != -1) { 3260 if (v && xs_str_in(v, "json") != -1) {
3293 body_fn = xs_fmt("%s/body.json", dir); 3261 body_fn = xs_fmt("%s/body.json", dir);
@@ -3356,7 +3324,7 @@ void srv_archive_error(const char *prefix, const xs_str *err,
3356} 3324}
3357 3325
3358 3326
3359void srv_archive_qitem(char *prefix, xs_dict *q_item) 3327void srv_archive_qitem(const char *prefix, xs_dict *q_item)
3360/* archives a q_item in the error folder */ 3328/* archives a q_item in the error folder */
3361{ 3329{
3362 xs *ntid = tid(0); 3330 xs *ntid = tid(0);
diff --git a/html.c b/html.c
index 6351823..6521726 100644
--- a/html.c
+++ b/html.c
@@ -41,7 +41,7 @@ int login(snac *snac, const xs_dict *headers)
41} 41}
42 42
43 43
44xs_str *replace_shortnames(xs_str *s, xs_list *tag, int ems) 44xs_str *replace_shortnames(xs_str *s, const xs_list *tag, int ems)
45/* replaces all the :shortnames: with the emojis in tag */ 45/* replaces all the :shortnames: with the emojis in tag */
46{ 46{
47 if (!xs_is_null(tag)) { 47 if (!xs_is_null(tag)) {
@@ -57,18 +57,18 @@ xs_str *replace_shortnames(xs_str *s, xs_list *tag, int ems)
57 57
58 xs *style = xs_fmt("height: %dem; width: %dem; vertical-align: middle;", ems, ems); 58 xs *style = xs_fmt("height: %dem; width: %dem; vertical-align: middle;", ems, ems);
59 59
60 xs_list *p = tag_list;
61 char *v; 60 char *v;
61 int c = 0;
62 62
63 while (xs_list_iter(&p, &v)) { 63 while (xs_list_next(tag_list, &v, &c)) {
64 char *t = xs_dict_get(v, "type"); 64 const char *t = xs_dict_get(v, "type");
65 65
66 if (t && strcmp(t, "Emoji") == 0) { 66 if (t && strcmp(t, "Emoji") == 0) {
67 char *n = xs_dict_get(v, "name"); 67 const char *n = xs_dict_get(v, "name");
68 char *i = xs_dict_get(v, "icon"); 68 const char *i = xs_dict_get(v, "icon");
69 69
70 if (n && i) { 70 if (n && i) {
71 char *u = xs_dict_get(i, "url"); 71 const char *u = xs_dict_get(i, "url");
72 xs_html *img = xs_html_sctag("img", 72 xs_html *img = xs_html_sctag("img",
73 xs_html_attr("loading", "lazy"), 73 xs_html_attr("loading", "lazy"),
74 xs_html_attr("src", u), 74 xs_html_attr("src", u),
@@ -88,7 +88,7 @@ xs_str *replace_shortnames(xs_str *s, xs_list *tag, int ems)
88xs_str *actor_name(xs_dict *actor) 88xs_str *actor_name(xs_dict *actor)
89/* gets the actor name */ 89/* gets the actor name */
90{ 90{
91 char *v; 91 const char *v;
92 92
93 if (xs_is_null((v = xs_dict_get(actor, "name"))) || *v == '\0') { 93 if (xs_is_null((v = xs_dict_get(actor, "name"))) || *v == '\0') {
94 if (xs_is_null(v = xs_dict_get(actor, "preferredUsername")) || *v == '\0') { 94 if (xs_is_null(v = xs_dict_get(actor, "preferredUsername")) || *v == '\0') {
@@ -106,7 +106,7 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date,
106 xs_html *actor_icon = xs_html_tag("p", NULL); 106 xs_html *actor_icon = xs_html_tag("p", NULL);
107 107
108 xs *avatar = NULL; 108 xs *avatar = NULL;
109 char *v; 109 const char *v;
110 int fwing = 0; 110 int fwing = 0;
111 int fwer = 0; 111 int fwer = 0;
112 112
@@ -125,7 +125,7 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date,
125 if (avatar == NULL) 125 if (avatar == NULL)
126 avatar = xs_fmt("data:image/png;base64, %s", default_avatar_base64()); 126 avatar = xs_fmt("data:image/png;base64, %s", default_avatar_base64());
127 127
128 char *actor_id = xs_dict_get(actor, "id"); 128 const char *actor_id = xs_dict_get(actor, "id");
129 xs *href = NULL; 129 xs *href = NULL;
130 130
131 if (user) { 131 if (user) {
@@ -216,7 +216,7 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date,
216 } 216 }
217 217
218 { 218 {
219 char *username, *id; 219 const char *username, *id;
220 220
221 if (xs_is_null(username = xs_dict_get(actor, "preferredUsername")) || *username == '\0') { 221 if (xs_is_null(username = xs_dict_get(actor, "preferredUsername")) || *username == '\0') {
222 /* This should never be reached */ 222 /* This should never be reached */
@@ -244,15 +244,15 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date,
244} 244}
245 245
246 246
247xs_html *html_msg_icon(snac *user, char *actor_id, const xs_dict *msg) 247xs_html *html_msg_icon(snac *user, const char *actor_id, const xs_dict *msg)
248{ 248{
249 xs *actor = NULL; 249 xs *actor = NULL;
250 xs_html *actor_icon = NULL; 250 xs_html *actor_icon = NULL;
251 251
252 if (actor_id && valid_status(actor_get_refresh(user, actor_id, &actor))) { 252 if (actor_id && valid_status(actor_get_refresh(user, actor_id, &actor))) {
253 char *date = NULL; 253 const char *date = NULL;
254 char *udate = NULL; 254 const char *udate = NULL;
255 char *url = NULL; 255 const char *url = NULL;
256 int priv = 0; 256 int priv = 0;
257 const char *type = xs_dict_get(msg, "type"); 257 const char *type = xs_dict_get(msg, "type");
258 258
@@ -271,14 +271,14 @@ xs_html *html_msg_icon(snac *user, char *actor_id, const xs_dict *msg)
271} 271}
272 272
273 273
274xs_html *html_note(snac *user, char *summary, 274xs_html *html_note(snac *user, const char *summary,
275 char *div_id, char *form_id, 275 const char *div_id, const char *form_id,
276 char *ta_plh, char *ta_content, 276 const char *ta_plh, const char *ta_content,
277 char *edit_id, char *actor_id, 277 const char *edit_id, const char *actor_id,
278 xs_val *cw_yn, char *cw_text, 278 const xs_val *cw_yn, const char *cw_text,
279 xs_val *mnt_only, char *redir, 279 const xs_val *mnt_only, const char *redir,
280 char *in_reply_to, int poll, 280 const char *in_reply_to, int poll,
281 char *att_file, char *att_alt_text) 281 const char *att_file, const char *att_alt_text)
282{ 282{
283 xs *action = xs_fmt("%s/admin/note", user->actor); 283 xs *action = xs_fmt("%s/admin/note", user->actor);
284 284
@@ -460,9 +460,11 @@ static xs_html *html_base_head(void)
460 /* add server CSS and favicon */ 460 /* add server CSS and favicon */
461 xs *f; 461 xs *f;
462 f = xs_fmt("%s/favicon.ico", srv_baseurl); 462 f = xs_fmt("%s/favicon.ico", srv_baseurl);
463 xs_list *p = xs_dict_get(srv_config, "cssurls"); 463 const xs_list *p = xs_dict_get(srv_config, "cssurls");
464 char *v; 464 char *v;
465 while (xs_list_iter(&p, &v)) { 465 int c = 0;
466
467 while (xs_list_next(p, &v, &c)) {
466 xs_html_add(head, 468 xs_html_add(head,
467 xs_html_sctag("link", 469 xs_html_sctag("link",
468 xs_html_attr("rel", "stylesheet"), 470 xs_html_attr("rel", "stylesheet"),
@@ -498,8 +500,8 @@ xs_html *html_instance_head(void)
498 } 500 }
499 } 501 }
500 502
501 char *host = xs_dict_get(srv_config, "host"); 503 const char *host = xs_dict_get(srv_config, "host");
502 char *title = xs_dict_get(srv_config, "title"); 504 const char *title = xs_dict_get(srv_config, "title");
503 505
504 xs_html_add(head, 506 xs_html_add(head,
505 xs_html_tag("title", 507 xs_html_tag("title",
@@ -511,10 +513,10 @@ xs_html *html_instance_head(void)
511 513
512static xs_html *html_instance_body(void) 514static xs_html *html_instance_body(void)
513{ 515{
514 char *host = xs_dict_get(srv_config, "host"); 516 const char *host = xs_dict_get(srv_config, "host");
515 char *sdesc = xs_dict_get(srv_config, "short_description"); 517 const char *sdesc = xs_dict_get(srv_config, "short_description");
516 char *email = xs_dict_get(srv_config, "admin_email"); 518 const char *email = xs_dict_get(srv_config, "admin_email");
517 char *acct = xs_dict_get(srv_config, "admin_account"); 519 const char *acct = xs_dict_get(srv_config, "admin_account");
518 520
519 xs *blurb = xs_replace(snac_blurb, "%host%", host); 521 xs *blurb = xs_replace(snac_blurb, "%host%", host);
520 522
@@ -760,7 +762,7 @@ static xs_html *html_user_body(snac *user, int read_only)
760 xs_html_attr("class", "h-card snac-top-user")); 762 xs_html_attr("class", "h-card snac-top-user"));
761 763
762 if (read_only) { 764 if (read_only) {
763 char *header = xs_dict_get(user->config, "header"); 765 const char *header = xs_dict_get(user->config, "header");
764 if (header && *header) { 766 if (header && *header) {
765 xs_html_add(top_user, 767 xs_html_add(top_user,
766 xs_html_tag("div", 768 xs_html_tag("div",
@@ -797,7 +799,7 @@ static xs_html *html_user_body(snac *user, int read_only)
797 xs_html_add(top_user, 799 xs_html_add(top_user,
798 top_user_bio); 800 top_user_bio);
799 801
800 xs_dict *metadata = xs_dict_get(user->config, "metadata"); 802 const xs_dict *metadata = xs_dict_get(user->config, "metadata");
801 if (xs_type(metadata) == XSTYPE_DICT) { 803 if (xs_type(metadata) == XSTYPE_DICT) {
802 xs_str *k; 804 xs_str *k;
803 xs_str *v; 805 xs_str *v;
@@ -816,7 +818,7 @@ static xs_html *html_user_body(snac *user, int read_only)
816 if (xs_startswith(v, "https:/") || xs_startswith(v, "http:/")) { 818 if (xs_startswith(v, "https:/") || xs_startswith(v, "http:/")) {
817 /* is this link validated? */ 819 /* is this link validated? */
818 xs *verified_link = NULL; 820 xs *verified_link = NULL;
819 xs_number *val_time = xs_dict_get(val_links, v); 821 const xs_number *val_time = xs_dict_get(val_links, v);
820 822
821 if (xs_type(val_time) == XSTYPE_NUMBER) { 823 if (xs_type(val_time) == XSTYPE_NUMBER) {
822 time_t t = xs_number_get(val_time); 824 time_t t = xs_number_get(val_time);
@@ -928,7 +930,7 @@ xs_html *html_top_controls(snac *snac)
928 930
929 /** user settings **/ 931 /** user settings **/
930 932
931 char *email = "[disabled by admin]"; 933 const char *email = "[disabled by admin]";
932 934
933 if (xs_type(xs_dict_get(srv_config, "disable_email_notifications")) != XSTYPE_TRUE) { 935 if (xs_type(xs_dict_get(srv_config, "disable_email_notifications")) != XSTYPE_TRUE) {
934 email = xs_dict_get(snac->config_o, "email"); 936 email = xs_dict_get(snac->config_o, "email");
@@ -940,38 +942,38 @@ xs_html *html_top_controls(snac *snac)
940 } 942 }
941 } 943 }
942 944
943 char *cw = xs_dict_get(snac->config, "cw"); 945 const char *cw = xs_dict_get(snac->config, "cw");
944 if (xs_is_null(cw)) 946 if (xs_is_null(cw))
945 cw = ""; 947 cw = "";
946 948
947 char *telegram_bot = xs_dict_get(snac->config, "telegram_bot"); 949 const char *telegram_bot = xs_dict_get(snac->config, "telegram_bot");
948 if (xs_is_null(telegram_bot)) 950 if (xs_is_null(telegram_bot))
949 telegram_bot = ""; 951 telegram_bot = "";
950 952
951 char *telegram_chat_id = xs_dict_get(snac->config, "telegram_chat_id"); 953 const char *telegram_chat_id = xs_dict_get(snac->config, "telegram_chat_id");
952 if (xs_is_null(telegram_chat_id)) 954 if (xs_is_null(telegram_chat_id))
953 telegram_chat_id = ""; 955 telegram_chat_id = "";
954 956
955 char *ntfy_server = xs_dict_get(snac->config, "ntfy_server"); 957 const char *ntfy_server = xs_dict_get(snac->config, "ntfy_server");
956 if (xs_is_null(ntfy_server)) 958 if (xs_is_null(ntfy_server))
957 ntfy_server = ""; 959 ntfy_server = "";
958 960
959 char *ntfy_token = xs_dict_get(snac->config, "ntfy_token"); 961 const char *ntfy_token = xs_dict_get(snac->config, "ntfy_token");
960 if (xs_is_null(ntfy_token)) 962 if (xs_is_null(ntfy_token))
961 ntfy_token = ""; 963 ntfy_token = "";
962 964
963 char *purge_days = xs_dict_get(snac->config, "purge_days"); 965 const char *purge_days = xs_dict_get(snac->config, "purge_days");
964 if (!xs_is_null(purge_days) && xs_type(purge_days) == XSTYPE_NUMBER) 966 if (!xs_is_null(purge_days) && xs_type(purge_days) == XSTYPE_NUMBER)
965 purge_days = (char *)xs_number_str(purge_days); 967 purge_days = (char *)xs_number_str(purge_days);
966 else 968 else
967 purge_days = "0"; 969 purge_days = "0";
968 970
969 xs_val *d_dm_f_u = xs_dict_get(snac->config, "drop_dm_from_unknown"); 971 const xs_val *d_dm_f_u = xs_dict_get(snac->config, "drop_dm_from_unknown");
970 xs_val *bot = xs_dict_get(snac->config, "bot"); 972 const xs_val *bot = xs_dict_get(snac->config, "bot");
971 xs_val *a_private = xs_dict_get(snac->config, "private"); 973 const xs_val *a_private = xs_dict_get(snac->config, "private");
972 974
973 xs *metadata = xs_str_new(NULL); 975 xs *metadata = xs_str_new(NULL);
974 xs_dict *md = xs_dict_get(snac->config, "metadata"); 976 const xs_dict *md = xs_dict_get(snac->config, "metadata");
975 xs_str *k; 977 xs_str *k;
976 xs_str *v; 978 xs_str *v;
977 979
@@ -1158,13 +1160,14 @@ xs_str *build_mentions(snac *snac, const xs_dict *msg)
1158/* returns a string with the mentions in msg */ 1160/* returns a string with the mentions in msg */
1159{ 1161{
1160 xs_str *s = xs_str_new(NULL); 1162 xs_str *s = xs_str_new(NULL);
1161 char *list = xs_dict_get(msg, "tag"); 1163 const char *list = xs_dict_get(msg, "tag");
1162 char *v; 1164 char *v;
1165 int c = 0;
1163 1166
1164 while (xs_list_iter(&list, &v)) { 1167 while (xs_list_next(list, &v, &c)) {
1165 char *type = xs_dict_get(v, "type"); 1168 const char *type = xs_dict_get(v, "type");
1166 char *href = xs_dict_get(v, "href"); 1169 const char *href = xs_dict_get(v, "href");
1167 char *name = xs_dict_get(v, "name"); 1170 const char *name = xs_dict_get(v, "name");
1168 1171
1169 if (type && strcmp(type, "Mention") == 0 && 1172 if (type && strcmp(type, "Mention") == 0 &&
1170 href && strcmp(href, snac->actor) != 0 && name) { 1173 href && strcmp(href, snac->actor) != 0 && name) {
@@ -1208,10 +1211,11 @@ xs_str *build_mentions(snac *snac, const xs_dict *msg)
1208} 1211}
1209 1212
1210 1213
1211xs_html *html_entry_controls(snac *snac, char *actor, const xs_dict *msg, const char *md5) 1214xs_html *html_entry_controls(snac *snac, const char *actor,
1215 const xs_dict *msg, const char *md5)
1212{ 1216{
1213 char *id = xs_dict_get(msg, "id"); 1217 const char *id = xs_dict_get(msg, "id");
1214 char *group = xs_dict_get(msg, "audience"); 1218 const char *group = xs_dict_get(msg, "audience");
1215 1219
1216 xs *likes = object_likes(id); 1220 xs *likes = object_likes(id);
1217 xs *boosts = object_announces(id); 1221 xs *boosts = object_announces(id);
@@ -1310,7 +1314,7 @@ xs_html *html_entry_controls(snac *snac, char *actor, const xs_dict *msg, const
1310 html_button("delete", L("Delete"), L("Delete this post")), 1314 html_button("delete", L("Delete"), L("Delete this post")),
1311 html_button("hide", L("Hide"), L("Hide this post and its children"))); 1315 html_button("hide", L("Hide"), L("Hide this post and its children")));
1312 1316
1313 char *prev_src = xs_dict_get(msg, "sourceContent"); 1317 const char *prev_src = xs_dict_get(msg, "sourceContent");
1314 1318
1315 if (!xs_is_null(prev_src) && strcmp(actor, snac->actor) == 0) { /** edit **/ 1319 if (!xs_is_null(prev_src) && strcmp(actor, snac->actor) == 0) { /** edit **/
1316 /* post can be edited */ 1320 /* post can be edited */
@@ -1318,13 +1322,13 @@ xs_html *html_entry_controls(snac *snac, char *actor, const xs_dict *msg, const
1318 xs *form_id = xs_fmt("%s_edit_form", md5); 1322 xs *form_id = xs_fmt("%s_edit_form", md5);
1319 xs *redir = xs_fmt("%s_entry", md5); 1323 xs *redir = xs_fmt("%s_entry", md5);
1320 1324
1321 char *att_file = ""; 1325 const char *att_file = "";
1322 char *att_alt_text = ""; 1326 const char *att_alt_text = "";
1323 xs_list *att_list = xs_dict_get(msg, "attachment"); 1327 const xs_list *att_list = xs_dict_get(msg, "attachment");
1324 1328
1325 /* does it have an attachment? */ 1329 /* does it have an attachment? */
1326 if (xs_type(att_list) == XSTYPE_LIST && xs_list_len(att_list)) { 1330 if (xs_type(att_list) == XSTYPE_LIST && xs_list_len(att_list)) {
1327 xs_dict *d = xs_list_get(att_list, 0); 1331 const xs_dict *d = xs_list_get(att_list, 0);
1328 1332
1329 if (xs_type(d) == XSTYPE_DICT) { 1333 if (xs_type(d) == XSTYPE_DICT) {
1330 att_file = xs_dict_get_def(d, "url", ""); 1334 att_file = xs_dict_get_def(d, "url", "");
@@ -1370,10 +1374,10 @@ xs_html *html_entry_controls(snac *snac, char *actor, const xs_dict *msg, const
1370xs_html *html_entry(snac *user, xs_dict *msg, int read_only, 1374xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
1371 int level, char *md5, int hide_children) 1375 int level, char *md5, int hide_children)
1372{ 1376{
1373 char *id = xs_dict_get(msg, "id"); 1377 const char *id = xs_dict_get(msg, "id");
1374 char *type = xs_dict_get(msg, "type"); 1378 const char *type = xs_dict_get(msg, "type");
1375 char *actor; 1379 const char *actor;
1376 char *v; 1380 const char *v;
1377 int has_title = 0; 1381 int has_title = 0;
1378 1382
1379 /* do not show non-public messages in the public timeline */ 1383 /* do not show non-public messages in the public timeline */
@@ -1509,7 +1513,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
1509 1513
1510 if (xs_list_len(boosts)) { 1514 if (xs_list_len(boosts)) {
1511 /* if somebody boosted this, show as origin */ 1515 /* if somebody boosted this, show as origin */
1512 char *p = xs_list_get(boosts, -1); 1516 const char *p = xs_list_get(boosts, -1);
1513 xs *actor_r = NULL; 1517 xs *actor_r = NULL;
1514 1518
1515 if (user && xs_list_in(boosts, user->md5) != -1) { 1519 if (user && xs_list_in(boosts, user->md5) != -1) {
@@ -1529,7 +1533,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
1529 1533
1530 if (!xs_is_null(name)) { 1534 if (!xs_is_null(name)) {
1531 xs *href = NULL; 1535 xs *href = NULL;
1532 char *id = xs_dict_get(actor_r, "id"); 1536 const char *id = xs_dict_get(actor_r, "id");
1533 int fwers = 0; 1537 int fwers = 0;
1534 int fwing = 0; 1538 int fwing = 0;
1535 1539
@@ -1558,7 +1562,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
1558 if (strcmp(type, "Note") == 0) { 1562 if (strcmp(type, "Note") == 0) {
1559 if (level == 0) { 1563 if (level == 0) {
1560 /* is the parent not here? */ 1564 /* is the parent not here? */
1561 char *parent = xs_dict_get(msg, "inReplyTo"); 1565 const char *parent = xs_dict_get(msg, "inReplyTo");
1562 1566
1563 if (user && !xs_is_null(parent) && *parent && !timeline_here(user, parent)) { 1567 if (user && !xs_is_null(parent) && *parent && !timeline_here(user, parent)) {
1564 xs_html_add(post_header, 1568 xs_html_add(post_header,
@@ -1603,7 +1607,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
1603 v = "..."; 1607 v = "...";
1604 1608
1605 /* only show it when not in the public timeline and the config setting is "open" */ 1609 /* only show it when not in the public timeline and the config setting is "open" */
1606 char *cw = xs_dict_get(user->config, "cw"); 1610 const char *cw = xs_dict_get(user->config, "cw");
1607 if (xs_is_null(cw) || read_only) 1611 if (xs_is_null(cw) || read_only)
1608 cw = ""; 1612 cw = "";
1609 1613
@@ -1632,7 +1636,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
1632 1636
1633 { 1637 {
1634 /** build the content string **/ 1638 /** build the content string **/
1635 char *content = xs_dict_get(msg, "content"); 1639 const char *content = xs_dict_get(msg, "content");
1636 1640
1637 xs *c = sanitize(xs_is_null(content) ? "" : content); 1641 xs *c = sanitize(xs_is_null(content) ? "" : content);
1638 1642
@@ -1650,7 +1654,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
1650 c = replace_shortnames(c, xs_dict_get(msg, "tag"), 2); 1654 c = replace_shortnames(c, xs_dict_get(msg, "tag"), 2);
1651 1655
1652 /* Peertube videos content is in markdown */ 1656 /* Peertube videos content is in markdown */
1653 char *mtype = xs_dict_get(msg, "mediaType"); 1657 const char *mtype = xs_dict_get(msg, "mediaType");
1654 if (xs_type(mtype) == XSTYPE_STRING && strcmp(mtype, "text/markdown") == 0) { 1658 if (xs_type(mtype) == XSTYPE_STRING && strcmp(mtype, "text/markdown") == 0) {
1655 /* a full conversion could be better */ 1659 /* a full conversion could be better */
1656 c = xs_replace_i(c, "\r", ""); 1660 c = xs_replace_i(c, "\r", "");
@@ -1663,12 +1667,12 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
1663 } 1667 }
1664 1668
1665 if (strcmp(type, "Question") == 0) { /** question content **/ 1669 if (strcmp(type, "Question") == 0) { /** question content **/
1666 xs_list *oo = xs_dict_get(msg, "oneOf"); 1670 const xs_list *oo = xs_dict_get(msg, "oneOf");
1667 xs_list *ao = xs_dict_get(msg, "anyOf"); 1671 const xs_list *ao = xs_dict_get(msg, "anyOf");
1668 xs_list *p; 1672 const xs_list *p;
1669 xs_dict *v; 1673 xs_dict *v;
1670 int closed = 0; 1674 int closed = 0;
1671 char *f_closed = NULL; 1675 const char *f_closed = NULL;
1672 1676
1673 xs_html *poll = xs_html_tag("div", NULL); 1677 xs_html *poll = xs_html_tag("div", NULL);
1674 1678
@@ -1697,10 +1701,11 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
1697 /* closed poll */ 1701 /* closed poll */
1698 xs_html *poll_result = xs_html_tag("table", 1702 xs_html *poll_result = xs_html_tag("table",
1699 xs_html_attr("class", "snac-poll-result")); 1703 xs_html_attr("class", "snac-poll-result"));
1704 int c = 0;
1700 1705
1701 while (xs_list_iter(&p, &v)) { 1706 while (xs_list_next(p, &v, &c)) {
1702 char *name = xs_dict_get(v, "name"); 1707 const char *name = xs_dict_get(v, "name");
1703 xs_dict *replies = xs_dict_get(v, "replies"); 1708 const xs_dict *replies = xs_dict_get(v, "replies");
1704 1709
1705 if (name && replies) { 1710 if (name && replies) {
1706 char *ti = (char *)xs_number_str(xs_dict_get(replies, "totalItems")); 1711 char *ti = (char *)xs_number_str(xs_dict_get(replies, "totalItems"));
@@ -1737,9 +1742,10 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
1737 xs_html_attr("name", "irt"), 1742 xs_html_attr("name", "irt"),
1738 xs_html_attr("value", id)))); 1743 xs_html_attr("value", id))));
1739 1744
1740 while (xs_list_iter(&p, &v)) { 1745 int c = 0;
1741 char *name = xs_dict_get(v, "name"); 1746 while (xs_list_next(p, &v, &c)) {
1742 xs_dict *replies = xs_dict_get(v, "replies"); 1747 const char *name = xs_dict_get(v, "name");
1748 const xs_dict *replies = xs_dict_get(v, "replies");
1743 1749
1744 if (name) { 1750 if (name) {
1745 char *ti = (char *)xs_number_str(xs_dict_get(replies, "totalItems")); 1751 char *ti = (char *)xs_number_str(xs_dict_get(replies, "totalItems"));
@@ -1777,7 +1783,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
1777 } 1783 }
1778 else { 1784 else {
1779 /* show when the poll closes */ 1785 /* show when the poll closes */
1780 char *end_time = xs_dict_get(msg, "endTime"); 1786 const char *end_time = xs_dict_get(msg, "endTime");
1781 1787
1782 /* Pleroma does not have an endTime field; 1788 /* Pleroma does not have an endTime field;
1783 it has a closed time in the future */ 1789 it has a closed time in the future */
@@ -1820,12 +1826,12 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
1820 xs_html_add(snac_content, 1826 xs_html_add(snac_content,
1821 content_attachments); 1827 content_attachments);
1822 1828
1823 xs_list *p = attach; 1829 int c = 0;
1824 1830 xs_dict *a;
1825 while (xs_list_iter(&p, &v)) { 1831 while (xs_list_next(attach, &a, &c)) {
1826 char *type = xs_dict_get(v, "type"); 1832 const char *type = xs_dict_get(a, "type");
1827 char *href = xs_dict_get(v, "href"); 1833 const char *href = xs_dict_get(a, "href");
1828 char *name = xs_dict_get(v, "name"); 1834 const char *name = xs_dict_get(a, "name");
1829 1835
1830 if (xs_startswith(type, "image/") || strcmp(type, "Image") == 0) { 1836 if (xs_startswith(type, "image/") || strcmp(type, "Image") == 0) {
1831 xs_html_add(content_attachments, 1837 xs_html_add(content_attachments,
@@ -1889,7 +1895,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
1889 } 1895 }
1890 1896
1891 /* has this message an audience (i.e., comes from a channel or community)? */ 1897 /* has this message an audience (i.e., comes from a channel or community)? */
1892 char *audience = xs_dict_get(msg, "audience"); 1898 const char *audience = xs_dict_get(msg, "audience");
1893 if (strcmp(type, "Page") == 0 && !xs_is_null(audience)) { 1899 if (strcmp(type, "Page") == 0 && !xs_is_null(audience)) {
1894 xs_html *au_tag = xs_html_tag("p", 1900 xs_html *au_tag = xs_html_tag("p",
1895 xs_html_text("("), 1901 xs_html_text("("),
@@ -2023,11 +2029,12 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only,
2023 2029
2024 if (xs_list_len(list) == 1) { 2030 if (xs_list_len(list) == 1) {
2025 /* only one element? pick the description from the source */ 2031 /* only one element? pick the description from the source */
2026 char *id = xs_list_get(list, 0); 2032 const char *id = xs_list_get(list, 0);
2027 xs *d = NULL; 2033 xs *d = NULL;
2028 object_get_by_md5(id, &d); 2034 object_get_by_md5(id, &d);
2029 if (d && (v = xs_dict_get(d, "sourceContent")) != NULL) 2035 const char *sc = xs_dict_get(d, "sourceContent");
2030 desc = xs_dup(v); 2036 if (d && sc != NULL)
2037 desc = xs_dup(sc);
2031 2038
2032 alternate = xs_dup(xs_dict_get(d, "id")); 2039 alternate = xs_dup(xs_dict_get(d, "id"));
2033 } 2040 }
@@ -2087,13 +2094,13 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only,
2087 2094
2088 /* is this message a non-public reply? */ 2095 /* is this message a non-public reply? */
2089 if (user != NULL && !is_msg_public(msg)) { 2096 if (user != NULL && !is_msg_public(msg)) {
2090 char *irt = xs_dict_get(msg, "inReplyTo"); 2097 const char *irt = xs_dict_get(msg, "inReplyTo");
2091 2098
2092 /* is it a reply to something not in the storage? */ 2099 /* is it a reply to something not in the storage? */
2093 if (!xs_is_null(irt) && !object_here(irt)) { 2100 if (!xs_is_null(irt) && !object_here(irt)) {
2094 /* is it for me? */ 2101 /* is it for me? */
2095 xs_list *to = xs_dict_get_def(msg, "to", xs_stock(XSTYPE_LIST)); 2102 const xs_list *to = xs_dict_get_def(msg, "to", xs_stock(XSTYPE_LIST));
2096 xs_list *cc = xs_dict_get_def(msg, "cc", xs_stock(XSTYPE_LIST)); 2103 const xs_list *cc = xs_dict_get_def(msg, "cc", xs_stock(XSTYPE_LIST));
2097 2104
2098 if (xs_list_in(to, user->actor) == -1 && xs_list_in(cc, user->actor) == -1) { 2105 if (xs_list_in(to, user->actor) == -1 && xs_list_in(cc, user->actor) == -1) {
2099 snac_debug(user, 1, xs_fmt("skipping non-public reply to an unknown post %s", v)); 2106 snac_debug(user, 1, xs_fmt("skipping non-public reply to an unknown post %s", v));
@@ -2212,7 +2219,7 @@ xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t)
2212 html_actor_icon(snac, actor, xs_dict_get(actor, "published"), NULL, NULL, 0, 1))); 2219 html_actor_icon(snac, actor, xs_dict_get(actor, "published"), NULL, NULL, 0, 1)));
2213 2220
2214 /* content (user bio) */ 2221 /* content (user bio) */
2215 char *c = xs_dict_get(actor, "summary"); 2222 const char *c = xs_dict_get(actor, "summary");
2216 2223
2217 if (!xs_is_null(c)) { 2224 if (!xs_is_null(c)) {
2218 xs *sc = sanitize(c); 2225 xs *sc = sanitize(c);
@@ -2364,10 +2371,10 @@ xs_str *html_notifications(snac *user, int skip, int show)
2364 continue; 2371 continue;
2365 2372
2366 xs *obj = NULL; 2373 xs *obj = NULL;
2367 char *type = xs_dict_get(noti, "type"); 2374 const char *type = xs_dict_get(noti, "type");
2368 char *utype = xs_dict_get(noti, "utype"); 2375 const char *utype = xs_dict_get(noti, "utype");
2369 char *id = xs_dict_get(noti, "objid"); 2376 const char *id = xs_dict_get(noti, "objid");
2370 char *date = xs_dict_get(noti, "date"); 2377 const char *date = xs_dict_get(noti, "date");
2371 2378
2372 if (xs_is_null(id) || !valid_status(object_get(id, &obj))) 2379 if (xs_is_null(id) || !valid_status(object_get(id, &obj)))
2373 continue; 2380 continue;
@@ -2375,14 +2382,14 @@ xs_str *html_notifications(snac *user, int skip, int show)
2375 if (is_hidden(user, id)) 2382 if (is_hidden(user, id))
2376 continue; 2383 continue;
2377 2384
2378 char *actor_id = xs_dict_get(noti, "actor"); 2385 const char *actor_id = xs_dict_get(noti, "actor");
2379 xs *actor = NULL; 2386 xs *actor = NULL;
2380 2387
2381 if (!valid_status(actor_get(actor_id, &actor))) 2388 if (!valid_status(actor_get(actor_id, &actor)))
2382 continue; 2389 continue;
2383 2390
2384 xs *a_name = actor_name(actor); 2391 xs *a_name = actor_name(actor);
2385 char *label = type; 2392 const char *label = type;
2386 2393
2387 if (strcmp(type, "Create") == 0) 2394 if (strcmp(type, "Create") == 0)
2388 label = L("Mention"); 2395 label = L("Mention");
@@ -2494,14 +2501,14 @@ xs_str *html_notifications(snac *user, int skip, int show)
2494int html_get_handler(const xs_dict *req, const char *q_path, 2501int html_get_handler(const xs_dict *req, const char *q_path,
2495 char **body, int *b_size, char **ctype, xs_str **etag) 2502 char **body, int *b_size, char **ctype, xs_str **etag)
2496{ 2503{
2497 char *accept = xs_dict_get(req, "accept"); 2504 const char *accept = xs_dict_get(req, "accept");
2498 int status = 404; 2505 int status = 404;
2499 snac snac; 2506 snac snac;
2500 xs *uid = NULL; 2507 xs *uid = NULL;
2501 char *p_path; 2508 const char *p_path;
2502 int cache = 1; 2509 int cache = 1;
2503 int save = 1; 2510 int save = 1;
2504 char *v; 2511 const char *v;
2505 2512
2506 xs *l = xs_split_n(q_path, "/", 2); 2513 xs *l = xs_split_n(q_path, "/", 2);
2507 v = xs_list_get(l, 1); 2514 v = xs_list_get(l, 1);
@@ -2540,7 +2547,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
2540 2547
2541 int skip = 0; 2548 int skip = 0;
2542 int show = xs_number_get(xs_dict_get(srv_config, "max_timeline_entries")); 2549 int show = xs_number_get(xs_dict_get(srv_config, "max_timeline_entries"));
2543 xs_dict *q_vars = xs_dict_get(req, "q_vars"); 2550 const xs_dict *q_vars = xs_dict_get(req, "q_vars");
2544 if ((v = xs_dict_get(q_vars, "skip")) != NULL) 2551 if ((v = xs_dict_get(q_vars, "skip")) != NULL)
2545 skip = atoi(v), cache = 0, save = 0; 2552 skip = atoi(v), cache = 0, save = 0;
2546 if ((v = xs_dict_get(q_vars, "show")) != NULL) 2553 if ((v = xs_dict_get(q_vars, "show")) != NULL)
@@ -2585,7 +2592,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
2585 status = 401; 2592 status = 401;
2586 } 2593 }
2587 else { 2594 else {
2588 char *q = xs_dict_get(q_vars, "q"); 2595 const char *q = xs_dict_get(q_vars, "q");
2589 2596
2590 if (q && *q) { 2597 if (q && *q) {
2591 if (*q == '#') { 2598 if (*q == '#') {
@@ -2669,7 +2676,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
2669 } 2676 }
2670 else { 2677 else {
2671 xs *l = xs_split(p_path, "/"); 2678 xs *l = xs_split(p_path, "/");
2672 char *md5 = xs_list_get(l, -1); 2679 const char *md5 = xs_list_get(l, -1);
2673 2680
2674 if (md5 && *md5 && timeline_here(&snac, md5)) { 2681 if (md5 && *md5 && timeline_here(&snac, md5)) {
2675 xs *list = xs_list_append(xs_list_new(), md5); 2682 xs *list = xs_list_append(xs_list_new(), md5);
@@ -2728,7 +2735,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
2728 } 2735 }
2729 else { 2736 else {
2730 xs *l = xs_split(p_path, "/"); 2737 xs *l = xs_split(p_path, "/");
2731 char *lid = xs_list_get(l, -1); 2738 const char *lid = xs_list_get(l, -1);
2732 2739
2733 xs *list = list_timeline(&snac, lid, skip, show); 2740 xs *list = list_timeline(&snac, lid, skip, show);
2734 xs *next = list_timeline(&snac, lid, skip + show, 1); 2741 xs *next = list_timeline(&snac, lid, skip + show, 1);
@@ -2767,7 +2774,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
2767 else 2774 else
2768 if (xs_startswith(p_path, "s/")) { /** a static file **/ 2775 if (xs_startswith(p_path, "s/")) { /** a static file **/
2769 xs *l = xs_split(p_path, "/"); 2776 xs *l = xs_split(p_path, "/");
2770 char *id = xs_list_get(l, 1); 2777 const char *id = xs_list_get(l, 1);
2771 int sz; 2778 int sz;
2772 2779
2773 if (id && *id) { 2780 if (id && *id) {
@@ -2788,8 +2795,8 @@ int html_get_handler(const xs_dict *req, const char *q_path,
2788 if (xs_type(xs_dict_get(srv_config, "disable_history")) == XSTYPE_TRUE) 2795 if (xs_type(xs_dict_get(srv_config, "disable_history")) == XSTYPE_TRUE)
2789 return 403; 2796 return 403;
2790 2797
2791 xs *l = xs_split(p_path, "/"); 2798 xs *l = xs_split(p_path, "/");
2792 char *id = xs_list_get(l, 1); 2799 const char *id = xs_list_get(l, 1);
2793 2800
2794 if (id && *id) { 2801 if (id && *id) {
2795 if (xs_endswith(id, "timeline.html_")) { 2802 if (xs_endswith(id, "timeline.html_")) {
@@ -2845,8 +2852,9 @@ int html_post_handler(const xs_dict *req, const char *q_path,
2845 2852
2846 int status = 0; 2853 int status = 0;
2847 snac snac; 2854 snac snac;
2848 char *uid, *p_path; 2855 const char *uid;
2849 xs_dict *p_vars; 2856 const char *p_path;
2857 const xs_dict *p_vars;
2850 2858
2851 xs *l = xs_split_n(q_path, "/", 2); 2859 xs *l = xs_split_n(q_path, "/", 2);
2852 2860
@@ -2874,15 +2882,15 @@ int html_post_handler(const xs_dict *req, const char *q_path,
2874 2882
2875 if (p_path && strcmp(p_path, "admin/note") == 0) { /** **/ 2883 if (p_path && strcmp(p_path, "admin/note") == 0) { /** **/
2876 /* post note */ 2884 /* post note */
2877 xs_str *content = xs_dict_get(p_vars, "content"); 2885 const xs_str *content = xs_dict_get(p_vars, "content");
2878 xs_str *in_reply_to = xs_dict_get(p_vars, "in_reply_to"); 2886 const xs_str *in_reply_to = xs_dict_get(p_vars, "in_reply_to");
2879 xs_str *attach_url = xs_dict_get(p_vars, "attach_url"); 2887 const xs_str *attach_url = xs_dict_get(p_vars, "attach_url");
2880 xs_list *attach_file = xs_dict_get(p_vars, "attach"); 2888 const xs_list *attach_file = xs_dict_get(p_vars, "attach");
2881 xs_str *to = xs_dict_get(p_vars, "to"); 2889 const xs_str *to = xs_dict_get(p_vars, "to");
2882 xs_str *sensitive = xs_dict_get(p_vars, "sensitive"); 2890 const xs_str *sensitive = xs_dict_get(p_vars, "sensitive");
2883 xs_str *summary = xs_dict_get(p_vars, "summary"); 2891 const xs_str *summary = xs_dict_get(p_vars, "summary");
2884 xs_str *edit_id = xs_dict_get(p_vars, "edit_id"); 2892 const xs_str *edit_id = xs_dict_get(p_vars, "edit_id");
2885 xs_str *alt_text = xs_dict_get(p_vars, "alt_text"); 2893 const xs_str *alt_text = xs_dict_get(p_vars, "alt_text");
2886 int priv = !xs_is_null(xs_dict_get(p_vars, "mentioned_only")); 2894 int priv = !xs_is_null(xs_dict_get(p_vars, "mentioned_only"));
2887 xs *attach_list = xs_list_new(); 2895 xs *attach_list = xs_list_new();
2888 2896
@@ -2902,7 +2910,7 @@ int html_post_handler(const xs_dict *req, const char *q_path,
2902 2910
2903 /* is attach_file set? */ 2911 /* is attach_file set? */
2904 if (!xs_is_null(attach_file) && xs_type(attach_file) == XSTYPE_LIST) { 2912 if (!xs_is_null(attach_file) && xs_type(attach_file) == XSTYPE_LIST) {
2905 char *fn = xs_list_get(attach_file, 0); 2913 const char *fn = xs_list_get(attach_file, 0);
2906 2914
2907 if (*fn != '\0') { 2915 if (*fn != '\0') {
2908 char *ext = strrchr(fn, '.'); 2916 char *ext = strrchr(fn, '.');
@@ -2978,7 +2986,7 @@ int html_post_handler(const xs_dict *req, const char *q_path,
2978 int n; 2986 int n;
2979 2987
2980 for (n = 0; fields[n]; n++) { 2988 for (n = 0; fields[n]; n++) {
2981 char *v = xs_dict_get(p_msg, fields[n]); 2989 const char *v = xs_dict_get(p_msg, fields[n]);
2982 msg = xs_dict_set(msg, fields[n], v); 2990 msg = xs_dict_set(msg, fields[n], v);
2983 } 2991 }
2984 2992
@@ -3007,10 +3015,10 @@ int html_post_handler(const xs_dict *req, const char *q_path,
3007 else 3015 else
3008 if (p_path && strcmp(p_path, "admin/action") == 0) { /** **/ 3016 if (p_path && strcmp(p_path, "admin/action") == 0) { /** **/
3009 /* action on an entry */ 3017 /* action on an entry */
3010 char *id = xs_dict_get(p_vars, "id"); 3018 const char *id = xs_dict_get(p_vars, "id");
3011 char *actor = xs_dict_get(p_vars, "actor"); 3019 const char *actor = xs_dict_get(p_vars, "actor");
3012 char *action = xs_dict_get(p_vars, "action"); 3020 const char *action = xs_dict_get(p_vars, "action");
3013 char *group = xs_dict_get(p_vars, "group"); 3021 const char *group = xs_dict_get(p_vars, "group");
3014 3022
3015 if (action == NULL) 3023 if (action == NULL)
3016 return 404; 3024 return 404;
@@ -3134,7 +3142,7 @@ int html_post_handler(const xs_dict *req, const char *q_path,
3134 } 3142 }
3135 else 3143 else
3136 if (strcmp(action, L("Delete")) == 0) { /** **/ 3144 if (strcmp(action, L("Delete")) == 0) { /** **/
3137 char *actor_form = xs_dict_get(p_vars, "actor-form"); 3145 const char *actor_form = xs_dict_get(p_vars, "actor-form");
3138 if (actor_form != NULL) { 3146 if (actor_form != NULL) {
3139 /* delete follower */ 3147 /* delete follower */
3140 if (valid_status(follower_del(&snac, actor))) 3148 if (valid_status(follower_del(&snac, actor)))
@@ -3178,8 +3186,8 @@ int html_post_handler(const xs_dict *req, const char *q_path,
3178 else 3186 else
3179 if (p_path && strcmp(p_path, "admin/user-setup") == 0) { /** **/ 3187 if (p_path && strcmp(p_path, "admin/user-setup") == 0) { /** **/
3180 /* change of user data */ 3188 /* change of user data */
3181 char *v; 3189 const char *v;
3182 char *p1, *p2; 3190 const char *p1, *p2;
3183 3191
3184 if ((v = xs_dict_get(p_vars, "name")) != NULL) 3192 if ((v = xs_dict_get(p_vars, "name")) != NULL)
3185 snac.config = xs_dict_set(snac.config, "name", v); 3193 snac.config = xs_dict_set(snac.config, "name", v);
@@ -3245,7 +3253,7 @@ int html_post_handler(const xs_dict *req, const char *q_path,
3245 for (n = 0; uploads[n]; n++) { 3253 for (n = 0; uploads[n]; n++) {
3246 xs *var_name = xs_fmt("%s_file", uploads[n]); 3254 xs *var_name = xs_fmt("%s_file", uploads[n]);
3247 3255
3248 xs_list *uploaded_file = xs_dict_get(p_vars, var_name); 3256 const xs_list *uploaded_file = xs_dict_get(p_vars, var_name);
3249 if (xs_type(uploaded_file) == XSTYPE_LIST) { 3257 if (xs_type(uploaded_file) == XSTYPE_LIST) {
3250 const char *fn = xs_list_get(uploaded_file, 0); 3258 const char *fn = xs_list_get(uploaded_file, 0);
3251 3259
@@ -3310,7 +3318,7 @@ int html_post_handler(const xs_dict *req, const char *q_path,
3310 } 3318 }
3311 else 3319 else
3312 if (p_path && strcmp(p_path, "admin/vote") == 0) { /** **/ 3320 if (p_path && strcmp(p_path, "admin/vote") == 0) { /** **/
3313 char *irt = xs_dict_get(p_vars, "irt"); 3321 const char *irt = xs_dict_get(p_vars, "irt");
3314 const char *opt = xs_dict_get(p_vars, "question"); 3322 const char *opt = xs_dict_get(p_vars, "question");
3315 const char *actor = xs_dict_get(p_vars, "actor"); 3323 const char *actor = xs_dict_get(p_vars, "actor");
3316 3324
@@ -3345,7 +3353,7 @@ int html_post_handler(const xs_dict *req, const char *q_path,
3345 xs *poll = NULL; 3353 xs *poll = NULL;
3346 3354
3347 if (valid_status(object_get(irt, &poll))) { 3355 if (valid_status(object_get(irt, &poll))) {
3348 char *date = xs_dict_get(poll, "endTime"); 3356 const char *date = xs_dict_get(poll, "endTime");
3349 if (xs_is_null(date)) 3357 if (xs_is_null(date))
3350 date = xs_dict_get(poll, "closed"); 3358 date = xs_dict_get(poll, "closed");
3351 3359
@@ -3363,7 +3371,7 @@ int html_post_handler(const xs_dict *req, const char *q_path,
3363 } 3371 }
3364 3372
3365 if (status == 303) { 3373 if (status == 303) {
3366 char *redir = xs_dict_get(p_vars, "redir"); 3374 const char *redir = xs_dict_get(p_vars, "redir");
3367 3375
3368 if (xs_is_null(redir)) 3376 if (xs_is_null(redir))
3369 redir = "top"; 3377 redir = "top";
@@ -3411,8 +3419,8 @@ xs_str *timeline_to_rss(snac *user, const xs_list *timeline, char *title, char *
3411 continue; 3419 continue;
3412 } 3420 }
3413 3421
3414 char *id = xs_dict_get(msg, "id"); 3422 const char *id = xs_dict_get(msg, "id");
3415 char *content = xs_dict_get(msg, "content"); 3423 const char *content = xs_dict_get(msg, "content");
3416 3424
3417 if (user && !xs_startswith(id, user->actor)) 3425 if (user && !xs_startswith(id, user->actor))
3418 continue; 3426 continue;
diff --git a/http.c b/http.c
index f7ff9ba..4d85631 100644
--- a/http.c
+++ b/http.c
@@ -12,7 +12,7 @@
12 12
13xs_dict *http_signed_request_raw(const char *keyid, const char *seckey, 13xs_dict *http_signed_request_raw(const char *keyid, const char *seckey,
14 const char *method, const char *url, 14 const char *method, const char *url,
15 xs_dict *headers, 15 const xs_dict *headers,
16 const char *body, int b_size, 16 const char *body, int b_size,
17 int *status, xs_str **payload, int *p_size, 17 int *status, xs_str **payload, int *p_size,
18 int timeout) 18 int timeout)
@@ -24,8 +24,8 @@ xs_dict *http_signed_request_raw(const char *keyid, const char *seckey,
24 xs *s64 = NULL; 24 xs *s64 = NULL;
25 xs *signature = NULL; 25 xs *signature = NULL;
26 xs *hdrs = NULL; 26 xs *hdrs = NULL;
27 char *host; 27 const char *host;
28 char *target; 28 const char *target;
29 char *k, *v; 29 char *k, *v;
30 xs_dict *response; 30 xs_dict *response;
31 31
@@ -106,13 +106,13 @@ xs_dict *http_signed_request_raw(const char *keyid, const char *seckey,
106 106
107 107
108xs_dict *http_signed_request(snac *snac, const char *method, const char *url, 108xs_dict *http_signed_request(snac *snac, const char *method, const char *url,
109 xs_dict *headers, 109 const xs_dict *headers,
110 const char *body, int b_size, 110 const char *body, int b_size,
111 int *status, xs_str **payload, int *p_size, 111 int *status, xs_str **payload, int *p_size,
112 int timeout) 112 int timeout)
113/* does a signed HTTP request */ 113/* does a signed HTTP request */
114{ 114{
115 char *seckey = xs_dict_get(snac->key, "secret"); 115 const char *seckey = xs_dict_get(snac->key, "secret");
116 xs_dict *response; 116 xs_dict *response;
117 117
118 response = http_signed_request_raw(snac->actor, seckey, method, url, 118 response = http_signed_request_raw(snac->actor, seckey, method, url,
@@ -122,17 +122,18 @@ xs_dict *http_signed_request(snac *snac, const char *method, const char *url,
122} 122}
123 123
124 124
125int check_signature(xs_dict *req, xs_str **err) 125int check_signature(const xs_dict *req, xs_str **err)
126/* check the signature */ 126/* check the signature */
127{ 127{
128 char *sig_hdr = xs_dict_get(req, "signature"); 128 const char *sig_hdr = xs_dict_get(req, "signature");
129 xs *keyId = NULL; 129 xs *keyId = NULL;
130 xs *headers = NULL; 130 xs *headers = NULL;
131 xs *signature = NULL; 131 xs *signature = NULL;
132 xs *created = NULL; 132 xs *created = NULL;
133 xs *expires = NULL; 133 xs *expires = NULL;
134 char *pubkey;
135 char *p; 134 char *p;
135 const char *pubkey;
136 const char *k;
136 137
137 if (xs_is_null(sig_hdr)) { 138 if (xs_is_null(sig_hdr)) {
138 *err = xs_fmt("missing 'signature' header"); 139 *err = xs_fmt("missing 'signature' header");
@@ -142,10 +143,10 @@ int check_signature(xs_dict *req, xs_str **err)
142 { 143 {
143 /* extract the values */ 144 /* extract the values */
144 xs *l = xs_split(sig_hdr, ","); 145 xs *l = xs_split(sig_hdr, ",");
145 xs_list *p = l; 146 int c = 0;
146 xs_val *v; 147 xs_val *v;
147 148
148 while (xs_list_iter(&p, &v)) { 149 while (xs_list_next(l, &v, &c)) {
149 xs *kv = xs_split_n(v, "=", 1); 150 xs *kv = xs_split_n(v, "=", 1);
150 151
151 if (xs_list_len(kv) != 2) 152 if (xs_list_len(kv) != 2)
@@ -192,8 +193,8 @@ int check_signature(xs_dict *req, xs_str **err)
192 return 0; 193 return 0;
193 } 194 }
194 195
195 if ((p = xs_dict_get(actor, "publicKey")) == NULL || 196 if ((k = xs_dict_get(actor, "publicKey")) == NULL ||
196 ((pubkey = xs_dict_get(p, "publicKeyPem")) == NULL)) { 197 ((pubkey = xs_dict_get(k, "publicKeyPem")) == NULL)) {
197 *err = xs_fmt("cannot get pubkey from %s", keyId); 198 *err = xs_fmt("cannot get pubkey from %s", keyId);
198 return 0; 199 return 0;
199 } 200 }
@@ -208,7 +209,7 @@ int check_signature(xs_dict *req, xs_str **err)
208 209
209 p = l; 210 p = l;
210 while (xs_list_iter(&p, &v)) { 211 while (xs_list_iter(&p, &v)) {
211 char *hc; 212 const char *hc;
212 xs *ss = NULL; 213 xs *ss = NULL;
213 214
214 if (*sig_str != '\0') 215 if (*sig_str != '\0')
diff --git a/httpd.c b/httpd.c
index 71cce5e..d63fa0f 100644
--- a/httpd.c
+++ b/httpd.c
@@ -125,7 +125,7 @@ static xs_str *greeting_html(void)
125 125
126 /* does it have a %userlist% mark? */ 126 /* does it have a %userlist% mark? */
127 if (xs_str_in(s, "%userlist%") != -1) { 127 if (xs_str_in(s, "%userlist%") != -1) {
128 char *host = xs_dict_get(srv_config, "host"); 128 const char *host = xs_dict_get(srv_config, "host");
129 xs *list = user_list(); 129 xs *list = user_list();
130 xs_list *p = list; 130 xs_list *p = list;
131 xs_str *uid; 131 xs_str *uid;
@@ -171,14 +171,14 @@ int server_get_handler(xs_dict *req, const char *q_path,
171 171
172 /* is it the server root? */ 172 /* is it the server root? */
173 if (*q_path == '\0') { 173 if (*q_path == '\0') {
174 xs_dict *q_vars = xs_dict_get(req, "q_vars"); 174 const xs_dict *q_vars = xs_dict_get(req, "q_vars");
175 char *t = NULL; 175 const char *t = NULL;
176 176
177 if (xs_type(q_vars) == XSTYPE_DICT && (t = xs_dict_get(q_vars, "t"))) { 177 if (xs_type(q_vars) == XSTYPE_DICT && (t = xs_dict_get(q_vars, "t"))) {
178 /** search by tag **/ 178 /** search by tag **/
179 int skip = 0; 179 int skip = 0;
180 int show = xs_number_get(xs_dict_get(srv_config, "max_timeline_entries")); 180 int show = xs_number_get(xs_dict_get(srv_config, "max_timeline_entries"));
181 char *v; 181 const char *v;
182 182
183 if ((v = xs_dict_get(q_vars, "skip")) != NULL) 183 if ((v = xs_dict_get(q_vars, "skip")) != NULL)
184 skip = atoi(v); 184 skip = atoi(v);
@@ -193,7 +193,7 @@ int server_get_handler(xs_dict *req, const char *q_path,
193 more = 1; 193 more = 1;
194 } 194 }
195 195
196 char *accept = xs_dict_get(req, "accept"); 196 const char *accept = xs_dict_get(req, "accept");
197 if (!xs_is_null(accept) && strcmp(accept, "application/rss+xml") == 0) { 197 if (!xs_is_null(accept) && strcmp(accept, "application/rss+xml") == 0) {
198 xs *link = xs_fmt("%s/?t=%s", srv_baseurl, t); 198 xs *link = xs_fmt("%s/?t=%s", srv_baseurl, t);
199 199
@@ -268,7 +268,7 @@ void httpd_connection(FILE *f)
268/* the connection processor */ 268/* the connection processor */
269{ 269{
270 xs *req; 270 xs *req;
271 char *method; 271 const char *method;
272 int status = 0; 272 int status = 0;
273 xs_str *body = NULL; 273 xs_str *body = NULL;
274 int b_size = 0; 274 int b_size = 0;
@@ -278,7 +278,7 @@ void httpd_connection(FILE *f)
278 xs *payload = NULL; 278 xs *payload = NULL;
279 xs *etag = NULL; 279 xs *etag = NULL;
280 int p_size = 0; 280 int p_size = 0;
281 char *p; 281 const char *p;
282 int fcgi_id; 282 int fcgi_id;
283 283
284 if (p_state->use_fcgi) 284 if (p_state->use_fcgi)
@@ -411,7 +411,7 @@ void httpd_connection(FILE *f)
411 headers = xs_dict_append(headers, "etag", etag); 411 headers = xs_dict_append(headers, "etag", etag);
412 412
413 /* if there are any additional headers, add them */ 413 /* if there are any additional headers, add them */
414 xs_dict *more_headers = xs_dict_get(srv_config, "http_headers"); 414 const xs_dict *more_headers = xs_dict_get(srv_config, "http_headers");
415 if (xs_type(more_headers) == XSTYPE_DICT) { 415 if (xs_type(more_headers) == XSTYPE_DICT) {
416 char *k, *v; 416 char *k, *v;
417 int c = 0; 417 int c = 0;
diff --git a/main.c b/main.c
index 819922f..9c906a6 100644
--- a/main.c
+++ b/main.c
@@ -315,7 +315,7 @@ int main(int argc, char *argv[])
315 xs *msg = msg_follow(&snac, url); 315 xs *msg = msg_follow(&snac, url);
316 316
317 if (msg != NULL) { 317 if (msg != NULL) {
318 char *actor = xs_dict_get(msg, "object"); 318 const char *actor = xs_dict_get(msg, "object");
319 319
320 following_add(&snac, actor, msg); 320 following_add(&snac, actor, msg);
321 321
diff --git a/mastoapi.c b/mastoapi.c
index 2bf5fdc..852713e 100644
--- a/mastoapi.c
+++ b/mastoapi.c
@@ -175,7 +175,7 @@ int oauth_get_handler(const xs_dict *req, const char *q_path,
175 return 0; 175 return 0;
176 176
177 int status = 404; 177 int status = 404;
178 xs_dict *msg = xs_dict_get(req, "q_vars"); 178 const xs_dict *msg = xs_dict_get(req, "q_vars");
179 xs *cmd = xs_replace_n(q_path, "/oauth", "", 1); 179 xs *cmd = xs_replace_n(q_path, "/oauth", "", 1);
180 180
181 srv_debug(1, xs_fmt("oauth_get_handler %s", q_path)); 181 srv_debug(1, xs_fmt("oauth_get_handler %s", q_path));
@@ -239,7 +239,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
239 239
240 int status = 404; 240 int status = 404;
241 241
242 char *i_ctype = xs_dict_get(req, "content-type"); 242 const char *i_ctype = xs_dict_get(req, "content-type");
243 xs *args = NULL; 243 xs *args = NULL;
244 244
245 if (i_ctype && xs_startswith(i_ctype, "application/json")) { 245 if (i_ctype && xs_startswith(i_ctype, "application/json")) {
@@ -568,10 +568,10 @@ xs_dict *mastoapi_account(const xs_dict *actor)
568 acct = xs_dict_append(acct, "uri", id); 568 acct = xs_dict_append(acct, "uri", id);
569 569
570 xs *avatar = NULL; 570 xs *avatar = NULL;
571 xs_dict *av = xs_dict_get(actor, "icon"); 571 const xs_dict *av = xs_dict_get(actor, "icon");
572 572
573 if (xs_type(av) == XSTYPE_DICT) { 573 if (xs_type(av) == XSTYPE_DICT) {
574 char *url = xs_dict_get(av, "url"); 574 const char *url = xs_dict_get(av, "url");
575 575
576 if (url != NULL) 576 if (url != NULL)
577 avatar = xs_dup(url); 577 avatar = xs_dup(url);
@@ -584,7 +584,7 @@ xs_dict *mastoapi_account(const xs_dict *actor)
584 acct = xs_dict_append(acct, "avatar_static", avatar); 584 acct = xs_dict_append(acct, "avatar_static", avatar);
585 585
586 xs *header = NULL; 586 xs *header = NULL;
587 xs_dict *hd = xs_dict_get(actor, "image"); 587 const xs_dict *hd = xs_dict_get(actor, "image");
588 588
589 if (xs_type(hd) == XSTYPE_DICT) 589 if (xs_type(hd) == XSTYPE_DICT)
590 header = xs_dup(xs_dict_get(hd, "url")); 590 header = xs_dup(xs_dict_get(hd, "url"));
@@ -596,12 +596,13 @@ xs_dict *mastoapi_account(const xs_dict *actor)
596 acct = xs_dict_append(acct, "header_static", header); 596 acct = xs_dict_append(acct, "header_static", header);
597 597
598 /* emojis */ 598 /* emojis */
599 xs_list *p; 599 const xs_list *p;
600 if (!xs_is_null(p = xs_dict_get(actor, "tag"))) { 600 if (!xs_is_null(p = xs_dict_get(actor, "tag"))) {
601 xs *eml = xs_list_new(); 601 xs *eml = xs_list_new();
602 xs_dict *v; 602 xs_dict *v;
603 int c = 0;
603 604
604 while (xs_list_iter(&p, &v)) { 605 while (xs_list_next(p, &v, &c)) {
605 const char *type = xs_dict_get(v, "type"); 606 const char *type = xs_dict_get(v, "type");
606 607
607 if (!xs_is_null(type) && strcmp(type, "Emoji") == 0) { 608 if (!xs_is_null(type) && strcmp(type, "Emoji") == 0) {
@@ -640,7 +641,7 @@ xs_dict *mastoapi_account(const xs_dict *actor)
640 641
641 /* dict of validated links */ 642 /* dict of validated links */
642 xs_dict *val_links = NULL; 643 xs_dict *val_links = NULL;
643 xs_dict *metadata = xs_stock(XSTYPE_DICT); 644 const xs_dict *metadata = xs_stock(XSTYPE_DICT);
644 snac user = {0}; 645 snac user = {0};
645 646
646 if (xs_startswith(id, srv_baseurl)) { 647 if (xs_startswith(id, srv_baseurl)) {
@@ -654,19 +655,20 @@ xs_dict *mastoapi_account(const xs_dict *actor)
654 if (xs_is_null(val_links)) 655 if (xs_is_null(val_links))
655 val_links = xs_stock(XSTYPE_DICT); 656 val_links = xs_stock(XSTYPE_DICT);
656 657
657 while (xs_list_iter(&p, &v)) { 658 int c = 0;
658 char *type = xs_dict_get(v, "type"); 659 while (xs_list_next(p, &v, &c)) {
659 char *name = xs_dict_get(v, "name"); 660 const char *type = xs_dict_get(v, "type");
660 char *value = xs_dict_get(v, "value"); 661 const char *name = xs_dict_get(v, "name");
662 const char *value = xs_dict_get(v, "value");
661 663
662 if (!xs_is_null(type) && !xs_is_null(name) && 664 if (!xs_is_null(type) && !xs_is_null(name) &&
663 !xs_is_null(value) && strcmp(type, "PropertyValue") == 0) { 665 !xs_is_null(value) && strcmp(type, "PropertyValue") == 0) {
664 xs *val_date = NULL; 666 xs *val_date = NULL;
665 667
666 char *url = xs_dict_get(metadata, name); 668 const char *url = xs_dict_get(metadata, name);
667 669
668 if (!xs_is_null(url) && xs_startswith(url, "https:/" "/")) { 670 if (!xs_is_null(url) && xs_startswith(url, "https:/" "/")) {
669 xs_number *verified_time = xs_dict_get(val_links, url); 671 const xs_number *verified_time = xs_dict_get(val_links, url);
670 if (xs_type(verified_time) == XSTYPE_NUMBER) { 672 if (xs_type(verified_time) == XSTYPE_NUMBER) {
671 time_t t = xs_number_get(verified_time); 673 time_t t = xs_number_get(verified_time);
672 674
@@ -695,7 +697,7 @@ xs_dict *mastoapi_account(const xs_dict *actor)
695} 697}
696 698
697 699
698xs_str *mastoapi_date(char *date) 700xs_str *mastoapi_date(const char *date)
699/* converts an ISO 8601 date to whatever format Mastodon uses */ 701/* converts an ISO 8601 date to whatever format Mastodon uses */
700{ 702{
701 xs_str *s = xs_crop_i(xs_dup(date), 0, 19); 703 xs_str *s = xs_crop_i(xs_dup(date), 0, 19);
@@ -710,13 +712,13 @@ xs_dict *mastoapi_poll(snac *snac, const xs_dict *msg)
710{ 712{
711 xs_dict *poll = xs_dict_new(); 713 xs_dict *poll = xs_dict_new();
712 xs *mid = mastoapi_id(msg); 714 xs *mid = mastoapi_id(msg);
713 xs_list *opts = NULL; 715 const xs_list *opts = NULL;
714 xs_val *v; 716 xs_val *v;
715 int num_votes = 0; 717 int num_votes = 0;
716 xs *options = xs_list_new(); 718 xs *options = xs_list_new();
717 719
718 poll = xs_dict_append(poll, "id", mid); 720 poll = xs_dict_append(poll, "id", mid);
719 char *date = xs_dict_get(msg, "endTime"); 721 const char *date = xs_dict_get(msg, "endTime");
720 if (date == NULL) 722 if (date == NULL)
721 date = xs_dict_get(msg, "closed"); 723 date = xs_dict_get(msg, "closed");
722 if (date == NULL) 724 if (date == NULL)
@@ -741,7 +743,8 @@ xs_dict *mastoapi_poll(snac *snac, const xs_dict *msg)
741 poll = xs_dict_append(poll, "multiple", xs_stock(XSTYPE_TRUE)); 743 poll = xs_dict_append(poll, "multiple", xs_stock(XSTYPE_TRUE));
742 } 744 }
743 745
744 while (xs_list_iter(&opts, &v)) { 746 int c = 0;
747 while (xs_list_next(opts, &v, &c)) {
745 const char *title = xs_dict_get(v, "name"); 748 const char *title = xs_dict_get(v, "name");
746 const char *replies = xs_dict_get(v, "replies"); 749 const char *replies = xs_dict_get(v, "replies");
747 750
@@ -794,7 +797,7 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg)
794 797
795 xs *idx = NULL; 798 xs *idx = NULL;
796 xs *ixc = NULL; 799 xs *ixc = NULL;
797 char *tmp; 800 const char *tmp;
798 xs *mid = mastoapi_id(msg); 801 xs *mid = mastoapi_id(msg);
799 802
800 xs_dict *st = xs_dict_new(); 803 xs_dict *st = xs_dict_new();
@@ -851,9 +854,9 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg)
851 xs *matt = xs_list_new(); 854 xs *matt = xs_list_new();
852 855
853 while (xs_list_iter(&p, &v)) { 856 while (xs_list_iter(&p, &v)) {
854 char *type = xs_dict_get(v, "type"); 857 const char *type = xs_dict_get(v, "type");
855 char *href = xs_dict_get(v, "href"); 858 const char *href = xs_dict_get(v, "href");
856 char *name = xs_dict_get(v, "name"); 859 const char *name = xs_dict_get(v, "name");
857 860
858 if (xs_match(type, "image/*|video/*|Image|Video")) { /* */ 861 if (xs_match(type, "image/*|video/*|Image|Video")) { /* */
859 xs *matteid = xs_fmt("%s_%d", id, xs_list_len(matt)); 862 xs *matteid = xs_fmt("%s_%d", id, xs_list_len(matt));
@@ -879,7 +882,7 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg)
879 xs *ml = xs_list_new(); 882 xs *ml = xs_list_new();
880 xs *htl = xs_list_new(); 883 xs *htl = xs_list_new();
881 xs *eml = xs_list_new(); 884 xs *eml = xs_list_new();
882 xs_list *tag = xs_dict_get(msg, "tag"); 885 const xs_list *tag = xs_dict_get(msg, "tag");
883 int n = 0; 886 int n = 0;
884 887
885 xs *tag_list = NULL; 888 xs *tag_list = NULL;
@@ -897,7 +900,8 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg)
897 tag = tag_list; 900 tag = tag_list;
898 xs_dict *v; 901 xs_dict *v;
899 902
900 while (xs_list_iter(&tag, &v)) { 903 int c = 0;
904 while (xs_list_next(tag, &v, &c)) {
901 const char *type = xs_dict_get(v, "type"); 905 const char *type = xs_dict_get(v, "type");
902 906
903 if (xs_is_null(type)) 907 if (xs_is_null(type))
@@ -1006,7 +1010,7 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg)
1006 xs *irt_mid = mastoapi_id(irto); 1010 xs *irt_mid = mastoapi_id(irto);
1007 st = xs_dict_set(st, "in_reply_to_id", irt_mid); 1011 st = xs_dict_set(st, "in_reply_to_id", irt_mid);
1008 1012
1009 char *at = NULL; 1013 const char *at = NULL;
1010 if (!xs_is_null(at = get_atto(irto))) { 1014 if (!xs_is_null(at = get_atto(irto))) {
1011 xs *at_md5 = xs_md5_hex(at, strlen(at)); 1015 xs *at_md5 = xs_md5_hex(at, strlen(at));
1012 st = xs_dict_set(st, "in_reply_to_account_id", at_md5); 1016 st = xs_dict_set(st, "in_reply_to_account_id", at_md5);
@@ -1118,7 +1122,7 @@ int process_auth_token(snac *snac, const xs_dict *req)
1118/* processes an authorization token, if there is one */ 1122/* processes an authorization token, if there is one */
1119{ 1123{
1120 int logged_in = 0; 1124 int logged_in = 0;
1121 char *v; 1125 const char *v;
1122 1126
1123 /* if there is an authorization field, try to validate it */ 1127 /* if there is an authorization field, try to validate it */
1124 if (!xs_is_null(v = xs_dict_get(req, "authorization")) && xs_startswith(v, "Bearer ")) { 1128 if (!xs_is_null(v = xs_dict_get(req, "authorization")) && xs_startswith(v, "Bearer ")) {
@@ -1156,7 +1160,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1156 return 0; 1160 return 0;
1157 1161
1158 int status = 404; 1162 int status = 404;
1159 xs_dict *args = xs_dict_get(req, "q_vars"); 1163 const xs_dict *args = xs_dict_get(req, "q_vars");
1160 xs *cmd = xs_replace_n(q_path, "/api", "", 1); 1164 xs *cmd = xs_replace_n(q_path, "/api", "", 1);
1161 1165
1162 snac snac1 = {0}; 1166 snac snac1 = {0};
@@ -1182,7 +1186,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1182 acct = xs_dict_append(acct, "source", src); 1186 acct = xs_dict_append(acct, "source", src);
1183 1187
1184 xs *avatar = NULL; 1188 xs *avatar = NULL;
1185 char *av = xs_dict_get(snac1.config, "avatar"); 1189 const char *av = xs_dict_get(snac1.config, "avatar");
1186 1190
1187 if (xs_is_null(av) || *av == '\0') 1191 if (xs_is_null(av) || *av == '\0')
1188 avatar = xs_fmt("%s/susie.png", srv_baseurl); 1192 avatar = xs_fmt("%s/susie.png", srv_baseurl);
@@ -1193,7 +1197,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1193 acct = xs_dict_append(acct, "avatar_static", avatar); 1197 acct = xs_dict_append(acct, "avatar_static", avatar);
1194 1198
1195 xs *header = NULL; 1199 xs *header = NULL;
1196 char *hd = xs_dict_get(snac1.config, "header"); 1200 const char *hd = xs_dict_get(snac1.config, "header");
1197 1201
1198 if (!xs_is_null(hd)) 1202 if (!xs_is_null(hd))
1199 header = xs_dup(hd); 1203 header = xs_dup(hd);
@@ -1203,7 +1207,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1203 acct = xs_dict_append(acct, "header", header); 1207 acct = xs_dict_append(acct, "header", header);
1204 acct = xs_dict_append(acct, "header_static", header); 1208 acct = xs_dict_append(acct, "header_static", header);
1205 1209
1206 xs_dict *metadata = xs_dict_get(snac1.config, "metadata"); 1210 const xs_dict *metadata = xs_dict_get(snac1.config, "metadata");
1207 if (xs_type(metadata) == XSTYPE_DICT) { 1211 if (xs_type(metadata) == XSTYPE_DICT) {
1208 xs *fields = xs_list_new(); 1212 xs *fields = xs_list_new();
1209 xs_str *k; 1213 xs_str *k;
@@ -1217,7 +1221,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1217 while (xs_dict_next(metadata, &k, &v, &c)) { 1221 while (xs_dict_next(metadata, &k, &v, &c)) {
1218 xs *val_date = NULL; 1222 xs *val_date = NULL;
1219 1223
1220 xs_number *verified_time = xs_dict_get(val_links, v); 1224 const xs_number *verified_time = xs_dict_get(val_links, v);
1221 if (xs_type(verified_time) == XSTYPE_NUMBER) { 1225 if (xs_type(verified_time) == XSTYPE_NUMBER) {
1222 time_t t = xs_number_get(verified_time); 1226 time_t t = xs_number_get(verified_time);
1223 1227
@@ -1283,13 +1287,13 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1283 else 1287 else
1284 if (strcmp(cmd, "/v1/accounts/lookup") == 0) { /** **/ 1288 if (strcmp(cmd, "/v1/accounts/lookup") == 0) { /** **/
1285 /* lookup an account */ 1289 /* lookup an account */
1286 char *acct = xs_dict_get(args, "acct"); 1290 const char *acct = xs_dict_get(args, "acct");
1287 1291
1288 if (!xs_is_null(acct)) { 1292 if (!xs_is_null(acct)) {
1289 xs *s = xs_strip_chars_i(xs_dup(acct), "@"); 1293 xs *s = xs_strip_chars_i(xs_dup(acct), "@");
1290 xs *l = xs_split_n(s, "@", 1); 1294 xs *l = xs_split_n(s, "@", 1);
1291 char *uid = xs_list_get(l, 0); 1295 const char *uid = xs_list_get(l, 0);
1292 char *host = xs_list_get(l, 1); 1296 const char *host = xs_list_get(l, 1);
1293 1297
1294 if (uid && (!host || strcmp(host, xs_dict_get(srv_config, "host")) == 0)) { 1298 if (uid && (!host || strcmp(host, xs_dict_get(srv_config, "host")) == 0)) {
1295 snac user; 1299 snac user;
@@ -1624,7 +1628,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1624 1628
1625 /* get the tag */ 1629 /* get the tag */
1626 xs *l = xs_split(cmd, "/"); 1630 xs *l = xs_split(cmd, "/");
1627 char *tag = xs_list_get(l, -1); 1631 const char *tag = xs_list_get(l, -1);
1628 1632
1629 xs *timeline = tag_search(tag, 0, limit); 1633 xs *timeline = tag_search(tag, 0, limit);
1630 xs *out = xs_list_new(); 1634 xs *out = xs_list_new();
@@ -1664,7 +1668,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1664 /* get the list id */ 1668 /* get the list id */
1665 if (logged_in) { 1669 if (logged_in) {
1666 xs *l = xs_split(cmd, "/"); 1670 xs *l = xs_split(cmd, "/");
1667 char *list = xs_list_get(l, -1); 1671 const char *list = xs_list_get(l, -1);
1668 1672
1669 xs *timeline = list_timeline(&snac1, list, 0, 2048); 1673 xs *timeline = list_timeline(&snac1, list, 0, 2048);
1670 xs *out = xs_list_new(); 1674 xs *out = xs_list_new();
@@ -1744,7 +1748,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1744 xs *out = xs_list_new(); 1748 xs *out = xs_list_new();
1745 xs_list *p = l; 1749 xs_list *p = l;
1746 xs_dict *v; 1750 xs_dict *v;
1747 xs_list *excl = xs_dict_get(args, "exclude_types[]"); 1751 const xs_list *excl = xs_dict_get(args, "exclude_types[]");
1748 1752
1749 while (xs_list_iter(&p, &v)) { 1753 while (xs_list_iter(&p, &v)) {
1750 xs *noti = notify_get(&snac1, v); 1754 xs *noti = notify_get(&snac1, v);
@@ -1876,7 +1880,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1876 if (xs_startswith(cmd, "/v1/lists/")) { /** list information **/ 1880 if (xs_startswith(cmd, "/v1/lists/")) { /** list information **/
1877 if (logged_in) { 1881 if (logged_in) {
1878 xs *l = xs_split(cmd, "/"); 1882 xs *l = xs_split(cmd, "/");
1879 char *p = xs_list_get(l, -1); 1883 const char *p = xs_list_get(l, -1);
1880 1884
1881 if (p) { 1885 if (p) {
1882 if (strcmp(p, "accounts") == 0) { 1886 if (strcmp(p, "accounts") == 0) {
@@ -1910,7 +1914,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1910 xs_list *v; 1914 xs_list *v;
1911 1915
1912 while (xs_list_next(lol, &v, &c)) { 1916 while (xs_list_next(lol, &v, &c)) {
1913 char *id = xs_list_get(v, 0); 1917 const char *id = xs_list_get(v, 0);
1914 1918
1915 if (id && strcmp(id, p) == 0) { 1919 if (id && strcmp(id, p) == 0) {
1916 xs *d = xs_dict_new(); 1920 xs *d = xs_dict_new();
@@ -2314,7 +2318,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
2314 2318
2315 int status = 404; 2319 int status = 404;
2316 xs *args = NULL; 2320 xs *args = NULL;
2317 char *i_ctype = xs_dict_get(req, "content-type"); 2321 const char *i_ctype = xs_dict_get(req, "content-type");
2318 2322
2319 if (i_ctype && xs_startswith(i_ctype, "application/json")) { 2323 if (i_ctype && xs_startswith(i_ctype, "application/json")) {
2320 if (!xs_is_null(payload)) 2324 if (!xs_is_null(payload))
@@ -2487,7 +2491,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
2487 mid = MID_TO_MD5(mid); 2491 mid = MID_TO_MD5(mid);
2488 2492
2489 if (valid_status(timeline_get_by_md5(&snac, mid, &msg))) { 2493 if (valid_status(timeline_get_by_md5(&snac, mid, &msg))) {
2490 char *id = xs_dict_get(msg, "id"); 2494 const char *id = xs_dict_get(msg, "id");
2491 2495
2492 if (op == NULL) { 2496 if (op == NULL) {
2493 /* no operation (?) */ 2497 /* no operation (?) */
@@ -2593,7 +2597,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
2593 if (strcmp(cmd, "/v1/push/subscription") == 0) { /** **/ 2597 if (strcmp(cmd, "/v1/push/subscription") == 0) { /** **/
2594 /* I don't know what I'm doing */ 2598 /* I don't know what I'm doing */
2595 if (logged_in) { 2599 if (logged_in) {
2596 char *v; 2600 const char *v;
2597 2601
2598 xs *wpush = xs_dict_new(); 2602 xs *wpush = xs_dict_new();
2599 2603
@@ -2765,7 +2769,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
2765 const char *id = xs_dict_get(msg, "id"); 2769 const char *id = xs_dict_get(msg, "id");
2766 const char *atto = get_atto(msg); 2770 const char *atto = get_atto(msg);
2767 2771
2768 xs_list *opts = xs_dict_get(msg, "oneOf"); 2772 const xs_list *opts = xs_dict_get(msg, "oneOf");
2769 if (opts == NULL) 2773 if (opts == NULL)
2770 opts = xs_dict_get(msg, "anyOf"); 2774 opts = xs_dict_get(msg, "anyOf");
2771 2775
@@ -2773,7 +2777,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
2773 } 2777 }
2774 else 2778 else
2775 if (strcmp(op, "votes") == 0) { 2779 if (strcmp(op, "votes") == 0) {
2776 xs_list *choices = xs_dict_get(args, "choices[]"); 2780 const xs_list *choices = xs_dict_get(args, "choices[]");
2777 2781
2778 if (xs_is_null(choices)) 2782 if (xs_is_null(choices))
2779 choices = xs_dict_get(args, "choices"); 2783 choices = xs_dict_get(args, "choices");
@@ -2781,7 +2785,8 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
2781 if (xs_type(choices) == XSTYPE_LIST) { 2785 if (xs_type(choices) == XSTYPE_LIST) {
2782 xs_str *v; 2786 xs_str *v;
2783 2787
2784 while (xs_list_iter(&choices, &v)) { 2788 int c = 0;
2789 while (xs_list_next(choices, &v, &c)) {
2785 int io = atoi(v); 2790 int io = atoi(v);
2786 const xs_dict *o = xs_list_get(opts, io); 2791 const xs_dict *o = xs_list_get(opts, io);
2787 2792
@@ -2843,12 +2848,12 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
2843 if (xs_startswith(cmd, "/v1/lists/")) { /** list maintenance **/ 2848 if (xs_startswith(cmd, "/v1/lists/")) { /** list maintenance **/
2844 if (logged_in) { 2849 if (logged_in) {
2845 xs *l = xs_split(cmd, "/"); 2850 xs *l = xs_split(cmd, "/");
2846 char *op = xs_list_get(l, -1); 2851 const char *op = xs_list_get(l, -1);
2847 char *id = xs_list_get(l, -2); 2852 const char *id = xs_list_get(l, -2);
2848 2853
2849 if (op && id && xs_is_hex(id)) { 2854 if (op && id && xs_is_hex(id)) {
2850 if (strcmp(op, "accounts") == 0) { 2855 if (strcmp(op, "accounts") == 0) {
2851 xs_list *accts = xs_dict_get(args, "account_ids[]"); 2856 const xs_list *accts = xs_dict_get(args, "account_ids[]");
2852 int c = 0; 2857 int c = 0;
2853 char *v; 2858 char *v;
2854 2859
@@ -2888,7 +2893,7 @@ int mastoapi_delete_handler(const xs_dict *req, const char *q_path,
2888 2893
2889 int status = 404; 2894 int status = 404;
2890 xs *args = NULL; 2895 xs *args = NULL;
2891 char *i_ctype = xs_dict_get(req, "content-type"); 2896 const char *i_ctype = xs_dict_get(req, "content-type");
2892 2897
2893 if (i_ctype && xs_startswith(i_ctype, "application/json")) { 2898 if (i_ctype && xs_startswith(i_ctype, "application/json")) {
2894 if (!xs_is_null(payload)) 2899 if (!xs_is_null(payload))
@@ -2921,13 +2926,13 @@ int mastoapi_delete_handler(const xs_dict *req, const char *q_path,
2921 if (xs_startswith(cmd, "/v1/lists/")) { 2926 if (xs_startswith(cmd, "/v1/lists/")) {
2922 if (logged_in) { 2927 if (logged_in) {
2923 xs *l = xs_split(cmd, "/"); 2928 xs *l = xs_split(cmd, "/");
2924 char *p = xs_list_get(l, -1); 2929 const char *p = xs_list_get(l, -1);
2925 2930
2926 if (p) { 2931 if (p) {
2927 if (strcmp(p, "accounts") == 0) { 2932 if (strcmp(p, "accounts") == 0) {
2928 /* delete account from list */ 2933 /* delete account from list */
2929 p = xs_list_get(l, -2); 2934 p = xs_list_get(l, -2);
2930 xs_list *accts = xs_dict_get(args, "account_ids[]"); 2935 const xs_list *accts = xs_dict_get(args, "account_ids[]");
2931 int c = 0; 2936 int c = 0;
2932 char *v; 2937 char *v;
2933 2938
@@ -2971,7 +2976,7 @@ int mastoapi_put_handler(const xs_dict *req, const char *q_path,
2971 2976
2972 int status = 404; 2977 int status = 404;
2973 xs *args = NULL; 2978 xs *args = NULL;
2974 char *i_ctype = xs_dict_get(req, "content-type"); 2979 const char *i_ctype = xs_dict_get(req, "content-type");
2975 2980
2976 if (i_ctype && xs_startswith(i_ctype, "application/json")) { 2981 if (i_ctype && xs_startswith(i_ctype, "application/json")) {
2977 if (!xs_is_null(payload)) 2982 if (!xs_is_null(payload))
diff --git a/snac.h b/snac.h
index b49fbe7..79e144a 100644
--- a/snac.h
+++ b/snac.h
@@ -69,7 +69,7 @@ void snac_log(snac *user, xs_str *str);
69#define snac_debug(user, level, str) do { if (dbglevel >= (level)) \ 69#define snac_debug(user, level, str) do { if (dbglevel >= (level)) \
70 { snac_log((user), (str)); } } while (0) 70 { snac_log((user), (str)); } } while (0)
71 71
72int srv_open(char *basedir, int auto_upgrade); 72int srv_open(const char *basedir, int auto_upgrade);
73void srv_free(void); 73void srv_free(void);
74 74
75int user_open(snac *snac, const char *uid); 75int user_open(snac *snac, const char *uid);
@@ -88,7 +88,7 @@ void srv_archive(const char *direction, const char *url, xs_dict *req,
88 const char *body, int b_size); 88 const char *body, int b_size);
89void srv_archive_error(const char *prefix, const xs_str *err, 89void srv_archive_error(const char *prefix, const xs_str *err,
90 const xs_dict *req, const xs_val *data); 90 const xs_dict *req, const xs_val *data);
91void srv_archive_qitem(char *prefix, xs_dict *q_item); 91void srv_archive_qitem(const char *prefix, xs_dict *q_item);
92 92
93double mtime_nl(const char *fn, int *n_link); 93double mtime_nl(const char *fn, int *n_link);
94#define mtime(fn) mtime_nl(fn, NULL) 94#define mtime(fn) mtime_nl(fn, NULL)
@@ -139,13 +139,13 @@ double timeline_mtime(snac *snac);
139int timeline_touch(snac *snac); 139int timeline_touch(snac *snac);
140int timeline_here(snac *snac, const char *md5); 140int timeline_here(snac *snac, const char *md5);
141int timeline_get_by_md5(snac *snac, const char *md5, xs_dict **msg); 141int timeline_get_by_md5(snac *snac, const char *md5, xs_dict **msg);
142int timeline_del(snac *snac, char *id); 142int timeline_del(snac *snac, const char *id);
143xs_list *timeline_simple_list(snac *snac, const char *idx_name, int skip, int show); 143xs_list *timeline_simple_list(snac *snac, const char *idx_name, int skip, int show);
144xs_list *timeline_list(snac *snac, const char *idx_name, int skip, int show); 144xs_list *timeline_list(snac *snac, const char *idx_name, int skip, int show);
145int timeline_add(snac *snac, const char *id, const xs_dict *o_msg); 145int timeline_add(snac *snac, const char *id, const xs_dict *o_msg);
146int timeline_admire(snac *snac, const char *id, const char *admirer, int like); 146int timeline_admire(snac *snac, const char *id, const char *admirer, int like);
147 147
148xs_list *timeline_top_level(snac *snac, xs_list *list); 148xs_list *timeline_top_level(snac *snac, const xs_list *list);
149xs_list *local_list(snac *snac, int max); 149xs_list *local_list(snac *snac, int max);
150xs_list *timeline_instance_list(int skip, int show); 150xs_list *timeline_instance_list(int skip, int show);
151 151
@@ -174,14 +174,14 @@ void hide(snac *snac, const char *id);
174int is_hidden(snac *snac, const char *id); 174int is_hidden(snac *snac, const char *id);
175 175
176void tag_index(const char *id, const xs_dict *obj); 176void tag_index(const char *id, const xs_dict *obj);
177xs_list *tag_search(char *tag, int skip, int show); 177xs_list *tag_search(const char *tag, int skip, int show);
178 178
179xs_val *list_maint(snac *user, const char *list, int op); 179xs_val *list_maint(snac *user, const char *list, int op);
180xs_list *list_timeline(snac *user, const char *list, int skip, int show); 180xs_list *list_timeline(snac *user, const char *list, int skip, int show);
181xs_val *list_content(snac *user, const char *list_id, const char *actor_md5, int op); 181xs_val *list_content(snac *user, const char *list_id, const char *actor_md5, int op);
182void list_distribute(snac *user, const char *who, const xs_dict *post); 182void list_distribute(snac *user, const char *who, const xs_dict *post);
183 183
184int actor_add(const char *actor, xs_dict *msg); 184int actor_add(const char *actor, const xs_dict *msg);
185int actor_get(const char *actor, xs_dict **data); 185int actor_get(const char *actor, xs_dict **data);
186int actor_get_refresh(snac *user, const char *actor, xs_dict **data); 186int actor_get_refresh(snac *user, const char *actor, xs_dict **data);
187 187
@@ -223,10 +223,13 @@ xs_list *content_search(snac *user, const char *regex,
223void enqueue_input(snac *snac, const xs_dict *msg, const xs_dict *req, int retries); 223void enqueue_input(snac *snac, const xs_dict *msg, const xs_dict *req, int retries);
224void enqueue_shared_input(const xs_dict *msg, const xs_dict *req, int retries); 224void enqueue_shared_input(const xs_dict *msg, const xs_dict *req, int retries);
225void enqueue_output_raw(const char *keyid, const char *seckey, 225void enqueue_output_raw(const char *keyid, const char *seckey,
226 xs_dict *msg, xs_str *inbox, int retries, int p_status); 226 const xs_dict *msg, const xs_str *inbox,
227void enqueue_output(snac *snac, xs_dict *msg, xs_str *inbox, int retries, int p_status); 227 int retries, int p_status);
228void enqueue_output_by_actor(snac *snac, xs_dict *msg, const xs_str *actor, int retries); 228void enqueue_output(snac *snac, const xs_dict *msg,
229void enqueue_email(xs_str *msg, int retries); 229 const xs_str *inbox, int retries, int p_status);
230void enqueue_output_by_actor(snac *snac, const xs_dict *msg,
231 const xs_str *actor, int retries);
232void enqueue_email(const xs_str *msg, int retries);
230void enqueue_telegram(const xs_str *msg, const char *bot, const char *chat_id); 233void enqueue_telegram(const xs_str *msg, const char *bot, const char *chat_id);
231void enqueue_ntfy(const xs_str *msg, const char *ntfy_server, const char *ntfy_token); 234void enqueue_ntfy(const xs_str *msg, const char *ntfy_server, const char *ntfy_token);
232void enqueue_message(snac *snac, const xs_dict *msg); 235void enqueue_message(snac *snac, const xs_dict *msg);
@@ -234,7 +237,6 @@ void enqueue_close_question(snac *user, const char *id, int end_secs);
234void enqueue_object_request(snac *user, const char *id, int forward_secs); 237void enqueue_object_request(snac *user, const char *id, int forward_secs);
235void enqueue_verify_links(snac *user); 238void enqueue_verify_links(snac *user);
236void enqueue_actor_refresh(snac *user, const char *actor, int forward_secs); 239void enqueue_actor_refresh(snac *user, const char *actor, int forward_secs);
237void enqueue_request_replies(snac *user, const char *id);
238int was_question_voted(snac *user, const char *id); 240int was_question_voted(snac *user, const char *id);
239 241
240xs_list *user_queue(snac *snac); 242xs_list *user_queue(snac *snac);
@@ -247,16 +249,16 @@ void purge_all(void);
247 249
248xs_dict *http_signed_request_raw(const char *keyid, const char *seckey, 250xs_dict *http_signed_request_raw(const char *keyid, const char *seckey,
249 const char *method, const char *url, 251 const char *method, const char *url,
250 xs_dict *headers, 252 const xs_dict *headers,
251 const char *body, int b_size, 253 const char *body, int b_size,
252 int *status, xs_str **payload, int *p_size, 254 int *status, xs_str **payload, int *p_size,
253 int timeout); 255 int timeout);
254xs_dict *http_signed_request(snac *snac, const char *method, const char *url, 256xs_dict *http_signed_request(snac *snac, const char *method, const char *url,
255 xs_dict *headers, 257 const xs_dict *headers,
256 const char *body, int b_size, 258 const char *body, int b_size,
257 int *status, xs_str **payload, int *p_size, 259 int *status, xs_str **payload, int *p_size,
258 int timeout); 260 int timeout);
259int check_signature(xs_dict *req, xs_str **err); 261int check_signature(const xs_dict *req, xs_str **err);
260 262
261srv_state *srv_state_op(xs_str **fname, int op); 263srv_state *srv_state_op(xs_str **fname, int op);
262void httpd(void); 264void httpd(void);
@@ -270,21 +272,21 @@ const char *default_avatar_base64(void);
270 272
271xs_str *process_tags(snac *snac, const char *content, xs_list **tag); 273xs_str *process_tags(snac *snac, const char *content, xs_list **tag);
272 274
273char *get_atto(const xs_dict *msg); 275const char *get_atto(const xs_dict *msg);
274xs_list *get_attachments(const xs_dict *msg); 276xs_list *get_attachments(const xs_dict *msg);
275 277
276xs_dict *msg_admiration(snac *snac, char *object, char *type); 278xs_dict *msg_admiration(snac *snac, const char *object, const char *type);
277xs_dict *msg_repulsion(snac *user, char *id, char *type); 279xs_dict *msg_repulsion(snac *user, const char *id, const char *type);
278xs_dict *msg_create(snac *snac, const xs_dict *object); 280xs_dict *msg_create(snac *snac, const xs_dict *object);
279xs_dict *msg_follow(snac *snac, const char *actor); 281xs_dict *msg_follow(snac *snac, const char *actor);
280 282
281xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts, 283xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts,
282 xs_str *in_reply_to, xs_list *attach, int priv); 284 const xs_str *in_reply_to, const xs_list *attach, int priv);
283 285
284xs_dict *msg_undo(snac *snac, char *object); 286xs_dict *msg_undo(snac *snac, const xs_val *object);
285xs_dict *msg_delete(snac *snac, char *id); 287xs_dict *msg_delete(snac *snac, const char *id);
286xs_dict *msg_actor(snac *snac); 288xs_dict *msg_actor(snac *snac);
287xs_dict *msg_update(snac *snac, xs_dict *object); 289xs_dict *msg_update(snac *snac, const xs_dict *object);
288xs_dict *msg_ping(snac *user, const char *rcpt); 290xs_dict *msg_ping(snac *user, const char *rcpt);
289xs_dict *msg_pong(snac *user, const char *rcpt, const char *object); 291xs_dict *msg_pong(snac *user, const char *rcpt, const char *object);
290xs_dict *msg_question(snac *user, const char *content, xs_list *attach, 292xs_dict *msg_question(snac *user, const char *content, xs_list *attach,
@@ -292,7 +294,6 @@ xs_dict *msg_question(snac *user, const char *content, xs_list *attach,
292 294
293int activitypub_request(snac *snac, const char *url, xs_dict **data); 295int activitypub_request(snac *snac, const char *url, xs_dict **data);
294int actor_request(snac *user, const char *actor, xs_dict **data); 296int actor_request(snac *user, const char *actor, xs_dict **data);
295void timeline_request_replies(snac *user, const char *id);
296int send_to_inbox_raw(const char *keyid, const char *seckey, 297int send_to_inbox_raw(const char *keyid, const char *seckey,
297 const xs_str *inbox, const xs_dict *msg, 298 const xs_str *inbox, const xs_dict *msg,
298 xs_val **payload, int *p_size, int timeout); 299 xs_val **payload, int *p_size, int timeout);
diff --git a/upgrade.c b/upgrade.c
index 7510ac8..266a4be 100644
--- a/upgrade.c
+++ b/upgrade.c
@@ -18,7 +18,7 @@ int snac_upgrade(xs_str **error)
18 double f = 0.0; 18 double f = 0.0;
19 19
20 for (;;) { 20 for (;;) {
21 char *layout = xs_dict_get(srv_config, "layout"); 21 const char *layout = xs_dict_get(srv_config, "layout");
22 double nf; 22 double nf;
23 23
24 f = nf = xs_number_get(layout); 24 f = nf = xs_number_get(layout);
@@ -56,8 +56,8 @@ int snac_upgrade(xs_str **error)
56 56
57 g = list; 57 g = list;
58 while (xs_list_iter(&g, &fn)) { 58 while (xs_list_iter(&g, &fn)) {
59 xs *l = xs_split(fn, "/"); 59 xs *l = xs_split(fn, "/");
60 char *b = xs_list_get(l, -1); 60 const char *b = xs_list_get(l, -1);
61 xs *dir = xs_fmt("%s/object/%c%c", srv_basedir, b[0], b[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); 62 xs *nfn = xs_fmt("%s/%s", dir, b);
63 63
@@ -152,12 +152,12 @@ int snac_upgrade(xs_str **error)
152 xs *o = xs_json_loads(s); 152 xs *o = xs_json_loads(s);
153 fclose(f); 153 fclose(f);
154 154
155 char *type = xs_dict_get(o, "type"); 155 const char *type = xs_dict_get(o, "type");
156 156
157 if (!xs_is_null(type) && strcmp(type, "Follow") == 0) { 157 if (!xs_is_null(type) && strcmp(type, "Follow") == 0) {
158 unlink(v); 158 unlink(v);
159 159
160 char *actor = xs_dict_get(o, "actor"); 160 const char *actor = xs_dict_get(o, "actor");
161 161
162 if (!xs_is_null(actor)) 162 if (!xs_is_null(actor))
163 follower_add(&snac, actor); 163 follower_add(&snac, actor);
@@ -198,22 +198,29 @@ int snac_upgrade(xs_str **error)
198 xs *meta = xs_dup(xs_dict_get(o, "_snac")); 198 xs *meta = xs_dup(xs_dict_get(o, "_snac"));
199 o = xs_dict_del(o, "_snac"); 199 o = xs_dict_del(o, "_snac");
200 200
201 char *id = xs_dict_get(o, "id"); 201 const char *id = xs_dict_get(o, "id");
202 202
203 /* store object */ 203 /* store object */
204 object_add_ow(id, o); 204 object_add_ow(id, o);
205 205
206 /* if it's from us, add to public */ 206 /* if it's from us, add to public */
207 if (xs_startswith(id, snac.actor)) { 207 if (xs_startswith(id, snac.actor)) {
208 char *p, *v; 208 const xs_list *p;
209 char *v;
210 int c;
209 211
210 object_user_cache_add(&snac, id, "public"); 212 object_user_cache_add(&snac, id, "public");
211 213
212 p = xs_dict_get(meta, "announced_by"); 214 p = xs_dict_get(meta, "announced_by");
213 while (xs_list_iter(&p, &v)) 215
216 c = 0;
217 while (xs_list_next(p, &v, &c))
214 object_admire(id, v, 0); 218 object_admire(id, v, 0);
219
215 p = xs_dict_get(meta, "liked_by"); 220 p = xs_dict_get(meta, "liked_by");
216 while (xs_list_iter(&p, &v)) 221
222 c = 0;
223 while (xs_list_next(p, &v, &c))
217 object_admire(id, v, 1); 224 object_admire(id, v, 1);
218 } 225 }
219 226
@@ -257,21 +264,28 @@ int snac_upgrade(xs_str **error)
257 xs *meta = xs_dup(xs_dict_get(o, "_snac")); 264 xs *meta = xs_dup(xs_dict_get(o, "_snac"));
258 o = xs_dict_del(o, "_snac"); 265 o = xs_dict_del(o, "_snac");
259 266
260 char *id = xs_dict_get(o, "id"); 267 const char *id = xs_dict_get(o, "id");
261 268
262 /* store object */ 269 /* store object */
263 object_add_ow(id, o); 270 object_add_ow(id, o);
264 271
265 { 272 {
266 char *p, *v; 273 const xs_list *p;
274 char *v;
275 int c = 0;
267 276
268 object_user_cache_add(&snac, id, "private"); 277 object_user_cache_add(&snac, id, "private");
269 278
270 p = xs_dict_get(meta, "announced_by"); 279 p = xs_dict_get(meta, "announced_by");
271 while (xs_list_iter(&p, &v)) 280
281 c = 0;
282 while (xs_list_next(p, &v, &c))
272 object_admire(id, v, 0); 283 object_admire(id, v, 0);
284
273 p = xs_dict_get(meta, "liked_by"); 285 p = xs_dict_get(meta, "liked_by");
274 while (xs_list_iter(&p, &v)) 286
287 c = 0;
288 while (xs_list_next(p, &v, &c))
275 object_admire(id, v, 1); 289 object_admire(id, v, 1);
276 } 290 }
277 291
diff --git a/utils.c b/utils.c
index 0523cac..daaa583 100644
--- a/utils.c
+++ b/utils.c
@@ -418,7 +418,7 @@ int deluser(snac *user)
418void verify_links(snac *user) 418void verify_links(snac *user)
419/* verifies a user's links */ 419/* verifies a user's links */
420{ 420{
421 xs_dict *p = xs_dict_get(user->config, "metadata"); 421 const xs_dict *p = xs_dict_get(user->config, "metadata");
422 char *k, *v; 422 char *k, *v;
423 int changed = 0; 423 int changed = 0;
424 424
diff --git a/webfinger.c b/webfinger.c
index 7255ae2..a12134d 100644
--- a/webfinger.c
+++ b/webfinger.c
@@ -16,7 +16,7 @@ int webfinger_request_signed(snac *snac, const char *qs, char **actor, char **us
16 int p_size = 0; 16 int p_size = 0;
17 xs *headers = xs_dict_new(); 17 xs *headers = xs_dict_new();
18 xs *l = NULL; 18 xs *l = NULL;
19 xs_str *host = NULL; 19 const char *host = NULL;
20 xs *resource = NULL; 20 xs *resource = NULL;
21 21
22 if (xs_startswith(qs, "https:/") || xs_startswith(qs, "http:/")) { 22 if (xs_startswith(qs, "https:/") || xs_startswith(qs, "http:/")) {
@@ -87,19 +87,20 @@ int webfinger_request_signed(snac *snac, const char *qs, char **actor, char **us
87 87
88 if (obj) { 88 if (obj) {
89 if (user != NULL) { 89 if (user != NULL) {
90 char *subject = xs_dict_get(obj, "subject"); 90 const char *subject = xs_dict_get(obj, "subject");
91 91
92 if (subject) 92 if (subject)
93 *user = xs_replace_n(subject, "acct:", "", 1); 93 *user = xs_replace_n(subject, "acct:", "", 1);
94 } 94 }
95 95
96 if (actor != NULL) { 96 if (actor != NULL) {
97 char *list = xs_dict_get(obj, "links"); 97 const xs_list *list = xs_dict_get(obj, "links");
98 int c = 0;
98 char *v; 99 char *v;
99 100
100 while (xs_list_iter(&list, &v)) { 101 while (xs_list_next(list, &v, &c)) {
101 if (xs_type(v) == XSTYPE_DICT) { 102 if (xs_type(v) == XSTYPE_DICT) {
102 char *type = xs_dict_get(v, "type"); 103 const char *type = xs_dict_get(v, "type");
103 104
104 if (type && (strcmp(type, "application/activity+json") == 0 || 105 if (type && (strcmp(type, "application/activity+json") == 0 ||
105 strcmp(type, "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") == 0)) { 106 strcmp(type, "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") == 0)) {
@@ -133,8 +134,8 @@ int webfinger_get_handler(xs_dict *req, char *q_path,
133 if (strcmp(q_path, "/.well-known/webfinger") != 0) 134 if (strcmp(q_path, "/.well-known/webfinger") != 0)
134 return 0; 135 return 0;
135 136
136 char *q_vars = xs_dict_get(req, "q_vars"); 137 const char *q_vars = xs_dict_get(req, "q_vars");
137 char *resource = xs_dict_get(q_vars, "resource"); 138 const char *resource = xs_dict_get(q_vars, "resource");
138 139
139 if (resource == NULL) 140 if (resource == NULL)
140 return 400; 141 return 400;
@@ -145,7 +146,7 @@ int webfinger_get_handler(xs_dict *req, char *q_path,
145 if (xs_startswith(resource, "https:/") || xs_startswith(resource, "http:/")) { 146 if (xs_startswith(resource, "https:/") || xs_startswith(resource, "http:/")) {
146 /* actor search: find a user with this actor */ 147 /* actor search: find a user with this actor */
147 xs *l = xs_split(resource, "/"); 148 xs *l = xs_split(resource, "/");
148 char *uid = xs_list_get(l, -1); 149 const char *uid = xs_list_get(l, -1);
149 150
150 if (uid) 151 if (uid)
151 found = user_open(&snac, uid); 152 found = user_open(&snac, uid);
@@ -163,8 +164,8 @@ int webfinger_get_handler(xs_dict *req, char *q_path,
163 l = xs_split_n(an, "@", 1); 164 l = xs_split_n(an, "@", 1);
164 165
165 if (xs_list_len(l) == 2) { 166 if (xs_list_len(l) == 2) {
166 char *uid = xs_list_get(l, 0); 167 const char *uid = xs_list_get(l, 0);
167 char *host = xs_list_get(l, 1); 168 const char *host = xs_list_get(l, 1);
168 169
169 if (strcmp(host, xs_dict_get(srv_config, "host")) == 0) 170 if (strcmp(host, xs_dict_get(srv_config, "host")) == 0)
170 found = user_open(&snac, uid); 171 found = user_open(&snac, uid);
@@ -194,7 +195,7 @@ int webfinger_get_handler(xs_dict *req, char *q_path,
194 195
195 links = xs_list_append(links, prof); 196 links = xs_list_append(links, prof);
196 197
197 char *avatar = xs_dict_get(snac.config, "avatar"); 198 const char *avatar = xs_dict_get(snac.config, "avatar");
198 if (!xs_is_null(avatar) && *avatar) { 199 if (!xs_is_null(avatar) && *avatar) {
199 xs *d = xs_dict_new(); 200 xs *d = xs_dict_new();
200 201
diff --git a/xs.h b/xs.h
index f5c87ef..b46f0e1 100644
--- a/xs.h
+++ b/xs.h
@@ -21,8 +21,8 @@ typedef enum {
21 XSTYPE_FALSE = 0x15, /* Boolean */ 21 XSTYPE_FALSE = 0x15, /* Boolean */
22 XSTYPE_LIST = 0x1d, /* Sequence of LITEMs up to EOM (with size) */ 22 XSTYPE_LIST = 0x1d, /* Sequence of LITEMs up to EOM (with size) */
23 XSTYPE_LITEM = 0x1f, /* Element of a list (any type) */ 23 XSTYPE_LITEM = 0x1f, /* Element of a list (any type) */
24 XSTYPE_DICT = 0x1c, /* Sequence of DITEMs up to EOM (with size) */ 24 XSTYPE_DICT = 0x1c, /* Sequence of KEYVALs up to EOM (with size) */
25 XSTYPE_DITEM = 0x1e, /* Element of a dict (STRING key + any type) */ 25 XSTYPE_KEYVAL = 0x1e, /* key + value (STRING key + any type) */
26 XSTYPE_EOM = 0x19, /* End of Multiple (LIST or DICT) */ 26 XSTYPE_EOM = 0x19, /* End of Multiple (LIST or DICT) */
27 XSTYPE_DATA = 0x10 /* A block of anonymous data */ 27 XSTYPE_DATA = 0x10 /* A block of anonymous data */
28} xstype; 28} xstype;
@@ -32,6 +32,7 @@ typedef enum {
32typedef char xs_val; 32typedef char xs_val;
33typedef char xs_str; 33typedef char xs_str;
34typedef char xs_list; 34typedef char xs_list;
35typedef char xs_keyval;
35typedef char xs_dict; 36typedef char xs_dict;
36typedef char xs_number; 37typedef char xs_number;
37typedef char xs_data; 38typedef char xs_data;
@@ -96,7 +97,7 @@ xs_list *_xs_list_append(xs_list *list, const xs_val *vals[]);
96int xs_list_iter(xs_list **list, xs_val **value); 97int xs_list_iter(xs_list **list, xs_val **value);
97int xs_list_next(const xs_list *list, xs_val **value, int *ctxt); 98int xs_list_next(const xs_list *list, xs_val **value, int *ctxt);
98int xs_list_len(const xs_list *list); 99int xs_list_len(const xs_list *list);
99xs_val *xs_list_get(const xs_list *list, int num); 100const xs_val *xs_list_get(const xs_list *list, int num);
100xs_list *xs_list_del(xs_list *list, int num); 101xs_list *xs_list_del(xs_list *list, int num);
101xs_list *xs_list_insert(xs_list *list, int num, const xs_val *data); 102xs_list *xs_list_insert(xs_list *list, int num, const xs_val *data);
102xs_list *xs_list_set(xs_list *list, int num, const xs_val *data); 103xs_list *xs_list_set(xs_list *list, int num, const xs_val *data);
@@ -109,14 +110,20 @@ xs_list *xs_split_n(const char *str, const char *sep, int times);
109#define xs_split(str, sep) xs_split_n(str, sep, XS_ALL) 110#define xs_split(str, sep) xs_split_n(str, sep, XS_ALL)
110xs_list *xs_list_cat(xs_list *l1, const xs_list *l2); 111xs_list *xs_list_cat(xs_list *l1, const xs_list *l2);
111 112
113int xs_keyval_size(const xs_str *key, const xs_val *value);
114xs_str *xs_keyval_key(const xs_keyval *keyval);
115xs_val *xs_keyval_value(const xs_keyval *keyval);
116xs_keyval *xs_keyval_make(xs_keyval *keyval, const xs_str *key, const xs_val *value);
117
112xs_dict *xs_dict_new(void); 118xs_dict *xs_dict_new(void);
113xs_dict *xs_dict_append(xs_dict *dict, const xs_str *key, const xs_val *value); 119xs_dict *xs_dict_append(xs_dict *dict, const xs_str *key, const xs_val *value);
114xs_dict *xs_dict_prepend(xs_dict *dict, const xs_str *key, const xs_val *value); 120xs_dict *xs_dict_prepend(xs_dict *dict, const xs_str *key, const xs_val *value);
115int xs_dict_next(const xs_dict *dict, xs_str **key, xs_val **value, int *ctxt); 121int xs_dict_next(const xs_dict *dict, xs_str **key, xs_val **value, int *ctxt);
116xs_val *xs_dict_get_def(const xs_dict *dict, const xs_str *key, const xs_val *def); 122const xs_val *xs_dict_get_def(const xs_dict *dict, const xs_str *key, const xs_val *def);
117#define xs_dict_get(dict, key) xs_dict_get_def(dict, key, NULL) 123#define xs_dict_get(dict, key) xs_dict_get_def(dict, key, NULL)
118xs_dict *xs_dict_del(xs_dict *dict, const xs_str *key); 124xs_dict *xs_dict_del(xs_dict *dict, const xs_str *key);
119xs_dict *xs_dict_set(xs_dict *dict, const xs_str *key, const xs_val *data); 125xs_dict *xs_dict_set(xs_dict *dict, const xs_str *key, const xs_val *data);
126xs_dict *xs_dict_gc(xs_dict *dict);
120 127
121xs_val *xs_val_new(xstype t); 128xs_val *xs_val_new(xstype t);
122xs_number *xs_number_new(double f); 129xs_number *xs_number_new(double f);
@@ -244,7 +251,7 @@ xstype xs_type(const xs_val *data)
244 case XSTYPE_LIST: 251 case XSTYPE_LIST:
245 case XSTYPE_LITEM: 252 case XSTYPE_LITEM:
246 case XSTYPE_DICT: 253 case XSTYPE_DICT:
247 case XSTYPE_DITEM: 254 case XSTYPE_KEYVAL:
248 case XSTYPE_NUMBER: 255 case XSTYPE_NUMBER:
249 case XSTYPE_EOM: 256 case XSTYPE_EOM:
250 case XSTYPE_DATA: 257 case XSTYPE_DATA:
@@ -262,7 +269,7 @@ xstype xs_type(const xs_val *data)
262void _xs_put_size(xs_val *ptr, int i) 269void _xs_put_size(xs_val *ptr, int i)
263/* must match _XS_TYPE_SIZE */ 270/* must match _XS_TYPE_SIZE */
264{ 271{
265 memcpy(ptr, &i, sizeof(i)); 272 memcpy(ptr + 1, &i, sizeof(i));
266} 273}
267 274
268 275
@@ -296,7 +303,7 @@ int xs_size(const xs_val *data)
296 303
297 break; 304 break;
298 305
299 case XSTYPE_DITEM: 306 case XSTYPE_KEYVAL:
300 /* calculate the size of the key and the value */ 307 /* calculate the size of the key and the value */
301 p = data + 1; 308 p = data + 1;
302 p += xs_size(p); 309 p += xs_size(p);
@@ -380,7 +387,7 @@ xs_val *xs_expand(xs_val *data, int offset, int size)
380 if (xs_type(data) == XSTYPE_LIST || 387 if (xs_type(data) == XSTYPE_LIST ||
381 xs_type(data) == XSTYPE_DICT || 388 xs_type(data) == XSTYPE_DICT ||
382 xs_type(data) == XSTYPE_DATA) 389 xs_type(data) == XSTYPE_DATA)
383 _xs_put_size(data + 1, sz); 390 _xs_put_size(data, sz);
384 391
385 return data; 392 return data;
386} 393}
@@ -405,7 +412,7 @@ xs_val *xs_collapse(xs_val *data, int offset, int size)
405 if (xs_type(data) == XSTYPE_LIST || 412 if (xs_type(data) == XSTYPE_LIST ||
406 xs_type(data) == XSTYPE_DICT || 413 xs_type(data) == XSTYPE_DICT ||
407 xs_type(data) == XSTYPE_DATA) 414 xs_type(data) == XSTYPE_DATA)
408 _xs_put_size(data + 1, sz); 415 _xs_put_size(data, sz);
409 416
410 return xs_realloc(data, _xs_blk_size(sz)); 417 return xs_realloc(data, _xs_blk_size(sz));
411} 418}
@@ -666,10 +673,10 @@ xs_list *xs_list_new(void)
666{ 673{
667 int sz = 1 + _XS_TYPE_SIZE + 1; 674 int sz = 1 + _XS_TYPE_SIZE + 1;
668 xs_list *l = xs_realloc(NULL, sz); 675 xs_list *l = xs_realloc(NULL, sz);
669 memset(l, '\0', sz); 676 memset(l, XSTYPE_EOM, sz);
670 677
671 l[0] = XSTYPE_LIST; 678 l[0] = XSTYPE_LIST;
672 _xs_put_size(&l[1], sz); 679 _xs_put_size(l, sz);
673 680
674 return l; 681 return l;
675} 682}
@@ -802,7 +809,7 @@ int xs_list_len(const xs_list *list)
802} 809}
803 810
804 811
805xs_val *xs_list_get(const xs_list *list, int num) 812const xs_val *xs_list_get(const xs_list *list, int num)
806/* returns the element #num */ 813/* returns the element #num */
807{ 814{
808 XS_ASSERT_TYPE(list, XSTYPE_LIST); 815 XS_ASSERT_TYPE(list, XSTYPE_LIST);
@@ -830,7 +837,7 @@ xs_list *xs_list_del(xs_list *list, int num)
830{ 837{
831 XS_ASSERT_TYPE(list, XSTYPE_LIST); 838 XS_ASSERT_TYPE(list, XSTYPE_LIST);
832 839
833 xs_val *v; 840 const xs_val *v;
834 841
835 if ((v = xs_list_get(list, num)) != NULL) 842 if ((v = xs_list_get(list, num)) != NULL)
836 list = xs_collapse(list, v - 1 - list, xs_size(v - 1)); 843 list = xs_collapse(list, v - 1 - list, xs_size(v - 1));
@@ -844,7 +851,7 @@ xs_list *xs_list_insert(xs_list *list, int num, const xs_val *data)
844{ 851{
845 XS_ASSERT_TYPE(list, XSTYPE_LIST); 852 XS_ASSERT_TYPE(list, XSTYPE_LIST);
846 853
847 xs_val *v; 854 const xs_val *v;
848 int offset; 855 int offset;
849 856
850 if ((v = xs_list_get(list, num)) != NULL) 857 if ((v = xs_list_get(list, num)) != NULL)
@@ -999,6 +1006,40 @@ xs_list *xs_list_cat(xs_list *l1, const xs_list *l2)
999} 1006}
1000 1007
1001 1008
1009/** keyvals **/
1010
1011int xs_keyval_size(const xs_str *key, const xs_val *value)
1012/* returns the needed size for a keyval */
1013{
1014 return 1 + xs_size(key) + xs_size(value);
1015}
1016
1017
1018xs_str *xs_keyval_key(const xs_keyval *keyval)
1019/* returns a pointer to the key of the keyval */
1020{
1021 return (xs_str *)&keyval[1];
1022}
1023
1024
1025xs_val *xs_keyval_value(const xs_keyval *keyval)
1026/* returns a pointer to the value of the keyval */
1027{
1028 return (xs_val *)&keyval[1 + xs_size(xs_keyval_key(keyval))];
1029}
1030
1031
1032xs_keyval *xs_keyval_make(xs_keyval *keyval, const xs_str *key, const xs_val *value)
1033/* builds a keyval into mem (should have enough size) */
1034{
1035 keyval[0] = XSTYPE_KEYVAL;
1036 memcpy(xs_keyval_key(keyval), key, xs_size(key));
1037 memcpy(xs_keyval_value(keyval), value, xs_size(value));
1038
1039 return keyval;
1040}
1041
1042
1002/** dicts **/ 1043/** dicts **/
1003 1044
1004xs_dict *xs_dict_new(void) 1045xs_dict *xs_dict_new(void)
@@ -1006,34 +1047,27 @@ xs_dict *xs_dict_new(void)
1006{ 1047{
1007 int sz = 1 + _XS_TYPE_SIZE + 1; 1048 int sz = 1 + _XS_TYPE_SIZE + 1;
1008 xs_dict *d = xs_realloc(NULL, sz); 1049 xs_dict *d = xs_realloc(NULL, sz);
1009 memset(d, '\0', sz); 1050 memset(d, XSTYPE_EOM, sz);
1010 1051
1011 d[0] = XSTYPE_DICT; 1052 d[0] = XSTYPE_DICT;
1012 _xs_put_size(&d[1], sz); 1053 _xs_put_size(d, sz);
1013 1054
1014 return d; 1055 return d;
1015} 1056}
1016 1057
1017 1058
1018xs_dict *_xs_dict_write_ditem(xs_dict *dict, int offset, const xs_str *key, 1059xs_dict *_xs_dict_write_keyval(xs_dict *dict, int offset, const xs_str *key, const xs_val *value)
1019 const xs_val *data, int dsz) 1060/* adds a new keyval to the dict */
1020/* inserts a memory block into the dict */
1021{ 1061{
1022 XS_ASSERT_TYPE(dict, XSTYPE_DICT); 1062 XS_ASSERT_TYPE(dict, XSTYPE_DICT);
1023 XS_ASSERT_TYPE(key, XSTYPE_STRING); 1063 XS_ASSERT_TYPE(key, XSTYPE_STRING);
1024 1064
1025 if (data == NULL) { 1065 if (value == NULL)
1026 data = xs_stock(XSTYPE_NULL); 1066 value = xs_stock(XSTYPE_NULL);
1027 dsz = xs_size(data);
1028 }
1029
1030 int ksz = xs_size(key);
1031 1067
1032 dict = xs_expand(dict, offset, 1 + ksz + dsz); 1068 dict = xs_expand(dict, offset, xs_keyval_size(key, value));
1033 1069
1034 dict[offset] = XSTYPE_DITEM; 1070 xs_keyval_make(&dict[offset], key, value);
1035 memcpy(&dict[offset + 1], key, ksz);
1036 memcpy(&dict[offset + 1 + ksz], data, dsz);
1037 1071
1038 return dict; 1072 return dict;
1039} 1073}
@@ -1042,14 +1076,14 @@ xs_dict *_xs_dict_write_ditem(xs_dict *dict, int offset, const xs_str *key,
1042xs_dict *xs_dict_append(xs_dict *dict, const xs_str *key, const xs_val *value) 1076xs_dict *xs_dict_append(xs_dict *dict, const xs_str *key, const xs_val *value)
1043/* appends a memory block to the dict */ 1077/* appends a memory block to the dict */
1044{ 1078{
1045 return _xs_dict_write_ditem(dict, xs_size(dict) - 1, key, value, xs_size(value)); 1079 return _xs_dict_write_keyval(dict, xs_size(dict) - 1, key, value);
1046} 1080}
1047 1081
1048 1082
1049xs_dict *xs_dict_prepend(xs_dict *dict, const xs_str *key, const xs_val *value) 1083xs_dict *xs_dict_prepend(xs_dict *dict, const xs_str *key, const xs_val *value)
1050/* prepends a memory block to the dict */ 1084/* prepends a memory block to the dict */
1051{ 1085{
1052 return _xs_dict_write_ditem(dict, 1 + _XS_TYPE_SIZE, key, value, xs_size(value)); 1086 return _xs_dict_write_keyval(dict, 1 + _XS_TYPE_SIZE, key, value);
1053} 1087}
1054 1088
1055 1089
@@ -1070,7 +1104,7 @@ int xs_dict_next(const xs_dict *dict, xs_str **key, xs_val **value, int *ctxt)
1070 p += *ctxt; 1104 p += *ctxt;
1071 1105
1072 /* an element? */ 1106 /* an element? */
1073 if (xs_type(p) == XSTYPE_DITEM) { 1107 if (xs_type(p) == XSTYPE_KEYVAL) {
1074 p++; 1108 p++;
1075 1109
1076 *key = p; 1110 *key = p;
@@ -1091,7 +1125,7 @@ int xs_dict_next(const xs_dict *dict, xs_str **key, xs_val **value, int *ctxt)
1091} 1125}
1092 1126
1093 1127
1094xs_val *xs_dict_get_def(const xs_dict *dict, const xs_str *key, const xs_val *def) 1128const xs_val *xs_dict_get_def(const xs_dict *dict, const xs_str *key, const xs_val *def)
1095/* returns the value directed by key, or the default value */ 1129/* returns the value directed by key, or the default value */
1096{ 1130{
1097 XS_ASSERT_TYPE(dict, XSTYPE_DICT); 1131 XS_ASSERT_TYPE(dict, XSTYPE_DICT);
@@ -1150,6 +1184,14 @@ xs_dict *xs_dict_set(xs_dict *dict, const xs_str *key, const xs_val *data)
1150} 1184}
1151 1185
1152 1186
1187xs_dict *xs_dict_gc(xs_dict *dict)
1188/* collects garbage (leaked values) inside a dict */
1189{
1190 /* this kind of dicts does not get garbage */
1191 return dict;
1192}
1193
1194
1153/** other values **/ 1195/** other values **/
1154 1196
1155xs_val *xs_val_new(xstype t) 1197xs_val *xs_val_new(xstype t)
@@ -1235,7 +1277,7 @@ xs_data *xs_data_new(const void *data, int size)
1235 v = xs_realloc(NULL, _xs_blk_size(total_size)); 1277 v = xs_realloc(NULL, _xs_blk_size(total_size));
1236 v[0] = XSTYPE_DATA; 1278 v[0] = XSTYPE_DATA;
1237 1279
1238 _xs_put_size(v + 1, total_size); 1280 _xs_put_size(v, total_size);
1239 1281
1240 memcpy(&v[1 + _XS_TYPE_SIZE], data, size); 1282 memcpy(&v[1 + _XS_TYPE_SIZE], data, size);
1241 1283
diff --git a/xs_curl.h b/xs_curl.h
index f7783b9..2628d91 100644
--- a/xs_curl.h
+++ b/xs_curl.h
@@ -28,7 +28,7 @@ static size_t _header_callback(char *buffer, size_t size,
28 if (xs_str_in(l, ": ") != -1) { 28 if (xs_str_in(l, ": ") != -1) {
29 xs *knv = xs_split_n(l, ": ", 1); 29 xs *knv = xs_split_n(l, ": ", 1);
30 30
31 xs_tolower_i(xs_list_get(knv, 0)); 31 xs_tolower_i((xs_str *)xs_list_get(knv, 0));
32 32
33 headers = xs_dict_set(headers, xs_list_get(knv, 0), xs_list_get(knv, 1)); 33 headers = xs_dict_set(headers, xs_list_get(knv, 0), xs_list_get(knv, 1));
34 } 34 }
diff --git a/xs_html.h b/xs_html.h
index 80ae652..a95d45a 100644
--- a/xs_html.h
+++ b/xs_html.h
@@ -6,26 +6,26 @@
6 6
7typedef struct xs_html xs_html; 7typedef struct xs_html xs_html;
8 8
9xs_str *xs_html_encode(char *str); 9xs_str *xs_html_encode(const char *str);
10 10
11xs_html *xs_html_attr(char *key, char *value); 11xs_html *xs_html_attr(const char *key, const char *value);
12xs_html *xs_html_text(char *content); 12xs_html *xs_html_text(const char *content);
13xs_html *xs_html_raw(char *content); 13xs_html *xs_html_raw(const char *content);
14 14
15xs_html *_xs_html_add(xs_html *tag, xs_html *var[]); 15xs_html *_xs_html_add(xs_html *tag, xs_html *var[]);
16#define xs_html_add(tag, ...) _xs_html_add(tag, (xs_html *[]) { __VA_ARGS__, NULL }) 16#define xs_html_add(tag, ...) _xs_html_add(tag, (xs_html *[]) { __VA_ARGS__, NULL })
17 17
18xs_html *_xs_html_tag(char *tag, xs_html *var[]); 18xs_html *_xs_html_tag(const char *tag, xs_html *var[]);
19#define xs_html_tag(tag, ...) _xs_html_tag(tag, (xs_html *[]) { __VA_ARGS__, NULL }) 19#define xs_html_tag(tag, ...) _xs_html_tag(tag, (xs_html *[]) { __VA_ARGS__, NULL })
20 20
21xs_html *_xs_html_sctag(char *tag, xs_html *var[]); 21xs_html *_xs_html_sctag(const char *tag, xs_html *var[]);
22#define xs_html_sctag(tag, ...) _xs_html_sctag(tag, (xs_html *[]) { __VA_ARGS__, NULL }) 22#define xs_html_sctag(tag, ...) _xs_html_sctag(tag, (xs_html *[]) { __VA_ARGS__, NULL })
23 23
24xs_html *_xs_html_container(xs_html *var[]); 24xs_html *_xs_html_container(xs_html *var[]);
25#define xs_html_container(...) _xs_html_container((xs_html *[]) { __VA_ARGS__, NULL }) 25#define xs_html_container(...) _xs_html_container((xs_html *[]) { __VA_ARGS__, NULL })
26 26
27void xs_html_render_f(xs_html *h, FILE *f); 27void xs_html_render_f(xs_html *h, FILE *f);
28xs_str *xs_html_render_s(xs_html *tag, char *prefix); 28xs_str *xs_html_render_s(xs_html *tag, const char *prefix);
29#define xs_html_render(tag) xs_html_render_s(tag, NULL) 29#define xs_html_render(tag) xs_html_render_s(tag, NULL)
30 30
31 31
@@ -47,16 +47,16 @@ struct xs_html {
47 xs_html *next; 47 xs_html *next;
48}; 48};
49 49
50xs_str *xs_html_encode(char *str) 50xs_str *xs_html_encode(const char *str)
51/* encodes str using HTML entities */ 51/* encodes str using HTML entities */
52{ 52{
53 xs_str *s = xs_str_new(NULL); 53 xs_str *s = xs_str_new(NULL);
54 int o = 0; 54 int o = 0;
55 char *e = str + strlen(str); 55 const char *e = str + strlen(str);
56 56
57 for (;;) { 57 for (;;) {
58 char *ec = "<>\"'&"; /* characters to escape */ 58 char *ec = "<>\"'&"; /* characters to escape */
59 char *q = e; 59 const char *q = e;
60 int z; 60 int z;
61 61
62 /* find the nearest happening of a char */ 62 /* find the nearest happening of a char */
@@ -90,7 +90,7 @@ xs_str *xs_html_encode(char *str)
90 90
91#define XS_HTML_NEW() memset(xs_realloc(NULL, sizeof(xs_html)), '\0', sizeof(xs_html)) 91#define XS_HTML_NEW() memset(xs_realloc(NULL, sizeof(xs_html)), '\0', sizeof(xs_html))
92 92
93xs_html *xs_html_attr(char *key, char *value) 93xs_html *xs_html_attr(const char *key, const char *value)
94/* creates an HTML block with an attribute */ 94/* creates an HTML block with an attribute */
95{ 95{
96 xs_html *a = XS_HTML_NEW(); 96 xs_html *a = XS_HTML_NEW();
@@ -108,7 +108,7 @@ xs_html *xs_html_attr(char *key, char *value)
108} 108}
109 109
110 110
111xs_html *xs_html_text(char *content) 111xs_html *xs_html_text(const char *content)
112/* creates an HTML block of text, escaping it previously */ 112/* creates an HTML block of text, escaping it previously */
113{ 113{
114 xs_html *a = XS_HTML_NEW(); 114 xs_html *a = XS_HTML_NEW();
@@ -120,7 +120,7 @@ xs_html *xs_html_text(char *content)
120} 120}
121 121
122 122
123xs_html *xs_html_raw(char *content) 123xs_html *xs_html_raw(const char *content)
124/* creates an HTML block without escaping (for pre-formatted HTML, comments, etc) */ 124/* creates an HTML block without escaping (for pre-formatted HTML, comments, etc) */
125{ 125{
126 xs_html *a = XS_HTML_NEW(); 126 xs_html *a = XS_HTML_NEW();
@@ -152,7 +152,7 @@ xs_html *_xs_html_add(xs_html *tag, xs_html *var[])
152} 152}
153 153
154 154
155static xs_html *_xs_html_tag_t(xs_html_type type, char *tag, xs_html *var[]) 155static xs_html *_xs_html_tag_t(xs_html_type type, const char *tag, xs_html *var[])
156/* creates a tag with a variable list of attributes and subtags */ 156/* creates a tag with a variable list of attributes and subtags */
157{ 157{
158 xs_html *a = XS_HTML_NEW(); 158 xs_html *a = XS_HTML_NEW();
@@ -169,13 +169,13 @@ static xs_html *_xs_html_tag_t(xs_html_type type, char *tag, xs_html *var[])
169} 169}
170 170
171 171
172xs_html *_xs_html_tag(char *tag, xs_html *var[]) 172xs_html *_xs_html_tag(const char *tag, xs_html *var[])
173{ 173{
174 return _xs_html_tag_t(XS_HTML_TAG, tag, var); 174 return _xs_html_tag_t(XS_HTML_TAG, tag, var);
175} 175}
176 176
177 177
178xs_html *_xs_html_sctag(char *tag, xs_html *var[]) 178xs_html *_xs_html_sctag(const char *tag, xs_html *var[])
179{ 179{
180 return _xs_html_tag_t(XS_HTML_SCTAG, tag, var); 180 return _xs_html_tag_t(XS_HTML_SCTAG, tag, var);
181} 181}
@@ -239,7 +239,7 @@ void xs_html_render_f(xs_html *h, FILE *f)
239} 239}
240 240
241 241
242xs_str *xs_html_render_s(xs_html *tag, char *prefix) 242xs_str *xs_html_render_s(xs_html *tag, const char *prefix)
243/* renders to a string */ 243/* renders to a string */
244{ 244{
245 xs_str *s = NULL; 245 xs_str *s = NULL;
diff --git a/xs_httpd.h b/xs_httpd.h
index 4d006d7..60933c8 100644
--- a/xs_httpd.h
+++ b/xs_httpd.h
@@ -16,7 +16,7 @@ xs_dict *xs_httpd_request(FILE *f, xs_str **payload, int *p_size)
16 xs *q_vars = NULL; 16 xs *q_vars = NULL;
17 xs *p_vars = NULL; 17 xs *p_vars = NULL;
18 xs *l1, *l2; 18 xs *l1, *l2;
19 char *v; 19 const char *v;
20 20
21 xs_socket_timeout(fileno(f), 2.0, 0.0); 21 xs_socket_timeout(fileno(f), 2.0, 0.0);
22 22
@@ -60,7 +60,8 @@ xs_dict *xs_httpd_request(FILE *f, xs_str **payload, int *p_size)
60 p = xs_split_n(l, ": ", 1); 60 p = xs_split_n(l, ": ", 1);
61 61
62 if (xs_list_len(p) == 2) 62 if (xs_list_len(p) == 2)
63 req = xs_dict_append(req, xs_tolower_i(xs_list_get(p, 0)), xs_list_get(p, 1)); 63 req = xs_dict_append(req, xs_tolower_i(
64 (xs_str *)xs_list_get(p, 0)), xs_list_get(p, 1));
64 } 65 }
65 66
66 xs_socket_timeout(fileno(f), 5.0, 0.0); 67 xs_socket_timeout(fileno(f), 5.0, 0.0);
diff --git a/xs_json.h b/xs_json.h
index 6706d7e..3a7742d 100644
--- a/xs_json.h
+++ b/xs_json.h
@@ -71,12 +71,12 @@ static void _xs_json_indent(int level, int indent, FILE *f)
71} 71}
72 72
73 73
74static void _xs_json_dump(const xs_val *s_data, int level, int indent, FILE *f) 74static void _xs_json_dump(const xs_val *data, int level, int indent, FILE *f)
75/* dumps partial data as JSON */ 75/* dumps partial data as JSON */
76{ 76{
77 int c = 0; 77 int c = 0;
78 int ct = 0;
78 xs_val *v; 79 xs_val *v;
79 xs_val *data = (xs_val *)s_data;
80 80
81 switch (xs_type(data)) { 81 switch (xs_type(data)) {
82 case XSTYPE_NULL: 82 case XSTYPE_NULL:
@@ -98,7 +98,7 @@ static void _xs_json_dump(const xs_val *s_data, int level, int indent, FILE *f)
98 case XSTYPE_LIST: 98 case XSTYPE_LIST:
99 fputc('[', f); 99 fputc('[', f);
100 100
101 while (xs_list_iter(&data, &v)) { 101 while (xs_list_next(data, &v, &ct)) {
102 if (c != 0) 102 if (c != 0)
103 fputc(',', f); 103 fputc(',', f);
104 104
@@ -117,9 +117,8 @@ static void _xs_json_dump(const xs_val *s_data, int level, int indent, FILE *f)
117 fputc('{', f); 117 fputc('{', f);
118 118
119 xs_str *k; 119 xs_str *k;
120 int ct = 0;
121 120
122 while (xs_dict_next(s_data, &k, &v, &ct)) { 121 while (xs_dict_next(data, &k, &v, &ct)) {
123 if (c != 0) 122 if (c != 0)
124 fputc(',', f); 123 fputc(',', f);
125 124
diff --git a/xs_set.h b/xs_set.h
index 3a334e4..b7eb091 100644
--- a/xs_set.h
+++ b/xs_set.h
@@ -104,7 +104,7 @@ int xs_set_add(xs_set *s, const xs_val *data)
104 104
105 /* if it's new, add the data */ 105 /* if it's new, add the data */
106 if (ret) 106 if (ret)
107 s->list = xs_list_append_m(s->list, data, xs_size(data)); 107 s->list = xs_list_append(s->list, data);
108 108
109 return ret; 109 return ret;
110} 110}
diff --git a/xs_unicode.h b/xs_unicode.h
index 6654da4..1799d89 100644
--- a/xs_unicode.h
+++ b/xs_unicode.h
@@ -6,7 +6,7 @@
6 6
7 int _xs_utf8_enc(char buf[4], unsigned int cpoint); 7 int _xs_utf8_enc(char buf[4], unsigned int cpoint);
8 int xs_is_utf8_cont_byte(char c); 8 int xs_is_utf8_cont_byte(char c);
9 unsigned int xs_utf8_dec(char **str); 9 unsigned int xs_utf8_dec(const char **str);
10 int xs_unicode_width(unsigned int cpoint); 10 int xs_unicode_width(unsigned int cpoint);
11 int xs_is_surrogate(unsigned int cpoint); 11 int xs_is_surrogate(unsigned int cpoint);
12 unsigned int xs_surrogate_dec(unsigned int p1, unsigned int p2); 12 unsigned int xs_surrogate_dec(unsigned int p1, unsigned int p2);
@@ -66,10 +66,10 @@ int xs_is_utf8_cont_byte(char c)
66} 66}
67 67
68 68
69unsigned int xs_utf8_dec(char **str) 69unsigned int xs_utf8_dec(const char **str)
70/* decodes an utf-8 char inside str and updates the pointer */ 70/* decodes an utf-8 char inside str and updates the pointer */
71{ 71{
72 char *p = *str; 72 const char *p = *str;
73 unsigned int cpoint = 0; 73 unsigned int cpoint = 0;
74 unsigned char c = *p++; 74 unsigned char c = *p++;
75 int cb = 0; 75 int cb = 0;
diff --git a/xs_url.h b/xs_url.h
index 6c9c8b5..69313b6 100644
--- a/xs_url.h
+++ b/xs_url.h
@@ -119,8 +119,8 @@ xs_dict *xs_multipart_form_data(const char *payload, int p_size, const char *hea
119 while ((p = xs_memmem(payload + offset, p_size - offset, boundary, bsz)) != NULL) { 119 while ((p = xs_memmem(payload + offset, p_size - offset, boundary, bsz)) != NULL) {
120 xs *s1 = NULL; 120 xs *s1 = NULL;
121 xs *l1 = NULL; 121 xs *l1 = NULL;
122 char *vn = NULL; 122 const char *vn = NULL;
123 char *fn = NULL; 123 const char *fn = NULL;
124 char *q; 124 char *q;
125 int po, ps; 125 int po, ps;
126 126
diff --git a/xs_version.h b/xs_version.h
index 16faf2b..9ecf9b8 100644
--- a/xs_version.h
+++ b/xs_version.h
@@ -1 +1 @@
/* 6e75e8736f7f1b6ea6c6774d4bd922b3ad56b771 2024-05-15T11:42:19+02:00 */ /* 34850dcdec50b669a2c0bbe9f16f6d9c4b16eafd 2024-05-21T14:06:02+02:00 */