commit 0e21d35

Louis Brauer  ·  2024-05-26 19:45:41 +0000 UTC
parent 9749a65
Use enum instead of numeric status codes for HTTP statuses
7 files changed,  +262, -206
M data.c
M html.c
M snac.h
+31, -25
  1@@ -96,19 +96,19 @@ int activitypub_request(snac *user, const char *url, xs_dict **data)
  2         ctype = xs_dict_get(response, "content-type");
  3 
  4         if (xs_is_null(ctype))
  5-            status = 400;
  6+            status = HTTP_STATUS_BAD_REQUEST;
  7         else
  8         if (xs_str_in(ctype, "application/activity+json") != -1 ||
  9             xs_str_in(ctype, "application/ld+json") != -1) {
 10 
 11             /* if there is no payload, fail */
 12             if (xs_is_null(payload))
 13-                status = 400;
 14+                status = HTTP_STATUS_BAD_REQUEST;
 15             else
 16                 *data = xs_json_loads(payload);
 17         }
 18         else
 19-            status = 500;
 20+            status = HTTP_STATUS_INTERNAL_SERVER_ERROR;
 21     }
 22 
 23     return status;
 24@@ -443,7 +443,7 @@ int send_to_actor(snac *snac, const char *actor, const xs_dict *msg,
 25                   xs_val **payload, int *p_size, int timeout)
 26 /* sends a message to an actor */
 27 {
 28-    int status = 400;
 29+    int status = HTTP_STATUS_BAD_REQUEST;
 30     xs *inbox = get_actor_inbox(actor);
 31 
 32     if (!xs_is_null(inbox))
 33@@ -1762,7 +1762,9 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req)
 34     a_status = actor_request(snac, actor, &actor_o);
 35 
 36     /* do not retry permanent failures */
 37-    if (a_status == 404 || a_status == 410 || a_status < 0) {
 38+    if (a_status == HTTP_STATUS_NOT_FOUND
 39+        || a_status == HTTP_STATUS_GONE
 40+        || a_status < 0) {
 41         srv_debug(1, xs_fmt("dropping message due to actor error %s %d", actor, a_status));
 42         return -1;
 43     }
 44@@ -1905,7 +1907,7 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req)
 45         }
 46         else
 47         if (strcmp(utype, "Announce") == 0) { /** **/
 48-            int status = 200;
 49+            int status = HTTP_STATUS_OK;
 50 
 51             /* commented out: if a followed user boosts something that
 52                is requested and then unboosts, the post remains here,
 53@@ -2015,7 +2017,7 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req)
 54         if (xs_type(object) == XSTYPE_DICT)
 55             object = xs_dict_get(object, "id");
 56 
 57-        if (timeline_admire(snac, object, actor, 1) == 201)
 58+        if (timeline_admire(snac, object, actor, 1) == HTTP_STATUS_CREATED)
 59             snac_log(snac, xs_fmt("new 'Like' %s %s", actor, object));
 60         else
 61             snac_log(snac, xs_fmt("repeated 'Like' from %s to %s", actor, object));
 62@@ -2046,7 +2048,7 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req)
 63                     xs *who_o = NULL;
 64 
 65                     if (valid_status(actor_request(snac, who, &who_o))) {
 66-                        if (timeline_admire(snac, object, actor, 0) == 201)
 67+                        if (timeline_admire(snac, object, actor, 0) == HTTP_STATUS_CREATED)
 68                             snac_log(snac, xs_fmt("new 'Announce' %s %s", actor, object));
 69                         else
 70                             snac_log(snac, xs_fmt("repeated 'Announce' from %s to %s",
 71@@ -2383,11 +2385,15 @@ void process_queue_item(xs_dict *q_item)
 72 
 73             /* if it's not the first time it fails with a timeout,
 74                penalize the server by skipping one retry */
 75-            if (p_status == status && status == 499)
 76+            if (p_status == status && status == HTTP_STATUS_CLIENT_CLOSED_REQUEST)
 77                 retries++;
 78 
 79             /* error sending; requeue? */
 80-            if (status == 400 || status == 404 || status == 405 || status == 410 || status < 0)
 81+            if (status == HTTP_STATUS_BAD_REQUEST
 82+                || status == HTTP_STATUS_NOT_FOUND
 83+                || status == HTTP_STATUS_METHOD_NOT_ALLOWED
 84+                || status == HTTP_STATUS_GONE
 85+                || status < 0)
 86                 /* explicit error: discard */
 87                 srv_log(xs_fmt("output message: fatal error %s %d", inbox, status));
 88             else
 89@@ -2574,7 +2580,7 @@ int process_queue(void)
 90 int activitypub_get_handler(const xs_dict *req, const char *q_path,
 91                             char **body, int *b_size, char **ctype)
 92 {
 93-    int status = 200;
 94+    int status = HTTP_STATUS_OK;
 95     const char *accept = xs_dict_get(req, "accept");
 96     snac snac;
 97     xs *msg = NULL;
 98@@ -2594,7 +2600,7 @@ int activitypub_get_handler(const xs_dict *req, const char *q_path,
 99     if (!user_open(&snac, uid)) {
100         /* invalid user */
101         srv_debug(1, xs_fmt("activitypub_get_handler bad user %s", uid));
102-        return 404;
103+        return HTTP_STATUS_NOT_FOUND;
104     }
105 
106     p_path = xs_list_get(l, 2);
107@@ -2652,12 +2658,12 @@ int activitypub_get_handler(const xs_dict *req, const char *q_path,
108 
109         /* don't return non-public objects */
110         if (valid_status(status) && !is_msg_public(msg))
111-            status = 404;
112+            status = HTTP_STATUS_NOT_FOUND;
113     }
114     else
115-        status = 404;
116+        status = HTTP_STATUS_NOT_FOUND;
117 
118-    if (status == 200 && msg != NULL) {
119+    if (status == HTTP_STATUS_OK && msg != NULL) {
120         *body   = xs_json_dumps(msg, 4);
121         *b_size = strlen(*body);
122     }
123@@ -2677,7 +2683,7 @@ int activitypub_post_handler(const xs_dict *req, const char *q_path,
124 {
125     (void)b_size;
126 
127-    int status = 202; /* accepted */
128+    int status = HTTP_STATUS_ACCEPTED;
129     const char *i_ctype = xs_dict_get(req, "content-type");
130     snac snac;
131     const char *v;
132@@ -2685,13 +2691,13 @@ int activitypub_post_handler(const xs_dict *req, const char *q_path,
133     if (i_ctype == NULL) {
134         *body  = xs_str_new("no content-type");
135         *ctype = "text/plain";
136-        return 400;
137+        return HTTP_STATUS_BAD_REQUEST;
138     }
139 
140     if (xs_is_null(payload)) {
141         *body  = xs_str_new("no payload");
142         *ctype = "text/plain";
143-        return 400;
144+        return HTTP_STATUS_BAD_REQUEST;
145     }
146 
147     if (xs_str_in(i_ctype, "application/activity+json") == -1 &&
148@@ -2709,7 +2715,7 @@ int activitypub_post_handler(const xs_dict *req, const char *q_path,
149 
150         *body  = xs_str_new("JSON error");
151         *ctype = "text/plain";
152-        return 400;
153+        return HTTP_STATUS_BAD_REQUEST;
154     }
155 
156     if (id && is_instance_blocked(id)) {
157@@ -2717,7 +2723,7 @@ int activitypub_post_handler(const xs_dict *req, const char *q_path,
158 
159         *body  = xs_str_new("blocked");
160         *ctype = "text/plain";
161-        return 403;
162+        return HTTP_STATUS_FORBIDDEN;
163     }
164 
165     /* get the user and path */
166@@ -2725,20 +2731,20 @@ int activitypub_post_handler(const xs_dict *req, const char *q_path,
167 
168     if (xs_list_len(l) == 2 && strcmp(xs_list_get(l, 1), "shared-inbox") == 0) {
169         enqueue_shared_input(msg, req, 0);
170-        return 202;
171+        return HTTP_STATUS_ACCEPTED;
172     }
173 
174     if (xs_list_len(l) != 3 || strcmp(xs_list_get(l, 2), "inbox") != 0) {
175         /* strange q_path */
176         srv_debug(1, xs_fmt("activitypub_post_handler unsupported path %s", q_path));
177-        return 404;
178+        return HTTP_STATUS_NOT_FOUND;
179     }
180 
181     const char *uid = xs_list_get(l, 1);
182     if (!user_open(&snac, uid)) {
183         /* invalid user */
184         srv_debug(1, xs_fmt("activitypub_post_handler bad user %s", uid));
185-        return 404;
186+        return HTTP_STATUS_NOT_FOUND;
187     }
188 
189     /* if it has a digest, check it now, because
190@@ -2752,7 +2758,7 @@ int activitypub_post_handler(const xs_dict *req, const char *q_path,
191 
192             *body  = xs_str_new("bad digest");
193             *ctype = "text/plain";
194-            status = 400;
195+            status = HTTP_STATUS_BAD_REQUEST;
196         }
197     }
198 
199@@ -2763,7 +2769,7 @@ int activitypub_post_handler(const xs_dict *req, const char *q_path,
200 
201             *body  = xs_str_new("rejected");
202             *ctype = "text/plain";
203-            status = 403;
204+            status = HTTP_STATUS_FORBIDDEN;
205         }
206     }
207 
M data.c
+34, -34
  1@@ -355,12 +355,12 @@ int is_md5_hex(const char *md5)
  2 int index_add_md5(const char *fn, const char *md5)
  3 /* adds an md5 to an index */
  4 {
  5-    int status = 201; /* Created */
  6+    int status = HTTP_STATUS_CREATED;
  7     FILE *f;
  8 
  9     if (!is_md5_hex(md5)) {
 10         srv_log(xs_fmt("index_add_md5: bad md5 %s %s", fn, md5));
 11-        return 400;
 12+        return HTTP_STATUS_BAD_REQUEST;
 13     }
 14 
 15     pthread_mutex_lock(&data_mutex);
 16@@ -375,7 +375,7 @@ int index_add_md5(const char *fn, const char *md5)
 17         fclose(f);
 18     }
 19     else
 20-        status = 500;
 21+        status = HTTP_STATUS_INTERNAL_SERVER_ERROR;
 22 
 23     pthread_mutex_unlock(&data_mutex);
 24 
 25@@ -394,7 +394,7 @@ int index_add(const char *fn, const char *id)
 26 int index_del_md5(const char *fn, const char *md5)
 27 /* deletes an md5 from an index */
 28 {
 29-    int status = 404;
 30+    int status = HTTP_STATUS_NOT_FOUND;
 31     FILE *f;
 32 
 33     pthread_mutex_lock(&data_mutex);
 34@@ -411,7 +411,7 @@ int index_del_md5(const char *fn, const char *md5)
 35                    [yes: this breaks index_len()] */
 36                 fseek(f, -33, SEEK_CUR);
 37                 fwrite("-", 1, 1, f);
 38-                status = 200;
 39+                status = HTTP_STATUS_OK;
 40 
 41                 break;
 42             }
 43@@ -420,7 +420,7 @@ int index_del_md5(const char *fn, const char *md5)
 44         fclose(f);
 45     }
 46     else
 47-        status = 410;
 48+        status = HTTP_STATUS_GONE;
 49 
 50     pthread_mutex_unlock(&data_mutex);
 51 
 52@@ -660,7 +660,7 @@ int object_here(const char *id)
 53 int object_get_by_md5(const char *md5, xs_dict **obj)
 54 /* returns a stored object, optionally of the requested type */
 55 {
 56-    int status = 404;
 57+    int status = HTTP_STATUS_NOT_FOUND;
 58     xs *fn     = _object_fn_by_md5(md5, "object_get_by_md5");
 59     FILE *f;
 60 
 61@@ -669,7 +669,7 @@ int object_get_by_md5(const char *md5, xs_dict **obj)
 62         fclose(f);
 63 
 64         if (*obj)
 65-            status = 200;
 66+            status = HTTP_STATUS_OK;
 67     }
 68     else
 69         *obj = NULL;
 70@@ -689,7 +689,7 @@ int object_get(const char *id, xs_dict **obj)
 71 int _object_add(const char *id, const xs_dict *obj, int ow)
 72 /* stores an object */
 73 {
 74-    int status = 201; /* Created */
 75+    int status = HTTP_STATUS_CREATED; /* Created */
 76     xs *fn     = _object_fn(id);
 77     FILE *f;
 78 
 79@@ -697,10 +697,10 @@ int _object_add(const char *id, const xs_dict *obj, int ow)
 80         if (!ow) {
 81             /* object already here */
 82             srv_debug(1, xs_fmt("object_add object already here %s", id));
 83-            return 204; /* No content */
 84+            return HTTP_STATUS_NO_CONTENT;
 85         }
 86         else
 87-            status = 200;
 88+            status = HTTP_STATUS_OK;
 89     }
 90 
 91     if ((f = fopen(fn, "w")) != NULL) {
 92@@ -736,7 +736,7 @@ int _object_add(const char *id, const xs_dict *obj, int ow)
 93     }
 94     else {
 95         srv_log(xs_fmt("object_add error writing %s (errno: %d)", fn, errno));
 96-        status = 500;
 97+        status = HTTP_STATUS_INTERNAL_SERVER_ERROR;
 98     }
 99 
100     srv_debug(1, xs_fmt("object_add %s %s %d", id, fn, status));
101@@ -762,11 +762,11 @@ int object_add_ow(const char *id, const xs_dict *obj)
102 int object_del_by_md5(const char *md5)
103 /* deletes an object by its md5 */
104 {
105-    int status = 404;
106+    int status = HTTP_STATUS_NOT_FOUND;
107     xs *fn     = _object_fn_by_md5(md5, "object_del_by_md5");
108 
109     if (unlink(fn) != -1) {
110-        status = 200;
111+        status = HTTP_STATUS_OK;
112 
113         /* also delete associated indexes */
114         xs *spec  = xs_dup(fn);
115@@ -907,7 +907,7 @@ int object_parent(const char *md5, char *buf, int size)
116 int object_admire(const char *id, const char *actor, int like)
117 /* actor likes or announces this object */
118 {
119-    int status = 200;
120+    int status = HTTP_STATUS_OK;
121     xs *fn     = _object_fn(id);
122 
123     fn = xs_replace_i(fn, ".json", like ? "_l.idx" : "_a.idx");
124@@ -1007,7 +1007,7 @@ int follower_add(snac *snac, const char *actor)
125 
126     snac_debug(snac, 2, xs_fmt("follower_add %s", actor));
127 
128-    return ret == -1 ? 500 : 200;
129+    return ret == -1 ? HTTP_STATUS_INTERNAL_SERVER_ERROR : HTTP_STATUS_OK;
130 }
131 
132 
133@@ -1018,7 +1018,7 @@ int follower_del(snac *snac, const char *actor)
134 
135     snac_debug(snac, 2, xs_fmt("follower_del %s", actor));
136 
137-    return ret == -1 ? 404 : 200;
138+    return ret == -1 ? HTTP_STATUS_NOT_FOUND : HTTP_STATUS_OK;
139 }
140 
141 
142@@ -1109,7 +1109,7 @@ int timeline_here(snac *snac, const char *md5)
143 int timeline_get_by_md5(snac *snac, const char *md5, xs_dict **msg)
144 /* gets a message from the timeline */
145 {
146-    int status = 404;
147+    int status = HTTP_STATUS_NOT_FOUND;
148     FILE *f    = NULL;
149 
150     xs *fn = timeline_fn_by_md5(snac, md5);
151@@ -1119,7 +1119,7 @@ int timeline_get_by_md5(snac *snac, const char *md5, xs_dict **msg)
152         fclose(f);
153 
154         if (*msg != NULL)
155-            status = 200;
156+            status = HTTP_STATUS_OK;
157     }
158 
159     return status;
160@@ -1282,7 +1282,7 @@ xs_str *_following_fn(snac *snac, const char *actor)
161 int following_add(snac *snac, const char *actor, const xs_dict *msg)
162 /* adds to the following list */
163 {
164-    int ret = 201; /* created */
165+    int ret = HTTP_STATUS_CREATED;
166     xs *fn = _following_fn(snac, actor);
167     FILE *f;
168     xs *p_object = NULL;
169@@ -1295,7 +1295,7 @@ int following_add(snac *snac, const char *actor, const xs_dict *msg)
170 
171         if (!xs_is_null(type) && strcmp(type, "Accept") == 0) {
172             snac_debug(snac, 1, xs_fmt("following_add actor already confirmed %s", actor));
173-            return 200;
174+            return HTTP_STATUS_OK;
175         }
176     }
177 
178@@ -1311,7 +1311,7 @@ int following_add(snac *snac, const char *actor, const xs_dict *msg)
179         link(actor_fn, fn);
180     }
181     else
182-        ret = 500;
183+        ret = HTTP_STATUS_INTERNAL_SERVER_ERROR;
184 
185     snac_debug(snac, 2, xs_fmt("following_add %s %s", actor, fn));
186 
187@@ -1332,7 +1332,7 @@ int following_del(snac *snac, const char *actor)
188     fn = xs_replace_i(fn, ".json", "_a.json");
189     unlink(fn);
190 
191-    return 200;
192+    return HTTP_STATUS_OK;
193 }
194 
195 
196@@ -1350,14 +1350,14 @@ int following_get(snac *snac, const char *actor, xs_dict **data)
197 {
198     xs *fn = _following_fn(snac, actor);
199     FILE *f;
200-    int status = 200;
201+    int status = HTTP_STATUS_OK;
202 
203     if ((f = fopen(fn, "r")) != NULL) {
204         *data = xs_json_load(f);
205         fclose(f);
206     }
207     else
208-        status = 404;
209+        status = HTTP_STATUS_NOT_FOUND;
210 
211     return status;
212 }
213@@ -1576,7 +1576,7 @@ int actor_add(const char *actor, const xs_dict *msg)
214 int actor_get(const char *actor, xs_dict **data)
215 /* returns an already downloaded actor */
216 {
217-    int status = 200;
218+    int status = HTTP_STATUS_OK;
219     xs_dict *d = NULL;
220 
221     if (xs_startswith(actor, srv_baseurl)) {
222@@ -1590,10 +1590,10 @@ int actor_get(const char *actor, xs_dict **data)
223                 *data = msg_actor(&user);
224 
225             user_free(&user);
226-            return 200;
227+            return HTTP_STATUS_OK;
228         }
229         else
230-            return 404;
231+            return HTTP_STATUS_NOT_FOUND;
232     }
233 
234     /* read the object */
235@@ -1606,7 +1606,7 @@ int actor_get(const char *actor, xs_dict **data)
236     if (xs_is_null(xs_dict_get(d, "id")) || xs_is_null(xs_dict_get(d, "type"))) {
237         srv_debug(1, xs_fmt("corrupted actor object %s", actor));
238         d = xs_free(d);
239-        return 404;
240+        return HTTP_STATUS_NOT_FOUND;
241     }
242 
243     if (data)
244@@ -1622,7 +1622,7 @@ int actor_get(const char *actor, xs_dict **data)
245 
246     if (mtime(fn) + max_time < (double) time(NULL)) {
247         /* actor data exists but also stinks */
248-        status = 205; /* "205: Reset Content" "110: Response Is Stale" */
249+        status = HTTP_STATUS_RESET_CONTENT; /* "110: Response Is Stale" */
250     }
251 
252     return status;
253@@ -1634,7 +1634,7 @@ int actor_get_refresh(snac *user, const char *actor, xs_dict **data)
254 {
255     int status = actor_get(actor, data);
256 
257-    if (status == 205 && user && !xs_startswith(actor, srv_baseurl))
258+    if (status == HTTP_STATUS_RESET_CONTENT && user && !xs_startswith(actor, srv_baseurl))
259         enqueue_actor_refresh(user, actor, 0);
260 
261     return status;
262@@ -1953,7 +1953,7 @@ static int _load_raw_file(const char *fn, xs_val **data, int *size,
263                         const char *inm, xs_str **etag)
264 /* loads a cached file */
265 {
266-    int status = 404;
267+    int status = HTTP_STATUS_NOT_FOUND;
268 
269     if (fn) {
270         double tm = mtime(fn);
271@@ -1965,7 +1965,7 @@ static int _load_raw_file(const char *fn, xs_val **data, int *size,
272             /* if if-none-match is set, check if it's the same */
273             if (!xs_is_null(inm) && strcmp(e, inm) == 0) {
274                 /* client has the newest version */
275-                status = 304;
276+                status = HTTP_STATUS_NOT_MODIFIED;
277             }
278             else {
279                 /* newer or never downloaded; read the full file */
280@@ -1976,7 +1976,7 @@ static int _load_raw_file(const char *fn, xs_val **data, int *size,
281                     *data = xs_read(f, size);
282                     fclose(f);
283 
284-                    status = 200;
285+                    status = HTTP_STATUS_OK;
286                 }
287             }
288 
M html.c
+38, -38
  1@@ -2540,7 +2540,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
  2                      char **body, int *b_size, char **ctype, xs_str **etag)
  3 {
  4     const char *accept = xs_dict_get(req, "accept");
  5-    int status = 404;
  6+    int status = HTTP_STATUS_NOT_FOUND;
  7     snac snac;
  8     xs *uid = NULL;
  9     const char *p_path;
 10@@ -2553,7 +2553,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
 11 
 12     if (xs_is_null(v)) {
 13         srv_log(xs_fmt("html_get_handler bad query '%s'", q_path));
 14-        return 404;
 15+        return HTTP_STATUS_NOT_FOUND;
 16     }
 17 
 18     uid = xs_dup(v);
 19@@ -2569,7 +2569,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
 20     if (!uid || !user_open(&snac, uid)) {
 21         /* invalid user */
 22         srv_debug(1, xs_fmt("html_get_handler bad user %s", uid));
 23-        return 404;
 24+        return HTTP_STATUS_NOT_FOUND;
 25     }
 26 
 27     /* return the RSS if requested by Accept header */
 28@@ -2598,7 +2598,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
 29             /** empty public timeline for private users **/
 30             *body = html_timeline(&snac, NULL, 1, 0, 0, 0, NULL, "", 1);
 31             *b_size = strlen(*body);
 32-            status  = 200;
 33+            status  = HTTP_STATUS_OK;
 34         }
 35         else
 36         if (cache && history_mtime(&snac, h) > timeline_mtime(&snac)) {
 37@@ -2617,7 +2617,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
 38             *body = html_timeline(&snac, pins, 1, skip, show, xs_list_len(next), NULL, "", 1);
 39 
 40             *b_size = strlen(*body);
 41-            status  = 200;
 42+            status  = HTTP_STATUS_OK;
 43 
 44             if (save)
 45                 history_add(&snac, h, *body, *b_size, etag);
 46@@ -2627,7 +2627,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
 47     if (strcmp(p_path, "admin") == 0) { /** private timeline **/
 48         if (!login(&snac, req)) {
 49             *body  = xs_dup(uid);
 50-            status = 401;
 51+            status = HTTP_STATUS_UNAUTHORIZED;
 52         }
 53         else {
 54             const char *q = xs_dict_get(q_vars, "q");
 55@@ -2649,7 +2649,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
 56 
 57                     *body = html_timeline(&snac, tl, 0, skip, show, more, title, page, 0);
 58                     *b_size = strlen(*body);
 59-                    status  = 200;
 60+                    status  = HTTP_STATUS_OK;
 61                 }
 62                 else {
 63                     /** search by content **/
 64@@ -2670,7 +2670,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
 65 
 66                     *body   = html_timeline(&snac, tl, 0, skip, tl_len, to || tl_len == show, title, page, 0);
 67                     *b_size = strlen(*body);
 68-                    status  = 200;
 69+                    status  = HTTP_STATUS_OK;
 70                 }
 71             }
 72             else {
 73@@ -2699,7 +2699,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
 74                             xs_list_len(next), NULL, "/admin", 1);
 75 
 76                     *b_size = strlen(*body);
 77-                    status  = 200;
 78+                    status  = HTTP_STATUS_OK;
 79 
 80                     if (save)
 81                         history_add(&snac, "timeline.html_", *body, *b_size, etag);
 82@@ -2711,7 +2711,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
 83     if (xs_startswith(p_path, "admin/p/")) { /** unique post by md5 **/
 84         if (!login(&snac, req)) {
 85             *body  = xs_dup(uid);
 86-            status = 401;
 87+            status = HTTP_STATUS_UNAUTHORIZED;
 88         }
 89         else {
 90             xs *l = xs_split(p_path, "/");
 91@@ -2722,7 +2722,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
 92 
 93                 *body   = html_timeline(&snac, list, 0, 0, 0, 0, NULL, "/admin", 1);
 94                 *b_size = strlen(*body);
 95-                status  = 200;
 96+                status  = HTTP_STATUS_OK;
 97             }
 98         }
 99     }
100@@ -2730,31 +2730,31 @@ int html_get_handler(const xs_dict *req, const char *q_path,
101     if (strcmp(p_path, "people") == 0) { /** the list of people **/
102         if (!login(&snac, req)) {
103             *body  = xs_dup(uid);
104-            status = 401;
105+            status = HTTP_STATUS_UNAUTHORIZED;
106         }
107         else {
108             *body   = html_people(&snac);
109             *b_size = strlen(*body);
110-            status  = 200;
111+            status  = HTTP_STATUS_OK;
112         }
113     }
114     else
115     if (strcmp(p_path, "notifications") == 0) { /** the list of notifications **/
116         if (!login(&snac, req)) {
117             *body  = xs_dup(uid);
118-            status = 401;
119+            status = HTTP_STATUS_UNAUTHORIZED;
120         }
121         else {
122             *body   = html_notifications(&snac, skip, show);
123             *b_size = strlen(*body);
124-            status  = 200;
125+            status  = HTTP_STATUS_OK;
126         }
127     }
128     else
129     if (strcmp(p_path, "instance") == 0) { /** instance timeline **/
130         if (!login(&snac, req)) {
131             *body  = xs_dup(uid);
132-            status = 401;
133+            status = HTTP_STATUS_UNAUTHORIZED;
134         }
135         else {
136             xs *list = timeline_instance_list(skip, show);
137@@ -2763,14 +2763,14 @@ int html_get_handler(const xs_dict *req, const char *q_path,
138             *body = html_timeline(&snac, list, 0, skip, show,
139                 xs_list_len(next), L("Showing instance timeline"), "/instance", 0);
140             *b_size = strlen(*body);
141-            status  = 200;
142+            status  = HTTP_STATUS_OK;
143         }
144     }
145     else
146     if (xs_startswith(p_path, "list/")) { /** list timelines **/
147         if (!login(&snac, req)) {
148             *body  = xs_dup(uid);
149-            status = 401;
150+            status = HTTP_STATUS_UNAUTHORIZED;
151         }
152         else {
153             xs *l = xs_split(p_path, "/");
154@@ -2787,14 +2787,14 @@ int html_get_handler(const xs_dict *req, const char *q_path,
155                 *body = html_timeline(&snac, list, 0, skip, show,
156                     xs_list_len(next), title, base, 1);
157                 *b_size = strlen(*body);
158-                status  = 200;
159+                status  = HTTP_STATUS_OK;
160             }
161         }
162     }
163     else
164     if (xs_startswith(p_path, "p/")) { /** a timeline with just one entry **/
165         if (xs_type(xs_dict_get(snac.config, "private")) == XSTYPE_TRUE)
166-            return 403;
167+            return HTTP_STATUS_FORBIDDEN;
168 
169         xs *id  = xs_fmt("%s/%s", snac.actor, p_path);
170         xs *msg = NULL;
171@@ -2807,7 +2807,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
172 
173             *body   = html_timeline(&snac, list, 1, 0, 0, 0, NULL, "", 1);
174             *b_size = strlen(*body);
175-            status  = 200;
176+            status  = HTTP_STATUS_OK;
177         }
178     }
179     else
180@@ -2829,10 +2829,10 @@ int html_get_handler(const xs_dict *req, const char *q_path,
181     else
182     if (xs_startswith(p_path, "h/")) { /** an entry from the history **/
183         if (xs_type(xs_dict_get(snac.config, "private")) == XSTYPE_TRUE)
184-            return 403;
185+            return HTTP_STATUS_FORBIDDEN;
186 
187         if (xs_type(xs_dict_get(srv_config, "disable_history")) == XSTYPE_TRUE)
188-            return 403;
189+            return HTTP_STATUS_FORBIDDEN;
190 
191         xs *l = xs_split(p_path, "/");
192         const char *id = xs_list_get(l, 1);
193@@ -2841,7 +2841,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
194             if (xs_endswith(id, "timeline.html_")) {
195                 /* Don't let them in */
196                 *b_size = 0;
197-                status = 404;
198+                status = HTTP_STATUS_NOT_FOUND;
199             }
200             else
201                 status = history_get(&snac, id, body, b_size,
202@@ -2851,7 +2851,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
203     else
204     if (strcmp(p_path, ".rss") == 0) { /** public timeline in RSS format **/
205         if (xs_type(xs_dict_get(snac.config, "private")) == XSTYPE_TRUE)
206-            return 403;
207+            return HTTP_STATUS_FORBIDDEN;
208 
209         xs *elems = timeline_simple_list(&snac, "public", 0, 20);
210         xs *bio   = not_really_markdown(xs_dict_get(snac.config, "bio"), NULL, NULL);
211@@ -2865,12 +2865,12 @@ int html_get_handler(const xs_dict *req, const char *q_path,
212         *body   = timeline_to_rss(&snac, elems, rss_title, rss_link, bio);
213         *b_size = strlen(*body);
214         *ctype  = "application/rss+xml; charset=utf-8";
215-        status  = 200;
216+        status  = HTTP_STATUS_OK;
217 
218         snac_debug(&snac, 1, xs_fmt("serving RSS"));
219     }
220     else
221-        status = 404;
222+        status = HTTP_STATUS_NOT_FOUND;
223 
224     user_free(&snac);
225 
226@@ -2901,7 +2901,7 @@ int html_post_handler(const xs_dict *req, const char *q_path,
227     if (!uid || !user_open(&snac, uid)) {
228         /* invalid user */
229         srv_debug(1, xs_fmt("html_post_handler bad user %s", uid));
230-        return 404;
231+        return HTTP_STATUS_NOT_FOUND;
232     }
233 
234     p_path = xs_list_get(l, 2);
235@@ -2910,7 +2910,7 @@ int html_post_handler(const xs_dict *req, const char *q_path,
236     if (!login(&snac, req)) {
237         user_free(&snac);
238         *body  = xs_dup(uid);
239-        return 401;
240+        return HTTP_STATUS_UNAUTHORIZED;
241     }
242 
243     p_vars = xs_dict_get(req, "p_vars");
244@@ -3049,7 +3049,7 @@ int html_post_handler(const xs_dict *req, const char *q_path,
245             history_del(&snac, "timeline.html_");
246         }
247 
248-        status = 303;
249+        status = HTTP_STATUS_SEE_OTHER;
250     }
251     else
252     if (p_path && strcmp(p_path, "admin/action") == 0) { /** **/
253@@ -3060,11 +3060,11 @@ int html_post_handler(const xs_dict *req, const char *q_path,
254         const char *group  = xs_dict_get(p_vars, "group");
255 
256         if (action == NULL)
257-            return 404;
258+            return HTTP_STATUS_NOT_FOUND;
259 
260         snac_debug(&snac, 1, xs_fmt("web action '%s' received", action));
261 
262-        status = 303;
263+        status = HTTP_STATUS_SEE_OTHER;
264 
265         if (strcmp(action, L("Like")) == 0) { /** **/
266             xs *msg = msg_admiration(&snac, id, "Like");
267@@ -3216,10 +3216,10 @@ int html_post_handler(const xs_dict *req, const char *q_path,
268             timeline_touch(&snac);
269         }
270         else
271-            status = 404;
272+            status = HTTP_STATUS_NOT_FOUND;
273 
274         /* delete the cached timeline */
275-        if (status == 303)
276+        if (status == HTTP_STATUS_SEE_OTHER)
277             history_del(&snac, "timeline.html_");
278     }
279     else
280@@ -3356,14 +3356,14 @@ int html_post_handler(const xs_dict *req, const char *q_path,
281 
282         enqueue_verify_links(&snac);
283 
284-        status = 303;
285+        status = HTTP_STATUS_SEE_OTHER;
286     }
287     else
288     if (p_path && strcmp(p_path, "admin/clear-notifications") == 0) { /** **/
289         notify_clear(&snac);
290         timeline_touch(&snac);
291 
292-        status = 303;
293+        status = HTTP_STATUS_SEE_OTHER;
294     }
295     else
296     if (p_path && strcmp(p_path, "admin/vote") == 0) { /** **/
297@@ -3416,10 +3416,10 @@ int html_post_handler(const xs_dict *req, const char *q_path,
298             }
299         }
300 
301-        status = 303;
302+        status = HTTP_STATUS_SEE_OTHER;
303     }
304 
305-    if (status == 303) {
306+    if (status == HTTP_STATUS_SEE_OTHER) {
307         const char *redir = xs_dict_get(p_vars, "redir");
308 
309         if (xs_is_null(redir))
+13, -13
 1@@ -217,17 +217,17 @@ int server_get_handler(xs_dict *req, const char *q_path,
 2             *body = greeting_html();
 3 
 4         if (*body)
 5-            status = 200;
 6+            status = HTTP_STATUS_OK;
 7     }
 8     else
 9     if (strcmp(q_path, "/susie.png") == 0 || strcmp(q_path, "/favicon.ico") == 0 ) {
10-        status = 200;
11+        status = HTTP_STATUS_OK;
12         *body  = xs_base64_dec(default_avatar_base64(), b_size);
13         *ctype = "image/png";
14     }
15     else
16     if (strcmp(q_path, "/.well-known/nodeinfo") == 0) {
17-        status = 200;
18+        status = HTTP_STATUS_OK;
19         *ctype = "application/json; charset=utf-8";
20         *body  = xs_fmt("{\"links\":["
21             "{\"rel\":\"http:/" "/nodeinfo.diaspora.software/ns/schema/2.0\","
22@@ -236,7 +236,7 @@ int server_get_handler(xs_dict *req, const char *q_path,
23     }
24     else
25     if (strcmp(q_path, "/.well-known/host-meta") == 0) {
26-        status = 200;
27+        status = HTTP_STATUS_OK;
28         *ctype = "application/xrd+xml";
29         *body  = xs_fmt("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
30                 "<XRD>"
31@@ -245,13 +245,13 @@ int server_get_handler(xs_dict *req, const char *q_path,
32     }
33     else
34     if (strcmp(q_path, "/nodeinfo_2_0") == 0) {
35-        status = 200;
36+        status = HTTP_STATUS_OK;
37         *ctype = "application/json; charset=utf-8";
38         *body  = nodeinfo_2_0();
39     }
40     else
41     if (strcmp(q_path, "/robots.txt") == 0) {
42-        status = 200;
43+        status = HTTP_STATUS_OK;
44         *ctype = "text/plain";
45         *body  = xs_str_new("User-agent: *\n"
46                             "Disallow: /\n");
47@@ -363,7 +363,7 @@ void httpd_connection(FILE *f)
48     }
49     else
50     if (strcmp(method, "OPTIONS") == 0) {
51-        status = 200;
52+        status = HTTP_STATUS_OK;
53     }
54     else
55     if (strcmp(method, "DELETE") == 0) {
56@@ -378,22 +378,22 @@ void httpd_connection(FILE *f)
57     if (status == 0) {
58         srv_archive_error("unattended_method", "unattended method", req, payload);
59         srv_debug(1, xs_fmt("httpd_connection unattended %s %s", method, q_path));
60-        status = 404;
61+        status = HTTP_STATUS_NOT_FOUND;
62     }
63 
64-    if (status == 403)
65+    if (status == HTTP_STATUS_FORBIDDEN)
66         body = xs_str_new("<h1>403 Forbidden</h1>");
67 
68-    if (status == 404)
69+    if (status == HTTP_STATUS_NOT_FOUND)
70         body = xs_str_new("<h1>404 Not Found</h1>");
71 
72-    if (status == 400 && body != NULL)
73+    if (status == HTTP_STATUS_BAD_REQUEST && body != NULL)
74         body = xs_str_new("<h1>400 Bad Request</h1>");
75 
76-    if (status == 303)
77+    if (status == HTTP_STATUS_SEE_OTHER)
78         headers = xs_dict_append(headers, "location", body);
79 
80-    if (status == 401) {
81+    if (status == HTTP_STATUS_UNAUTHORIZED) {
82         xs *www_auth = xs_fmt("Basic realm=\"@%s@%s snac login\"",
83                                 body, xs_dict_get(srv_config, "host"));
84 
+92, -92
  1@@ -32,9 +32,9 @@ int app_add(const char *id, const xs_dict *app)
  2 /* stores an app */
  3 {
  4     if (!xs_is_hex(id))
  5-        return 500;
  6+        return HTTP_STATUS_INTERNAL_SERVER_ERROR;
  7 
  8-    int status = 201;
  9+    int status = HTTP_STATUS_CREATED;
 10     xs *fn     = xs_fmt("%s/app/", srv_basedir);
 11     FILE *f;
 12 
 13@@ -47,7 +47,7 @@ int app_add(const char *id, const xs_dict *app)
 14         fclose(f);
 15     }
 16     else
 17-        status = 500;
 18+        status = HTTP_STATUS_INTERNAL_SERVER_ERROR;
 19 
 20     return status;
 21 }
 22@@ -95,9 +95,9 @@ int token_add(const char *id, const xs_dict *token)
 23 /* stores a token */
 24 {
 25     if (!xs_is_hex(id))
 26-        return 500;
 27+        return HTTP_STATUS_INTERNAL_SERVER_ERROR;
 28 
 29-    int status = 201;
 30+    int status = HTTP_STATUS_CREATED;
 31     xs *fn     = xs_fmt("%s/token/", srv_basedir);
 32     FILE *f;
 33 
 34@@ -110,7 +110,7 @@ int token_add(const char *id, const xs_dict *token)
 35         fclose(f);
 36     }
 37     else
 38-        status = 500;
 39+        status = HTTP_STATUS_INTERNAL_SERVER_ERROR;
 40 
 41     return status;
 42 }
 43@@ -174,7 +174,7 @@ int oauth_get_handler(const xs_dict *req, const char *q_path,
 44     if (!xs_startswith(q_path, "/oauth/"))
 45         return 0;
 46 
 47-    int status   = 404;
 48+    int status   = HTTP_STATUS_NOT_FOUND;
 49     const xs_dict *msg = xs_dict_get(req, "q_vars");
 50     xs *cmd      = xs_replace_n(q_path, "/oauth", "", 1);
 51 
 52@@ -186,7 +186,7 @@ int oauth_get_handler(const xs_dict *req, const char *q_path,
 53         const char *rtype = xs_dict_get(msg, "response_type");
 54         const char *state = xs_dict_get(msg, "state");
 55 
 56-        status = 400;
 57+        status = HTTP_STATUS_BAD_REQUEST;
 58 
 59         if (cid && ruri && rtype && strcmp(rtype, "code") == 0) {
 60             xs *app = app_get(cid);
 61@@ -201,7 +201,7 @@ int oauth_get_handler(const xs_dict *req, const char *q_path,
 62                 *body  = xs_fmt(login_page, host, host, "", proto, host, "oauth/x-snac-login",
 63                                 ruri, cid, state, USER_AGENT);
 64                 *ctype = "text/html";
 65-                status = 200;
 66+                status = HTTP_STATUS_OK;
 67 
 68                 srv_debug(1, xs_fmt("oauth authorize: generating login page"));
 69             }
 70@@ -219,7 +219,7 @@ int oauth_get_handler(const xs_dict *req, const char *q_path,
 71         *body  = xs_fmt(login_page, host, host, "", proto, host, "oauth/x-snac-get-token",
 72                         "", "", "", USER_AGENT);
 73         *ctype = "text/html";
 74-        status = 200;
 75+        status = HTTP_STATUS_OK;
 76 
 77     }
 78 
 79@@ -237,7 +237,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
 80     if (!xs_startswith(q_path, "/oauth/"))
 81         return 0;
 82 
 83-    int status   = 404;
 84+    int status   = HTTP_STATUS_NOT_FOUND;
 85 
 86     const char *i_ctype = xs_dict_get(req, "content-type");
 87     xs *args      = NULL;
 88@@ -255,7 +255,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
 89         args = xs_dup(xs_dict_get(req, "p_vars"));
 90 
 91     if (args == NULL)
 92-        return 400;
 93+        return HTTP_STATUS_BAD_REQUEST;
 94 
 95     xs *cmd = xs_replace_n(q_path, "/oauth", "", 1);
 96 
 97@@ -274,7 +274,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
 98         *body  = xs_fmt(login_page, host, host, "LOGIN INCORRECT", proto, host, "oauth/x-snac-login",
 99                         redir, cid, state, USER_AGENT);
100         *ctype = "text/html";
101-        status = 200;
102+        status = HTTP_STATUS_OK;
103 
104         if (login && passwd && redir && cid) {
105             snac snac;
106@@ -296,7 +296,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
107                         else
108                             *body = xs_fmt("%s?code=%s", redir, code);
109 
110-                        status = 303;
111+                        status = HTTP_STATUS_SEE_OTHER;
112                     }
113 
114                     /* if there is a state, add it */
115@@ -375,12 +375,12 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
116             xs *app = app_get(cid);
117 
118             if (app == NULL) {
119-                status = 401;
120+                status = HTTP_STATUS_UNAUTHORIZED;
121                 srv_log(xs_fmt("oauth token: invalid app %s", cid));
122             }
123             else
124             if (strcmp(csec, xs_dict_get(app, "client_secret")) != 0) {
125-                status = 401;
126+                status = HTTP_STATUS_UNAUTHORIZED;
127                 srv_log(xs_fmt("oauth token: invalid client_secret for app %s", cid));
128             }
129             else {
130@@ -397,7 +397,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
131 
132                 *body  = xs_json_dumps(rsp, 4);
133                 *ctype = "application/json";
134-                status = 200;
135+                status = HTTP_STATUS_OK;
136 
137                 const char *uid = xs_dict_get(app, "uid");
138 
139@@ -416,7 +416,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
140         }
141         else {
142             srv_debug(1, xs_fmt("oauth token: invalid or unset arguments"));
143-            status = 400;
144+            status = HTTP_STATUS_BAD_REQUEST;
145         }
146     }
147     else
148@@ -433,12 +433,12 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
149 
150             if (token == NULL || strcmp(csec, xs_dict_get(token, "client_secret")) != 0) {
151                 srv_debug(1, xs_fmt("oauth revoke: bad secret for token %s", tokid));
152-                status = 403;
153+                status = HTTP_STATUS_FORBIDDEN;
154             }
155             else {
156                 token_del(tokid);
157                 srv_debug(1, xs_fmt("oauth revoke: revoked token %s", tokid));
158-                status = 200;
159+                status = HTTP_STATUS_OK;
160 
161                 /* also delete the app, as it serves no purpose from now on */
162                 app_del(cid);
163@@ -446,7 +446,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
164         }
165         else {
166             srv_debug(1, xs_fmt("oauth revoke: invalid or unset arguments"));
167-            status = 403;
168+            status = HTTP_STATUS_FORBIDDEN;
169         }
170     }
171     if (strcmp(cmd, "/x-snac-get-token") == 0) { /** **/
172@@ -459,7 +459,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
173         *body  = xs_fmt(login_page, host, host, "LOGIN INCORRECT", proto, host, "oauth/x-snac-get-token",
174                         "", "", "", USER_AGENT);
175         *ctype = "text/html";
176-        status = 200;
177+        status = HTTP_STATUS_OK;
178 
179         if (login && passwd) {
180             snac user;
181@@ -1159,7 +1159,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
182     if (!xs_startswith(q_path, "/api/v1/") && !xs_startswith(q_path, "/api/v2/"))
183         return 0;
184 
185-    int status    = 404;
186+    int status    = HTTP_STATUS_NOT_FOUND;
187     const xs_dict *args = xs_dict_get(req, "q_vars");
188     xs *cmd       = xs_replace_n(q_path, "/api", "", 1);
189 
190@@ -1249,10 +1249,10 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
191 
192             *body  = xs_json_dumps(acct, 4);
193             *ctype = "application/json";
194-            status = 200;
195+            status = HTTP_STATUS_OK;
196         }
197         else {
198-            status = 422;   // "Unprocessable entity" (no login)
199+            status = HTTP_STATUS_UNPROCESSABLE_CONTENT; // (no login)
200         }
201     }
202     else
203@@ -1279,10 +1279,10 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
204 
205             *body  = xs_json_dumps(res, 4);
206             *ctype = "application/json";
207-            status = 200;
208+            status = HTTP_STATUS_OK;
209         }
210         else
211-            status = 422;
212+            status = HTTP_STATUS_UNPROCESSABLE_CONTENT;
213     }
214     else
215     if (strcmp(cmd, "/v1/accounts/lookup") == 0) { /** **/
216@@ -1304,7 +1304,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
217 
218                     *body  = xs_json_dumps(macct, 4);
219                     *ctype = "application/json";
220-                    status = 200;
221+                    status = HTTP_STATUS_OK;
222 
223                     user_free(&user);
224                 }
225@@ -1450,7 +1450,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
226             if (out != NULL) {
227                 *body  = xs_json_dumps(out, 4);
228                 *ctype = "application/json";
229-                status = 200;
230+                status = HTTP_STATUS_OK;
231             }
232         }
233     }
234@@ -1554,12 +1554,12 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
235 
236             *body  = xs_json_dumps(out, 4);
237             *ctype = "application/json";
238-            status = 200;
239+            status = HTTP_STATUS_OK;
240 
241             srv_debug(2, xs_fmt("mastoapi timeline: returned %d entries", xs_list_len(out)));
242         }
243         else {
244-            status = 401; // unauthorized
245+            status = HTTP_STATUS_UNAUTHORIZED;
246         }
247     }
248     else
249@@ -1612,7 +1612,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
250 
251         *body  = xs_json_dumps(out, 4);
252         *ctype = "application/json";
253-        status = 200;
254+        status = HTTP_STATUS_OK;
255     }
256     else
257     if (xs_startswith(cmd, "/v1/timelines/tag/")) { /** **/
258@@ -1661,7 +1661,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
259 
260         *body  = xs_json_dumps(out, 4);
261         *ctype = "application/json";
262-        status = 200;
263+        status = HTTP_STATUS_OK;
264     }
265     else
266     if (xs_startswith(cmd, "/v1/timelines/list/")) { /** **/
267@@ -1729,17 +1729,17 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
268 
269             *body  = xs_json_dumps(out, 4);
270             *ctype = "application/json";
271-            status = 200;
272+            status = HTTP_STATUS_OK;
273         }
274         else
275-            status = 421;
276+            status = HTTP_STATUS_MISDIRECTED_REQUEST;
277     }
278     else
279     if (strcmp(cmd, "/v1/conversations") == 0) { /** **/
280         /* TBD */
281         *body  = xs_dup("[]");
282         *ctype = "application/json";
283-        status = 200;
284+        status = HTTP_STATUS_OK;
285     }
286     else
287     if (strcmp(cmd, "/v1/notifications") == 0) { /** **/
288@@ -1817,17 +1817,17 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
289 
290             *body  = xs_json_dumps(out, 4);
291             *ctype = "application/json";
292-            status = 200;
293+            status = HTTP_STATUS_OK;
294         }
295         else
296-            status = 401;
297+            status = HTTP_STATUS_UNAUTHORIZED;
298     }
299     else
300     if (strcmp(cmd, "/v1/filters") == 0) { /** **/
301         /* snac will never have filters */
302         *body  = xs_dup("[]");
303         *ctype = "application/json";
304-        status = 200;
305+        status = HTTP_STATUS_OK;
306     }
307     else
308     if (strcmp(cmd, "/v2/filters") == 0) { /** **/
309@@ -1836,21 +1836,21 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
310          * in some apps */
311         *body  = xs_dup("[]");
312         *ctype = "application/json";
313-        status = 200;
314+        status = HTTP_STATUS_OK;
315     }
316     else
317     if (strcmp(cmd, "/v1/favourites") == 0) { /** **/
318         /* snac will never support a list of favourites */
319         *body  = xs_dup("[]");
320         *ctype = "application/json";
321-        status = 200;
322+        status = HTTP_STATUS_OK;
323     }
324     else
325     if (strcmp(cmd, "/v1/bookmarks") == 0) { /** **/
326         /* snac does not support bookmarks */
327         *body  = xs_dup("[]");
328         *ctype = "application/json";
329-        status = 200;
330+        status = HTTP_STATUS_OK;
331     }
332     else
333     if (strcmp(cmd, "/v1/lists") == 0) { /** list of lists **/
334@@ -1873,7 +1873,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
335 
336             *body  = xs_json_dumps(l, 4);
337             *ctype = "application/json";
338-            status = 200;
339+            status = HTTP_STATUS_OK;
340         }
341     }
342     else
343@@ -1903,7 +1903,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
344 
345                         *body  = xs_json_dumps(out, 4);
346                         *ctype = "application/json";
347-                        status = 200;
348+                        status = HTTP_STATUS_OK;
349                     }
350                 }
351                 else
352@@ -1931,7 +1931,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
353 
354                     *body  = xs_json_dumps(out, 4);
355                     *ctype = "application/json";
356-                    status = 200;
357+                    status = HTTP_STATUS_OK;
358                 }
359             }
360         }
361@@ -1941,28 +1941,28 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
362         /* snac does not schedule notes */
363         *body  = xs_dup("[]");
364         *ctype = "application/json";
365-        status = 200;
366+        status = HTTP_STATUS_OK;
367     }
368     else
369     if (strcmp(cmd, "/v1/follow_requests") == 0) { /** **/
370         /* snac does not support optional follow confirmations */
371         *body  = xs_dup("[]");
372         *ctype = "application/json";
373-        status = 200;
374+        status = HTTP_STATUS_OK;
375     }
376     else
377     if (strcmp(cmd, "/v1/announcements") == 0) { /** **/
378         /* snac has no announcements (yet?) */
379         *body  = xs_dup("[]");
380         *ctype = "application/json";
381-        status = 200;
382+        status = HTTP_STATUS_OK;
383     }
384     else
385     if (strcmp(cmd, "/v1/custom_emojis") == 0) { /** **/
386         /* are you kidding me? */
387         *body  = xs_dup("[]");
388         *ctype = "application/json";
389-        status = 200;
390+        status = HTTP_STATUS_OK;
391     }
392     else
393     if (strcmp(cmd, "/v1/instance") == 0) { /** **/
394@@ -2075,7 +2075,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
395 
396         *body  = xs_json_dumps(ins, 4);
397         *ctype = "application/json";
398-        status = 200;
399+        status = HTTP_STATUS_OK;
400     }
401     else
402     if (xs_startswith(cmd, "/v1/statuses/")) { /** **/
403@@ -2188,30 +2188,30 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
404                 if (out != NULL) {
405                     *body  = xs_json_dumps(out, 4);
406                     *ctype = "application/json";
407-                    status = 200;
408+                    status = HTTP_STATUS_OK;
409                 }
410             }
411         }
412         else
413-            status = 401;
414+            status = HTTP_STATUS_UNAUTHORIZED;
415     }
416     else
417     if (strcmp(cmd, "/v1/preferences") == 0) { /** **/
418         *body  = xs_dup("{}");
419         *ctype = "application/json";
420-        status = 200;
421+        status = HTTP_STATUS_OK;
422     }
423     else
424     if (strcmp(cmd, "/v1/markers") == 0) { /** **/
425         *body  = xs_dup("{}");
426         *ctype = "application/json";
427-        status = 200;
428+        status = HTTP_STATUS_OK;
429     }
430     else
431     if (strcmp(cmd, "/v1/followed_tags") == 0) { /** **/
432         *body  = xs_dup("[]");
433         *ctype = "application/json";
434-        status = 200;
435+        status = HTTP_STATUS_OK;
436     }
437     else
438     if (strcmp(cmd, "/v2/search") == 0) { /** **/
439@@ -2290,10 +2290,10 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
440 
441             *body  = xs_json_dumps(res, 4);
442             *ctype = "application/json";
443-            status = 200;
444+            status = HTTP_STATUS_OK;
445         }
446         else
447-            status = 401;
448+            status = HTTP_STATUS_UNAUTHORIZED;
449     }
450 
451     /* user cleanup */
452@@ -2316,7 +2316,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
453     if (!xs_startswith(q_path, "/api/v1/") && !xs_startswith(q_path, "/api/v2/"))
454         return 0;
455 
456-    int status    = 404;
457+    int status    = HTTP_STATUS_NOT_FOUND;
458     xs *args      = NULL;
459     const char *i_ctype = xs_dict_get(req, "content-type");
460 
461@@ -2336,7 +2336,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
462         args = xs_dup(xs_dict_get(req, "p_vars"));
463 
464     if (args == NULL)
465-        return 400;
466+        return HTTP_STATUS_BAD_REQUEST;
467 
468     xs *cmd = xs_replace_n(q_path, "/api", "", 1);
469 
470@@ -2378,7 +2378,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
471 
472             *body  = xs_json_dumps(app, 4);
473             *ctype = "application/json";
474-            status = 200;
475+            status = HTTP_STATUS_OK;
476 
477             app = xs_dict_append(app, "code", "");
478 
479@@ -2470,10 +2470,10 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
480 
481             *body  = xs_json_dumps(st, 4);
482             *ctype = "application/json";
483-            status = 200;
484+            status = HTTP_STATUS_OK;
485         }
486         else
487-            status = 401;
488+            status = HTTP_STATUS_UNAUTHORIZED;
489     }
490     else
491     if (xs_startswith(cmd, "/v1/statuses")) { /** **/
492@@ -2552,7 +2552,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
493                         if (pin(&snac, id))
494                             out = mastoapi_status(&snac, msg);
495                         else
496-                            status = 422;
497+                            status = HTTP_STATUS_UNPROCESSABLE_CONTENT;
498                     }
499                     else
500                     if (strcmp(op, "unpin") == 0) { /** **/
501@@ -2573,12 +2573,12 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
502                 if (out != NULL) {
503                     *body  = xs_json_dumps(out, 4);
504                     *ctype = "application/json";
505-                    status = 200;
506+                    status = HTTP_STATUS_OK;
507                 }
508             }
509         }
510         else
511-            status = 401;
512+            status = HTTP_STATUS_UNAUTHORIZED;
513     }
514     else
515     if (strcmp(cmd, "/v1/notifications/clear") == 0) { /** **/
516@@ -2588,10 +2588,10 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
517 
518             *body  = xs_dup("{}");
519             *ctype = "application/json";
520-            status = 200;
521+            status = HTTP_STATUS_OK;
522         }
523         else
524-            status = 401;
525+            status = HTTP_STATUS_UNAUTHORIZED;
526     }
527     else
528     if (strcmp(cmd, "/v1/push/subscription") == 0) { /** **/
529@@ -2616,10 +2616,10 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
530 
531             *body  = xs_json_dumps(wpush, 4);
532             *ctype = "application/json";
533-            status = 200;
534+            status = HTTP_STATUS_OK;
535         }
536         else
537-            status = 401;
538+            status = HTTP_STATUS_UNAUTHORIZED;
539     }
540     else
541     if (strcmp(cmd, "/v1/media") == 0 || strcmp(cmd, "/v2/media") == 0) { /** **/
542@@ -2630,7 +2630,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
543             if (xs_is_null(desc))
544                 desc = "";
545 
546-            status = 400;
547+            status = HTTP_STATUS_BAD_REQUEST;
548 
549             if (xs_type(file) == XSTYPE_LIST) {
550                 const char *fn = xs_list_get(file, 0);
551@@ -2659,12 +2659,12 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
552 
553                     *body  = xs_json_dumps(rsp, 4);
554                     *ctype = "application/json";
555-                    status = 200;
556+                    status = HTTP_STATUS_OK;
557                 }
558             }
559         }
560         else
561-            status = 401;
562+            status = HTTP_STATUS_UNAUTHORIZED;
563     }
564     else
565     if (xs_startswith(cmd, "/v1/accounts")) { /** **/
566@@ -2744,11 +2744,11 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
567             if (rsp != NULL) {
568                 *body  = xs_json_dumps(rsp, 4);
569                 *ctype = "application/json";
570-                status = 200;
571+                status = HTTP_STATUS_OK;
572             }
573         }
574         else
575-            status = 401;
576+            status = HTTP_STATUS_UNAUTHORIZED;
577     }
578     else
579     if (xs_startswith(cmd, "/v1/polls")) { /** **/
580@@ -2810,12 +2810,12 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
581                 if (out != NULL) {
582                     *body  = xs_json_dumps(out, 4);
583                     *ctype = "application/json";
584-                    status = 200;
585+                    status = HTTP_STATUS_OK;
586                 }
587             }
588         }
589         else
590-            status = 401;
591+            status = HTTP_STATUS_UNAUTHORIZED;
592     }
593     else
594     if (strcmp(cmd, "/v1/lists") == 0) {
595@@ -2831,18 +2831,18 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
596                     out = xs_dict_append(out, "replies_policy", xs_dict_get_def(args, "replies_policy", "list"));
597                     out = xs_dict_append(out, "exclusive", xs_stock(XSTYPE_FALSE));
598 
599-                    status = 200;
600+                    status = HTTP_STATUS_OK;
601                 }
602                 else {
603                     out = xs_dict_append(out, "error", "cannot create list");
604-                    status = 422;
605+                    status = HTTP_STATUS_UNPROCESSABLE_CONTENT;
606                 }
607 
608                 *body  = xs_json_dumps(out, 4);
609                 *ctype = "application/json";
610             }
611             else
612-                status = 422;
613+                status = HTTP_STATUS_UNPROCESSABLE_CONTENT;
614         }
615     }
616     if (xs_startswith(cmd, "/v1/lists/")) { /** list maintenance **/
617@@ -2861,12 +2861,12 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
618                         list_content(&snac, id, v, 1);
619                     }
620 
621-                    status = 200;
622+                    status = HTTP_STATUS_OK;
623                 }
624             }
625         }
626         else
627-            status = 422;
628+            status = HTTP_STATUS_UNPROCESSABLE_CONTENT;
629     }
630 
631     /* user cleanup */
632@@ -2891,7 +2891,7 @@ int mastoapi_delete_handler(const xs_dict *req, const char *q_path,
633     if (!xs_startswith(q_path, "/api/v1/") && !xs_startswith(q_path, "/api/v2/"))
634         return 0;
635 
636-    int status    = 404;
637+    int status    = HTTP_STATUS_NOT_FOUND;
638     xs *args      = NULL;
639     const char *i_ctype = xs_dict_get(req, "content-type");
640 
641@@ -2911,7 +2911,7 @@ int mastoapi_delete_handler(const xs_dict *req, const char *q_path,
642         args = xs_dup(xs_dict_get(req, "p_vars"));
643 
644     if (args == NULL)
645-        return 400;
646+        return HTTP_STATUS_BAD_REQUEST;
647 
648     snac snac = {0};
649     int logged_in = process_auth_token(&snac, req);
650@@ -2920,7 +2920,7 @@ int mastoapi_delete_handler(const xs_dict *req, const char *q_path,
651 
652     if (xs_startswith(cmd, "/v1/push/subscription") || xs_startswith(cmd, "/v2/push/subscription")) { /** **/
653         // pretend we deleted it, since it doesn't exist anyway
654-        status = 200;
655+        status = HTTP_STATUS_OK;
656     }
657     else
658     if (xs_startswith(cmd, "/v1/lists/")) {
659@@ -2948,10 +2948,10 @@ int mastoapi_delete_handler(const xs_dict *req, const char *q_path,
660                 }
661             }
662 
663-            status = 200;
664+            status = HTTP_STATUS_OK;
665         }
666         else
667-            status = 401;
668+            status = HTTP_STATUS_UNAUTHORIZED;
669     }
670 
671     /* user cleanup */
672@@ -2974,7 +2974,7 @@ int mastoapi_put_handler(const xs_dict *req, const char *q_path,
673     if (!xs_startswith(q_path, "/api/v1/") && !xs_startswith(q_path, "/api/v2/"))
674         return 0;
675 
676-    int status    = 404;
677+    int status    = HTTP_STATUS_NOT_FOUND;
678     xs *args      = NULL;
679     const char *i_ctype = xs_dict_get(req, "content-type");
680 
681@@ -2986,7 +2986,7 @@ int mastoapi_put_handler(const xs_dict *req, const char *q_path,
682         args = xs_dup(xs_dict_get(req, "p_vars"));
683 
684     if (args == NULL)
685-        return 400;
686+        return HTTP_STATUS_BAD_REQUEST;
687 
688     xs *cmd = xs_replace_n(q_path, "/api", "", 1);
689 
690@@ -3017,11 +3017,11 @@ int mastoapi_put_handler(const xs_dict *req, const char *q_path,
691 
692                 *body  = xs_json_dumps(rsp, 4);
693                 *ctype = "application/json";
694-                status = 200;
695+                status = HTTP_STATUS_OK;
696             }
697         }
698         else
699-            status = 401;
700+            status = HTTP_STATUS_UNAUTHORIZED;
701     }
702     else
703     if (xs_startswith(cmd, "/v1/statuses")) {
704@@ -3060,12 +3060,12 @@ int mastoapi_put_handler(const xs_dict *req, const char *q_path,
705                 if (rsp != NULL) {
706                     *body  = xs_json_dumps(rsp, 4);
707                     *ctype = "application/json";
708-                    status = 200;
709+                    status = HTTP_STATUS_OK;
710                 }
711             }
712         }
713         else
714-            status = 401;
715+            status = HTTP_STATUS_UNAUTHORIZED;
716     }
717 
718     /* user cleanup */
M snac.h
+50, -0
 1@@ -361,3 +361,53 @@ int mastoapi_put_handler(const xs_dict *req, const char *q_path,
 2 void mastoapi_purge(void);
 3 
 4 void verify_links(snac *user);
 5+
 6+/* HTTP responses RFC 9110 plus some extensions */
 7+
 8+typedef enum {
 9+    HTTP_STATUS_CONTINUE                      = 100,
10+    HTTP_STATUS_SWITCHTING_PROTOCOLS          = 101,
11+    HTTP_STATUS_PROCESSING                    = 102,
12+    HTTP_STATUS_EARLY_HINTS                   = 103,
13+    HTTP_STATUS_OK                            = 200,
14+    HTTP_STATUS_CREATED                       = 201,
15+    HTTP_STATUS_ACCEPTED                      = 202,
16+    HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION = 203,
17+    HTTP_STATUS_NO_CONTENT                    = 204,
18+    HTTP_STATUS_RESET_CONTENT                 = 205,
19+    HTTP_STATUS_PARTIAL_CONTENT               = 206,
20+    HTTP_STATUS_MULTI_STATUS                  = 207,
21+    HTTP_STATUS_ALREADY_REPORTED              = 208,
22+    HTTP_STATUS_THIS_IS_FINE                  = 218,
23+    HTTP_STATUS_IM_USED                       = 226,
24+    HTTP_STATUS_MULTIPLE_CHOICES              = 300,
25+    HTTP_STATUS_MOVED_PERMANENTLY             = 301,
26+    HTTP_STATUS_FOUND                         = 302,
27+    HTTP_STATUS_SEE_OTHER                     = 303,
28+    HTTP_STATUS_NOT_MODIFIED                  = 304,
29+    HTTP_STATUS_USE_PROXY                     = 305,
30+    HTTP_STATUS_SWITCH_PROXY                  = 306,
31+    HTTP_STATUS_TEMPORARY_REDIRECT            = 307,
32+    HTTP_STATUS_PERMANENT_REDIRECT            = 308,
33+    HTTP_STATUS_BAD_REQUEST                   = 400,
34+    HTTP_STATUS_UNAUTHORIZED                  = 401,
35+    HTTP_STATUS_PAYMENT_REQUIRED              = 402,
36+    HTTP_STATUS_FORBIDDEN                     = 403,
37+    HTTP_STATUS_NOT_FOUND                     = 404,
38+    HTTP_STATUS_METHOD_NOT_ALLOWED            = 405,
39+    HTTP_STATUS_NOT_ACCEPTABLE                = 406,
40+    HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED = 407,
41+    HTTP_STATUS_REQUEST_TIMEOUT               = 408,
42+    HTTP_STATUS_CONFLICT                      = 409,
43+    HTTP_STATUS_GONE                          = 410,
44+    HTTP_STATUS_MISDIRECTED_REQUEST           = 421,
45+    HTTP_STATUS_UNPROCESSABLE_CONTENT         = 422,
46+    HTTP_STATUS_CLIENT_CLOSED_REQUEST         = 499,
47+    HTTP_STATUS_INTERNAL_SERVER_ERROR         = 500,
48+    HTTP_STATUS_NOT_IMPLEMENTED               = 501,
49+    HTTP_STATUS_BAD_GATEWAY                   = 502,
50+    HTTP_STATUS_SERVICE_UNAVAILABLE           = 503,
51+    HTTP_STATUS_GATEWAY_TIMEOUT               = 504,
52+    HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED    = 505,
53+    HTTP_STATUS_INSUFFICIENT_STORAGE          = 507
54+} http_status;
+4, -4
 1@@ -42,7 +42,7 @@ int webfinger_request_signed(snac *snac, const char *qs, char **actor, char **us
 2     }
 3 
 4     if (host == NULL || resource == NULL)
 5-        return 400;
 6+        return HTTP_STATUS_BAD_REQUEST;
 7 
 8     headers = xs_dict_append(headers, "accept",     "application/json");
 9     headers = xs_dict_append(headers, "user-agent", USER_AGENT);
10@@ -139,7 +139,7 @@ int webfinger_get_handler(xs_dict *req, char *q_path,
11     const char *resource = xs_dict_get(q_vars, "resource");
12 
13     if (resource == NULL)
14-        return 400;
15+        return HTTP_STATUS_BAD_REQUEST;
16 
17     snac snac;
18     int found = 0;
19@@ -220,12 +220,12 @@ int webfinger_get_handler(xs_dict *req, char *q_path,
20 
21         user_free(&snac);
22 
23-        status = 200;
24+        status = HTTP_STATUS_OK;
25         *body  = j;
26         *ctype = "application/jrd+json";
27     }
28     else
29-        status = 404;
30+        status = HTTP_STATUS_NOT_FOUND;
31 
32     srv_debug(1, xs_fmt("webfinger_get_handler resource=%s %d", resource, status));
33