commit 4777fc8

default  ·  2024-05-21 12:12:15 +0000 UTC
parent b95fbe4
Added const everywhere.
20 files changed,  +549, -580
M data.c
M html.c
M http.c
M main.c
M snac.h
M xs.h
+106, -177
  1@@ -67,7 +67,7 @@ int activitypub_request(snac *user, const char *url, xs_dict **data)
  2     xs *response = NULL;
  3     xs *payload = NULL;
  4     int p_size;
  5-    char *ctype;
  6+    const char *ctype;
  7 
  8     *data = NULL;
  9 
 10@@ -154,20 +154,21 @@ int actor_request(snac *user, const char *actor, xs_dict **data)
 11 }
 12 
 13 
 14-char *get_atto(const xs_dict *msg)
 15+const char *get_atto(const xs_dict *msg)
 16 /* gets the attributedTo field (an actor) */
 17 {
 18-    char *actor = xs_dict_get(msg, "attributedTo");
 19+    const xs_val *actor = xs_dict_get(msg, "attributedTo");
 20 
 21     /* if the actor is a list of objects (like on Peertube videos), pick the Person */
 22     if (xs_type(actor) == XSTYPE_LIST) {
 23-        xs_list *p = actor;
 24+        const xs_list *p = actor;
 25+        int c = 0;
 26         xs_dict *v;
 27         actor = NULL;
 28 
 29-        while (actor == NULL && xs_list_iter(&p, &v)) {
 30+        while (actor == NULL && xs_list_next(p, &v, &c)) {
 31             if (xs_type(v) == XSTYPE_DICT) {
 32-                char *type = xs_dict_get(v, "type");
 33+                const char *type = xs_dict_get(v, "type");
 34                 if (xs_type(type) == XSTYPE_STRING && strcmp(type, "Person") == 0) {
 35                     actor = xs_dict_get(v, "id");
 36 
 37@@ -186,7 +187,7 @@ xs_list *get_attachments(const xs_dict *msg)
 38 /* unify the garbage fire that are the attachments */
 39 {
 40     xs_list *l = xs_list_new();
 41-    xs_list *p;
 42+    const xs_list *p;
 43 
 44     /* try first the attachments list */
 45     if (!xs_is_null(p = xs_dict_get(msg, "attachment"))) {
 46@@ -203,23 +204,24 @@ xs_list *get_attachments(const xs_dict *msg)
 47 
 48         if (xs_type(attach) == XSTYPE_LIST) {
 49             /* does the message have an image? */
 50-            if (xs_type(v = xs_dict_get(msg, "image")) == XSTYPE_DICT) {
 51+            const xs_dict *d = xs_dict_get(msg, "image");
 52+            if (xs_type(d) == XSTYPE_DICT) {
 53                 /* add it to the attachment list */
 54-                attach = xs_list_append(attach, v);
 55+                attach = xs_list_append(attach, d);
 56             }
 57         }
 58 
 59         /* now iterate the list */
 60-        p = attach;
 61-        while (xs_list_iter(&p, &v)) {
 62-            char *type = xs_dict_get(v, "mediaType");
 63+        int c = 0;
 64+        while (xs_list_next(attach, &v, &c)) {
 65+            const char *type = xs_dict_get(v, "mediaType");
 66             if (xs_is_null(type))
 67                 type = xs_dict_get(v, "type");
 68 
 69             if (xs_is_null(type))
 70                 continue;
 71 
 72-            char *href = xs_dict_get(v, "url");
 73+            const char *href = xs_dict_get(v, "url");
 74             if (xs_is_null(href))
 75                 href = xs_dict_get(v, "href");
 76             if (xs_is_null(href))
 77@@ -233,7 +235,7 @@ xs_list *get_attachments(const xs_dict *msg)
 78                     type = mt;
 79             }
 80 
 81-            char *name = xs_dict_get(v, "name");
 82+            const char *name = xs_dict_get(v, "name");
 83             if (xs_is_null(name))
 84                 name = xs_dict_get(msg, "name");
 85             if (xs_is_null(name))
 86@@ -252,29 +254,31 @@ xs_list *get_attachments(const xs_dict *msg)
 87     p = xs_dict_get(msg, "url");
 88 
 89     if (xs_type(p) == XSTYPE_LIST) {
 90-        char *href = NULL;
 91-        char *type = NULL;
 92+        const char *href = NULL;
 93+        const char *type = NULL;
 94+        int c = 0;
 95         xs_val *v;
 96 
 97-        while (href == NULL && xs_list_iter(&p, &v)) {
 98+        while (href == NULL && xs_list_next(p, &v, &c)) {
 99             if (xs_type(v) == XSTYPE_DICT) {
100-                char *mtype = xs_dict_get(v, "type");
101+                const char *mtype = xs_dict_get(v, "type");
102 
103                 if (xs_type(mtype) == XSTYPE_STRING && strcmp(mtype, "Link") == 0) {
104                     mtype = xs_dict_get(v, "mediaType");
105-                    xs_list *tag = xs_dict_get(v, "tag");
106+                    const xs_list *tag = xs_dict_get(v, "tag");
107 
108                     if (xs_type(mtype) == XSTYPE_STRING &&
109                         strcmp(mtype, "application/x-mpegURL") == 0 &&
110                         xs_type(tag) == XSTYPE_LIST) {
111                         /* now iterate the tag list, looking for a video URL */
112                         xs_dict *d;
113+                        int c = 0;
114 
115-                        while (href == NULL && xs_list_iter(&tag, &d)) {
116+                        while (href == NULL && xs_list_next(tag, &d, &c)) {
117                             if (xs_type(d) == XSTYPE_DICT) {
118                                 if (xs_type(mtype = xs_dict_get(d, "mediaType")) == XSTYPE_STRING &&
119                                     xs_startswith(mtype, "video/")) {
120-                                    char *h = xs_dict_get(d, "href");
121+                                    const char *h = xs_dict_get(d, "href");
122 
123                                     /* this is probably it */
124                                     if (xs_type(h) == XSTYPE_STRING) {
125@@ -303,7 +307,7 @@ xs_list *get_attachments(const xs_dict *msg)
126 }
127 
128 
129-int timeline_request(snac *snac, char **id, xs_str **wrk, int level)
130+int timeline_request(snac *snac, const char **id, xs_str **wrk, int level)
131 /* ensures that an entry and its ancestors are in the timeline */
132 {
133     int status = 0;
134@@ -323,7 +327,7 @@ int timeline_request(snac *snac, char **id, xs_str **wrk, int level)
135             status = activitypub_request(snac, *id, &msg);
136 
137             if (valid_status(status)) {
138-                xs_dict *object  = msg;
139+                const xs_dict *object = msg;
140                 const char *type = xs_dict_get(object, "type");
141 
142                 /* get the id again from the object, as it may be different */
143@@ -369,7 +373,7 @@ int timeline_request(snac *snac, char **id, xs_str **wrk, int level)
144                             }
145 
146                             /* does it have an ancestor? */
147-                            char *in_reply_to = xs_dict_get(object, "inReplyTo");
148+                            const char *in_reply_to = xs_dict_get(object, "inReplyTo");
149 
150                             /* store */
151                             timeline_add(snac, nid, object);
152@@ -381,83 +385,12 @@ int timeline_request(snac *snac, char **id, xs_str **wrk, int level)
153                 }
154             }
155         }
156-
157-        enqueue_request_replies(snac, *id);
158     }
159 
160     return status;
161 }
162 
163 
164-void timeline_request_replies(snac *user, const char *id)
165-/* requests all replies of a message */
166-/* FIXME: experimental -- needs more testing */
167-{
168-    /* FIXME: TEMPORARILY DISABLED */
169-    /* Reason: I've found that many of the posts in the 'replies' Collection
170-       do not have an inReplyTo field (why??? aren't they 'replies'???).
171-       For this reason, these requested objects are not stored as children
172-       of the original post and they are shown as out-of-context, top level posts.
173-       This process is disabled until I find an elegant way of providing a parent
174-       for these 'stray' children. */
175-    return;
176-
177-    xs *msg = NULL;
178-
179-    if (!valid_status(object_get(id, &msg)))
180-        return;
181-
182-    /* does it have a replies collection? */
183-    const xs_dict *replies = xs_dict_get(msg, "replies");
184-
185-    if (!xs_is_null(replies)) {
186-        const char *type  = xs_dict_get(replies, "type");
187-        const char *first = xs_dict_get(replies, "first");
188-
189-        if (!xs_is_null(type) && !xs_is_null(first) && strcmp(type, "Collection") == 0) {
190-            const char *next = xs_dict_get(first, "next");
191-
192-            if (!xs_is_null(next)) {
193-                xs *rpls = NULL;
194-                int status = activitypub_request(user, next, &rpls);
195-
196-                /* request the Collection of replies */
197-                if (valid_status(status)) {
198-                    xs_list *items = xs_dict_get(rpls, "items");
199-
200-                    if (xs_type(items) == XSTYPE_LIST) {
201-                        xs_val *v;
202-
203-                        /* request them all */
204-                        while (xs_list_iter(&items, &v)) {
205-                            if (xs_type(v) == XSTYPE_DICT) {
206-                                /* not an id, but the object itself (!) */
207-                                const char *c_id = xs_dict_get(v, "id");
208-
209-                                if (!xs_is_null(id)) {
210-                                    snac_debug(user, 0, xs_fmt("embedded reply %s", c_id));
211-
212-                                    object_add(c_id, v);
213-
214-                                    /* get its own children */
215-                                    timeline_request_replies(user, v);
216-                                }
217-                            }
218-                            else {
219-                                snac_debug(user, 0, xs_fmt("request reply %s", v));
220-                                timeline_request(user, &v, NULL, 0);
221-                            }
222-                        }
223-                    }
224-                }
225-                else
226-                    snac_debug(user, 0, xs_fmt("replies request error %s %d", next, status));
227-            }
228-        }
229-    }
230-}
231-
232-
233 int send_to_inbox_raw(const char *keyid, const char *seckey,
234                   const xs_str *inbox, const xs_dict *msg,
235                   xs_val **payload, int *p_size, int timeout)
236@@ -480,7 +413,7 @@ int send_to_inbox(snac *snac, const xs_str *inbox, const xs_dict *msg,
237                   xs_val **payload, int *p_size, int timeout)
238 /* sends a message to an Inbox */
239 {
240-    char *seckey = xs_dict_get(snac->key, "secret");
241+    const char *seckey = xs_dict_get(snac->key, "secret");
242 
243     return send_to_inbox_raw(snac->actor, seckey, inbox, msg, payload, p_size, timeout);
244 }
245@@ -490,7 +423,7 @@ xs_str *get_actor_inbox(const char *actor)
246 /* gets an actor's inbox */
247 {
248     xs *data = NULL;
249-    char *v = NULL;
250+    const char *v = NULL;
251 
252     if (valid_status(actor_request(NULL, actor, &data))) {
253         /* try first endpoints/sharedInbox */
254@@ -539,16 +472,16 @@ void post_message(snac *snac, const char *actor, const xs_dict *msg)
255 xs_list *recipient_list(snac *snac, const xs_dict *msg, int expand_public)
256 /* returns the list of recipients for a message */
257 {
258-    char *to = xs_dict_get(msg, "to");
259-    char *cc = xs_dict_get(msg, "cc");
260+    const xs_val *to = xs_dict_get(msg, "to");
261+    const xs_val *cc = xs_dict_get(msg, "cc");
262     xs_set rcpts;
263     int n;
264 
265     xs_set_init(&rcpts);
266 
267-    char *lists[] = { to, cc, NULL };
268+    const xs_list *lists[] = { to, cc, NULL };
269     for (n = 0; lists[n]; n++) {
270-        char *l = lists[n];
271+        xs_list *l = (xs_list *)lists[n];
272         char *v;
273         xs *tl = NULL;
274 
275@@ -671,13 +604,13 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg)
276 
277     /* if it's a Follow, it must be explicitly for us */
278     if (xs_match(type, "Follow")) {
279-        char *object = xs_dict_get(c_msg, "object");
280+        const char *object = xs_dict_get(c_msg, "object");
281         return !xs_is_null(object) && strcmp(snac->actor, object) == 0;
282     }
283 
284     /* only accept Ping directed to us */
285     if (xs_match(type, "Ping")) {
286-        char *dest = xs_dict_get(c_msg, "to");
287+        const char *dest = xs_dict_get(c_msg, "to");
288         return !xs_is_null(dest) && strcmp(snac->actor, dest) == 0;
289     }
290 
291@@ -692,7 +625,7 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg)
292     if (pub_msg && following_check(snac, actor))
293         return 1;
294 
295-    xs_dict *msg = xs_dict_get(c_msg, "object");
296+    const xs_dict *msg = xs_dict_get(c_msg, "object");
297     xs *rcpts = recipient_list(snac, msg, 0);
298     xs_list *p = rcpts;
299     xs_str *v;
300@@ -704,8 +637,9 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg)
301         xs *actor_obj = NULL;
302 
303         if (valid_status(object_get(actor, &actor_obj))) {
304-            if ((v = xs_dict_get(actor_obj, "followers")))
305-                actor_followers = xs_dup(v);
306+            const xs_val *fw = xs_dict_get(actor_obj, "followers");
307+            if (fw)
308+                actor_followers = xs_dup(fw);
309         }
310     }
311 
312@@ -728,13 +662,13 @@ int is_msg_for_me(snac *snac, const xs_dict *c_msg)
313     }
314 
315     /* accept if it's by someone we follow */
316-    char *atto = get_atto(msg);
317+    const char *atto = get_atto(msg);
318 
319     if (pub_msg && !xs_is_null(atto) && following_check(snac, atto))
320         return 3;
321 
322     /* is this message a reply to another? */
323-    char *irt = xs_dict_get(msg, "inReplyTo");
324+    const char *irt = xs_dict_get(msg, "inReplyTo");
325     if (!xs_is_null(irt)) {
326         xs *r_msg = NULL;
327 
328@@ -987,8 +921,8 @@ void notify(snac *snac, const char *type, const char *utype, const char *actor,
329 
330     /* telegram */
331 
332-    char *bot     = xs_dict_get(snac->config, "telegram_bot");
333-    char *chat_id = xs_dict_get(snac->config, "telegram_chat_id");
334+    const char *bot     = xs_dict_get(snac->config, "telegram_bot");
335+    const char *chat_id = xs_dict_get(snac->config, "telegram_chat_id");
336 
337     if (!xs_is_null(bot) && !xs_is_null(chat_id) && *bot && *chat_id)
338         enqueue_telegram(body, bot, chat_id);
339@@ -1001,8 +935,8 @@ void notify(snac *snac, const char *type, const char *utype, const char *actor,
340         objid = actor;
341 
342     /* ntfy */
343-    char *ntfy_server = xs_dict_get(snac->config, "ntfy_server");
344-    char *ntfy_token  = xs_dict_get(snac->config, "ntfy_token");
345+    const char *ntfy_server = xs_dict_get(snac->config, "ntfy_server");
346+    const char *ntfy_token  = xs_dict_get(snac->config, "ntfy_token");
347 
348     if (!xs_is_null(ntfy_server) && *ntfy_server)
349         enqueue_ntfy(body, ntfy_server, ntfy_token);
350@@ -1088,7 +1022,7 @@ xs_dict *msg_base(snac *snac, const char *type, const char *id,
351 }
352 
353 
354-xs_dict *msg_collection(snac *snac, char *id)
355+xs_dict *msg_collection(snac *snac, const char *id)
356 /* creates an empty OrderedCollection message */
357 {
358     xs_dict *msg = msg_base(snac, "OrderedCollection", id, NULL, NULL, NULL);
359@@ -1102,7 +1036,7 @@ xs_dict *msg_collection(snac *snac, char *id)
360 }
361 
362 
363-xs_dict *msg_accept(snac *snac, char *object, char *to)
364+xs_dict *msg_accept(snac *snac, const xs_val *object, const char *to)
365 /* creates an Accept message (as a response to a Follow) */
366 {
367     xs_dict *msg = msg_base(snac, "Accept", "@dummy", snac->actor, NULL, object);
368@@ -1113,12 +1047,12 @@ xs_dict *msg_accept(snac *snac, char *object, char *to)
369 }
370 
371 
372-xs_dict *msg_update(snac *snac, xs_dict *object)
373+xs_dict *msg_update(snac *snac, const xs_dict *object)
374 /* creates an Update message */
375 {
376     xs_dict *msg = msg_base(snac, "Update", "@object", snac->actor, "@now", object);
377 
378-    char *type = xs_dict_get(object, "type");
379+    const char *type = xs_dict_get(object, "type");
380 
381     if (strcmp(type, "Note") == 0) {
382         msg = xs_dict_append(msg, "to", xs_dict_get(object, "to"));
383@@ -1141,7 +1075,7 @@ xs_dict *msg_update(snac *snac, xs_dict *object)
384 }
385 
386 
387-xs_dict *msg_admiration(snac *snac, char *object, char *type)
388+xs_dict *msg_admiration(snac *snac, const char *object, const char *type)
389 /* creates a Like or Announce message */
390 {
391     xs *a_msg    = NULL;
392@@ -1172,7 +1106,7 @@ xs_dict *msg_admiration(snac *snac, char *object, char *type)
393 }
394 
395 
396-xs_dict *msg_repulsion(snac *user, char *id, char *type)
397+xs_dict *msg_repulsion(snac *user, const char *id, const char *type)
398 /* creates an Undo + admiration message */
399 {
400     xs *a_msg    = NULL;
401@@ -1210,7 +1144,7 @@ xs_dict *msg_actor(snac *snac)
402     xs *kid      = NULL;
403     xs *f_bio    = NULL;
404     xs_dict *msg = msg_base(snac, "Person", snac->actor, NULL, NULL, NULL);
405-    char *p;
406+    const char *p;
407     int n;
408 
409     /* change the @context (is this really necessary?) */
410@@ -1268,7 +1202,7 @@ xs_dict *msg_actor(snac *snac)
411     }
412 
413     /* add the metadata as attachments of PropertyValue */
414-    xs_dict *metadata = xs_dict_get(snac->config, "metadata");
415+    const xs_dict *metadata = xs_dict_get(snac->config, "metadata");
416     if (xs_type(metadata) == XSTYPE_DICT) {
417         xs *attach = xs_list_new();
418         xs_str *k;
419@@ -1314,7 +1248,7 @@ xs_dict *msg_create(snac *snac, const xs_dict *object)
420 /* creates a 'Create' message */
421 {
422     xs_dict *msg = msg_base(snac, "Create", "@wrapper", snac->actor, NULL, object);
423-    xs_val *v;
424+    const xs_val *v;
425 
426     if ((v = get_atto(object)))
427         msg = xs_dict_append(msg, "attributedTo", v);
428@@ -1331,7 +1265,7 @@ xs_dict *msg_create(snac *snac, const xs_dict *object)
429 }
430 
431 
432-xs_dict *msg_undo(snac *snac, char *object)
433+xs_dict *msg_undo(snac *snac, const xs_val *object)
434 /* creates an 'Undo' message */
435 {
436     xs_dict *msg = msg_base(snac, "Undo", "@object", snac->actor, "@now", object);
437@@ -1344,7 +1278,7 @@ xs_dict *msg_undo(snac *snac, char *object)
438 }
439 
440 
441-xs_dict *msg_delete(snac *snac, char *id)
442+xs_dict *msg_delete(snac *snac, const char *id)
443 /* creates a 'Delete' + 'Tombstone' for a local entry */
444 {
445     xs *tomb = xs_dict_new();
446@@ -1386,7 +1320,7 @@ xs_dict *msg_follow(snac *snac, const char *q)
447 
448     if (valid_status(status)) {
449         /* check if the actor is an alias */
450-        char *r_actor = xs_dict_get(actor_o, "id");
451+        const char *r_actor = xs_dict_get(actor_o, "id");
452 
453         if (r_actor && strcmp(actor, r_actor) != 0) {
454             snac_log(snac, xs_fmt("actor to follow is an alias %s -> %s", actor, r_actor));
455@@ -1402,7 +1336,7 @@ xs_dict *msg_follow(snac *snac, const char *q)
456 
457 
458 xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts,
459-                  xs_str *in_reply_to, xs_list *attach, int priv)
460+                  const xs_str *in_reply_to, const xs_list *attach, int priv)
461 /* creates a 'Note' message */
462 {
463     xs *ntid = tid(0);
464@@ -1442,7 +1376,7 @@ xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts,
465 
466         if (valid_status(object_get(in_reply_to, &p_msg))) {
467             /* add this author as recipient */
468-            char *a, *v;
469+            const char *a, *v;
470 
471             if ((a = get_atto(p_msg)) && xs_list_in(to, a) == -1)
472                 to = xs_list_append(to, a);
473@@ -1453,7 +1387,7 @@ xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts,
474                 xs *actor_o = NULL;
475 
476                 if (xs_list_len(l) > 3 && valid_status(object_get(a, &actor_o))) {
477-                    char *uname = xs_dict_get(actor_o, "preferredUsername");
478+                    const char *uname = xs_dict_get(actor_o, "preferredUsername");
479 
480                     if (!xs_is_null(uname) && *uname) {
481                         xs *handle = xs_fmt("@%s@%s", uname, xs_list_get(l, 2));
482@@ -1492,7 +1426,8 @@ xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts,
483 
484     /* create the attachment list, if there are any */
485     if (!xs_is_null(attach)) {
486-        while (xs_list_iter(&attach, &v)) {
487+        int c = 0;
488+        while (xs_list_next(attach, &v, &c)) {
489             xs *d            = xs_dict_new();
490             const char *url  = xs_list_get(v, 0);
491             const char *alt  = xs_list_get(v, 1);
492@@ -1515,7 +1450,7 @@ xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts,
493     p = tag;
494     while (xs_list_iter(&p, &v)) {
495         if (xs_type(v) == XSTYPE_DICT) {
496-            char *t;
497+            const char *t;
498 
499             if (!xs_is_null(t = xs_dict_get(v, "type")) && strcmp(t, "Mention") == 0) {
500                 if (!xs_is_null(t = xs_dict_get(v, "href")))
501@@ -1639,7 +1574,7 @@ int update_question(snac *user, const char *id)
502     xs *msg   = NULL;
503     xs *rcnt  = xs_dict_new();
504     xs *lopts = xs_list_new();
505-    xs_list *opts;
506+    const xs_list *opts;
507     xs_list *p;
508     xs_val *v;
509 
510@@ -1657,8 +1592,8 @@ int update_question(snac *user, const char *id)
511         return -3;
512 
513     /* fill the initial count */
514-    p = opts;
515-    while (xs_list_iter(&p, &v)) {
516+    int c = 0;
517+    while (xs_list_next(opts, &v, &c)) {
518         const char *name = xs_dict_get(v, "name");
519         if (name) {
520             lopts = xs_list_append(lopts, name);
521@@ -1764,13 +1699,13 @@ int update_question(snac *user, const char *id)
522 
523 /** queues **/
524 
525-int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
526+int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req)
527 /* processes an ActivityPub message from the input queue */
528 /* return values: -1, fatal error; 0, transient error, retry;
529    1, processed and done; 2, propagate to users (only when no user is set) */
530 {
531-    char *actor = xs_dict_get(msg, "actor");
532-    char *type  = xs_dict_get(msg, "type");
533+    const char *actor = xs_dict_get(msg, "actor");
534+    const char *type  = xs_dict_get(msg, "type");
535     xs *actor_o = NULL;
536     int a_status;
537     int do_notify = 0;
538@@ -1790,7 +1725,7 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
539         return -1;
540     }
541 
542-    char *object, *utype;
543+    const char *object, *utype;
544 
545     object = xs_dict_get(msg, "object");
546     if (object != NULL && xs_type(object) == XSTYPE_DICT)
547@@ -1813,7 +1748,7 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
548         }
549 
550         /* also discard if the object to be deleted is not here */
551-        char *obj_id = object;
552+        const char *obj_id = object;
553         if (xs_type(obj_id) == XSTYPE_DICT)
554             obj_id = xs_dict_get(obj_id, "id");
555 
556@@ -1885,7 +1820,7 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
557     int min_account_age = xs_number_get(xs_dict_get(srv_config, "min_account_age"));
558 
559     if (min_account_age > 0) {
560-        char *actor_date = xs_dict_get(actor_o, "published");
561+        const char *actor_date = xs_dict_get(actor_o, "published");
562         if (!xs_is_null(actor_date)) {
563             time_t actor_t = xs_parse_iso_date(actor_date, 0);
564 
565@@ -1945,7 +1880,7 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
566     }
567     else
568     if (strcmp(type, "Undo") == 0) { /** **/
569-        char *id = xs_dict_get(object, "object");
570+        const char *id = xs_dict_get(object, "object");
571 
572         if (xs_type(object) != XSTYPE_DICT)
573             utype = "Follow";
574@@ -1990,9 +1925,9 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
575         }
576 
577         if (xs_match(utype, "Note|Article")) { /** **/
578-            char *id          = xs_dict_get(object, "id");
579-            char *in_reply_to = xs_dict_get(object, "inReplyTo");
580-            char *atto        = get_atto(object);
581+            const char *id          = xs_dict_get(object, "id");
582+            const char *in_reply_to = xs_dict_get(object, "inReplyTo");
583+            const char *atto        = get_atto(object);
584             xs *wrk           = NULL;
585 
586             if (xs_is_null(id))
587@@ -2029,14 +1964,14 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
588         }
589         else
590         if (strcmp(utype, "Question") == 0) { /**  **/
591-            char *id = xs_dict_get(object, "id");
592+            const char *id = xs_dict_get(object, "id");
593 
594             if (timeline_add(snac, id, object))
595                 snac_log(snac, xs_fmt("new 'Question' %s %s", actor, id));
596         }
597         else
598         if (strcmp(utype, "Video") == 0) { /** **/
599-            char *id = xs_dict_get(object, "id");
600+            const char *id = xs_dict_get(object, "id");
601 
602             if (timeline_add(snac, id, object))
603                 snac_log(snac, xs_fmt("new 'Video' %s %s", actor, id));
604@@ -2212,7 +2147,7 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
605 }
606 
607 
608-int send_email(char *msg)
609+int send_email(const char *msg)
610 /* invoke sendmail with email headers and body in msg */
611 {
612     FILE *f;
613@@ -2244,14 +2179,14 @@ int send_email(char *msg)
614 void process_user_queue_item(snac *snac, xs_dict *q_item)
615 /* processes an item from the user queue */
616 {
617-    char *type;
618+    const char *type;
619     int queue_retry_max = xs_number_get(xs_dict_get(srv_config, "queue_retry_max"));
620 
621     if ((type = xs_dict_get(q_item, "type")) == NULL)
622         type = "output";
623 
624     if (strcmp(type, "message") == 0) {
625-        xs_dict *msg = xs_dict_get(q_item, "message");
626+        const xs_dict *msg = xs_dict_get(q_item, "message");
627         xs *rcpts    = recipient_list(snac, msg, 1);
628         xs_set inboxes;
629         xs_list *p;
630@@ -2292,8 +2227,8 @@ void process_user_queue_item(snac *snac, xs_dict *q_item)
631     else
632     if (strcmp(type, "input") == 0) {
633         /* process the message */
634-        xs_dict *msg = xs_dict_get(q_item, "message");
635-        xs_dict *req = xs_dict_get(q_item, "req");
636+        const xs_dict *msg = xs_dict_get(q_item, "message");
637+        const xs_dict *req = xs_dict_get(q_item, "req");
638         int retries  = xs_number_get(xs_dict_get(q_item, "retries"));
639 
640         if (xs_is_null(msg))
641@@ -2320,13 +2255,6 @@ void process_user_queue_item(snac *snac, xs_dict *q_item)
642             update_question(snac, id);
643     }
644     else
645-    if (strcmp(type, "request_replies") == 0) {
646-        const char *id = xs_dict_get(q_item, "message");
647-
648-        if (!xs_is_null(id))
649-            timeline_request_replies(snac, id);
650-    }
651-    else
652     if (strcmp(type, "object_request") == 0) {
653         const char *id = xs_dict_get(q_item, "message");
654 
655@@ -2395,15 +2323,15 @@ int process_user_queue(snac *snac)
656 void process_queue_item(xs_dict *q_item)
657 /* processes an item from the global queue */
658 {
659-    char *type = xs_dict_get(q_item, "type");
660+    const char *type = xs_dict_get(q_item, "type");
661     int queue_retry_max = xs_number_get(xs_dict_get(srv_config, "queue_retry_max"));
662 
663     if (strcmp(type, "output") == 0) {
664         int status;
665-        xs_str *inbox  = xs_dict_get(q_item, "inbox");
666-        xs_str *keyid  = xs_dict_get(q_item, "keyid");
667-        xs_str *seckey = xs_dict_get(q_item, "seckey");
668-        xs_dict *msg   = xs_dict_get(q_item, "message");
669+        const xs_str *inbox  = xs_dict_get(q_item, "inbox");
670+        const xs_str *keyid  = xs_dict_get(q_item, "keyid");
671+        const xs_str *seckey = xs_dict_get(q_item, "seckey");
672+        const xs_dict *msg   = xs_dict_get(q_item, "message");
673         int retries    = xs_number_get(xs_dict_get(q_item, "retries"));
674         int p_status   = xs_number_get(xs_dict_get(q_item, "p_status"));
675         xs *payload    = NULL;
676@@ -2475,7 +2403,7 @@ void process_queue_item(xs_dict *q_item)
677     else
678     if (strcmp(type, "email") == 0) {
679         /* send this email */
680-        xs_str *msg = xs_dict_get(q_item, "message");
681+        const xs_str *msg = xs_dict_get(q_item, "message");
682         int retries = xs_number_get(xs_dict_get(q_item, "retries"));
683 
684         if (!send_email(msg))
685@@ -2497,8 +2425,8 @@ void process_queue_item(xs_dict *q_item)
686     else
687     if (strcmp(type, "telegram") == 0) {
688         /* send this via telegram */
689-        char *bot   = xs_dict_get(q_item, "bot");
690-        char *msg   = xs_dict_get(q_item, "message");
691+        const char *bot   = xs_dict_get(q_item, "bot");
692+        const char *msg   = xs_dict_get(q_item, "message");
693         xs *chat_id = xs_dup(xs_dict_get(q_item, "chat_id"));
694         int status  = 0;
695 
696@@ -2521,9 +2449,9 @@ void process_queue_item(xs_dict *q_item)
697     else
698     if (strcmp(type, "ntfy") == 0) {
699         /* send this via ntfy */
700-        char *ntfy_server   = xs_dict_get(q_item, "ntfy_server");
701-        char *msg   = xs_dict_get(q_item, "message");
702-        char *ntfy_token   = xs_dict_get(q_item, "ntfy_token");
703+        const char *ntfy_server   = xs_dict_get(q_item, "ntfy_server");
704+        const char *msg   = xs_dict_get(q_item, "message");
705+        const char *ntfy_token   = xs_dict_get(q_item, "ntfy_token");
706         int status  = 0;
707 
708         xs *url  = xs_fmt("%s", ntfy_server);
709@@ -2552,8 +2480,8 @@ void process_queue_item(xs_dict *q_item)
710     }
711     else
712     if (strcmp(type, "input") == 0) {
713-        xs_dict *msg = xs_dict_get(q_item, "message");
714-        xs_dict *req = xs_dict_get(q_item, "req");
715+        const xs_dict *msg = xs_dict_get(q_item, "message");
716+        const xs_dict *req = xs_dict_get(q_item, "req");
717         int retries  = xs_number_get(xs_dict_get(q_item, "retries"));
718 
719         /* do some instance-level checks */
720@@ -2572,7 +2500,7 @@ void process_queue_item(xs_dict *q_item)
721         else
722         if (r == 2) {
723             /* redistribute the input message to all users */
724-            char *ntid = xs_dict_get(q_item, "ntid");
725+            const char *ntid = xs_dict_get(q_item, "ntid");
726             xs *tmpfn  = xs_fmt("%s/tmp/%s.json", srv_basedir, ntid);
727             FILE *f;
728 
729@@ -2647,7 +2575,7 @@ int activitypub_get_handler(const xs_dict *req, const char *q_path,
730                             char **body, int *b_size, char **ctype)
731 {
732     int status = 200;
733-    char *accept = xs_dict_get(req, "accept");
734+    const char *accept = xs_dict_get(req, "accept");
735     snac snac;
736     xs *msg = NULL;
737 
738@@ -2659,7 +2587,8 @@ int activitypub_get_handler(const xs_dict *req, const char *q_path,
739         return 0;
740 
741     xs *l = xs_split_n(q_path, "/", 2);
742-    char *uid, *p_path;
743+    const char *uid;
744+    const char *p_path;
745 
746     uid = xs_list_get(l, 1);
747     if (!user_open(&snac, uid)) {
748@@ -2677,7 +2606,7 @@ int activitypub_get_handler(const xs_dict *req, const char *q_path,
749         msg = msg_actor(&snac);
750         *ctype = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"";
751 
752-        char *ua = xs_dict_get(req, "user-agent");
753+        const char *ua = xs_dict_get(req, "user-agent");
754 
755         snac_debug(&snac, 0, xs_fmt("serving actor [%s]", ua ? ua : "No UA"));
756     }
757@@ -2694,8 +2623,8 @@ int activitypub_get_handler(const xs_dict *req, const char *q_path,
758             xs *i = NULL;
759 
760             if (valid_status(object_get_by_md5(v, &i))) {
761-                char *type = xs_dict_get(i, "type");
762-                char *id   = xs_dict_get(i, "id");
763+                const char *type = xs_dict_get(i, "type");
764+                const char *id   = xs_dict_get(i, "id");
765 
766                 if (type && id && strcmp(type, "Note") == 0 && xs_startswith(id, snac.actor)) {
767                     xs *c_msg = msg_create(&snac, i);
768@@ -2748,9 +2677,9 @@ int activitypub_post_handler(const xs_dict *req, const char *q_path,
769     (void)b_size;
770 
771     int status = 202; /* accepted */
772-    char *i_ctype = xs_dict_get(req, "content-type");
773+    const char *i_ctype = xs_dict_get(req, "content-type");
774     snac snac;
775-    char *v;
776+    const char *v;
777 
778     if (i_ctype == NULL) {
779         *body  = xs_str_new("no content-type");
M data.c
+49, -81
  1@@ -29,7 +29,7 @@ pthread_mutex_t data_mutex = {0};
  2 int snac_upgrade(xs_str **error);
  3 
  4 
  5-int srv_open(char *basedir, int auto_upgrade)
  6+int srv_open(const char *basedir, int auto_upgrade)
  7 /* opens a server */
  8 {
  9     int ret = 0;
 10@@ -58,10 +58,10 @@ int srv_open(char *basedir, int auto_upgrade)
 11         if (srv_config == NULL)
 12             error = xs_fmt("ERROR: cannot parse '%s'", cfg_file);
 13         else {
 14-            char *host;
 15-            char *prefix;
 16-            char *dbglvl;
 17-            char *proto;
 18+            const char *host;
 19+            const char *prefix;
 20+            const char *dbglvl;
 21+            const char *proto;
 22 
 23             host   = xs_dict_get(srv_config, "host");
 24             prefix = xs_dict_get(srv_config, "prefix");
 25@@ -710,7 +710,7 @@ int _object_add(const char *id, const xs_dict *obj, int ow)
 26         fclose(f);
 27 
 28         /* does this object has a parent? */
 29-        char *in_reply_to = xs_dict_get(obj, "inReplyTo");
 30+        const char *in_reply_to = xs_dict_get(obj, "inReplyTo");
 31 
 32         if (!xs_is_null(in_reply_to) && *in_reply_to) {
 33             /* update the children index of the parent */
 34@@ -1124,7 +1124,7 @@ int timeline_get_by_md5(snac *snac, const char *md5, xs_dict **msg)
 35 }
 36 
 37 
 38-int timeline_del(snac *snac, char *id)
 39+int timeline_del(snac *snac, const char *id)
 40 /* deletes a message from the timeline */
 41 {
 42     /* delete from the user's caches */
 43@@ -1192,17 +1192,16 @@ int timeline_admire(snac *snac, const char *id, const char *admirer, int like)
 44 }
 45 
 46 
 47-xs_list *timeline_top_level(snac *snac, xs_list *list)
 48+xs_list *timeline_top_level(snac *snac, const xs_list *list)
 49 /* returns the top level md5 entries from this index */
 50 {
 51     xs_set seen;
 52-    xs_list *p;
 53     xs_str *v;
 54 
 55     xs_set_init(&seen);
 56 
 57-    p = list;
 58-    while (xs_list_iter(&p, &v)) {
 59+    int c = 0;
 60+    while (xs_list_next(list, &v, &c)) {
 61         char line[256] = "";
 62 
 63         strncpy(line, v, sizeof(line));
 64@@ -1290,7 +1289,7 @@ int following_add(snac *snac, const char *actor, const xs_dict *msg)
 65         /* object already exists; if it's of type Accept,
 66            the actor is already being followed and confirmed,
 67            so do nothing */
 68-        char *type = xs_dict_get(p_object, "type");
 69+        const char *type = xs_dict_get(p_object, "type");
 70 
 71         if (!xs_is_null(type) && strcmp(type, "Accept") == 0) {
 72             snac_debug(snac, 1, xs_fmt("following_add actor already confirmed %s", actor));
 73@@ -1546,8 +1545,9 @@ void hide(snac *snac, const char *id)
 74 
 75             /* resolve to get the id */
 76             if (valid_status(object_get_by_md5(v, &co))) {
 77-                if ((v = xs_dict_get(co, "id")) != NULL)
 78-                    hide(snac, v);
 79+                const char *id = xs_dict_get(co, "id");
 80+                if (id != NULL)
 81+                    hide(snac, id);
 82             }
 83         }
 84     }
 85@@ -1563,7 +1563,7 @@ int is_hidden(snac *snac, const char *id)
 86 }
 87 
 88 
 89-int actor_add(const char *actor, xs_dict *msg)
 90+int actor_add(const char *actor, const xs_dict *msg)
 91 /* adds an actor */
 92 {
 93     return object_add_ow(actor, msg);
 94@@ -1687,7 +1687,7 @@ int limited(snac *user, const char *id, int cmd)
 95 void tag_index(const char *id, const xs_dict *obj)
 96 /* update the tag indexes for this object */
 97 {
 98-    xs_list *tags = xs_dict_get(obj, "tag");
 99+    const xs_list *tags = xs_dict_get(obj, "tag");
100 
101     if (is_msg_public(obj) && xs_type(tags) == XSTYPE_LIST && xs_list_len(tags) > 0) {
102         xs *g_tag_dir = xs_fmt("%s/tag", srv_basedir);
103@@ -1695,9 +1695,10 @@ void tag_index(const char *id, const xs_dict *obj)
104         mkdirx(g_tag_dir);
105 
106         xs_dict *v;
107-        while (xs_list_iter(&tags, &v)) {
108-            char *type = xs_dict_get(v, "type");
109-            char *name = xs_dict_get(v, "name");
110+        int ct = 0;
111+        while (xs_list_next(tags, &v, &ct)) {
112+            const char *type = xs_dict_get(v, "type");
113+            const char *name = xs_dict_get(v, "name");
114 
115             if (!xs_is_null(type) && !xs_is_null(name) && strcmp(type, "Hashtag") == 0) {
116                 while (*name == '#' || *name == '@')
117@@ -1706,7 +1707,7 @@ void tag_index(const char *id, const xs_dict *obj)
118                 if (*name == '\0')
119                     continue;
120 
121-                name = xs_tolower_i(name);
122+                name = xs_tolower_i((xs_str *)name);
123 
124                 xs *md5_tag   = xs_md5_hex(name, strlen(name));
125                 xs *tag_dir   = xs_fmt("%s/%c%c", g_tag_dir, md5_tag[0], md5_tag[1]);
126@@ -1729,7 +1730,7 @@ void tag_index(const char *id, const xs_dict *obj)
127 }
128 
129 
130-xs_list *tag_search(char *tag, int skip, int show)
131+xs_list *tag_search(const char *tag, int skip, int show)
132 /* returns the list of posts tagged with tag */
133 {
134     if (*tag == '#')
135@@ -1912,7 +1913,7 @@ xs_val *list_content(snac *user, const char *list, const char *actor_md5, int op
136 void list_distribute(snac *user, const char *who, const xs_dict *post)
137 /* distributes the post to all appropriate lists */
138 {
139-    char *id = xs_dict_get(post, "id");
140+    const char *id = xs_dict_get(post, "id");
141 
142     /* if who is not set, use the attributedTo in the message */
143     if (xs_is_null(who))
144@@ -2164,7 +2165,7 @@ void inbox_add(const char *inbox)
145 void inbox_add_by_actor(const xs_dict *actor)
146 /* collects an actor's shared inbox, if it has one */
147 {
148-    char *v;
149+    const char *v;
150 
151     if (!xs_is_null(v = xs_dict_get(actor, "endpoints")) &&
152         !xs_is_null(v = xs_dict_get(v, "sharedInbox"))) {
153@@ -2210,7 +2211,7 @@ xs_str *_instance_block_fn(const char *instance)
154     xs *s   = xs_replace(instance, "http:/" "/", "");
155     xs *s1  = xs_replace(s, "https:/" "/", "");
156     xs *l   = xs_split(s1, "/");
157-    char *p = xs_list_get(l, 0);
158+    const char *p = xs_list_get(l, 0);
159     xs *md5 = xs_md5_hex(p, strlen(p));
160 
161     return xs_fmt("%s/block/%s", srv_basedir, md5);
162@@ -2279,7 +2280,7 @@ int content_match(const char *file, const xs_dict *msg)
163     xs *fn = xs_fmt("%s/%s", srv_basedir, file);
164     FILE *f;
165     int r = 0;
166-    char *v = xs_dict_get(msg, "content");
167+    const char *v = xs_dict_get(msg, "content");
168 
169     if (xs_type(v) == XSTYPE_STRING && *v) {
170         if ((f = fopen(fn, "r")) != NULL) {
171@@ -2386,7 +2387,7 @@ xs_list *content_search(snac *user, const char *regex,
172         if (id == NULL || is_hidden(user, id))
173             continue;
174 
175-        char *content = xs_dict_get(post, "content");
176+        const char *content = xs_dict_get(post, "content");
177 
178         if (xs_is_null(content))
179             continue;
180@@ -2639,7 +2640,7 @@ void enqueue_input(snac *snac, const xs_dict *msg, const xs_dict *req, int retri
181 /* enqueues an input message */
182 {
183     xs *qmsg   = _new_qmsg("input", msg, retries);
184-    char *ntid = xs_dict_get(qmsg, "ntid");
185+    const char *ntid = xs_dict_get(qmsg, "ntid");
186     xs *fn     = xs_fmt("%s/queue/%s.json", snac->basedir, ntid);
187 
188     qmsg = xs_dict_append(qmsg, "req", req);
189@@ -2654,7 +2655,7 @@ void enqueue_shared_input(const xs_dict *msg, const xs_dict *req, int retries)
190 /* enqueues an input message from the shared input */
191 {
192     xs *qmsg   = _new_qmsg("input", msg, retries);
193-    char *ntid = xs_dict_get(qmsg, "ntid");
194+    const char *ntid = xs_dict_get(qmsg, "ntid");
195     xs *fn     = xs_fmt("%s/queue/%s.json", srv_basedir, ntid);
196 
197     qmsg = xs_dict_append(qmsg, "req", req);
198@@ -2666,11 +2667,12 @@ void enqueue_shared_input(const xs_dict *msg, const xs_dict *req, int retries)
199 
200 
201 void enqueue_output_raw(const char *keyid, const char *seckey,
202-                        xs_dict *msg, xs_str *inbox, int retries, int p_status)
203+                        const xs_dict *msg, const xs_str *inbox,
204+                        int retries, int p_status)
205 /* enqueues an output message to an inbox */
206 {
207     xs *qmsg   = _new_qmsg("output", msg, retries);
208-    char *ntid = xs_dict_get(qmsg, "ntid");
209+    const char *ntid = xs_dict_get(qmsg, "ntid");
210     xs *fn     = xs_fmt("%s/queue/%s.json", srv_basedir, ntid);
211 
212     xs *ns = xs_number_new(p_status);
213@@ -2690,7 +2692,8 @@ void enqueue_output_raw(const char *keyid, const char *seckey,
214 }
215 
216 
217-void enqueue_output(snac *snac, xs_dict *msg, xs_str *inbox, int retries, int p_status)
218+void enqueue_output(snac *snac, const xs_dict *msg,
219+                    const xs_str *inbox, int retries, int p_status)
220 /* enqueues an output message to an inbox */
221 {
222     if (xs_startswith(inbox, snac->actor)) {
223@@ -2698,13 +2701,14 @@ void enqueue_output(snac *snac, xs_dict *msg, xs_str *inbox, int retries, int p_
224         return;
225     }
226 
227-    char *seckey = xs_dict_get(snac->key, "secret");
228+    const char *seckey = xs_dict_get(snac->key, "secret");
229 
230     enqueue_output_raw(snac->actor, seckey, msg, inbox, retries, p_status);
231 }
232 
233 
234-void enqueue_output_by_actor(snac *snac, xs_dict *msg, const xs_str *actor, int retries)
235+void enqueue_output_by_actor(snac *snac, const xs_dict *msg,
236+                            const xs_str *actor, int retries)
237 /* enqueues an output message for an actor */
238 {
239     xs *inbox = get_actor_inbox(actor);
240@@ -2716,11 +2720,11 @@ void enqueue_output_by_actor(snac *snac, xs_dict *msg, const xs_str *actor, int
241 }
242 
243 
244-void enqueue_email(xs_str *msg, int retries)
245+void enqueue_email(const xs_str *msg, int retries)
246 /* enqueues an email message to be sent */
247 {
248     xs *qmsg   = _new_qmsg("email", msg, retries);
249-    char *ntid = xs_dict_get(qmsg, "ntid");
250+    const char *ntid = xs_dict_get(qmsg, "ntid");
251     xs *fn     = xs_fmt("%s/queue/%s.json", srv_basedir, ntid);
252 
253     qmsg = _enqueue_put(fn, qmsg);
254@@ -2733,7 +2737,7 @@ void enqueue_telegram(const xs_str *msg, const char *bot, const char *chat_id)
255 /* enqueues a message to be sent via Telegram */
256 {
257     xs *qmsg   = _new_qmsg("telegram", msg, 0);
258-    char *ntid = xs_dict_get(qmsg, "ntid");
259+    const char *ntid = xs_dict_get(qmsg, "ntid");
260     xs *fn     = xs_fmt("%s/queue/%s.json", srv_basedir, ntid);
261 
262     qmsg = xs_dict_append(qmsg, "bot",      bot);
263@@ -2748,7 +2752,7 @@ void enqueue_ntfy(const xs_str *msg, const char *ntfy_server, const char *ntfy_t
264 /* enqueues a message to be sent via ntfy */
265 {
266     xs *qmsg   = _new_qmsg("ntfy", msg, 0);
267-    char *ntid = xs_dict_get(qmsg, "ntid");
268+    const char *ntid = xs_dict_get(qmsg, "ntid");
269     xs *fn     = xs_fmt("%s/queue/%s.json", srv_basedir, ntid);
270 
271     qmsg = xs_dict_append(qmsg, "ntfy_server", ntfy_server);
272@@ -2764,7 +2768,7 @@ void enqueue_message(snac *snac, const xs_dict *msg)
273 /* enqueues an output message */
274 {
275     xs *qmsg   = _new_qmsg("message", msg, 0);
276-    char *ntid = xs_dict_get(qmsg, "ntid");
277+    const char *ntid = xs_dict_get(qmsg, "ntid");
278     xs *fn     = xs_fmt("%s/queue/%s.json", snac->basedir, ntid);
279 
280     qmsg = _enqueue_put(fn, qmsg);
281@@ -2807,7 +2811,7 @@ void enqueue_verify_links(snac *user)
282 /* enqueues a link verification */
283 {
284     xs *qmsg   = _new_qmsg("verify_links", "", 0);
285-    char *ntid = xs_dict_get(qmsg, "ntid");
286+    const char *ntid = xs_dict_get(qmsg, "ntid");
287     xs *fn     = xs_fmt("%s/queue/%s.json", user->basedir, ntid);
288 
289     qmsg = _enqueue_put(fn, qmsg);
290@@ -2832,42 +2836,6 @@ void enqueue_actor_refresh(snac *user, const char *actor, int forward_secs)
291 }
292 
293 
294-void enqueue_request_replies(snac *user, const char *id)
295-/* enqueues a request for the replies of a message */
296-{
297-    /* test first if this precise request is already in the queue */
298-    xs *queue = user_queue(user);
299-    xs_list *p = queue;
300-    xs_str *v;
301-
302-    while (xs_list_iter(&p, &v)) {
303-        xs *q_item = queue_get(v);
304-
305-        if (q_item != NULL) {
306-            const char *type = xs_dict_get(q_item, "type");
307-            const char *msg  = xs_dict_get(q_item, "message");
308-
309-            if (type && msg && strcmp(type, "request_replies") == 0 && strcmp(msg, id) == 0) {
310-                /* don't requeue */
311-                snac_debug(user, 1, xs_fmt("enqueue_request_replies already here %s", id));
312-                return;
313-            }
314-        }
315-    }
316-
317-    /* not there; enqueue the request with a small delay */
318-    xs *qmsg = _new_qmsg("request_replies", id, 0);
319-    xs *ntid = tid(10);
320-    xs *fn   = xs_fmt("%s/queue/%s.json", user->basedir, ntid);
321-
322-    qmsg = xs_dict_set(qmsg, "ntid", ntid);
323-
324-    qmsg = _enqueue_put(fn, qmsg);
325-
326-    snac_debug(user, 2, xs_fmt("enqueue_request_replies %s", id));
327-}
328-
329-
330 int was_question_voted(snac *user, const char *id)
331 /* returns true if the user voted in this poll */
332 {
333@@ -2881,7 +2849,7 @@ int was_question_voted(snac *user, const char *id)
334         xs *obj = NULL;
335 
336         if (valid_status(object_get_by_md5(md5, &obj))) {
337-            char *atto = get_atto(obj);
338+            const char *atto = get_atto(obj);
339             if (atto && strcmp(atto, user->actor) == 0 &&
340                 !xs_is_null(xs_dict_get(obj, "name"))) {
341                 voted = 1;
342@@ -3055,7 +3023,7 @@ void purge_server(void)
343                 if (mtime_nl(v2, &n_link) < mt && n_link < 2) {
344                     xs *s1    = xs_replace(v2, ".json", "");
345                     xs *l     = xs_split(s1, "/");
346-                    char *md5 = xs_list_get(l, -1);
347+                    const char *md5 = xs_list_get(l, -1);
348 
349                     object_del_by_md5(md5);
350                     cnt++;
351@@ -3147,7 +3115,7 @@ void purge_user(snac *snac)
352 /* do the purge for this user */
353 {
354     int priv_days, pub_days, user_days = 0;
355-    char *v;
356+    const char *v;
357     int n;
358 
359     priv_days = xs_number_get(xs_dict_get(srv_config, "timeline_purge_days"));
360@@ -3256,7 +3224,7 @@ void srv_archive(const char *direction, const char *url, xs_dict *req,
361         if (p_size && payload) {
362             xs *payload_fn = NULL;
363             xs *payload_fn_raw = NULL;
364-            char *v = xs_dict_get(req, "content-type");
365+            const char *v = xs_dict_get(req, "content-type");
366 
367             if (v && xs_str_in(v, "json") != -1) {
368                 payload_fn = xs_fmt("%s/payload.json", dir);
369@@ -3287,7 +3255,7 @@ void srv_archive(const char *direction, const char *url, xs_dict *req,
370 
371         if (b_size && body) {
372             xs *body_fn = NULL;
373-            char *v = xs_dict_get(headers, "content-type");
374+            const char *v = xs_dict_get(headers, "content-type");
375 
376             if (v && xs_str_in(v, "json") != -1) {
377                 body_fn = xs_fmt("%s/body.json", dir);
378@@ -3356,7 +3324,7 @@ void srv_archive_error(const char *prefix, const xs_str *err,
379 }
380 
381 
382-void srv_archive_qitem(char *prefix, xs_dict *q_item)
383+void srv_archive_qitem(const char *prefix, xs_dict *q_item)
384 /* archives a q_item in the error folder */
385 {
386     xs *ntid = tid(0);
M html.c
+143, -135
  1@@ -41,7 +41,7 @@ int login(snac *snac, const xs_dict *headers)
  2 }
  3 
  4 
  5-xs_str *replace_shortnames(xs_str *s, xs_list *tag, int ems)
  6+xs_str *replace_shortnames(xs_str *s, const xs_list *tag, int ems)
  7 /* replaces all the :shortnames: with the emojis in tag */
  8 {
  9     if (!xs_is_null(tag)) {
 10@@ -57,18 +57,18 @@ xs_str *replace_shortnames(xs_str *s, xs_list *tag, int ems)
 11 
 12         xs *style = xs_fmt("height: %dem; width: %dem; vertical-align: middle;", ems, ems);
 13 
 14-        xs_list *p = tag_list;
 15         char *v;
 16+        int c = 0;
 17 
 18-        while (xs_list_iter(&p, &v)) {
 19-            char *t = xs_dict_get(v, "type");
 20+        while (xs_list_next(tag_list, &v, &c)) {
 21+            const char *t = xs_dict_get(v, "type");
 22 
 23             if (t && strcmp(t, "Emoji") == 0) {
 24-                char *n = xs_dict_get(v, "name");
 25-                char *i = xs_dict_get(v, "icon");
 26+                const char *n = xs_dict_get(v, "name");
 27+                const char *i = xs_dict_get(v, "icon");
 28 
 29                 if (n && i) {
 30-                    char *u = xs_dict_get(i, "url");
 31+                    const char *u = xs_dict_get(i, "url");
 32                     xs_html *img = xs_html_sctag("img",
 33                         xs_html_attr("loading", "lazy"),
 34                         xs_html_attr("src", u),
 35@@ -88,7 +88,7 @@ xs_str *replace_shortnames(xs_str *s, xs_list *tag, int ems)
 36 xs_str *actor_name(xs_dict *actor)
 37 /* gets the actor name */
 38 {
 39-    char *v;
 40+    const char *v;
 41 
 42     if (xs_is_null((v = xs_dict_get(actor, "name"))) || *v == '\0') {
 43         if (xs_is_null(v = xs_dict_get(actor, "preferredUsername")) || *v == '\0') {
 44@@ -106,7 +106,7 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date,
 45     xs_html *actor_icon = xs_html_tag("p", NULL);
 46 
 47     xs *avatar = NULL;
 48-    char *v;
 49+    const char *v;
 50     int fwing = 0;
 51     int fwer = 0;
 52 
 53@@ -125,7 +125,7 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date,
 54     if (avatar == NULL)
 55         avatar = xs_fmt("data:image/png;base64, %s", default_avatar_base64());
 56 
 57-    char *actor_id = xs_dict_get(actor, "id");
 58+    const char *actor_id = xs_dict_get(actor, "id");
 59     xs *href = NULL;
 60 
 61     if (user) {
 62@@ -216,7 +216,7 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date,
 63     }
 64 
 65     {
 66-        char *username, *id;
 67+        const char *username, *id;
 68 
 69         if (xs_is_null(username = xs_dict_get(actor, "preferredUsername")) || *username == '\0') {
 70             /* This should never be reached */
 71@@ -244,15 +244,15 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date,
 72 }
 73 
 74 
 75-xs_html *html_msg_icon(snac *user, char *actor_id, const xs_dict *msg)
 76+xs_html *html_msg_icon(snac *user, const char *actor_id, const xs_dict *msg)
 77 {
 78     xs *actor = NULL;
 79     xs_html *actor_icon = NULL;
 80 
 81     if (actor_id && valid_status(actor_get_refresh(user, actor_id, &actor))) {
 82-        char *date  = NULL;
 83-        char *udate = NULL;
 84-        char *url   = NULL;
 85+        const char *date  = NULL;
 86+        const char *udate = NULL;
 87+        const char *url   = NULL;
 88         int priv    = 0;
 89         const char *type = xs_dict_get(msg, "type");
 90 
 91@@ -271,14 +271,14 @@ xs_html *html_msg_icon(snac *user, char *actor_id, const xs_dict *msg)
 92 }
 93 
 94 
 95-xs_html *html_note(snac *user, char *summary,
 96-                   char *div_id, char *form_id,
 97-                   char *ta_plh, char *ta_content,
 98-                   char *edit_id, char *actor_id,
 99-                   xs_val *cw_yn, char *cw_text,
100-                   xs_val *mnt_only, char *redir,
101-                   char *in_reply_to, int poll,
102-                   char *att_file, char *att_alt_text)
103+xs_html *html_note(snac *user, const char *summary,
104+                   const char *div_id, const char *form_id,
105+                   const char *ta_plh, const char *ta_content,
106+                   const char *edit_id, const char *actor_id,
107+                   const xs_val *cw_yn, const char *cw_text,
108+                   const xs_val *mnt_only, const char *redir,
109+                   const char *in_reply_to, int poll,
110+                   const char *att_file, const char *att_alt_text)
111 {
112     xs *action = xs_fmt("%s/admin/note", user->actor);
113 
114@@ -460,9 +460,11 @@ static xs_html *html_base_head(void)
115     /* add server CSS and favicon */
116     xs *f;
117     f = xs_fmt("%s/favicon.ico", srv_baseurl);
118-    xs_list *p = xs_dict_get(srv_config, "cssurls");
119+    const xs_list *p = xs_dict_get(srv_config, "cssurls");
120     char *v;
121-    while (xs_list_iter(&p, &v)) {
122+    int c = 0;
123+
124+    while (xs_list_next(p, &v, &c)) {
125         xs_html_add(head,
126             xs_html_sctag("link",
127                 xs_html_attr("rel",  "stylesheet"),
128@@ -498,8 +500,8 @@ xs_html *html_instance_head(void)
129         }
130     }
131 
132-    char *host  = xs_dict_get(srv_config, "host");
133-    char *title = xs_dict_get(srv_config, "title");
134+    const char *host  = xs_dict_get(srv_config, "host");
135+    const char *title = xs_dict_get(srv_config, "title");
136 
137     xs_html_add(head,
138         xs_html_tag("title",
139@@ -511,10 +513,10 @@ xs_html *html_instance_head(void)
140 
141 static xs_html *html_instance_body(void)
142 {
143-    char *host  = xs_dict_get(srv_config, "host");
144-    char *sdesc = xs_dict_get(srv_config, "short_description");
145-    char *email = xs_dict_get(srv_config, "admin_email");
146-    char *acct  = xs_dict_get(srv_config, "admin_account");
147+    const char *host  = xs_dict_get(srv_config, "host");
148+    const char *sdesc = xs_dict_get(srv_config, "short_description");
149+    const char *email = xs_dict_get(srv_config, "admin_email");
150+    const char *acct  = xs_dict_get(srv_config, "admin_account");
151 
152     xs *blurb = xs_replace(snac_blurb, "%host%", host);
153 
154@@ -760,7 +762,7 @@ static xs_html *html_user_body(snac *user, int read_only)
155         xs_html_attr("class", "h-card snac-top-user"));
156 
157     if (read_only) {
158-        char *header = xs_dict_get(user->config, "header");
159+        const char *header = xs_dict_get(user->config, "header");
160         if (header && *header) {
161             xs_html_add(top_user,
162                 xs_html_tag("div",
163@@ -797,7 +799,7 @@ static xs_html *html_user_body(snac *user, int read_only)
164         xs_html_add(top_user,
165             top_user_bio);
166 
167-        xs_dict *metadata = xs_dict_get(user->config, "metadata");
168+        const xs_dict *metadata = xs_dict_get(user->config, "metadata");
169         if (xs_type(metadata) == XSTYPE_DICT) {
170             xs_str *k;
171             xs_str *v;
172@@ -816,7 +818,7 @@ static xs_html *html_user_body(snac *user, int read_only)
173                 if (xs_startswith(v, "https:/") || xs_startswith(v, "http:/")) {
174                     /* is this link validated? */
175                     xs *verified_link = NULL;
176-                    xs_number *val_time = xs_dict_get(val_links, v);
177+                    const xs_number *val_time = xs_dict_get(val_links, v);
178 
179                     if (xs_type(val_time) == XSTYPE_NUMBER) {
180                         time_t t = xs_number_get(val_time);
181@@ -928,7 +930,7 @@ xs_html *html_top_controls(snac *snac)
182 
183     /** user settings **/
184 
185-    char *email = "[disabled by admin]";
186+    const char *email = "[disabled by admin]";
187 
188     if (xs_type(xs_dict_get(srv_config, "disable_email_notifications")) != XSTYPE_TRUE) {
189         email = xs_dict_get(snac->config_o, "email");
190@@ -940,38 +942,38 @@ xs_html *html_top_controls(snac *snac)
191         }
192     }
193 
194-    char *cw = xs_dict_get(snac->config, "cw");
195+    const char *cw = xs_dict_get(snac->config, "cw");
196     if (xs_is_null(cw))
197         cw = "";
198 
199-    char *telegram_bot = xs_dict_get(snac->config, "telegram_bot");
200+    const char *telegram_bot = xs_dict_get(snac->config, "telegram_bot");
201     if (xs_is_null(telegram_bot))
202         telegram_bot = "";
203 
204-    char *telegram_chat_id = xs_dict_get(snac->config, "telegram_chat_id");
205+    const char *telegram_chat_id = xs_dict_get(snac->config, "telegram_chat_id");
206     if (xs_is_null(telegram_chat_id))
207         telegram_chat_id = "";
208 
209-    char *ntfy_server = xs_dict_get(snac->config, "ntfy_server");
210+    const char *ntfy_server = xs_dict_get(snac->config, "ntfy_server");
211     if (xs_is_null(ntfy_server))
212         ntfy_server = "";
213 
214-    char *ntfy_token = xs_dict_get(snac->config, "ntfy_token");
215+    const char *ntfy_token = xs_dict_get(snac->config, "ntfy_token");
216     if (xs_is_null(ntfy_token))
217         ntfy_token = "";
218 
219-    char *purge_days = xs_dict_get(snac->config, "purge_days");
220+    const char *purge_days = xs_dict_get(snac->config, "purge_days");
221     if (!xs_is_null(purge_days) && xs_type(purge_days) == XSTYPE_NUMBER)
222         purge_days = (char *)xs_number_str(purge_days);
223     else
224         purge_days = "0";
225 
226-    xs_val *d_dm_f_u  = xs_dict_get(snac->config, "drop_dm_from_unknown");
227-    xs_val *bot       = xs_dict_get(snac->config, "bot");
228-    xs_val *a_private = xs_dict_get(snac->config, "private");
229+    const xs_val *d_dm_f_u  = xs_dict_get(snac->config, "drop_dm_from_unknown");
230+    const xs_val *bot       = xs_dict_get(snac->config, "bot");
231+    const xs_val *a_private = xs_dict_get(snac->config, "private");
232 
233     xs *metadata = xs_str_new(NULL);
234-    xs_dict *md = xs_dict_get(snac->config, "metadata");
235+    const xs_dict *md = xs_dict_get(snac->config, "metadata");
236     xs_str *k;
237     xs_str *v;
238 
239@@ -1158,13 +1160,14 @@ xs_str *build_mentions(snac *snac, const xs_dict *msg)
240 /* returns a string with the mentions in msg */
241 {
242     xs_str *s = xs_str_new(NULL);
243-    char *list = xs_dict_get(msg, "tag");
244+    const char *list = xs_dict_get(msg, "tag");
245     char *v;
246+    int c = 0;
247 
248-    while (xs_list_iter(&list, &v)) {
249-        char *type = xs_dict_get(v, "type");
250-        char *href = xs_dict_get(v, "href");
251-        char *name = xs_dict_get(v, "name");
252+    while (xs_list_next(list, &v, &c)) {
253+        const char *type = xs_dict_get(v, "type");
254+        const char *href = xs_dict_get(v, "href");
255+        const char *name = xs_dict_get(v, "name");
256 
257         if (type && strcmp(type, "Mention") == 0 &&
258             href && strcmp(href, snac->actor) != 0 && name) {
259@@ -1208,10 +1211,11 @@ xs_str *build_mentions(snac *snac, const xs_dict *msg)
260 }
261 
262 
263-xs_html *html_entry_controls(snac *snac, char *actor, const xs_dict *msg, const char *md5)
264+xs_html *html_entry_controls(snac *snac, const char *actor,
265+                            const xs_dict *msg, const char *md5)
266 {
267-    char *id    = xs_dict_get(msg, "id");
268-    char *group = xs_dict_get(msg, "audience");
269+    const char *id    = xs_dict_get(msg, "id");
270+    const char *group = xs_dict_get(msg, "audience");
271 
272     xs *likes   = object_likes(id);
273     xs *boosts  = object_announces(id);
274@@ -1310,7 +1314,7 @@ xs_html *html_entry_controls(snac *snac, char *actor, const xs_dict *msg, const
275         html_button("delete", L("Delete"), L("Delete this post")),
276         html_button("hide",   L("Hide"), L("Hide this post and its children")));
277 
278-    char *prev_src = xs_dict_get(msg, "sourceContent");
279+    const char *prev_src = xs_dict_get(msg, "sourceContent");
280 
281     if (!xs_is_null(prev_src) && strcmp(actor, snac->actor) == 0) { /** edit **/
282         /* post can be edited */
283@@ -1318,13 +1322,13 @@ xs_html *html_entry_controls(snac *snac, char *actor, const xs_dict *msg, const
284         xs *form_id = xs_fmt("%s_edit_form", md5);
285         xs *redir   = xs_fmt("%s_entry", md5);
286 
287-        char *att_file = "";
288-        char *att_alt_text = "";
289-        xs_list *att_list = xs_dict_get(msg, "attachment");
290+        const char *att_file = "";
291+        const char *att_alt_text = "";
292+        const xs_list *att_list = xs_dict_get(msg, "attachment");
293 
294         /* does it have an attachment? */
295         if (xs_type(att_list) == XSTYPE_LIST && xs_list_len(att_list)) {
296-            xs_dict *d = xs_list_get(att_list, 0);
297+            const xs_dict *d = xs_list_get(att_list, 0);
298 
299             if (xs_type(d) == XSTYPE_DICT) {
300                 att_file = xs_dict_get_def(d, "url", "");
301@@ -1370,10 +1374,10 @@ xs_html *html_entry_controls(snac *snac, char *actor, const xs_dict *msg, const
302 xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
303                    int level, char *md5, int hide_children)
304 {
305-    char *id    = xs_dict_get(msg, "id");
306-    char *type  = xs_dict_get(msg, "type");
307-    char *actor;
308-    char *v;
309+    const char *id    = xs_dict_get(msg, "id");
310+    const char *type  = xs_dict_get(msg, "type");
311+    const char *actor;
312+    const char *v;
313     int has_title = 0;
314 
315     /* do not show non-public messages in the public timeline */
316@@ -1509,7 +1513,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
317 
318     if (xs_list_len(boosts)) {
319         /* if somebody boosted this, show as origin */
320-        char *p = xs_list_get(boosts, -1);
321+        const char *p = xs_list_get(boosts, -1);
322         xs *actor_r = NULL;
323 
324         if (user && xs_list_in(boosts, user->md5) != -1) {
325@@ -1529,7 +1533,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
326 
327             if (!xs_is_null(name)) {
328                 xs *href = NULL;
329-                char *id = xs_dict_get(actor_r, "id");
330+                const char *id = xs_dict_get(actor_r, "id");
331                 int fwers = 0;
332                 int fwing = 0;
333 
334@@ -1558,7 +1562,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
335     if (strcmp(type, "Note") == 0) {
336         if (level == 0) {
337             /* is the parent not here? */
338-            char *parent = xs_dict_get(msg, "inReplyTo");
339+            const char *parent = xs_dict_get(msg, "inReplyTo");
340 
341             if (user && !xs_is_null(parent) && *parent && !timeline_here(user, parent)) {
342                 xs_html_add(post_header,
343@@ -1603,7 +1607,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
344             v = "...";
345 
346         /* only show it when not in the public timeline and the config setting is "open" */
347-        char *cw = xs_dict_get(user->config, "cw");
348+        const char *cw = xs_dict_get(user->config, "cw");
349         if (xs_is_null(cw) || read_only)
350             cw = "";
351 
352@@ -1632,7 +1636,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
353 
354     {
355         /** build the content string **/
356-        char *content = xs_dict_get(msg, "content");
357+        const char *content = xs_dict_get(msg, "content");
358 
359         xs *c = sanitize(xs_is_null(content) ? "" : content);
360 
361@@ -1650,7 +1654,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
362         c = replace_shortnames(c, xs_dict_get(msg, "tag"), 2);
363 
364         /* Peertube videos content is in markdown */
365-        char *mtype = xs_dict_get(msg, "mediaType");
366+        const char *mtype = xs_dict_get(msg, "mediaType");
367         if (xs_type(mtype) == XSTYPE_STRING && strcmp(mtype, "text/markdown") == 0) {
368             /* a full conversion could be better */
369             c = xs_replace_i(c, "\r", "");
370@@ -1663,12 +1667,12 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
371     }
372 
373     if (strcmp(type, "Question") == 0) { /** question content **/
374-        xs_list *oo = xs_dict_get(msg, "oneOf");
375-        xs_list *ao = xs_dict_get(msg, "anyOf");
376-        xs_list *p;
377+        const xs_list *oo = xs_dict_get(msg, "oneOf");
378+        const xs_list *ao = xs_dict_get(msg, "anyOf");
379+        const xs_list *p;
380         xs_dict *v;
381         int closed = 0;
382-        char *f_closed = NULL;
383+        const char *f_closed = NULL;
384 
385         xs_html *poll = xs_html_tag("div", NULL);
386 
387@@ -1697,10 +1701,11 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
388             /* closed poll */
389             xs_html *poll_result = xs_html_tag("table",
390                 xs_html_attr("class", "snac-poll-result"));
391+            int c = 0;
392 
393-            while (xs_list_iter(&p, &v)) {
394-                char *name       = xs_dict_get(v, "name");
395-                xs_dict *replies = xs_dict_get(v, "replies");
396+            while (xs_list_next(p, &v, &c)) {
397+                const char *name       = xs_dict_get(v, "name");
398+                const xs_dict *replies = xs_dict_get(v, "replies");
399 
400                 if (name && replies) {
401                     char *ti = (char *)xs_number_str(xs_dict_get(replies, "totalItems"));
402@@ -1737,9 +1742,10 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
403                         xs_html_attr("name", "irt"),
404                         xs_html_attr("value", id))));
405 
406-            while (xs_list_iter(&p, &v)) {
407-                char *name = xs_dict_get(v, "name");
408-                xs_dict *replies = xs_dict_get(v, "replies");
409+            int c = 0;
410+            while (xs_list_next(p, &v, &c)) {
411+                const char *name = xs_dict_get(v, "name");
412+                const xs_dict *replies = xs_dict_get(v, "replies");
413 
414                 if (name) {
415                     char *ti = (char *)xs_number_str(xs_dict_get(replies, "totalItems"));
416@@ -1777,7 +1783,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
417         }
418         else {
419             /* show when the poll closes */
420-            char *end_time = xs_dict_get(msg, "endTime");
421+            const char *end_time = xs_dict_get(msg, "endTime");
422 
423             /* Pleroma does not have an endTime field;
424                it has a closed time in the future */
425@@ -1820,12 +1826,12 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
426         xs_html_add(snac_content,
427             content_attachments);
428 
429-        xs_list *p = attach;
430-
431-        while (xs_list_iter(&p, &v)) {
432-            char *type = xs_dict_get(v, "type");
433-            char *href = xs_dict_get(v, "href");
434-            char *name = xs_dict_get(v, "name");
435+        int c = 0;
436+        xs_dict *a;
437+        while (xs_list_next(attach, &a, &c)) {
438+            const char *type = xs_dict_get(a, "type");
439+            const char *href = xs_dict_get(a, "href");
440+            const char *name = xs_dict_get(a, "name");
441 
442             if (xs_startswith(type, "image/") || strcmp(type, "Image") == 0) {
443                 xs_html_add(content_attachments,
444@@ -1889,7 +1895,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
445     }
446 
447     /* has this message an audience (i.e., comes from a channel or community)? */
448-    char *audience = xs_dict_get(msg, "audience");
449+    const char *audience = xs_dict_get(msg, "audience");
450     if (strcmp(type, "Page") == 0 && !xs_is_null(audience)) {
451         xs_html *au_tag = xs_html_tag("p",
452             xs_html_text("("),
453@@ -2023,11 +2029,12 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only,
454 
455     if (xs_list_len(list) == 1) {
456         /* only one element? pick the description from the source */
457-        char *id = xs_list_get(list, 0);
458+        const char *id = xs_list_get(list, 0);
459         xs *d = NULL;
460         object_get_by_md5(id, &d);
461-        if (d && (v = xs_dict_get(d, "sourceContent")) != NULL)
462-            desc = xs_dup(v);
463+        const char *sc = xs_dict_get(d, "sourceContent");
464+        if (d && sc != NULL)
465+            desc = xs_dup(sc);
466 
467         alternate = xs_dup(xs_dict_get(d, "id"));
468     }
469@@ -2087,13 +2094,13 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only,
470 
471         /* is this message a non-public reply? */
472         if (user != NULL && !is_msg_public(msg)) {
473-            char *irt = xs_dict_get(msg, "inReplyTo");
474+            const char *irt = xs_dict_get(msg, "inReplyTo");
475 
476             /* is it a reply to something not in the storage? */
477             if (!xs_is_null(irt) && !object_here(irt)) {
478                 /* is it for me? */
479-                xs_list *to = xs_dict_get_def(msg, "to", xs_stock(XSTYPE_LIST));
480-                xs_list *cc = xs_dict_get_def(msg, "cc", xs_stock(XSTYPE_LIST));
481+                const xs_list *to = xs_dict_get_def(msg, "to", xs_stock(XSTYPE_LIST));
482+                const xs_list *cc = xs_dict_get_def(msg, "cc", xs_stock(XSTYPE_LIST));
483 
484                 if (xs_list_in(to, user->actor) == -1 && xs_list_in(cc, user->actor) == -1) {
485                     snac_debug(user, 1, xs_fmt("skipping non-public reply to an unknown post %s", v));
486@@ -2212,7 +2219,7 @@ xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t)
487                     html_actor_icon(snac, actor, xs_dict_get(actor, "published"), NULL, NULL, 0, 1)));
488 
489             /* content (user bio) */
490-            char *c = xs_dict_get(actor, "summary");
491+            const char *c = xs_dict_get(actor, "summary");
492 
493             if (!xs_is_null(c)) {
494                 xs *sc = sanitize(c);
495@@ -2364,10 +2371,10 @@ xs_str *html_notifications(snac *user, int skip, int show)
496             continue;
497 
498         xs *obj = NULL;
499-        char *type  = xs_dict_get(noti, "type");
500-        char *utype = xs_dict_get(noti, "utype");
501-        char *id    = xs_dict_get(noti, "objid");
502-        char *date  = xs_dict_get(noti, "date");
503+        const char *type  = xs_dict_get(noti, "type");
504+        const char *utype = xs_dict_get(noti, "utype");
505+        const char *id    = xs_dict_get(noti, "objid");
506+        const char *date  = xs_dict_get(noti, "date");
507 
508         if (xs_is_null(id) || !valid_status(object_get(id, &obj)))
509             continue;
510@@ -2375,14 +2382,14 @@ xs_str *html_notifications(snac *user, int skip, int show)
511         if (is_hidden(user, id))
512             continue;
513 
514-        char *actor_id = xs_dict_get(noti, "actor");
515+        const char *actor_id = xs_dict_get(noti, "actor");
516         xs *actor = NULL;
517 
518         if (!valid_status(actor_get(actor_id, &actor)))
519             continue;
520 
521         xs *a_name = actor_name(actor);
522-        char *label = type;
523+        const char *label = type;
524 
525         if (strcmp(type, "Create") == 0)
526             label = L("Mention");
527@@ -2494,14 +2501,14 @@ xs_str *html_notifications(snac *user, int skip, int show)
528 int html_get_handler(const xs_dict *req, const char *q_path,
529                      char **body, int *b_size, char **ctype, xs_str **etag)
530 {
531-    char *accept = xs_dict_get(req, "accept");
532+    const char *accept = xs_dict_get(req, "accept");
533     int status = 404;
534     snac snac;
535     xs *uid = NULL;
536-    char *p_path;
537+    const char *p_path;
538     int cache = 1;
539     int save = 1;
540-    char *v;
541+    const char *v;
542 
543     xs *l = xs_split_n(q_path, "/", 2);
544     v = xs_list_get(l, 1);
545@@ -2540,7 +2547,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
546 
547     int skip = 0;
548     int show = xs_number_get(xs_dict_get(srv_config, "max_timeline_entries"));
549-    xs_dict *q_vars = xs_dict_get(req, "q_vars");
550+    const xs_dict *q_vars = xs_dict_get(req, "q_vars");
551     if ((v = xs_dict_get(q_vars, "skip")) != NULL)
552         skip = atoi(v), cache = 0, save = 0;
553     if ((v = xs_dict_get(q_vars, "show")) != NULL)
554@@ -2585,7 +2592,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
555             status = 401;
556         }
557         else {
558-            char *q = xs_dict_get(q_vars, "q");
559+            const char *q = xs_dict_get(q_vars, "q");
560 
561             if (q && *q) {
562                 if (*q == '#') {
563@@ -2669,7 +2676,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
564         }
565         else {
566             xs *l = xs_split(p_path, "/");
567-            char *md5 = xs_list_get(l, -1);
568+            const char *md5 = xs_list_get(l, -1);
569 
570             if (md5 && *md5 && timeline_here(&snac, md5)) {
571                 xs *list = xs_list_append(xs_list_new(), md5);
572@@ -2728,7 +2735,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
573         }
574         else {
575             xs *l = xs_split(p_path, "/");
576-            char *lid = xs_list_get(l, -1);
577+            const char *lid = xs_list_get(l, -1);
578 
579             xs *list = list_timeline(&snac, lid, skip, show);
580             xs *next = list_timeline(&snac, lid, skip + show, 1);
581@@ -2767,7 +2774,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
582     else
583     if (xs_startswith(p_path, "s/")) { /** a static file **/
584         xs *l    = xs_split(p_path, "/");
585-        char *id = xs_list_get(l, 1);
586+        const char *id = xs_list_get(l, 1);
587         int sz;
588 
589         if (id && *id) {
590@@ -2788,8 +2795,8 @@ int html_get_handler(const xs_dict *req, const char *q_path,
591         if (xs_type(xs_dict_get(srv_config, "disable_history")) == XSTYPE_TRUE)
592             return 403;
593 
594-        xs *l    = xs_split(p_path, "/");
595-        char *id = xs_list_get(l, 1);
596+        xs *l = xs_split(p_path, "/");
597+        const char *id = xs_list_get(l, 1);
598 
599         if (id && *id) {
600             if (xs_endswith(id, "timeline.html_")) {
601@@ -2845,8 +2852,9 @@ int html_post_handler(const xs_dict *req, const char *q_path,
602 
603     int status = 0;
604     snac snac;
605-    char *uid, *p_path;
606-    xs_dict *p_vars;
607+    const char *uid;
608+    const char *p_path;
609+    const xs_dict *p_vars;
610 
611     xs *l = xs_split_n(q_path, "/", 2);
612 
613@@ -2874,15 +2882,15 @@ int html_post_handler(const xs_dict *req, const char *q_path,
614 
615     if (p_path && strcmp(p_path, "admin/note") == 0) { /** **/
616         /* post note */
617-        xs_str *content      = xs_dict_get(p_vars, "content");
618-        xs_str *in_reply_to  = xs_dict_get(p_vars, "in_reply_to");
619-        xs_str *attach_url   = xs_dict_get(p_vars, "attach_url");
620-        xs_list *attach_file = xs_dict_get(p_vars, "attach");
621-        xs_str *to           = xs_dict_get(p_vars, "to");
622-        xs_str *sensitive    = xs_dict_get(p_vars, "sensitive");
623-        xs_str *summary      = xs_dict_get(p_vars, "summary");
624-        xs_str *edit_id      = xs_dict_get(p_vars, "edit_id");
625-        xs_str *alt_text     = xs_dict_get(p_vars, "alt_text");
626+        const xs_str *content      = xs_dict_get(p_vars, "content");
627+        const xs_str *in_reply_to  = xs_dict_get(p_vars, "in_reply_to");
628+        const xs_str *attach_url   = xs_dict_get(p_vars, "attach_url");
629+        const xs_list *attach_file = xs_dict_get(p_vars, "attach");
630+        const xs_str *to           = xs_dict_get(p_vars, "to");
631+        const xs_str *sensitive    = xs_dict_get(p_vars, "sensitive");
632+        const xs_str *summary      = xs_dict_get(p_vars, "summary");
633+        const xs_str *edit_id      = xs_dict_get(p_vars, "edit_id");
634+        const xs_str *alt_text     = xs_dict_get(p_vars, "alt_text");
635         int priv             = !xs_is_null(xs_dict_get(p_vars, "mentioned_only"));
636         xs *attach_list      = xs_list_new();
637 
638@@ -2902,7 +2910,7 @@ int html_post_handler(const xs_dict *req, const char *q_path,
639 
640         /* is attach_file set? */
641         if (!xs_is_null(attach_file) && xs_type(attach_file) == XSTYPE_LIST) {
642-            char *fn = xs_list_get(attach_file, 0);
643+            const char *fn = xs_list_get(attach_file, 0);
644 
645             if (*fn != '\0') {
646                 char *ext = strrchr(fn, '.');
647@@ -2978,7 +2986,7 @@ int html_post_handler(const xs_dict *req, const char *q_path,
648                     int n;
649 
650                     for (n = 0; fields[n]; n++) {
651-                        char *v = xs_dict_get(p_msg, fields[n]);
652+                        const char *v = xs_dict_get(p_msg, fields[n]);
653                         msg = xs_dict_set(msg, fields[n], v);
654                     }
655 
656@@ -3007,10 +3015,10 @@ int html_post_handler(const xs_dict *req, const char *q_path,
657     else
658     if (p_path && strcmp(p_path, "admin/action") == 0) { /** **/
659         /* action on an entry */
660-        char *id     = xs_dict_get(p_vars, "id");
661-        char *actor  = xs_dict_get(p_vars, "actor");
662-        char *action = xs_dict_get(p_vars, "action");
663-        char *group  = xs_dict_get(p_vars, "group");
664+        const char *id     = xs_dict_get(p_vars, "id");
665+        const char *actor  = xs_dict_get(p_vars, "actor");
666+        const char *action = xs_dict_get(p_vars, "action");
667+        const char *group  = xs_dict_get(p_vars, "group");
668 
669         if (action == NULL)
670             return 404;
671@@ -3134,7 +3142,7 @@ int html_post_handler(const xs_dict *req, const char *q_path,
672         }
673         else
674         if (strcmp(action, L("Delete")) == 0) { /** **/
675-            char *actor_form = xs_dict_get(p_vars, "actor-form");
676+            const char *actor_form = xs_dict_get(p_vars, "actor-form");
677             if (actor_form != NULL) {
678                 /* delete follower */
679                 if (valid_status(follower_del(&snac, actor)))
680@@ -3178,8 +3186,8 @@ int html_post_handler(const xs_dict *req, const char *q_path,
681     else
682     if (p_path && strcmp(p_path, "admin/user-setup") == 0) { /** **/
683         /* change of user data */
684-        char *v;
685-        char *p1, *p2;
686+        const char *v;
687+        const char *p1, *p2;
688 
689         if ((v = xs_dict_get(p_vars, "name")) != NULL)
690             snac.config = xs_dict_set(snac.config, "name", v);
691@@ -3245,7 +3253,7 @@ int html_post_handler(const xs_dict *req, const char *q_path,
692         for (n = 0; uploads[n]; n++) {
693             xs *var_name = xs_fmt("%s_file", uploads[n]);
694 
695-            xs_list *uploaded_file = xs_dict_get(p_vars, var_name);
696+            const xs_list *uploaded_file = xs_dict_get(p_vars, var_name);
697             if (xs_type(uploaded_file) == XSTYPE_LIST) {
698                 const char *fn = xs_list_get(uploaded_file, 0);
699 
700@@ -3310,7 +3318,7 @@ int html_post_handler(const xs_dict *req, const char *q_path,
701     }
702     else
703     if (p_path && strcmp(p_path, "admin/vote") == 0) { /** **/
704-        char *irt         = xs_dict_get(p_vars, "irt");
705+        const char *irt   = xs_dict_get(p_vars, "irt");
706         const char *opt   = xs_dict_get(p_vars, "question");
707         const char *actor = xs_dict_get(p_vars, "actor");
708 
709@@ -3345,7 +3353,7 @@ int html_post_handler(const xs_dict *req, const char *q_path,
710             xs *poll = NULL;
711 
712             if (valid_status(object_get(irt, &poll))) {
713-                char *date = xs_dict_get(poll, "endTime");
714+                const char *date = xs_dict_get(poll, "endTime");
715                 if (xs_is_null(date))
716                     date = xs_dict_get(poll, "closed");
717 
718@@ -3363,7 +3371,7 @@ int html_post_handler(const xs_dict *req, const char *q_path,
719     }
720 
721     if (status == 303) {
722-        char *redir = xs_dict_get(p_vars, "redir");
723+        const char *redir = xs_dict_get(p_vars, "redir");
724 
725         if (xs_is_null(redir))
726             redir = "top";
727@@ -3411,8 +3419,8 @@ xs_str *timeline_to_rss(snac *user, const xs_list *timeline, char *title, char *
728                 continue;
729         }
730 
731-        char *id = xs_dict_get(msg, "id");
732-        char *content = xs_dict_get(msg, "content");
733+        const char *id = xs_dict_get(msg, "id");
734+        const char *content = xs_dict_get(msg, "content");
735 
736         if (user && !xs_startswith(id, user->actor))
737             continue;
M http.c
+14, -13
 1@@ -12,7 +12,7 @@
 2 
 3 xs_dict *http_signed_request_raw(const char *keyid, const char *seckey,
 4                             const char *method, const char *url,
 5-                            xs_dict *headers,
 6+                            const xs_dict *headers,
 7                             const char *body, int b_size,
 8                             int *status, xs_str **payload, int *p_size,
 9                             int timeout)
10@@ -24,8 +24,8 @@ xs_dict *http_signed_request_raw(const char *keyid, const char *seckey,
11     xs *s64 = NULL;
12     xs *signature = NULL;
13     xs *hdrs = NULL;
14-    char *host;
15-    char *target;
16+    const char *host;
17+    const char *target;
18     char *k, *v;
19     xs_dict *response;
20 
21@@ -106,13 +106,13 @@ xs_dict *http_signed_request_raw(const char *keyid, const char *seckey,
22 
23 
24 xs_dict *http_signed_request(snac *snac, const char *method, const char *url,
25-                            xs_dict *headers,
26+                            const xs_dict *headers,
27                             const char *body, int b_size,
28                             int *status, xs_str **payload, int *p_size,
29                             int timeout)
30 /* does a signed HTTP request */
31 {
32-    char *seckey = xs_dict_get(snac->key, "secret");
33+    const char *seckey = xs_dict_get(snac->key, "secret");
34     xs_dict *response;
35 
36     response = http_signed_request_raw(snac->actor, seckey, method, url,
37@@ -122,17 +122,18 @@ xs_dict *http_signed_request(snac *snac, const char *method, const char *url,
38 }
39 
40 
41-int check_signature(xs_dict *req, xs_str **err)
42+int check_signature(const xs_dict *req, xs_str **err)
43 /* check the signature */
44 {
45-    char *sig_hdr = xs_dict_get(req, "signature");
46+    const char *sig_hdr = xs_dict_get(req, "signature");
47     xs *keyId = NULL;
48     xs *headers = NULL;
49     xs *signature = NULL;
50     xs *created = NULL;
51     xs *expires = NULL;
52-    char *pubkey;
53     char *p;
54+    const char *pubkey;
55+    const char *k;
56 
57     if (xs_is_null(sig_hdr)) {
58         *err = xs_fmt("missing 'signature' header");
59@@ -142,10 +143,10 @@ int check_signature(xs_dict *req, xs_str **err)
60     {
61         /* extract the values */
62         xs *l = xs_split(sig_hdr, ",");
63-        xs_list *p = l;
64+        int c = 0;
65         xs_val *v;
66 
67-        while (xs_list_iter(&p, &v)) {
68+        while (xs_list_next(l, &v, &c)) {
69             xs *kv = xs_split_n(v, "=", 1);
70 
71             if (xs_list_len(kv) != 2)
72@@ -192,8 +193,8 @@ int check_signature(xs_dict *req, xs_str **err)
73         return 0;
74     }
75 
76-    if ((p = xs_dict_get(actor, "publicKey")) == NULL ||
77-        ((pubkey = xs_dict_get(p, "publicKeyPem")) == NULL)) {
78+    if ((k = xs_dict_get(actor, "publicKey")) == NULL ||
79+        ((pubkey = xs_dict_get(k, "publicKeyPem")) == NULL)) {
80         *err = xs_fmt("cannot get pubkey from %s", keyId);
81         return 0;
82     }
83@@ -208,7 +209,7 @@ int check_signature(xs_dict *req, xs_str **err)
84 
85         p = l;
86         while (xs_list_iter(&p, &v)) {
87-            char *hc;
88+            const char *hc;
89             xs *ss = NULL;
90 
91             if (*sig_str != '\0')
+8, -8
 1@@ -125,7 +125,7 @@ static xs_str *greeting_html(void)
 2 
 3         /* does it have a %userlist% mark? */
 4         if (xs_str_in(s, "%userlist%") != -1) {
 5-            char *host = xs_dict_get(srv_config, "host");
 6+            const char *host = xs_dict_get(srv_config, "host");
 7             xs *list = user_list();
 8             xs_list *p = list;
 9             xs_str *uid;
10@@ -171,14 +171,14 @@ int server_get_handler(xs_dict *req, const char *q_path,
11 
12     /* is it the server root? */
13     if (*q_path == '\0') {
14-        xs_dict *q_vars = xs_dict_get(req, "q_vars");
15-        char *t = NULL;
16+        const xs_dict *q_vars = xs_dict_get(req, "q_vars");
17+        const char *t = NULL;
18 
19         if (xs_type(q_vars) == XSTYPE_DICT && (t = xs_dict_get(q_vars, "t"))) {
20             /** search by tag **/
21             int skip = 0;
22             int show = xs_number_get(xs_dict_get(srv_config, "max_timeline_entries"));
23-            char *v;
24+            const char *v;
25 
26             if ((v = xs_dict_get(q_vars, "skip")) != NULL)
27                 skip = atoi(v);
28@@ -193,7 +193,7 @@ int server_get_handler(xs_dict *req, const char *q_path,
29                 more = 1;
30             }
31 
32-            char *accept = xs_dict_get(req, "accept");
33+            const char *accept = xs_dict_get(req, "accept");
34             if (!xs_is_null(accept) && strcmp(accept, "application/rss+xml") == 0) {
35                 xs *link = xs_fmt("%s/?t=%s", srv_baseurl, t);
36 
37@@ -268,7 +268,7 @@ void httpd_connection(FILE *f)
38 /* the connection processor */
39 {
40     xs *req;
41-    char *method;
42+    const char *method;
43     int status   = 0;
44     xs_str *body = NULL;
45     int b_size   = 0;
46@@ -278,7 +278,7 @@ void httpd_connection(FILE *f)
47     xs *payload  = NULL;
48     xs *etag     = NULL;
49     int p_size   = 0;
50-    char *p;
51+    const char *p;
52     int fcgi_id;
53 
54     if (p_state->use_fcgi)
55@@ -411,7 +411,7 @@ void httpd_connection(FILE *f)
56         headers = xs_dict_append(headers, "etag", etag);
57 
58     /* if there are any additional headers, add them */
59-    xs_dict *more_headers = xs_dict_get(srv_config, "http_headers");
60+    const xs_dict *more_headers = xs_dict_get(srv_config, "http_headers");
61     if (xs_type(more_headers) == XSTYPE_DICT) {
62         char *k, *v;
63         int c = 0;
M main.c
+1, -1
1@@ -315,7 +315,7 @@ int main(int argc, char *argv[])
2         xs *msg = msg_follow(&snac, url);
3 
4         if (msg != NULL) {
5-            char *actor = xs_dict_get(msg, "object");
6+            const char *actor = xs_dict_get(msg, "object");
7 
8             following_add(&snac, actor, msg);
9 
+57, -52
  1@@ -175,7 +175,7 @@ int oauth_get_handler(const xs_dict *req, const char *q_path,
  2         return 0;
  3 
  4     int status   = 404;
  5-    xs_dict *msg = xs_dict_get(req, "q_vars");
  6+    const xs_dict *msg = xs_dict_get(req, "q_vars");
  7     xs *cmd      = xs_replace_n(q_path, "/oauth", "", 1);
  8 
  9     srv_debug(1, xs_fmt("oauth_get_handler %s", q_path));
 10@@ -239,7 +239,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
 11 
 12     int status   = 404;
 13 
 14-    char *i_ctype = xs_dict_get(req, "content-type");
 15+    const char *i_ctype = xs_dict_get(req, "content-type");
 16     xs *args      = NULL;
 17 
 18     if (i_ctype && xs_startswith(i_ctype, "application/json")) {
 19@@ -568,10 +568,10 @@ xs_dict *mastoapi_account(const xs_dict *actor)
 20     acct = xs_dict_append(acct, "uri", id);
 21 
 22     xs *avatar  = NULL;
 23-    xs_dict *av = xs_dict_get(actor, "icon");
 24+    const xs_dict *av = xs_dict_get(actor, "icon");
 25 
 26     if (xs_type(av) == XSTYPE_DICT) {
 27-        char *url = xs_dict_get(av, "url");
 28+        const char *url = xs_dict_get(av, "url");
 29 
 30         if (url != NULL)
 31             avatar = xs_dup(url);
 32@@ -584,7 +584,7 @@ xs_dict *mastoapi_account(const xs_dict *actor)
 33     acct = xs_dict_append(acct, "avatar_static", avatar);
 34 
 35     xs *header  = NULL;
 36-    xs_dict *hd = xs_dict_get(actor, "image");
 37+    const xs_dict *hd = xs_dict_get(actor, "image");
 38 
 39     if (xs_type(hd) == XSTYPE_DICT)
 40         header = xs_dup(xs_dict_get(hd, "url"));
 41@@ -596,12 +596,13 @@ xs_dict *mastoapi_account(const xs_dict *actor)
 42     acct = xs_dict_append(acct, "header_static", header);
 43 
 44     /* emojis */
 45-    xs_list *p;
 46+    const xs_list *p;
 47     if (!xs_is_null(p = xs_dict_get(actor, "tag"))) {
 48         xs *eml = xs_list_new();
 49         xs_dict *v;
 50+        int c = 0;
 51 
 52-        while (xs_list_iter(&p, &v)) {
 53+        while (xs_list_next(p, &v, &c)) {
 54             const char *type = xs_dict_get(v, "type");
 55 
 56             if (!xs_is_null(type) && strcmp(type, "Emoji") == 0) {
 57@@ -640,7 +641,7 @@ xs_dict *mastoapi_account(const xs_dict *actor)
 58 
 59     /* dict of validated links */
 60     xs_dict *val_links = NULL;
 61-    xs_dict *metadata  = xs_stock(XSTYPE_DICT);
 62+    const xs_dict *metadata  = xs_stock(XSTYPE_DICT);
 63     snac user = {0};
 64 
 65     if (xs_startswith(id, srv_baseurl)) {
 66@@ -654,19 +655,20 @@ xs_dict *mastoapi_account(const xs_dict *actor)
 67     if (xs_is_null(val_links))
 68         val_links = xs_stock(XSTYPE_DICT);
 69 
 70-    while (xs_list_iter(&p, &v)) {
 71-        char *type  = xs_dict_get(v, "type");
 72-        char *name  = xs_dict_get(v, "name");
 73-        char *value = xs_dict_get(v, "value");
 74+    int c = 0;
 75+    while (xs_list_next(p, &v, &c)) {
 76+        const char *type  = xs_dict_get(v, "type");
 77+        const char *name  = xs_dict_get(v, "name");
 78+        const char *value = xs_dict_get(v, "value");
 79 
 80         if (!xs_is_null(type) && !xs_is_null(name) &&
 81             !xs_is_null(value) && strcmp(type, "PropertyValue") == 0) {
 82             xs *val_date = NULL;
 83 
 84-            char *url = xs_dict_get(metadata, name);
 85+            const char *url = xs_dict_get(metadata, name);
 86 
 87             if (!xs_is_null(url) && xs_startswith(url, "https:/" "/")) {
 88-                xs_number *verified_time = xs_dict_get(val_links, url);
 89+                const xs_number *verified_time = xs_dict_get(val_links, url);
 90                 if (xs_type(verified_time) == XSTYPE_NUMBER) {
 91                     time_t t = xs_number_get(verified_time);
 92 
 93@@ -695,7 +697,7 @@ xs_dict *mastoapi_account(const xs_dict *actor)
 94 }
 95 
 96 
 97-xs_str *mastoapi_date(char *date)
 98+xs_str *mastoapi_date(const char *date)
 99 /* converts an ISO 8601 date to whatever format Mastodon uses */
100 {
101     xs_str *s = xs_crop_i(xs_dup(date), 0, 19);
102@@ -710,13 +712,13 @@ xs_dict *mastoapi_poll(snac *snac, const xs_dict *msg)
103 {
104     xs_dict *poll = xs_dict_new();
105     xs *mid       = mastoapi_id(msg);
106-    xs_list *opts = NULL;
107+    const xs_list *opts = NULL;
108     xs_val *v;
109     int num_votes = 0;
110     xs *options = xs_list_new();
111 
112     poll = xs_dict_append(poll, "id", mid);
113-    char *date = xs_dict_get(msg, "endTime");
114+    const char *date = xs_dict_get(msg, "endTime");
115     if (date == NULL)
116         date = xs_dict_get(msg, "closed");
117     if (date == NULL)
118@@ -741,7 +743,8 @@ xs_dict *mastoapi_poll(snac *snac, const xs_dict *msg)
119         poll = xs_dict_append(poll, "multiple", xs_stock(XSTYPE_TRUE));
120     }
121 
122-    while (xs_list_iter(&opts, &v)) {
123+    int c = 0;
124+    while (xs_list_next(opts, &v, &c)) {
125         const char *title   = xs_dict_get(v, "name");
126         const char *replies = xs_dict_get(v, "replies");
127 
128@@ -794,7 +797,7 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg)
129 
130     xs *idx = NULL;
131     xs *ixc = NULL;
132-    char *tmp;
133+    const char *tmp;
134     xs *mid  = mastoapi_id(msg);
135 
136     xs_dict *st = xs_dict_new();
137@@ -851,9 +854,9 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg)
138         xs *matt = xs_list_new();
139 
140         while (xs_list_iter(&p, &v)) {
141-            char *type = xs_dict_get(v, "type");
142-            char *href = xs_dict_get(v, "href");
143-            char *name = xs_dict_get(v, "name");
144+            const char *type = xs_dict_get(v, "type");
145+            const char *href = xs_dict_get(v, "href");
146+            const char *name = xs_dict_get(v, "name");
147 
148             if (xs_match(type, "image/*|video/*|Image|Video")) { /* */
149                 xs *matteid = xs_fmt("%s_%d", id, xs_list_len(matt));
150@@ -879,7 +882,7 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg)
151         xs *ml  = xs_list_new();
152         xs *htl = xs_list_new();
153         xs *eml = xs_list_new();
154-        xs_list *tag = xs_dict_get(msg, "tag");
155+        const xs_list *tag = xs_dict_get(msg, "tag");
156         int n = 0;
157 
158         xs *tag_list = NULL;
159@@ -897,7 +900,8 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg)
160         tag = tag_list;
161         xs_dict *v;
162 
163-        while (xs_list_iter(&tag, &v)) {
164+        int c = 0;
165+        while (xs_list_next(tag, &v, &c)) {
166             const char *type = xs_dict_get(v, "type");
167 
168             if (xs_is_null(type))
169@@ -1006,7 +1010,7 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg)
170             xs *irt_mid = mastoapi_id(irto);
171             st = xs_dict_set(st, "in_reply_to_id", irt_mid);
172 
173-            char *at = NULL;
174+            const char *at = NULL;
175             if (!xs_is_null(at = get_atto(irto))) {
176                 xs *at_md5 = xs_md5_hex(at, strlen(at));
177                 st = xs_dict_set(st, "in_reply_to_account_id", at_md5);
178@@ -1118,7 +1122,7 @@ int process_auth_token(snac *snac, const xs_dict *req)
179 /* processes an authorization token, if there is one */
180 {
181     int logged_in = 0;
182-    char *v;
183+    const char *v;
184 
185     /* if there is an authorization field, try to validate it */
186     if (!xs_is_null(v = xs_dict_get(req, "authorization")) && xs_startswith(v, "Bearer ")) {
187@@ -1156,7 +1160,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
188         return 0;
189 
190     int status    = 404;
191-    xs_dict *args = xs_dict_get(req, "q_vars");
192+    const xs_dict *args = xs_dict_get(req, "q_vars");
193     xs *cmd       = xs_replace_n(q_path, "/api", "", 1);
194 
195     snac snac1 = {0};
196@@ -1182,7 +1186,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
197             acct = xs_dict_append(acct, "source", src);
198 
199             xs *avatar = NULL;
200-            char *av   = xs_dict_get(snac1.config, "avatar");
201+            const char *av   = xs_dict_get(snac1.config, "avatar");
202 
203             if (xs_is_null(av) || *av == '\0')
204                 avatar = xs_fmt("%s/susie.png", srv_baseurl);
205@@ -1193,7 +1197,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
206             acct = xs_dict_append(acct, "avatar_static", avatar);
207 
208             xs *header = NULL;
209-            char *hd = xs_dict_get(snac1.config, "header");
210+            const char *hd = xs_dict_get(snac1.config, "header");
211 
212             if (!xs_is_null(hd))
213                 header = xs_dup(hd);
214@@ -1203,7 +1207,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
215             acct = xs_dict_append(acct, "header",        header);
216             acct = xs_dict_append(acct, "header_static", header);
217 
218-            xs_dict *metadata = xs_dict_get(snac1.config, "metadata");
219+            const xs_dict *metadata = xs_dict_get(snac1.config, "metadata");
220             if (xs_type(metadata) == XSTYPE_DICT) {
221                 xs *fields = xs_list_new();
222                 xs_str *k;
223@@ -1217,7 +1221,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
224                 while (xs_dict_next(metadata, &k, &v, &c)) {
225                     xs *val_date = NULL;
226 
227-                    xs_number *verified_time = xs_dict_get(val_links, v);
228+                    const xs_number *verified_time = xs_dict_get(val_links, v);
229                     if (xs_type(verified_time) == XSTYPE_NUMBER) {
230                         time_t t = xs_number_get(verified_time);
231 
232@@ -1283,13 +1287,13 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
233     else
234     if (strcmp(cmd, "/v1/accounts/lookup") == 0) { /** **/
235         /* lookup an account */
236-        char *acct = xs_dict_get(args, "acct");
237+        const char *acct = xs_dict_get(args, "acct");
238 
239         if (!xs_is_null(acct)) {
240             xs *s = xs_strip_chars_i(xs_dup(acct), "@");
241             xs *l = xs_split_n(s, "@", 1);
242-            char *uid = xs_list_get(l, 0);
243-            char *host = xs_list_get(l, 1);
244+            const char *uid = xs_list_get(l, 0);
245+            const char *host = xs_list_get(l, 1);
246 
247             if (uid && (!host || strcmp(host, xs_dict_get(srv_config, "host")) == 0)) {
248                 snac user;
249@@ -1624,7 +1628,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
250 
251         /* get the tag */
252         xs *l = xs_split(cmd, "/");
253-        char *tag = xs_list_get(l, -1);
254+        const char *tag = xs_list_get(l, -1);
255 
256         xs *timeline = tag_search(tag, 0, limit);
257         xs *out      = xs_list_new();
258@@ -1664,7 +1668,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
259         /* get the list id */
260         if (logged_in) {
261             xs *l = xs_split(cmd, "/");
262-            char *list = xs_list_get(l, -1);
263+            const char *list = xs_list_get(l, -1);
264 
265             xs *timeline = list_timeline(&snac1, list, 0, 2048);
266             xs *out      = xs_list_new();
267@@ -1744,7 +1748,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
268             xs *out    = xs_list_new();
269             xs_list *p = l;
270             xs_dict *v;
271-            xs_list *excl = xs_dict_get(args, "exclude_types[]");
272+            const xs_list *excl = xs_dict_get(args, "exclude_types[]");
273 
274             while (xs_list_iter(&p, &v)) {
275                 xs *noti = notify_get(&snac1, v);
276@@ -1876,7 +1880,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
277     if (xs_startswith(cmd, "/v1/lists/")) { /** list information **/
278         if (logged_in) {
279             xs *l = xs_split(cmd, "/");
280-            char *p = xs_list_get(l, -1);
281+            const char *p = xs_list_get(l, -1);
282 
283             if (p) {
284                 if (strcmp(p, "accounts") == 0) {
285@@ -1910,7 +1914,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
286                     xs_list *v;
287 
288                     while (xs_list_next(lol, &v, &c)) {
289-                        char *id = xs_list_get(v, 0);
290+                        const char *id = xs_list_get(v, 0);
291 
292                         if (id && strcmp(id, p) == 0) {
293                             xs *d = xs_dict_new();
294@@ -2314,7 +2318,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
295 
296     int status    = 404;
297     xs *args      = NULL;
298-    char *i_ctype = xs_dict_get(req, "content-type");
299+    const char *i_ctype = xs_dict_get(req, "content-type");
300 
301     if (i_ctype && xs_startswith(i_ctype, "application/json")) {
302         if (!xs_is_null(payload))
303@@ -2487,7 +2491,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
304                 mid = MID_TO_MD5(mid);
305 
306                 if (valid_status(timeline_get_by_md5(&snac, mid, &msg))) {
307-                    char *id = xs_dict_get(msg, "id");
308+                    const char *id = xs_dict_get(msg, "id");
309 
310                     if (op == NULL) {
311                         /* no operation (?) */
312@@ -2593,7 +2597,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
313     if (strcmp(cmd, "/v1/push/subscription") == 0) { /** **/
314         /* I don't know what I'm doing */
315         if (logged_in) {
316-            char *v;
317+            const char *v;
318 
319             xs *wpush = xs_dict_new();
320 
321@@ -2765,7 +2769,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
322                     const char *id   = xs_dict_get(msg, "id");
323                     const char *atto = get_atto(msg);
324 
325-                    xs_list *opts = xs_dict_get(msg, "oneOf");
326+                    const xs_list *opts = xs_dict_get(msg, "oneOf");
327                     if (opts == NULL)
328                         opts = xs_dict_get(msg, "anyOf");
329 
330@@ -2773,7 +2777,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
331                     }
332                     else
333                     if (strcmp(op, "votes") == 0) {
334-                        xs_list *choices = xs_dict_get(args, "choices[]");
335+                        const xs_list *choices = xs_dict_get(args, "choices[]");
336 
337                         if (xs_is_null(choices))
338                             choices = xs_dict_get(args, "choices");
339@@ -2781,7 +2785,8 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
340                         if (xs_type(choices) == XSTYPE_LIST) {
341                             xs_str *v;
342 
343-                            while (xs_list_iter(&choices, &v)) {
344+                            int c = 0;
345+                            while (xs_list_next(choices, &v, &c)) {
346                                 int io           = atoi(v);
347                                 const xs_dict *o = xs_list_get(opts, io);
348 
349@@ -2843,12 +2848,12 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
350     if (xs_startswith(cmd, "/v1/lists/")) { /** list maintenance **/
351         if (logged_in) {
352             xs *l = xs_split(cmd, "/");
353-            char *op = xs_list_get(l, -1);
354-            char *id = xs_list_get(l, -2);
355+            const char *op = xs_list_get(l, -1);
356+            const char *id = xs_list_get(l, -2);
357 
358             if (op && id && xs_is_hex(id)) {
359                 if (strcmp(op, "accounts") == 0) {
360-                    xs_list *accts = xs_dict_get(args, "account_ids[]");
361+                    const xs_list *accts = xs_dict_get(args, "account_ids[]");
362                     int c = 0;
363                     char *v;
364 
365@@ -2888,7 +2893,7 @@ int mastoapi_delete_handler(const xs_dict *req, const char *q_path,
366 
367     int status    = 404;
368     xs *args      = NULL;
369-    char *i_ctype = xs_dict_get(req, "content-type");
370+    const char *i_ctype = xs_dict_get(req, "content-type");
371 
372     if (i_ctype && xs_startswith(i_ctype, "application/json")) {
373         if (!xs_is_null(payload))
374@@ -2921,13 +2926,13 @@ int mastoapi_delete_handler(const xs_dict *req, const char *q_path,
375     if (xs_startswith(cmd, "/v1/lists/")) {
376         if (logged_in) {
377             xs *l = xs_split(cmd, "/");
378-            char *p = xs_list_get(l, -1);
379+            const char *p = xs_list_get(l, -1);
380 
381             if (p) {
382                 if (strcmp(p, "accounts") == 0) {
383                     /* delete account from list */
384                     p = xs_list_get(l, -2);
385-                    xs_list *accts = xs_dict_get(args, "account_ids[]");
386+                    const xs_list *accts = xs_dict_get(args, "account_ids[]");
387                     int c = 0;
388                     char *v;
389 
390@@ -2971,7 +2976,7 @@ int mastoapi_put_handler(const xs_dict *req, const char *q_path,
391 
392     int status    = 404;
393     xs *args      = NULL;
394-    char *i_ctype = xs_dict_get(req, "content-type");
395+    const char *i_ctype = xs_dict_get(req, "content-type");
396 
397     if (i_ctype && xs_startswith(i_ctype, "application/json")) {
398         if (!xs_is_null(payload))
M snac.h
+23, -22
  1@@ -69,7 +69,7 @@ void snac_log(snac *user, xs_str *str);
  2 #define snac_debug(user, level, str) do { if (dbglevel >= (level)) \
  3     { snac_log((user), (str)); } } while (0)
  4 
  5-int srv_open(char *basedir, int auto_upgrade);
  6+int srv_open(const char *basedir, int auto_upgrade);
  7 void srv_free(void);
  8 
  9 int user_open(snac *snac, const char *uid);
 10@@ -88,7 +88,7 @@ void srv_archive(const char *direction, const char *url, xs_dict *req,
 11                  const char *body, int b_size);
 12 void srv_archive_error(const char *prefix, const xs_str *err,
 13                        const xs_dict *req, const xs_val *data);
 14-void srv_archive_qitem(char *prefix, xs_dict *q_item);
 15+void srv_archive_qitem(const char *prefix, xs_dict *q_item);
 16 
 17 double mtime_nl(const char *fn, int *n_link);
 18 #define mtime(fn) mtime_nl(fn, NULL)
 19@@ -139,13 +139,13 @@ double timeline_mtime(snac *snac);
 20 int timeline_touch(snac *snac);
 21 int timeline_here(snac *snac, const char *md5);
 22 int timeline_get_by_md5(snac *snac, const char *md5, xs_dict **msg);
 23-int timeline_del(snac *snac, char *id);
 24+int timeline_del(snac *snac, const char *id);
 25 xs_list *timeline_simple_list(snac *snac, const char *idx_name, int skip, int show);
 26 xs_list *timeline_list(snac *snac, const char *idx_name, int skip, int show);
 27 int timeline_add(snac *snac, const char *id, const xs_dict *o_msg);
 28 int timeline_admire(snac *snac, const char *id, const char *admirer, int like);
 29 
 30-xs_list *timeline_top_level(snac *snac, xs_list *list);
 31+xs_list *timeline_top_level(snac *snac, const xs_list *list);
 32 xs_list *local_list(snac *snac, int max);
 33 xs_list *timeline_instance_list(int skip, int show);
 34 
 35@@ -174,14 +174,14 @@ void hide(snac *snac, const char *id);
 36 int is_hidden(snac *snac, const char *id);
 37 
 38 void tag_index(const char *id, const xs_dict *obj);
 39-xs_list *tag_search(char *tag, int skip, int show);
 40+xs_list *tag_search(const char *tag, int skip, int show);
 41 
 42 xs_val *list_maint(snac *user, const char *list, int op);
 43 xs_list *list_timeline(snac *user, const char *list, int skip, int show);
 44 xs_val *list_content(snac *user, const char *list_id, const char *actor_md5, int op);
 45 void list_distribute(snac *user, const char *who, const xs_dict *post);
 46 
 47-int actor_add(const char *actor, xs_dict *msg);
 48+int actor_add(const char *actor, const xs_dict *msg);
 49 int actor_get(const char *actor, xs_dict **data);
 50 int actor_get_refresh(snac *user, const char *actor, xs_dict **data);
 51 
 52@@ -223,10 +223,13 @@ xs_list *content_search(snac *user, const char *regex,
 53 void enqueue_input(snac *snac, const xs_dict *msg, const xs_dict *req, int retries);
 54 void enqueue_shared_input(const xs_dict *msg, const xs_dict *req, int retries);
 55 void enqueue_output_raw(const char *keyid, const char *seckey,
 56-                        xs_dict *msg, xs_str *inbox, int retries, int p_status);
 57-void enqueue_output(snac *snac, xs_dict *msg, xs_str *inbox, int retries, int p_status);
 58-void enqueue_output_by_actor(snac *snac, xs_dict *msg, const xs_str *actor, int retries);
 59-void enqueue_email(xs_str *msg, int retries);
 60+                        const xs_dict *msg, const xs_str *inbox,
 61+                        int retries, int p_status);
 62+void enqueue_output(snac *snac, const xs_dict *msg,
 63+                    const xs_str *inbox, int retries, int p_status);
 64+void enqueue_output_by_actor(snac *snac, const xs_dict *msg,
 65+                             const xs_str *actor, int retries);
 66+void enqueue_email(const xs_str *msg, int retries);
 67 void enqueue_telegram(const xs_str *msg, const char *bot, const char *chat_id);
 68 void enqueue_ntfy(const xs_str *msg, const char *ntfy_server, const char *ntfy_token);
 69 void enqueue_message(snac *snac, const xs_dict *msg);
 70@@ -234,7 +237,6 @@ void enqueue_close_question(snac *user, const char *id, int end_secs);
 71 void enqueue_object_request(snac *user, const char *id, int forward_secs);
 72 void enqueue_verify_links(snac *user);
 73 void enqueue_actor_refresh(snac *user, const char *actor, int forward_secs);
 74-void enqueue_request_replies(snac *user, const char *id);
 75 int was_question_voted(snac *user, const char *id);
 76 
 77 xs_list *user_queue(snac *snac);
 78@@ -247,16 +249,16 @@ void purge_all(void);
 79 
 80 xs_dict *http_signed_request_raw(const char *keyid, const char *seckey,
 81                             const char *method, const char *url,
 82-                            xs_dict *headers,
 83+                            const xs_dict *headers,
 84                             const char *body, int b_size,
 85                             int *status, xs_str **payload, int *p_size,
 86                             int timeout);
 87 xs_dict *http_signed_request(snac *snac, const char *method, const char *url,
 88-                            xs_dict *headers,
 89+                            const xs_dict *headers,
 90                             const char *body, int b_size,
 91                             int *status, xs_str **payload, int *p_size,
 92                             int timeout);
 93-int check_signature(xs_dict *req, xs_str **err);
 94+int check_signature(const xs_dict *req, xs_str **err);
 95 
 96 srv_state *srv_state_op(xs_str **fname, int op);
 97 void httpd(void);
 98@@ -270,21 +272,21 @@ const char *default_avatar_base64(void);
 99 
100 xs_str *process_tags(snac *snac, const char *content, xs_list **tag);
101 
102-char *get_atto(const xs_dict *msg);
103+const char *get_atto(const xs_dict *msg);
104 xs_list *get_attachments(const xs_dict *msg);
105 
106-xs_dict *msg_admiration(snac *snac, char *object, char *type);
107-xs_dict *msg_repulsion(snac *user, char *id, char *type);
108+xs_dict *msg_admiration(snac *snac, const char *object, const char *type);
109+xs_dict *msg_repulsion(snac *user, const char *id, const char *type);
110 xs_dict *msg_create(snac *snac, const xs_dict *object);
111 xs_dict *msg_follow(snac *snac, const char *actor);
112 
113 xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts,
114-                  xs_str *in_reply_to, xs_list *attach, int priv);
115+                  const xs_str *in_reply_to, const xs_list *attach, int priv);
116 
117-xs_dict *msg_undo(snac *snac, char *object);
118-xs_dict *msg_delete(snac *snac, char *id);
119+xs_dict *msg_undo(snac *snac, const xs_val *object);
120+xs_dict *msg_delete(snac *snac, const char *id);
121 xs_dict *msg_actor(snac *snac);
122-xs_dict *msg_update(snac *snac, xs_dict *object);
123+xs_dict *msg_update(snac *snac, const xs_dict *object);
124 xs_dict *msg_ping(snac *user, const char *rcpt);
125 xs_dict *msg_pong(snac *user, const char *rcpt, const char *object);
126 xs_dict *msg_question(snac *user, const char *content, xs_list *attach,
127@@ -292,7 +294,6 @@ xs_dict *msg_question(snac *user, const char *content, xs_list *attach,
128 
129 int activitypub_request(snac *snac, const char *url, xs_dict **data);
130 int actor_request(snac *user, const char *actor, xs_dict **data);
131-void timeline_request_replies(snac *user, const char *id);
132 int send_to_inbox_raw(const char *keyid, const char *seckey,
133                   const xs_str *inbox, const xs_dict *msg,
134                   xs_val **payload, int *p_size, int timeout);
+27, -13
  1@@ -18,7 +18,7 @@ int snac_upgrade(xs_str **error)
  2     double f = 0.0;
  3 
  4     for (;;) {
  5-        char *layout = xs_dict_get(srv_config, "layout");
  6+        const char *layout = xs_dict_get(srv_config, "layout");
  7         double nf;
  8 
  9         f = nf = xs_number_get(layout);
 10@@ -56,8 +56,8 @@ int snac_upgrade(xs_str **error)
 11 
 12                     g = list;
 13                     while (xs_list_iter(&g, &fn)) {
 14-                        xs *l   = xs_split(fn, "/");
 15-                        char *b = xs_list_get(l, -1);
 16+                        xs *l = xs_split(fn, "/");
 17+                        const char *b = xs_list_get(l, -1);
 18                         xs *dir = xs_fmt("%s/object/%c%c", srv_basedir, b[0], b[1]);
 19                         xs *nfn = xs_fmt("%s/%s", dir, b);
 20 
 21@@ -152,12 +152,12 @@ int snac_upgrade(xs_str **error)
 22                             xs *o = xs_json_loads(s);
 23                             fclose(f);
 24 
 25-                            char *type = xs_dict_get(o, "type");
 26+                            const char *type = xs_dict_get(o, "type");
 27 
 28                             if (!xs_is_null(type) && strcmp(type, "Follow") == 0) {
 29                                 unlink(v);
 30 
 31-                                char *actor = xs_dict_get(o, "actor");
 32+                                const char *actor = xs_dict_get(o, "actor");
 33 
 34                                 if (!xs_is_null(actor))
 35                                     follower_add(&snac, actor);
 36@@ -198,22 +198,29 @@ int snac_upgrade(xs_str **error)
 37                             xs *meta = xs_dup(xs_dict_get(o, "_snac"));
 38                             o = xs_dict_del(o, "_snac");
 39 
 40-                            char *id = xs_dict_get(o, "id");
 41+                            const char *id = xs_dict_get(o, "id");
 42 
 43                             /* store object */
 44                             object_add_ow(id, o);
 45 
 46                             /* if it's from us, add to public */
 47                             if (xs_startswith(id, snac.actor)) {
 48-                                char *p, *v;
 49+                                const xs_list *p;
 50+                                char *v;
 51+                                int c;
 52 
 53                                 object_user_cache_add(&snac, id, "public");
 54 
 55                                 p = xs_dict_get(meta, "announced_by");
 56-                                while (xs_list_iter(&p, &v))
 57+
 58+                                c = 0;
 59+                                while (xs_list_next(p, &v, &c))
 60                                     object_admire(id, v, 0);
 61+
 62                                 p = xs_dict_get(meta, "liked_by");
 63-                                while (xs_list_iter(&p, &v))
 64+
 65+                                c = 0;
 66+                                while (xs_list_next(p, &v, &c))
 67                                     object_admire(id, v, 1);
 68                             }
 69 
 70@@ -257,21 +264,28 @@ int snac_upgrade(xs_str **error)
 71                             xs *meta = xs_dup(xs_dict_get(o, "_snac"));
 72                             o = xs_dict_del(o, "_snac");
 73 
 74-                            char *id = xs_dict_get(o, "id");
 75+                            const char *id = xs_dict_get(o, "id");
 76 
 77                             /* store object */
 78                             object_add_ow(id, o);
 79 
 80                             {
 81-                                char *p, *v;
 82+                                const xs_list *p;
 83+                                char *v;
 84+                                int c = 0;
 85 
 86                                 object_user_cache_add(&snac, id, "private");
 87 
 88                                 p = xs_dict_get(meta, "announced_by");
 89-                                while (xs_list_iter(&p, &v))
 90+
 91+                                c = 0;
 92+                                while (xs_list_next(p, &v, &c))
 93                                     object_admire(id, v, 0);
 94+
 95                                 p = xs_dict_get(meta, "liked_by");
 96-                                while (xs_list_iter(&p, &v))
 97+
 98+                                c = 0;
 99+                                while (xs_list_next(p, &v, &c))
100                                     object_admire(id, v, 1);
101                             }
102 
+1, -1
1@@ -418,7 +418,7 @@ int deluser(snac *user)
2 void verify_links(snac *user)
3 /* verifies a user's links */
4 {
5-    xs_dict *p = xs_dict_get(user->config, "metadata");
6+    const xs_dict *p = xs_dict_get(user->config, "metadata");
7     char *k, *v;
8     int changed = 0;
9 
+12, -11
 1@@ -16,7 +16,7 @@ int webfinger_request_signed(snac *snac, const char *qs, char **actor, char **us
 2     int p_size = 0;
 3     xs *headers = xs_dict_new();
 4     xs *l = NULL;
 5-    xs_str *host = NULL;
 6+    const char *host = NULL;
 7     xs *resource = NULL;
 8 
 9     if (xs_startswith(qs, "https:/") || xs_startswith(qs, "http:/")) {
10@@ -87,19 +87,20 @@ int webfinger_request_signed(snac *snac, const char *qs, char **actor, char **us
11 
12     if (obj) {
13         if (user != NULL) {
14-            char *subject = xs_dict_get(obj, "subject");
15+            const char *subject = xs_dict_get(obj, "subject");
16 
17             if (subject)
18                 *user = xs_replace_n(subject, "acct:", "", 1);
19         }
20 
21         if (actor != NULL) {
22-            char *list = xs_dict_get(obj, "links");
23+            const xs_list *list = xs_dict_get(obj, "links");
24+            int c = 0;
25             char *v;
26 
27-            while (xs_list_iter(&list, &v)) {
28+            while (xs_list_next(list, &v, &c)) {
29                 if (xs_type(v) == XSTYPE_DICT) {
30-                    char *type = xs_dict_get(v, "type");
31+                    const char *type = xs_dict_get(v, "type");
32 
33                     if (type && (strcmp(type, "application/activity+json") == 0 ||
34                                 strcmp(type, "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") == 0)) {
35@@ -133,8 +134,8 @@ int webfinger_get_handler(xs_dict *req, char *q_path,
36     if (strcmp(q_path, "/.well-known/webfinger") != 0)
37         return 0;
38 
39-    char *q_vars   = xs_dict_get(req, "q_vars");
40-    char *resource = xs_dict_get(q_vars, "resource");
41+    const char *q_vars   = xs_dict_get(req, "q_vars");
42+    const char *resource = xs_dict_get(q_vars, "resource");
43 
44     if (resource == NULL)
45         return 400;
46@@ -145,7 +146,7 @@ int webfinger_get_handler(xs_dict *req, char *q_path,
47     if (xs_startswith(resource, "https:/") || xs_startswith(resource, "http:/")) {
48         /* actor search: find a user with this actor */
49         xs *l = xs_split(resource, "/");
50-        char *uid = xs_list_get(l, -1);
51+        const char *uid = xs_list_get(l, -1);
52 
53         if (uid)
54             found = user_open(&snac, uid);
55@@ -163,8 +164,8 @@ int webfinger_get_handler(xs_dict *req, char *q_path,
56         l = xs_split_n(an, "@", 1);
57 
58         if (xs_list_len(l) == 2) {
59-            char *uid  = xs_list_get(l, 0);
60-            char *host = xs_list_get(l, 1);
61+            const char *uid  = xs_list_get(l, 0);
62+            const char *host = xs_list_get(l, 1);
63 
64             if (strcmp(host, xs_dict_get(srv_config, "host")) == 0)
65                 found = user_open(&snac, uid);
66@@ -194,7 +195,7 @@ int webfinger_get_handler(xs_dict *req, char *q_path,
67 
68         links = xs_list_append(links, prof);
69 
70-        char *avatar = xs_dict_get(snac.config, "avatar");
71+        const char *avatar = xs_dict_get(snac.config, "avatar");
72         if (!xs_is_null(avatar) && *avatar) {
73             xs *d = xs_dict_new();
74 
M xs.h
+76, -34
  1@@ -21,8 +21,8 @@ typedef enum {
  2     XSTYPE_FALSE  = 0x15,       /* Boolean */
  3     XSTYPE_LIST   = 0x1d,       /* Sequence of LITEMs up to EOM (with size) */
  4     XSTYPE_LITEM  = 0x1f,       /* Element of a list (any type) */
  5-    XSTYPE_DICT   = 0x1c,       /* Sequence of DITEMs up to EOM (with size) */
  6-    XSTYPE_DITEM  = 0x1e,       /* Element of a dict (STRING key + any type) */
  7+    XSTYPE_DICT   = 0x1c,       /* Sequence of KEYVALs up to EOM (with size) */
  8+    XSTYPE_KEYVAL = 0x1e,       /* key + value (STRING key + any type) */
  9     XSTYPE_EOM    = 0x19,       /* End of Multiple (LIST or DICT) */
 10     XSTYPE_DATA   = 0x10        /* A block of anonymous data */
 11 } xstype;
 12@@ -32,6 +32,7 @@ typedef enum {
 13 typedef char xs_val;
 14 typedef char xs_str;
 15 typedef char xs_list;
 16+typedef char xs_keyval;
 17 typedef char xs_dict;
 18 typedef char xs_number;
 19 typedef char xs_data;
 20@@ -96,7 +97,7 @@ xs_list *_xs_list_append(xs_list *list, const xs_val *vals[]);
 21 int xs_list_iter(xs_list **list, xs_val **value);
 22 int xs_list_next(const xs_list *list, xs_val **value, int *ctxt);
 23 int xs_list_len(const xs_list *list);
 24-xs_val *xs_list_get(const xs_list *list, int num);
 25+const xs_val *xs_list_get(const xs_list *list, int num);
 26 xs_list *xs_list_del(xs_list *list, int num);
 27 xs_list *xs_list_insert(xs_list *list, int num, const xs_val *data);
 28 xs_list *xs_list_set(xs_list *list, int num, const xs_val *data);
 29@@ -109,14 +110,20 @@ xs_list *xs_split_n(const char *str, const char *sep, int times);
 30 #define xs_split(str, sep) xs_split_n(str, sep, XS_ALL)
 31 xs_list *xs_list_cat(xs_list *l1, const xs_list *l2);
 32 
 33+int xs_keyval_size(const xs_str *key, const xs_val *value);
 34+xs_str *xs_keyval_key(const xs_keyval *keyval);
 35+xs_val *xs_keyval_value(const xs_keyval *keyval);
 36+xs_keyval *xs_keyval_make(xs_keyval *keyval, const xs_str *key, const xs_val *value);
 37+
 38 xs_dict *xs_dict_new(void);
 39 xs_dict *xs_dict_append(xs_dict *dict, const xs_str *key, const xs_val *value);
 40 xs_dict *xs_dict_prepend(xs_dict *dict, const xs_str *key, const xs_val *value);
 41 int xs_dict_next(const xs_dict *dict, xs_str **key, xs_val **value, int *ctxt);
 42-xs_val *xs_dict_get_def(const xs_dict *dict, const xs_str *key, const xs_val *def);
 43+const xs_val *xs_dict_get_def(const xs_dict *dict, const xs_str *key, const xs_val *def);
 44 #define xs_dict_get(dict, key) xs_dict_get_def(dict, key, NULL)
 45 xs_dict *xs_dict_del(xs_dict *dict, const xs_str *key);
 46 xs_dict *xs_dict_set(xs_dict *dict, const xs_str *key, const xs_val *data);
 47+xs_dict *xs_dict_gc(xs_dict *dict);
 48 
 49 xs_val *xs_val_new(xstype t);
 50 xs_number *xs_number_new(double f);
 51@@ -244,7 +251,7 @@ xstype xs_type(const xs_val *data)
 52     case XSTYPE_LIST:
 53     case XSTYPE_LITEM:
 54     case XSTYPE_DICT:
 55-    case XSTYPE_DITEM:
 56+    case XSTYPE_KEYVAL:
 57     case XSTYPE_NUMBER:
 58     case XSTYPE_EOM:
 59     case XSTYPE_DATA:
 60@@ -262,7 +269,7 @@ xstype xs_type(const xs_val *data)
 61 void _xs_put_size(xs_val *ptr, int i)
 62 /* must match _XS_TYPE_SIZE */
 63 {
 64-    memcpy(ptr, &i, sizeof(i));
 65+    memcpy(ptr + 1, &i, sizeof(i));
 66 }
 67 
 68 
 69@@ -296,7 +303,7 @@ int xs_size(const xs_val *data)
 70 
 71         break;
 72 
 73-    case XSTYPE_DITEM:
 74+    case XSTYPE_KEYVAL:
 75         /* calculate the size of the key and the value */
 76         p = data + 1;
 77         p += xs_size(p);
 78@@ -380,7 +387,7 @@ xs_val *xs_expand(xs_val *data, int offset, int size)
 79     if (xs_type(data) == XSTYPE_LIST ||
 80         xs_type(data) == XSTYPE_DICT ||
 81         xs_type(data) == XSTYPE_DATA)
 82-        _xs_put_size(data + 1, sz);
 83+        _xs_put_size(data, sz);
 84 
 85     return data;
 86 }
 87@@ -405,7 +412,7 @@ xs_val *xs_collapse(xs_val *data, int offset, int size)
 88     if (xs_type(data) == XSTYPE_LIST ||
 89         xs_type(data) == XSTYPE_DICT ||
 90         xs_type(data) == XSTYPE_DATA)
 91-        _xs_put_size(data + 1, sz);
 92+        _xs_put_size(data, sz);
 93 
 94     return xs_realloc(data, _xs_blk_size(sz));
 95 }
 96@@ -666,10 +673,10 @@ xs_list *xs_list_new(void)
 97 {
 98     int sz = 1 + _XS_TYPE_SIZE + 1;
 99     xs_list *l = xs_realloc(NULL, sz);
100-    memset(l, '\0', sz);
101+    memset(l, XSTYPE_EOM, sz);
102 
103     l[0] = XSTYPE_LIST;
104-    _xs_put_size(&l[1], sz);
105+    _xs_put_size(l, sz);
106 
107     return l;
108 }
109@@ -802,7 +809,7 @@ int xs_list_len(const xs_list *list)
110 }
111 
112 
113-xs_val *xs_list_get(const xs_list *list, int num)
114+const xs_val *xs_list_get(const xs_list *list, int num)
115 /* returns the element #num */
116 {
117     XS_ASSERT_TYPE(list, XSTYPE_LIST);
118@@ -830,7 +837,7 @@ xs_list *xs_list_del(xs_list *list, int num)
119 {
120     XS_ASSERT_TYPE(list, XSTYPE_LIST);
121 
122-    xs_val *v;
123+    const xs_val *v;
124 
125     if ((v = xs_list_get(list, num)) != NULL)
126         list = xs_collapse(list, v - 1 - list, xs_size(v - 1));
127@@ -844,7 +851,7 @@ xs_list *xs_list_insert(xs_list *list, int num, const xs_val *data)
128 {
129     XS_ASSERT_TYPE(list, XSTYPE_LIST);
130 
131-    xs_val *v;
132+    const xs_val *v;
133     int offset;
134 
135     if ((v = xs_list_get(list, num)) != NULL)
136@@ -999,6 +1006,40 @@ xs_list *xs_list_cat(xs_list *l1, const xs_list *l2)
137 }
138 
139 
140+/** keyvals **/
141+
142+int xs_keyval_size(const xs_str *key, const xs_val *value)
143+/* returns the needed size for a keyval */
144+{
145+    return 1 + xs_size(key) + xs_size(value);
146+}
147+
148+
149+xs_str *xs_keyval_key(const xs_keyval *keyval)
150+/* returns a pointer to the key of the keyval */
151+{
152+    return (xs_str *)&keyval[1];
153+}
154+
155+
156+xs_val *xs_keyval_value(const xs_keyval *keyval)
157+/* returns a pointer to the value of the keyval */
158+{
159+    return (xs_val *)&keyval[1 + xs_size(xs_keyval_key(keyval))];
160+}
161+
162+
163+xs_keyval *xs_keyval_make(xs_keyval *keyval, const xs_str *key, const xs_val *value)
164+/* builds a keyval into mem (should have enough size) */
165+{
166+    keyval[0] = XSTYPE_KEYVAL;
167+    memcpy(xs_keyval_key(keyval),   key,   xs_size(key));
168+    memcpy(xs_keyval_value(keyval), value, xs_size(value));
169+
170+    return keyval;
171+}
172+
173+
174 /** dicts **/
175 
176 xs_dict *xs_dict_new(void)
177@@ -1006,34 +1047,27 @@ xs_dict *xs_dict_new(void)
178 {
179     int sz = 1 + _XS_TYPE_SIZE + 1;
180     xs_dict *d = xs_realloc(NULL, sz);
181-    memset(d, '\0', sz);
182+    memset(d, XSTYPE_EOM, sz);
183 
184     d[0] = XSTYPE_DICT;
185-    _xs_put_size(&d[1], sz);
186+    _xs_put_size(d, sz);
187 
188     return d;
189 }
190 
191 
192-xs_dict *_xs_dict_write_ditem(xs_dict *dict, int offset, const xs_str *key,
193-                          const xs_val *data, int dsz)
194-/* inserts a memory block into the dict */
195+xs_dict *_xs_dict_write_keyval(xs_dict *dict, int offset, const xs_str *key, const xs_val *value)
196+/* adds a new keyval to the dict */
197 {
198     XS_ASSERT_TYPE(dict, XSTYPE_DICT);
199     XS_ASSERT_TYPE(key, XSTYPE_STRING);
200 
201-    if (data == NULL) {
202-        data = xs_stock(XSTYPE_NULL);
203-        dsz  = xs_size(data);
204-    }
205-
206-    int ksz = xs_size(key);
207+    if (value == NULL)
208+        value = xs_stock(XSTYPE_NULL);
209 
210-    dict = xs_expand(dict, offset, 1 + ksz + dsz);
211+    dict = xs_expand(dict, offset, xs_keyval_size(key, value));
212 
213-    dict[offset] = XSTYPE_DITEM;
214-    memcpy(&dict[offset + 1], key, ksz);
215-    memcpy(&dict[offset + 1 + ksz], data, dsz);
216+    xs_keyval_make(&dict[offset], key, value);
217 
218     return dict;
219 }
220@@ -1042,14 +1076,14 @@ xs_dict *_xs_dict_write_ditem(xs_dict *dict, int offset, const xs_str *key,
221 xs_dict *xs_dict_append(xs_dict *dict, const xs_str *key, const xs_val *value)
222 /* appends a memory block to the dict */
223 {
224-    return _xs_dict_write_ditem(dict, xs_size(dict) - 1, key, value, xs_size(value));
225+    return _xs_dict_write_keyval(dict, xs_size(dict) - 1, key, value);
226 }
227 
228 
229 xs_dict *xs_dict_prepend(xs_dict *dict, const xs_str *key, const xs_val *value)
230 /* prepends a memory block to the dict */
231 {
232-    return _xs_dict_write_ditem(dict, 1 + _XS_TYPE_SIZE, key, value, xs_size(value));
233+    return _xs_dict_write_keyval(dict, 1 + _XS_TYPE_SIZE, key, value);
234 }
235 
236 
237@@ -1070,7 +1104,7 @@ int xs_dict_next(const xs_dict *dict, xs_str **key, xs_val **value, int *ctxt)
238     p += *ctxt;
239 
240     /* an element? */
241-    if (xs_type(p) == XSTYPE_DITEM) {
242+    if (xs_type(p) == XSTYPE_KEYVAL) {
243         p++;
244 
245         *key = p;
246@@ -1091,7 +1125,7 @@ int xs_dict_next(const xs_dict *dict, xs_str **key, xs_val **value, int *ctxt)
247 }
248 
249 
250-xs_val *xs_dict_get_def(const xs_dict *dict, const xs_str *key, const xs_val *def)
251+const xs_val *xs_dict_get_def(const xs_dict *dict, const xs_str *key, const xs_val *def)
252 /* returns the value directed by key, or the default value */
253 {
254     XS_ASSERT_TYPE(dict, XSTYPE_DICT);
255@@ -1150,6 +1184,14 @@ xs_dict *xs_dict_set(xs_dict *dict, const xs_str *key, const xs_val *data)
256 }
257 
258 
259+xs_dict *xs_dict_gc(xs_dict *dict)
260+/* collects garbage (leaked values) inside a dict */
261+{
262+    /* this kind of dicts does not get garbage */
263+    return dict;
264+}
265+
266+
267 /** other values **/
268 
269 xs_val *xs_val_new(xstype t)
270@@ -1235,7 +1277,7 @@ xs_data *xs_data_new(const void *data, int size)
271     v = xs_realloc(NULL, _xs_blk_size(total_size));
272     v[0] = XSTYPE_DATA;
273 
274-    _xs_put_size(v + 1, total_size);
275+    _xs_put_size(v, total_size);
276 
277     memcpy(&v[1 + _XS_TYPE_SIZE], data, size);
278 
+1, -1
1@@ -28,7 +28,7 @@ static size_t _header_callback(char *buffer, size_t size,
2     if (xs_str_in(l, ": ") != -1) {
3         xs *knv = xs_split_n(l, ": ", 1);
4 
5-        xs_tolower_i(xs_list_get(knv, 0));
6+        xs_tolower_i((xs_str *)xs_list_get(knv, 0));
7 
8         headers = xs_dict_set(headers, xs_list_get(knv, 0), xs_list_get(knv, 1));
9     }
+17, -17
  1@@ -6,26 +6,26 @@
  2 
  3 typedef struct xs_html xs_html;
  4 
  5-xs_str *xs_html_encode(char *str);
  6+xs_str *xs_html_encode(const char *str);
  7 
  8-xs_html *xs_html_attr(char *key, char *value);
  9-xs_html *xs_html_text(char *content);
 10-xs_html *xs_html_raw(char *content);
 11+xs_html *xs_html_attr(const char *key, const char *value);
 12+xs_html *xs_html_text(const char *content);
 13+xs_html *xs_html_raw(const char *content);
 14 
 15 xs_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 })
 17 
 18-xs_html *_xs_html_tag(char *tag, xs_html *var[]);
 19+xs_html *_xs_html_tag(const char *tag, xs_html *var[]);
 20 #define xs_html_tag(tag, ...) _xs_html_tag(tag, (xs_html *[]) { __VA_ARGS__, NULL })
 21 
 22-xs_html *_xs_html_sctag(char *tag, xs_html *var[]);
 23+xs_html *_xs_html_sctag(const char *tag, xs_html *var[]);
 24 #define xs_html_sctag(tag, ...) _xs_html_sctag(tag, (xs_html *[]) { __VA_ARGS__, NULL })
 25 
 26 xs_html *_xs_html_container(xs_html *var[]);
 27 #define xs_html_container(...) _xs_html_container((xs_html *[]) { __VA_ARGS__, NULL })
 28 
 29 void xs_html_render_f(xs_html *h, FILE *f);
 30-xs_str *xs_html_render_s(xs_html *tag, char *prefix);
 31+xs_str *xs_html_render_s(xs_html *tag, const char *prefix);
 32 #define xs_html_render(tag) xs_html_render_s(tag, NULL)
 33 
 34 
 35@@ -47,16 +47,16 @@ struct xs_html {
 36     xs_html *next;
 37 };
 38 
 39-xs_str *xs_html_encode(char *str)
 40+xs_str *xs_html_encode(const char *str)
 41 /* encodes str using HTML entities */
 42 {
 43     xs_str *s = xs_str_new(NULL);
 44     int o = 0;
 45-    char *e = str + strlen(str);
 46+    const char *e = str + strlen(str);
 47 
 48     for (;;) {
 49         char *ec = "<>\"'&";   /* characters to escape */
 50-        char *q = e;
 51+        const char *q = e;
 52         int z;
 53 
 54         /* find the nearest happening of a char */
 55@@ -90,7 +90,7 @@ xs_str *xs_html_encode(char *str)
 56 
 57 #define XS_HTML_NEW() memset(xs_realloc(NULL, sizeof(xs_html)), '\0', sizeof(xs_html))
 58 
 59-xs_html *xs_html_attr(char *key, char *value)
 60+xs_html *xs_html_attr(const char *key, const char *value)
 61 /* creates an HTML block with an attribute */
 62 {
 63     xs_html *a = XS_HTML_NEW();
 64@@ -108,7 +108,7 @@ xs_html *xs_html_attr(char *key, char *value)
 65 }
 66 
 67 
 68-xs_html *xs_html_text(char *content)
 69+xs_html *xs_html_text(const char *content)
 70 /* creates an HTML block of text, escaping it previously */
 71 {
 72     xs_html *a = XS_HTML_NEW();
 73@@ -120,7 +120,7 @@ xs_html *xs_html_text(char *content)
 74 }
 75 
 76 
 77-xs_html *xs_html_raw(char *content)
 78+xs_html *xs_html_raw(const char *content)
 79 /* creates an HTML block without escaping (for pre-formatted HTML, comments, etc) */
 80 {
 81     xs_html *a = XS_HTML_NEW();
 82@@ -152,7 +152,7 @@ xs_html *_xs_html_add(xs_html *tag, xs_html *var[])
 83 }
 84 
 85 
 86-static xs_html *_xs_html_tag_t(xs_html_type type, char *tag, xs_html *var[])
 87+static xs_html *_xs_html_tag_t(xs_html_type type, const char *tag, xs_html *var[])
 88 /* creates a tag with a variable list of attributes and subtags */
 89 {
 90     xs_html *a = XS_HTML_NEW();
 91@@ -169,13 +169,13 @@ static xs_html *_xs_html_tag_t(xs_html_type type, char *tag, xs_html *var[])
 92 }
 93 
 94 
 95-xs_html *_xs_html_tag(char *tag, xs_html *var[])
 96+xs_html *_xs_html_tag(const char *tag, xs_html *var[])
 97 {
 98     return _xs_html_tag_t(XS_HTML_TAG, tag, var);
 99 }
100 
101 
102-xs_html *_xs_html_sctag(char *tag, xs_html *var[])
103+xs_html *_xs_html_sctag(const char *tag, xs_html *var[])
104 {
105     return _xs_html_tag_t(XS_HTML_SCTAG, tag, var);
106 }
107@@ -239,7 +239,7 @@ void xs_html_render_f(xs_html *h, FILE *f)
108 }
109 
110 
111-xs_str *xs_html_render_s(xs_html *tag, char *prefix)
112+xs_str *xs_html_render_s(xs_html *tag, const char *prefix)
113 /* renders to a string */
114 {
115     xs_str *s = NULL;
+3, -2
 1@@ -16,7 +16,7 @@ xs_dict *xs_httpd_request(FILE *f, xs_str **payload, int *p_size)
 2     xs *q_vars = NULL;
 3     xs *p_vars = NULL;
 4     xs *l1, *l2;
 5-    char *v;
 6+    const char *v;
 7 
 8     xs_socket_timeout(fileno(f), 2.0, 0.0);
 9 
10@@ -60,7 +60,8 @@ xs_dict *xs_httpd_request(FILE *f, xs_str **payload, int *p_size)
11         p = xs_split_n(l, ": ", 1);
12 
13         if (xs_list_len(p) == 2)
14-            req = xs_dict_append(req, xs_tolower_i(xs_list_get(p, 0)), xs_list_get(p, 1));
15+            req = xs_dict_append(req, xs_tolower_i(
16+                    (xs_str *)xs_list_get(p, 0)), xs_list_get(p, 1));
17     }
18 
19     xs_socket_timeout(fileno(f), 5.0, 0.0);
+4, -5
 1@@ -71,12 +71,12 @@ static void _xs_json_indent(int level, int indent, FILE *f)
 2 }
 3 
 4 
 5-static void _xs_json_dump(const xs_val *s_data, int level, int indent, FILE *f)
 6+static void _xs_json_dump(const xs_val *data, int level, int indent, FILE *f)
 7 /* dumps partial data as JSON */
 8 {
 9     int c = 0;
10+    int ct = 0;
11     xs_val *v;
12-    xs_val *data = (xs_val *)s_data;
13 
14     switch (xs_type(data)) {
15     case XSTYPE_NULL:
16@@ -98,7 +98,7 @@ static void _xs_json_dump(const xs_val *s_data, int level, int indent, FILE *f)
17     case XSTYPE_LIST:
18         fputc('[', f);
19 
20-        while (xs_list_iter(&data, &v)) {
21+        while (xs_list_next(data, &v, &ct)) {
22             if (c != 0)
23                 fputc(',', f);
24 
25@@ -117,9 +117,8 @@ static void _xs_json_dump(const xs_val *s_data, int level, int indent, FILE *f)
26         fputc('{', f);
27 
28         xs_str *k;
29-        int ct = 0;
30 
31-        while (xs_dict_next(s_data, &k, &v, &ct)) {
32+        while (xs_dict_next(data, &k, &v, &ct)) {
33             if (c != 0)
34                 fputc(',', f);
35 
+1, -1
1@@ -104,7 +104,7 @@ int xs_set_add(xs_set *s, const xs_val *data)
2 
3     /* if it's new, add the data */
4     if (ret)
5-        s->list = xs_list_append_m(s->list, data, xs_size(data));
6+        s->list = xs_list_append(s->list, data);
7 
8     return ret;
9 }
+3, -3
 1@@ -6,7 +6,7 @@
 2 
 3  int _xs_utf8_enc(char buf[4], unsigned int cpoint);
 4  int xs_is_utf8_cont_byte(char c);
 5- unsigned int xs_utf8_dec(char **str);
 6+ unsigned int xs_utf8_dec(const char **str);
 7  int xs_unicode_width(unsigned int cpoint);
 8  int xs_is_surrogate(unsigned int cpoint);
 9  unsigned int xs_surrogate_dec(unsigned int p1, unsigned int p2);
10@@ -66,10 +66,10 @@ int xs_is_utf8_cont_byte(char c)
11 }
12 
13 
14-unsigned int xs_utf8_dec(char **str)
15+unsigned int xs_utf8_dec(const char **str)
16 /* decodes an utf-8 char inside str and updates the pointer */
17 {
18-    char *p = *str;
19+    const char *p = *str;
20     unsigned int cpoint = 0;
21     unsigned char c = *p++;
22     int cb = 0;
+2, -2
 1@@ -119,8 +119,8 @@ xs_dict *xs_multipart_form_data(const char *payload, int p_size, const char *hea
 2     while ((p = xs_memmem(payload + offset, p_size - offset, boundary, bsz)) != NULL) {
 3         xs *s1 = NULL;
 4         xs *l1 = NULL;
 5-        char *vn = NULL;
 6-        char *fn = NULL;
 7+        const char *vn = NULL;
 8+        const char *fn = NULL;
 9         char *q;
10         int po, ps;
11 
+1, -1
1@@ -1 +1 @@
2-/* 6e75e8736f7f1b6ea6c6774d4bd922b3ad56b771 2024-05-15T11:42:19+02:00 */
3+/* 34850dcdec50b669a2c0bbe9f16f6d9c4b16eafd 2024-05-21T14:06:02+02:00 */