summaryrefslogtreecommitdiff
path: root/mastoapi.c
diff options
context:
space:
mode:
Diffstat (limited to 'mastoapi.c')
-rw-r--r--mastoapi.c496
1 files changed, 326 insertions, 170 deletions
diff --git a/mastoapi.c b/mastoapi.c
index 3936c2a..120c1aa 100644
--- a/mastoapi.c
+++ b/mastoapi.c
@@ -32,9 +32,9 @@ int app_add(const char *id, const xs_dict *app)
32/* stores an app */ 32/* stores an app */
33{ 33{
34 if (!xs_is_hex(id)) 34 if (!xs_is_hex(id))
35 return 500; 35 return HTTP_STATUS_INTERNAL_SERVER_ERROR;
36 36
37 int status = 201; 37 int status = HTTP_STATUS_CREATED;
38 xs *fn = xs_fmt("%s/app/", srv_basedir); 38 xs *fn = xs_fmt("%s/app/", srv_basedir);
39 FILE *f; 39 FILE *f;
40 40
@@ -47,7 +47,7 @@ int app_add(const char *id, const xs_dict *app)
47 fclose(f); 47 fclose(f);
48 } 48 }
49 else 49 else
50 status = 500; 50 status = HTTP_STATUS_INTERNAL_SERVER_ERROR;
51 51
52 return status; 52 return status;
53} 53}
@@ -95,9 +95,9 @@ int token_add(const char *id, const xs_dict *token)
95/* stores a token */ 95/* stores a token */
96{ 96{
97 if (!xs_is_hex(id)) 97 if (!xs_is_hex(id))
98 return 500; 98 return HTTP_STATUS_INTERNAL_SERVER_ERROR;
99 99
100 int status = 201; 100 int status = HTTP_STATUS_CREATED;
101 xs *fn = xs_fmt("%s/token/", srv_basedir); 101 xs *fn = xs_fmt("%s/token/", srv_basedir);
102 FILE *f; 102 FILE *f;
103 103
@@ -110,7 +110,7 @@ int token_add(const char *id, const xs_dict *token)
110 fclose(f); 110 fclose(f);
111 } 111 }
112 else 112 else
113 status = 500; 113 status = HTTP_STATUS_INTERNAL_SERVER_ERROR;
114 114
115 return status; 115 return status;
116} 116}
@@ -174,7 +174,7 @@ int oauth_get_handler(const xs_dict *req, const char *q_path,
174 if (!xs_startswith(q_path, "/oauth/")) 174 if (!xs_startswith(q_path, "/oauth/"))
175 return 0; 175 return 0;
176 176
177 int status = 404; 177 int status = HTTP_STATUS_NOT_FOUND;
178 const xs_dict *msg = xs_dict_get(req, "q_vars"); 178 const xs_dict *msg = xs_dict_get(req, "q_vars");
179 xs *cmd = xs_replace_n(q_path, "/oauth", "", 1); 179 xs *cmd = xs_replace_n(q_path, "/oauth", "", 1);
180 180
@@ -186,7 +186,7 @@ int oauth_get_handler(const xs_dict *req, const char *q_path,
186 const char *rtype = xs_dict_get(msg, "response_type"); 186 const char *rtype = xs_dict_get(msg, "response_type");
187 const char *state = xs_dict_get(msg, "state"); 187 const char *state = xs_dict_get(msg, "state");
188 188
189 status = 400; 189 status = HTTP_STATUS_BAD_REQUEST;
190 190
191 if (cid && ruri && rtype && strcmp(rtype, "code") == 0) { 191 if (cid && ruri && rtype && strcmp(rtype, "code") == 0) {
192 xs *app = app_get(cid); 192 xs *app = app_get(cid);
@@ -201,7 +201,7 @@ int oauth_get_handler(const xs_dict *req, const char *q_path,
201 *body = xs_fmt(login_page, host, host, "", proto, host, "oauth/x-snac-login", 201 *body = xs_fmt(login_page, host, host, "", proto, host, "oauth/x-snac-login",
202 ruri, cid, state, USER_AGENT); 202 ruri, cid, state, USER_AGENT);
203 *ctype = "text/html"; 203 *ctype = "text/html";
204 status = 200; 204 status = HTTP_STATUS_OK;
205 205
206 srv_debug(1, xs_fmt("oauth authorize: generating login page")); 206 srv_debug(1, xs_fmt("oauth authorize: generating login page"));
207 } 207 }
@@ -219,7 +219,7 @@ int oauth_get_handler(const xs_dict *req, const char *q_path,
219 *body = xs_fmt(login_page, host, host, "", proto, host, "oauth/x-snac-get-token", 219 *body = xs_fmt(login_page, host, host, "", proto, host, "oauth/x-snac-get-token",
220 "", "", "", USER_AGENT); 220 "", "", "", USER_AGENT);
221 *ctype = "text/html"; 221 *ctype = "text/html";
222 status = 200; 222 status = HTTP_STATUS_OK;
223 223
224 } 224 }
225 225
@@ -237,7 +237,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
237 if (!xs_startswith(q_path, "/oauth/")) 237 if (!xs_startswith(q_path, "/oauth/"))
238 return 0; 238 return 0;
239 239
240 int status = 404; 240 int status = HTTP_STATUS_NOT_FOUND;
241 241
242 const char *i_ctype = xs_dict_get(req, "content-type"); 242 const char *i_ctype = xs_dict_get(req, "content-type");
243 xs *args = NULL; 243 xs *args = NULL;
@@ -255,7 +255,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
255 args = xs_dup(xs_dict_get(req, "p_vars")); 255 args = xs_dup(xs_dict_get(req, "p_vars"));
256 256
257 if (args == NULL) 257 if (args == NULL)
258 return 400; 258 return HTTP_STATUS_BAD_REQUEST;
259 259
260 xs *cmd = xs_replace_n(q_path, "/oauth", "", 1); 260 xs *cmd = xs_replace_n(q_path, "/oauth", "", 1);
261 261
@@ -274,7 +274,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
274 *body = xs_fmt(login_page, host, host, "LOGIN INCORRECT", proto, host, "oauth/x-snac-login", 274 *body = xs_fmt(login_page, host, host, "LOGIN INCORRECT", proto, host, "oauth/x-snac-login",
275 redir, cid, state, USER_AGENT); 275 redir, cid, state, USER_AGENT);
276 *ctype = "text/html"; 276 *ctype = "text/html";
277 status = 200; 277 status = HTTP_STATUS_OK;
278 278
279 if (login && passwd && redir && cid) { 279 if (login && passwd && redir && cid) {
280 snac snac; 280 snac snac;
@@ -296,7 +296,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
296 else 296 else
297 *body = xs_fmt("%s?code=%s", redir, code); 297 *body = xs_fmt("%s?code=%s", redir, code);
298 298
299 status = 303; 299 status = HTTP_STATUS_SEE_OTHER;
300 } 300 }
301 301
302 /* if there is a state, add it */ 302 /* if there is a state, add it */
@@ -375,12 +375,12 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
375 xs *app = app_get(cid); 375 xs *app = app_get(cid);
376 376
377 if (app == NULL) { 377 if (app == NULL) {
378 status = 401; 378 status = HTTP_STATUS_UNAUTHORIZED;
379 srv_log(xs_fmt("oauth token: invalid app %s", cid)); 379 srv_log(xs_fmt("oauth token: invalid app %s", cid));
380 } 380 }
381 else 381 else
382 if (strcmp(csec, xs_dict_get(app, "client_secret")) != 0) { 382 if (strcmp(csec, xs_dict_get(app, "client_secret")) != 0) {
383 status = 401; 383 status = HTTP_STATUS_UNAUTHORIZED;
384 srv_log(xs_fmt("oauth token: invalid client_secret for app %s", cid)); 384 srv_log(xs_fmt("oauth token: invalid client_secret for app %s", cid));
385 } 385 }
386 else { 386 else {
@@ -397,7 +397,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
397 397
398 *body = xs_json_dumps(rsp, 4); 398 *body = xs_json_dumps(rsp, 4);
399 *ctype = "application/json"; 399 *ctype = "application/json";
400 status = 200; 400 status = HTTP_STATUS_OK;
401 401
402 const char *uid = xs_dict_get(app, "uid"); 402 const char *uid = xs_dict_get(app, "uid");
403 403
@@ -416,7 +416,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
416 } 416 }
417 else { 417 else {
418 srv_debug(1, xs_fmt("oauth token: invalid or unset arguments")); 418 srv_debug(1, xs_fmt("oauth token: invalid or unset arguments"));
419 status = 400; 419 status = HTTP_STATUS_BAD_REQUEST;
420 } 420 }
421 } 421 }
422 else 422 else
@@ -433,12 +433,12 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
433 433
434 if (token == NULL || strcmp(csec, xs_dict_get(token, "client_secret")) != 0) { 434 if (token == NULL || strcmp(csec, xs_dict_get(token, "client_secret")) != 0) {
435 srv_debug(1, xs_fmt("oauth revoke: bad secret for token %s", tokid)); 435 srv_debug(1, xs_fmt("oauth revoke: bad secret for token %s", tokid));
436 status = 403; 436 status = HTTP_STATUS_FORBIDDEN;
437 } 437 }
438 else { 438 else {
439 token_del(tokid); 439 token_del(tokid);
440 srv_debug(1, xs_fmt("oauth revoke: revoked token %s", tokid)); 440 srv_debug(1, xs_fmt("oauth revoke: revoked token %s", tokid));
441 status = 200; 441 status = HTTP_STATUS_OK;
442 442
443 /* also delete the app, as it serves no purpose from now on */ 443 /* also delete the app, as it serves no purpose from now on */
444 app_del(cid); 444 app_del(cid);
@@ -446,7 +446,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
446 } 446 }
447 else { 447 else {
448 srv_debug(1, xs_fmt("oauth revoke: invalid or unset arguments")); 448 srv_debug(1, xs_fmt("oauth revoke: invalid or unset arguments"));
449 status = 403; 449 status = HTTP_STATUS_FORBIDDEN;
450 } 450 }
451 } 451 }
452 if (strcmp(cmd, "/x-snac-get-token") == 0) { /** **/ 452 if (strcmp(cmd, "/x-snac-get-token") == 0) { /** **/
@@ -459,7 +459,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
459 *body = xs_fmt(login_page, host, host, "LOGIN INCORRECT", proto, host, "oauth/x-snac-get-token", 459 *body = xs_fmt(login_page, host, host, "LOGIN INCORRECT", proto, host, "oauth/x-snac-get-token",
460 "", "", "", USER_AGENT); 460 "", "", "", USER_AGENT);
461 *ctype = "text/html"; 461 *ctype = "text/html";
462 status = 200; 462 status = HTTP_STATUS_OK;
463 463
464 if (login && passwd) { 464 if (login && passwd) {
465 snac user; 465 snac user;
@@ -1150,109 +1150,123 @@ int process_auth_token(snac *snac, const xs_dict *req)
1150 return logged_in; 1150 return logged_in;
1151} 1151}
1152 1152
1153 1153void credentials_get(char **body, char **ctype, int *status, snac snac)
1154int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1155 char **body, int *b_size, char **ctype)
1156{ 1154{
1157 (void)b_size; 1155 xs *acct = xs_dict_new();
1156
1157 acct = xs_dict_append(acct, "id", snac.md5);
1158 acct = xs_dict_append(acct, "username", xs_dict_get(snac.config, "uid"));
1159 acct = xs_dict_append(acct, "acct", xs_dict_get(snac.config, "uid"));
1160 acct = xs_dict_append(acct, "display_name", xs_dict_get(snac.config, "name"));
1161 acct = xs_dict_append(acct, "created_at", xs_dict_get(snac.config, "published"));
1162 acct = xs_dict_append(acct, "last_status_at", xs_dict_get(snac.config, "published"));
1163 acct = xs_dict_append(acct, "note", xs_dict_get(snac.config, "bio"));
1164 acct = xs_dict_append(acct, "url", snac.actor);
1165 acct = xs_dict_append(acct, "locked", xs_stock(XSTYPE_FALSE));
1166 acct = xs_dict_append(acct, "bot", xs_dict_get(snac.config, "bot"));
1158 1167
1159 if (!xs_startswith(q_path, "/api/v1/") && !xs_startswith(q_path, "/api/v2/")) 1168 xs *src = xs_json_loads("{\"privacy\":\"public\","
1160 return 0; 1169 "\"sensitive\":false,\"fields\":[],\"note\":\"\"}");
1170 /* some apps take the note from the source object */
1171 src = xs_dict_set(src, "note", xs_dict_get(snac.config, "bio"));
1172 src = xs_dict_set(src, "privacy", xs_type(xs_dict_get(snac.config, "private")) == XSTYPE_TRUE ? "private" : "public");
1161 1173
1162 int status = 404; 1174 const xs_str *cw = xs_dict_get(snac.config, "cw");
1163 const xs_dict *args = xs_dict_get(req, "q_vars"); 1175 src = xs_dict_set(src, "sensitive",
1164 xs *cmd = xs_replace_n(q_path, "/api", "", 1); 1176 strcmp(cw, "open") == 0 ? xs_stock(XSTYPE_TRUE) : xs_stock(XSTYPE_FALSE));
1165 1177
1166 snac snac1 = {0}; 1178 src = xs_dict_set(src, "bot", xs_dict_get(snac.config, "bot"));
1167 int logged_in = process_auth_token(&snac1, req);
1168 1179
1169 if (strcmp(cmd, "/v1/accounts/verify_credentials") == 0) { /** **/ 1180 xs *avatar = NULL;
1170 if (logged_in) { 1181 const char *av = xs_dict_get(snac.config, "avatar");
1171 xs *acct = xs_dict_new();
1172
1173 acct = xs_dict_append(acct, "id", snac1.md5);
1174 acct = xs_dict_append(acct, "username", xs_dict_get(snac1.config, "uid"));
1175 acct = xs_dict_append(acct, "acct", xs_dict_get(snac1.config, "uid"));
1176 acct = xs_dict_append(acct, "display_name", xs_dict_get(snac1.config, "name"));
1177 acct = xs_dict_append(acct, "created_at", xs_dict_get(snac1.config, "published"));
1178 acct = xs_dict_append(acct, "last_status_at", xs_dict_get(snac1.config, "published"));
1179 acct = xs_dict_append(acct, "note", xs_dict_get(snac1.config, "bio"));
1180 acct = xs_dict_append(acct, "url", snac1.actor);
1181 acct = xs_dict_append(acct, "locked", xs_stock(XSTYPE_FALSE));
1182 acct = xs_dict_append(acct, "bot", xs_dict_get(snac1.config, "bot"));
1183
1184 xs *src = xs_json_loads("{\"privacy\":\"public\","
1185 "\"sensitive\":false,\"fields\":[],\"note\":\"\"}");
1186 acct = xs_dict_append(acct, "source", src);
1187
1188 xs *avatar = NULL;
1189 const char *av = xs_dict_get(snac1.config, "avatar");
1190
1191 if (xs_is_null(av) || *av == '\0')
1192 avatar = xs_fmt("%s/susie.png", srv_baseurl);
1193 else
1194 avatar = xs_dup(av);
1195 1182
1196 acct = xs_dict_append(acct, "avatar", avatar); 1183 if (xs_is_null(av) || *av == '\0')
1197 acct = xs_dict_append(acct, "avatar_static", avatar); 1184 avatar = xs_fmt("%s/susie.png", srv_baseurl);
1185 else
1186 avatar = xs_dup(av);
1198 1187
1199 xs *header = NULL; 1188 acct = xs_dict_append(acct, "avatar", avatar);
1200 const char *hd = xs_dict_get(snac1.config, "header"); 1189 acct = xs_dict_append(acct, "avatar_static", avatar);
1201 1190
1202 if (!xs_is_null(hd)) 1191 xs *header = NULL;
1203 header = xs_dup(hd); 1192 const char *hd = xs_dict_get(snac.config, "header");
1204 else
1205 header = xs_fmt("%s/header.png", srv_baseurl);
1206 1193
1207 acct = xs_dict_append(acct, "header", header); 1194 if (!xs_is_null(hd))
1208 acct = xs_dict_append(acct, "header_static", header); 1195 header = xs_dup(hd);
1196 else
1197 header = xs_fmt("%s/header.png", srv_baseurl);
1209 1198
1210 const xs_dict *metadata = xs_dict_get(snac1.config, "metadata"); 1199 acct = xs_dict_append(acct, "header", header);
1211 if (xs_type(metadata) == XSTYPE_DICT) { 1200 acct = xs_dict_append(acct, "header_static", header);
1212 xs *fields = xs_list_new();
1213 const xs_str *k;
1214 const xs_str *v;
1215 1201
1216 xs_dict *val_links = snac1.links; 1202 const xs_dict *metadata = xs_dict_get(snac.config, "metadata");
1217 if (xs_is_null(val_links)) 1203 if (xs_type(metadata) == XSTYPE_DICT) {
1218 val_links = xs_stock(XSTYPE_DICT); 1204 xs *fields = xs_list_new();
1205 const xs_str *k;
1206 const xs_str *v;
1219 1207
1220 int c = 0; 1208 xs_dict *val_links = snac.links;
1221 while (xs_dict_next(metadata, &k, &v, &c)) { 1209 if (xs_is_null(val_links))
1222 xs *val_date = NULL; 1210 val_links = xs_stock(XSTYPE_DICT);
1223 1211
1224 const xs_number *verified_time = xs_dict_get(val_links, v); 1212 int c = 0;
1225 if (xs_type(verified_time) == XSTYPE_NUMBER) { 1213 while (xs_dict_next(metadata, &k, &v, &c)) {
1226 time_t t = xs_number_get(verified_time); 1214 xs *val_date = NULL;
1227 1215
1228 if (t > 0) 1216 const xs_number *verified_time = xs_dict_get(val_links, v);
1229 val_date = xs_str_utctime(t, ISO_DATE_SPEC); 1217 if (xs_type(verified_time) == XSTYPE_NUMBER) {
1230 } 1218 time_t t = xs_number_get(verified_time);
1231 1219
1232 xs *d = xs_dict_new(); 1220 if (t > 0)
1221 val_date = xs_str_utctime(t, ISO_DATE_SPEC);
1222 }
1233 1223
1234 d = xs_dict_append(d, "name", k); 1224 xs *d = xs_dict_new();
1235 d = xs_dict_append(d, "value", v);
1236 d = xs_dict_append(d, "verified_at",
1237 xs_type(val_date) == XSTYPE_STRING && *val_date ?
1238 val_date : xs_stock(XSTYPE_NULL));
1239 1225
1240 fields = xs_list_append(fields, d); 1226 d = xs_dict_append(d, "name", k);
1241 } 1227 d = xs_dict_append(d, "value", v);
1228 d = xs_dict_append(d, "verified_at",
1229 xs_type(val_date) == XSTYPE_STRING && *val_date ? val_date : xs_stock(XSTYPE_NULL));
1242 1230
1243 acct = xs_dict_set(acct, "fields", fields); 1231 fields = xs_list_append(fields, d);
1244 } 1232 }
1245 1233
1246 acct = xs_dict_append(acct, "followers_count", xs_stock(0)); 1234 acct = xs_dict_set(acct, "fields", fields);
1247 acct = xs_dict_append(acct, "following_count", xs_stock(0)); 1235 /* some apps take the fields from the source object */
1248 acct = xs_dict_append(acct, "statuses_count", xs_stock(0)); 1236 src = xs_dict_set(src, "fields", fields);
1237 }
1249 1238
1250 *body = xs_json_dumps(acct, 4); 1239 acct = xs_dict_append(acct, "source", src);
1251 *ctype = "application/json"; 1240 acct = xs_dict_append(acct, "followers_count", xs_stock(0));
1252 status = 200; 1241 acct = xs_dict_append(acct, "following_count", xs_stock(0));
1242 acct = xs_dict_append(acct, "statuses_count", xs_stock(0));
1243
1244 *body = xs_json_dumps(acct, 4);
1245 *ctype = "application/json";
1246 *status = HTTP_STATUS_OK;
1247}
1248
1249int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1250 char **body, int *b_size, char **ctype)
1251{
1252 (void)b_size;
1253
1254 if (!xs_startswith(q_path, "/api/v1/") && !xs_startswith(q_path, "/api/v2/"))
1255 return 0;
1256
1257 int status = HTTP_STATUS_NOT_FOUND;
1258 const xs_dict *args = xs_dict_get(req, "q_vars");
1259 xs *cmd = xs_replace_n(q_path, "/api", "", 1);
1260
1261 snac snac1 = {0};
1262 int logged_in = process_auth_token(&snac1, req);
1263
1264 if (strcmp(cmd, "/v1/accounts/verify_credentials") == 0) { /** **/
1265 if (logged_in) {
1266 credentials_get(body, ctype, &status, snac1);
1253 } 1267 }
1254 else { 1268 else {
1255 status = 422; // "Unprocessable entity" (no login) 1269 status = HTTP_STATUS_UNPROCESSABLE_CONTENT; // (no login)
1256 } 1270 }
1257 } 1271 }
1258 else 1272 else
@@ -1279,10 +1293,10 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1279 1293
1280 *body = xs_json_dumps(res, 4); 1294 *body = xs_json_dumps(res, 4);
1281 *ctype = "application/json"; 1295 *ctype = "application/json";
1282 status = 200; 1296 status = HTTP_STATUS_OK;
1283 } 1297 }
1284 else 1298 else
1285 status = 422; 1299 status = HTTP_STATUS_UNPROCESSABLE_CONTENT;
1286 } 1300 }
1287 else 1301 else
1288 if (strcmp(cmd, "/v1/accounts/lookup") == 0) { /** **/ 1302 if (strcmp(cmd, "/v1/accounts/lookup") == 0) { /** **/
@@ -1304,7 +1318,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1304 1318
1305 *body = xs_json_dumps(macct, 4); 1319 *body = xs_json_dumps(macct, 4);
1306 *ctype = "application/json"; 1320 *ctype = "application/json";
1307 status = 200; 1321 status = HTTP_STATUS_OK;
1308 1322
1309 user_free(&user); 1323 user_free(&user);
1310 } 1324 }
@@ -1450,7 +1464,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1450 if (out != NULL) { 1464 if (out != NULL) {
1451 *body = xs_json_dumps(out, 4); 1465 *body = xs_json_dumps(out, 4);
1452 *ctype = "application/json"; 1466 *ctype = "application/json";
1453 status = 200; 1467 status = HTTP_STATUS_OK;
1454 } 1468 }
1455 } 1469 }
1456 } 1470 }
@@ -1554,12 +1568,12 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1554 1568
1555 *body = xs_json_dumps(out, 4); 1569 *body = xs_json_dumps(out, 4);
1556 *ctype = "application/json"; 1570 *ctype = "application/json";
1557 status = 200; 1571 status = HTTP_STATUS_OK;
1558 1572
1559 srv_debug(2, xs_fmt("mastoapi timeline: returned %d entries", xs_list_len(out))); 1573 srv_debug(2, xs_fmt("mastoapi timeline: returned %d entries", xs_list_len(out)));
1560 } 1574 }
1561 else { 1575 else {
1562 status = 401; // unauthorized 1576 status = HTTP_STATUS_UNAUTHORIZED;
1563 } 1577 }
1564 } 1578 }
1565 else 1579 else
@@ -1612,7 +1626,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1612 1626
1613 *body = xs_json_dumps(out, 4); 1627 *body = xs_json_dumps(out, 4);
1614 *ctype = "application/json"; 1628 *ctype = "application/json";
1615 status = 200; 1629 status = HTTP_STATUS_OK;
1616 } 1630 }
1617 else 1631 else
1618 if (xs_startswith(cmd, "/v1/timelines/tag/")) { /** **/ 1632 if (xs_startswith(cmd, "/v1/timelines/tag/")) { /** **/
@@ -1661,7 +1675,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1661 1675
1662 *body = xs_json_dumps(out, 4); 1676 *body = xs_json_dumps(out, 4);
1663 *ctype = "application/json"; 1677 *ctype = "application/json";
1664 status = 200; 1678 status = HTTP_STATUS_OK;
1665 } 1679 }
1666 else 1680 else
1667 if (xs_startswith(cmd, "/v1/timelines/list/")) { /** **/ 1681 if (xs_startswith(cmd, "/v1/timelines/list/")) { /** **/
@@ -1729,17 +1743,17 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1729 1743
1730 *body = xs_json_dumps(out, 4); 1744 *body = xs_json_dumps(out, 4);
1731 *ctype = "application/json"; 1745 *ctype = "application/json";
1732 status = 200; 1746 status = HTTP_STATUS_OK;
1733 } 1747 }
1734 else 1748 else
1735 status = 421; 1749 status = HTTP_STATUS_MISDIRECTED_REQUEST;
1736 } 1750 }
1737 else 1751 else
1738 if (strcmp(cmd, "/v1/conversations") == 0) { /** **/ 1752 if (strcmp(cmd, "/v1/conversations") == 0) { /** **/
1739 /* TBD */ 1753 /* TBD */
1740 *body = xs_dup("[]"); 1754 *body = xs_dup("[]");
1741 *ctype = "application/json"; 1755 *ctype = "application/json";
1742 status = 200; 1756 status = HTTP_STATUS_OK;
1743 } 1757 }
1744 else 1758 else
1745 if (strcmp(cmd, "/v1/notifications") == 0) { /** **/ 1759 if (strcmp(cmd, "/v1/notifications") == 0) { /** **/
@@ -1817,17 +1831,17 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1817 1831
1818 *body = xs_json_dumps(out, 4); 1832 *body = xs_json_dumps(out, 4);
1819 *ctype = "application/json"; 1833 *ctype = "application/json";
1820 status = 200; 1834 status = HTTP_STATUS_OK;
1821 } 1835 }
1822 else 1836 else
1823 status = 401; 1837 status = HTTP_STATUS_UNAUTHORIZED;
1824 } 1838 }
1825 else 1839 else
1826 if (strcmp(cmd, "/v1/filters") == 0) { /** **/ 1840 if (strcmp(cmd, "/v1/filters") == 0) { /** **/
1827 /* snac will never have filters */ 1841 /* snac will never have filters */
1828 *body = xs_dup("[]"); 1842 *body = xs_dup("[]");
1829 *ctype = "application/json"; 1843 *ctype = "application/json";
1830 status = 200; 1844 status = HTTP_STATUS_OK;
1831 } 1845 }
1832 else 1846 else
1833 if (strcmp(cmd, "/v2/filters") == 0) { /** **/ 1847 if (strcmp(cmd, "/v2/filters") == 0) { /** **/
@@ -1836,21 +1850,21 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1836 * in some apps */ 1850 * in some apps */
1837 *body = xs_dup("[]"); 1851 *body = xs_dup("[]");
1838 *ctype = "application/json"; 1852 *ctype = "application/json";
1839 status = 200; 1853 status = HTTP_STATUS_OK;
1840 } 1854 }
1841 else 1855 else
1842 if (strcmp(cmd, "/v1/favourites") == 0) { /** **/ 1856 if (strcmp(cmd, "/v1/favourites") == 0) { /** **/
1843 /* snac will never support a list of favourites */ 1857 /* snac will never support a list of favourites */
1844 *body = xs_dup("[]"); 1858 *body = xs_dup("[]");
1845 *ctype = "application/json"; 1859 *ctype = "application/json";
1846 status = 200; 1860 status = HTTP_STATUS_OK;
1847 } 1861 }
1848 else 1862 else
1849 if (strcmp(cmd, "/v1/bookmarks") == 0) { /** **/ 1863 if (strcmp(cmd, "/v1/bookmarks") == 0) { /** **/
1850 /* snac does not support bookmarks */ 1864 /* snac does not support bookmarks */
1851 *body = xs_dup("[]"); 1865 *body = xs_dup("[]");
1852 *ctype = "application/json"; 1866 *ctype = "application/json";
1853 status = 200; 1867 status = HTTP_STATUS_OK;
1854 } 1868 }
1855 else 1869 else
1856 if (strcmp(cmd, "/v1/lists") == 0) { /** list of lists **/ 1870 if (strcmp(cmd, "/v1/lists") == 0) { /** list of lists **/
@@ -1873,7 +1887,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1873 1887
1874 *body = xs_json_dumps(l, 4); 1888 *body = xs_json_dumps(l, 4);
1875 *ctype = "application/json"; 1889 *ctype = "application/json";
1876 status = 200; 1890 status = HTTP_STATUS_OK;
1877 } 1891 }
1878 } 1892 }
1879 else 1893 else
@@ -1903,7 +1917,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1903 1917
1904 *body = xs_json_dumps(out, 4); 1918 *body = xs_json_dumps(out, 4);
1905 *ctype = "application/json"; 1919 *ctype = "application/json";
1906 status = 200; 1920 status = HTTP_STATUS_OK;
1907 } 1921 }
1908 } 1922 }
1909 else 1923 else
@@ -1931,7 +1945,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1931 1945
1932 *body = xs_json_dumps(out, 4); 1946 *body = xs_json_dumps(out, 4);
1933 *ctype = "application/json"; 1947 *ctype = "application/json";
1934 status = 200; 1948 status = HTTP_STATUS_OK;
1935 } 1949 }
1936 } 1950 }
1937 } 1951 }
@@ -1941,28 +1955,28 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1941 /* snac does not schedule notes */ 1955 /* snac does not schedule notes */
1942 *body = xs_dup("[]"); 1956 *body = xs_dup("[]");
1943 *ctype = "application/json"; 1957 *ctype = "application/json";
1944 status = 200; 1958 status = HTTP_STATUS_OK;
1945 } 1959 }
1946 else 1960 else
1947 if (strcmp(cmd, "/v1/follow_requests") == 0) { /** **/ 1961 if (strcmp(cmd, "/v1/follow_requests") == 0) { /** **/
1948 /* snac does not support optional follow confirmations */ 1962 /* snac does not support optional follow confirmations */
1949 *body = xs_dup("[]"); 1963 *body = xs_dup("[]");
1950 *ctype = "application/json"; 1964 *ctype = "application/json";
1951 status = 200; 1965 status = HTTP_STATUS_OK;
1952 } 1966 }
1953 else 1967 else
1954 if (strcmp(cmd, "/v1/announcements") == 0) { /** **/ 1968 if (strcmp(cmd, "/v1/announcements") == 0) { /** **/
1955 /* snac has no announcements (yet?) */ 1969 /* snac has no announcements (yet?) */
1956 *body = xs_dup("[]"); 1970 *body = xs_dup("[]");
1957 *ctype = "application/json"; 1971 *ctype = "application/json";
1958 status = 200; 1972 status = HTTP_STATUS_OK;
1959 } 1973 }
1960 else 1974 else
1961 if (strcmp(cmd, "/v1/custom_emojis") == 0) { /** **/ 1975 if (strcmp(cmd, "/v1/custom_emojis") == 0) { /** **/
1962 /* are you kidding me? */ 1976 /* are you kidding me? */
1963 *body = xs_dup("[]"); 1977 *body = xs_dup("[]");
1964 *ctype = "application/json"; 1978 *ctype = "application/json";
1965 status = 200; 1979 status = HTTP_STATUS_OK;
1966 } 1980 }
1967 else 1981 else
1968 if (strcmp(cmd, "/v1/instance") == 0) { /** **/ 1982 if (strcmp(cmd, "/v1/instance") == 0) { /** **/
@@ -2075,7 +2089,7 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
2075 2089
2076 *body = xs_json_dumps(ins, 4); 2090 *body = xs_json_dumps(ins, 4);
2077 *ctype = "application/json"; 2091 *ctype = "application/json";
2078 status = 200; 2092 status = HTTP_STATUS_OK;
2079 } 2093 }
2080 else 2094 else
2081 if (xs_startswith(cmd, "/v1/statuses/")) { /** **/ 2095 if (xs_startswith(cmd, "/v1/statuses/")) { /** **/
@@ -2188,30 +2202,30 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
2188 if (out != NULL) { 2202 if (out != NULL) {
2189 *body = xs_json_dumps(out, 4); 2203 *body = xs_json_dumps(out, 4);
2190 *ctype = "application/json"; 2204 *ctype = "application/json";
2191 status = 200; 2205 status = HTTP_STATUS_OK;
2192 } 2206 }
2193 } 2207 }
2194 } 2208 }
2195 else 2209 else
2196 status = 401; 2210 status = HTTP_STATUS_UNAUTHORIZED;
2197 } 2211 }
2198 else 2212 else
2199 if (strcmp(cmd, "/v1/preferences") == 0) { /** **/ 2213 if (strcmp(cmd, "/v1/preferences") == 0) { /** **/
2200 *body = xs_dup("{}"); 2214 *body = xs_dup("{}");
2201 *ctype = "application/json"; 2215 *ctype = "application/json";
2202 status = 200; 2216 status = HTTP_STATUS_OK;
2203 } 2217 }
2204 else 2218 else
2205 if (strcmp(cmd, "/v1/markers") == 0) { /** **/ 2219 if (strcmp(cmd, "/v1/markers") == 0) { /** **/
2206 *body = xs_dup("{}"); 2220 *body = xs_dup("{}");
2207 *ctype = "application/json"; 2221 *ctype = "application/json";
2208 status = 200; 2222 status = HTTP_STATUS_OK;
2209 } 2223 }
2210 else 2224 else
2211 if (strcmp(cmd, "/v1/followed_tags") == 0) { /** **/ 2225 if (strcmp(cmd, "/v1/followed_tags") == 0) { /** **/
2212 *body = xs_dup("[]"); 2226 *body = xs_dup("[]");
2213 *ctype = "application/json"; 2227 *ctype = "application/json";
2214 status = 200; 2228 status = HTTP_STATUS_OK;
2215 } 2229 }
2216 else 2230 else
2217 if (strcmp(cmd, "/v2/search") == 0) { /** **/ 2231 if (strcmp(cmd, "/v2/search") == 0) { /** **/
@@ -2290,10 +2304,10 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
2290 2304
2291 *body = xs_json_dumps(res, 4); 2305 *body = xs_json_dumps(res, 4);
2292 *ctype = "application/json"; 2306 *ctype = "application/json";
2293 status = 200; 2307 status = HTTP_STATUS_OK;
2294 } 2308 }
2295 else 2309 else
2296 status = 401; 2310 status = HTTP_STATUS_UNAUTHORIZED;
2297 } 2311 }
2298 2312
2299 /* user cleanup */ 2313 /* user cleanup */
@@ -2316,7 +2330,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
2316 if (!xs_startswith(q_path, "/api/v1/") && !xs_startswith(q_path, "/api/v2/")) 2330 if (!xs_startswith(q_path, "/api/v1/") && !xs_startswith(q_path, "/api/v2/"))
2317 return 0; 2331 return 0;
2318 2332
2319 int status = 404; 2333 int status = HTTP_STATUS_NOT_FOUND;
2320 xs *args = NULL; 2334 xs *args = NULL;
2321 const char *i_ctype = xs_dict_get(req, "content-type"); 2335 const char *i_ctype = xs_dict_get(req, "content-type");
2322 2336
@@ -2336,7 +2350,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
2336 args = xs_dup(xs_dict_get(req, "p_vars")); 2350 args = xs_dup(xs_dict_get(req, "p_vars"));
2337 2351
2338 if (args == NULL) 2352 if (args == NULL)
2339 return 400; 2353 return HTTP_STATUS_BAD_REQUEST;
2340 2354
2341 xs *cmd = xs_replace_n(q_path, "/api", "", 1); 2355 xs *cmd = xs_replace_n(q_path, "/api", "", 1);
2342 2356
@@ -2378,7 +2392,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
2378 2392
2379 *body = xs_json_dumps(app, 4); 2393 *body = xs_json_dumps(app, 4);
2380 *ctype = "application/json"; 2394 *ctype = "application/json";
2381 status = 200; 2395 status = HTTP_STATUS_OK;
2382 2396
2383 app = xs_dict_append(app, "code", ""); 2397 app = xs_dict_append(app, "code", "");
2384 2398
@@ -2470,10 +2484,10 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
2470 2484
2471 *body = xs_json_dumps(st, 4); 2485 *body = xs_json_dumps(st, 4);
2472 *ctype = "application/json"; 2486 *ctype = "application/json";
2473 status = 200; 2487 status = HTTP_STATUS_OK;
2474 } 2488 }
2475 else 2489 else
2476 status = 401; 2490 status = HTTP_STATUS_UNAUTHORIZED;
2477 } 2491 }
2478 else 2492 else
2479 if (xs_startswith(cmd, "/v1/statuses")) { /** **/ 2493 if (xs_startswith(cmd, "/v1/statuses")) { /** **/
@@ -2552,7 +2566,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
2552 if (pin(&snac, id)) 2566 if (pin(&snac, id))
2553 out = mastoapi_status(&snac, msg); 2567 out = mastoapi_status(&snac, msg);
2554 else 2568 else
2555 status = 422; 2569 status = HTTP_STATUS_UNPROCESSABLE_CONTENT;
2556 } 2570 }
2557 else 2571 else
2558 if (strcmp(op, "unpin") == 0) { /** **/ 2572 if (strcmp(op, "unpin") == 0) { /** **/
@@ -2573,12 +2587,12 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
2573 if (out != NULL) { 2587 if (out != NULL) {
2574 *body = xs_json_dumps(out, 4); 2588 *body = xs_json_dumps(out, 4);
2575 *ctype = "application/json"; 2589 *ctype = "application/json";
2576 status = 200; 2590 status = HTTP_STATUS_OK;
2577 } 2591 }
2578 } 2592 }
2579 } 2593 }
2580 else 2594 else
2581 status = 401; 2595 status = HTTP_STATUS_UNAUTHORIZED;
2582 } 2596 }
2583 else 2597 else
2584 if (strcmp(cmd, "/v1/notifications/clear") == 0) { /** **/ 2598 if (strcmp(cmd, "/v1/notifications/clear") == 0) { /** **/
@@ -2588,10 +2602,10 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
2588 2602
2589 *body = xs_dup("{}"); 2603 *body = xs_dup("{}");
2590 *ctype = "application/json"; 2604 *ctype = "application/json";
2591 status = 200; 2605 status = HTTP_STATUS_OK;
2592 } 2606 }
2593 else 2607 else
2594 status = 401; 2608 status = HTTP_STATUS_UNAUTHORIZED;
2595 } 2609 }
2596 else 2610 else
2597 if (strcmp(cmd, "/v1/push/subscription") == 0) { /** **/ 2611 if (strcmp(cmd, "/v1/push/subscription") == 0) { /** **/
@@ -2616,10 +2630,10 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
2616 2630
2617 *body = xs_json_dumps(wpush, 4); 2631 *body = xs_json_dumps(wpush, 4);
2618 *ctype = "application/json"; 2632 *ctype = "application/json";
2619 status = 200; 2633 status = HTTP_STATUS_OK;
2620 } 2634 }
2621 else 2635 else
2622 status = 401; 2636 status = HTTP_STATUS_UNAUTHORIZED;
2623 } 2637 }
2624 else 2638 else
2625 if (strcmp(cmd, "/v1/media") == 0 || strcmp(cmd, "/v2/media") == 0) { /** **/ 2639 if (strcmp(cmd, "/v1/media") == 0 || strcmp(cmd, "/v2/media") == 0) { /** **/
@@ -2630,7 +2644,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
2630 if (xs_is_null(desc)) 2644 if (xs_is_null(desc))
2631 desc = ""; 2645 desc = "";
2632 2646
2633 status = 400; 2647 status = HTTP_STATUS_BAD_REQUEST;
2634 2648
2635 if (xs_type(file) == XSTYPE_LIST) { 2649 if (xs_type(file) == XSTYPE_LIST) {
2636 const char *fn = xs_list_get(file, 0); 2650 const char *fn = xs_list_get(file, 0);
@@ -2659,12 +2673,12 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
2659 2673
2660 *body = xs_json_dumps(rsp, 4); 2674 *body = xs_json_dumps(rsp, 4);
2661 *ctype = "application/json"; 2675 *ctype = "application/json";
2662 status = 200; 2676 status = HTTP_STATUS_OK;
2663 } 2677 }
2664 } 2678 }
2665 } 2679 }
2666 else 2680 else
2667 status = 401; 2681 status = HTTP_STATUS_UNAUTHORIZED;
2668 } 2682 }
2669 else 2683 else
2670 if (xs_startswith(cmd, "/v1/accounts")) { /** **/ 2684 if (xs_startswith(cmd, "/v1/accounts")) { /** **/
@@ -2744,11 +2758,11 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
2744 if (rsp != NULL) { 2758 if (rsp != NULL) {
2745 *body = xs_json_dumps(rsp, 4); 2759 *body = xs_json_dumps(rsp, 4);
2746 *ctype = "application/json"; 2760 *ctype = "application/json";
2747 status = 200; 2761 status = HTTP_STATUS_OK;
2748 } 2762 }
2749 } 2763 }
2750 else 2764 else
2751 status = 401; 2765 status = HTTP_STATUS_UNAUTHORIZED;
2752 } 2766 }
2753 else 2767 else
2754 if (xs_startswith(cmd, "/v1/polls")) { /** **/ 2768 if (xs_startswith(cmd, "/v1/polls")) { /** **/
@@ -2810,12 +2824,12 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
2810 if (out != NULL) { 2824 if (out != NULL) {
2811 *body = xs_json_dumps(out, 4); 2825 *body = xs_json_dumps(out, 4);
2812 *ctype = "application/json"; 2826 *ctype = "application/json";
2813 status = 200; 2827 status = HTTP_STATUS_OK;
2814 } 2828 }
2815 } 2829 }
2816 } 2830 }
2817 else 2831 else
2818 status = 401; 2832 status = HTTP_STATUS_UNAUTHORIZED;
2819 } 2833 }
2820 else 2834 else
2821 if (strcmp(cmd, "/v1/lists") == 0) { 2835 if (strcmp(cmd, "/v1/lists") == 0) {
@@ -2831,18 +2845,18 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
2831 out = xs_dict_append(out, "replies_policy", xs_dict_get_def(args, "replies_policy", "list")); 2845 out = xs_dict_append(out, "replies_policy", xs_dict_get_def(args, "replies_policy", "list"));
2832 out = xs_dict_append(out, "exclusive", xs_stock(XSTYPE_FALSE)); 2846 out = xs_dict_append(out, "exclusive", xs_stock(XSTYPE_FALSE));
2833 2847
2834 status = 200; 2848 status = HTTP_STATUS_OK;
2835 } 2849 }
2836 else { 2850 else {
2837 out = xs_dict_append(out, "error", "cannot create list"); 2851 out = xs_dict_append(out, "error", "cannot create list");
2838 status = 422; 2852 status = HTTP_STATUS_UNPROCESSABLE_CONTENT;
2839 } 2853 }
2840 2854
2841 *body = xs_json_dumps(out, 4); 2855 *body = xs_json_dumps(out, 4);
2842 *ctype = "application/json"; 2856 *ctype = "application/json";
2843 } 2857 }
2844 else 2858 else
2845 status = 422; 2859 status = HTTP_STATUS_UNPROCESSABLE_CONTENT;
2846 } 2860 }
2847 } 2861 }
2848 if (xs_startswith(cmd, "/v1/lists/")) { /** list maintenance **/ 2862 if (xs_startswith(cmd, "/v1/lists/")) { /** list maintenance **/
@@ -2861,12 +2875,12 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
2861 list_content(&snac, id, v, 1); 2875 list_content(&snac, id, v, 1);
2862 } 2876 }
2863 2877
2864 status = 200; 2878 status = HTTP_STATUS_OK;
2865 } 2879 }
2866 } 2880 }
2867 } 2881 }
2868 else 2882 else
2869 status = 422; 2883 status = HTTP_STATUS_UNPROCESSABLE_CONTENT;
2870 } 2884 }
2871 2885
2872 /* user cleanup */ 2886 /* user cleanup */
@@ -2891,7 +2905,7 @@ int mastoapi_delete_handler(const xs_dict *req, const char *q_path,
2891 if (!xs_startswith(q_path, "/api/v1/") && !xs_startswith(q_path, "/api/v2/")) 2905 if (!xs_startswith(q_path, "/api/v1/") && !xs_startswith(q_path, "/api/v2/"))
2892 return 0; 2906 return 0;
2893 2907
2894 int status = 404; 2908 int status = HTTP_STATUS_NOT_FOUND;
2895 xs *args = NULL; 2909 xs *args = NULL;
2896 const char *i_ctype = xs_dict_get(req, "content-type"); 2910 const char *i_ctype = xs_dict_get(req, "content-type");
2897 2911
@@ -2911,7 +2925,7 @@ int mastoapi_delete_handler(const xs_dict *req, const char *q_path,
2911 args = xs_dup(xs_dict_get(req, "p_vars")); 2925 args = xs_dup(xs_dict_get(req, "p_vars"));
2912 2926
2913 if (args == NULL) 2927 if (args == NULL)
2914 return 400; 2928 return HTTP_STATUS_BAD_REQUEST;
2915 2929
2916 snac snac = {0}; 2930 snac snac = {0};
2917 int logged_in = process_auth_token(&snac, req); 2931 int logged_in = process_auth_token(&snac, req);
@@ -2920,7 +2934,7 @@ int mastoapi_delete_handler(const xs_dict *req, const char *q_path,
2920 2934
2921 if (xs_startswith(cmd, "/v1/push/subscription") || xs_startswith(cmd, "/v2/push/subscription")) { /** **/ 2935 if (xs_startswith(cmd, "/v1/push/subscription") || xs_startswith(cmd, "/v2/push/subscription")) { /** **/
2922 // pretend we deleted it, since it doesn't exist anyway 2936 // pretend we deleted it, since it doesn't exist anyway
2923 status = 200; 2937 status = HTTP_STATUS_OK;
2924 } 2938 }
2925 else 2939 else
2926 if (xs_startswith(cmd, "/v1/lists/")) { 2940 if (xs_startswith(cmd, "/v1/lists/")) {
@@ -2948,10 +2962,10 @@ int mastoapi_delete_handler(const xs_dict *req, const char *q_path,
2948 } 2962 }
2949 } 2963 }
2950 2964
2951 status = 200; 2965 status = HTTP_STATUS_OK;
2952 } 2966 }
2953 else 2967 else
2954 status = 401; 2968 status = HTTP_STATUS_UNAUTHORIZED;
2955 } 2969 }
2956 2970
2957 /* user cleanup */ 2971 /* user cleanup */
@@ -2974,7 +2988,7 @@ int mastoapi_put_handler(const xs_dict *req, const char *q_path,
2974 if (!xs_startswith(q_path, "/api/v1/") && !xs_startswith(q_path, "/api/v2/")) 2988 if (!xs_startswith(q_path, "/api/v1/") && !xs_startswith(q_path, "/api/v2/"))
2975 return 0; 2989 return 0;
2976 2990
2977 int status = 404; 2991 int status = HTTP_STATUS_NOT_FOUND;
2978 xs *args = NULL; 2992 xs *args = NULL;
2979 const char *i_ctype = xs_dict_get(req, "content-type"); 2993 const char *i_ctype = xs_dict_get(req, "content-type");
2980 2994
@@ -2986,7 +3000,7 @@ int mastoapi_put_handler(const xs_dict *req, const char *q_path,
2986 args = xs_dup(xs_dict_get(req, "p_vars")); 3000 args = xs_dup(xs_dict_get(req, "p_vars"));
2987 3001
2988 if (args == NULL) 3002 if (args == NULL)
2989 return 400; 3003 return HTTP_STATUS_BAD_REQUEST;
2990 3004
2991 xs *cmd = xs_replace_n(q_path, "/api", "", 1); 3005 xs *cmd = xs_replace_n(q_path, "/api", "", 1);
2992 3006
@@ -3017,11 +3031,11 @@ int mastoapi_put_handler(const xs_dict *req, const char *q_path,
3017 3031
3018 *body = xs_json_dumps(rsp, 4); 3032 *body = xs_json_dumps(rsp, 4);
3019 *ctype = "application/json"; 3033 *ctype = "application/json";
3020 status = 200; 3034 status = HTTP_STATUS_OK;
3021 } 3035 }
3022 } 3036 }
3023 else 3037 else
3024 status = 401; 3038 status = HTTP_STATUS_UNAUTHORIZED;
3025 } 3039 }
3026 else 3040 else
3027 if (xs_startswith(cmd, "/v1/statuses")) { 3041 if (xs_startswith(cmd, "/v1/statuses")) {
@@ -3060,12 +3074,12 @@ int mastoapi_put_handler(const xs_dict *req, const char *q_path,
3060 if (rsp != NULL) { 3074 if (rsp != NULL) {
3061 *body = xs_json_dumps(rsp, 4); 3075 *body = xs_json_dumps(rsp, 4);
3062 *ctype = "application/json"; 3076 *ctype = "application/json";
3063 status = 200; 3077 status = HTTP_STATUS_OK;
3064 } 3078 }
3065 } 3079 }
3066 } 3080 }
3067 else 3081 else
3068 status = 401; 3082 status = HTTP_STATUS_UNAUTHORIZED;
3069 } 3083 }
3070 3084
3071 /* user cleanup */ 3085 /* user cleanup */
@@ -3077,6 +3091,148 @@ int mastoapi_put_handler(const xs_dict *req, const char *q_path,
3077 return status; 3091 return status;
3078} 3092}
3079 3093
3094void persist_image(const char *key, const xs_val *data, const char *payload, snac *snac)
3095/* Store header or avatar */
3096{
3097 if (data != NULL) {
3098 if (xs_type(data) == XSTYPE_LIST) {
3099 const char *fn = xs_list_get(data, 0);
3100
3101 if (fn && *fn) {
3102 const char *ext = strrchr(fn, '.');
3103 /* Mona iOS sends JPG file as application/octet-stream with filename "header"
3104 * Make sure we have a unique file name, otherwise updated images will not be
3105 * loaded by clients.
3106 */
3107 if (ext == NULL || strcmp(fn, key) == 0) {
3108 fn = random_str();
3109 ext = ".jpg";
3110 }
3111 xs *hash = xs_md5_hex(fn, strlen(fn));
3112 xs *id = xs_fmt("%s%s", hash, ext);
3113 xs *url = xs_fmt("%s/s/%s", snac->actor, id);
3114 int fo = xs_number_get(xs_list_get(data, 1));
3115 int fs = xs_number_get(xs_list_get(data, 2));
3116
3117 /* store */
3118 static_put(snac, id, payload + fo, fs);
3119
3120 snac->config = xs_dict_set(snac->config, key, url);
3121 }
3122 }
3123 }
3124}
3125
3126int mastoapi_patch_handler(const xs_dict *req, const char *q_path,
3127 const char *payload, int p_size,
3128 char **body, int *b_size, char **ctype)
3129/* Handle profile updates */
3130{
3131 (void)p_size;
3132 (void)b_size;
3133
3134 if (!xs_startswith(q_path, "/api/v1/"))
3135 return 0;
3136
3137 int status = HTTP_STATUS_NOT_FOUND;
3138 xs *args = NULL;
3139 const char *i_ctype = xs_dict_get(req, "content-type");
3140
3141 if (i_ctype && xs_startswith(i_ctype, "application/json")) {
3142 if (!xs_is_null(payload))
3143 args = xs_json_loads(payload);
3144 }
3145 else
3146 args = xs_dup(xs_dict_get(req, "p_vars"));
3147
3148 if (args == NULL)
3149 return HTTP_STATUS_BAD_REQUEST;
3150
3151 xs *cmd = xs_replace_n(q_path, "/api", "", 1);
3152
3153 snac snac = {0};
3154 int logged_in = process_auth_token(&snac, req);
3155
3156 if (xs_startswith(cmd, "/v1/accounts/update_credentials")) {
3157 /* Update user profile fields */
3158 if (logged_in) {
3159 /*
3160 xs_str *dump = xs_json_dumps(args, 4);
3161 printf("%s\n\n", dump);
3162 */
3163 int c = 0;
3164 const xs_str *k;
3165 const xs_val *v;
3166 const xs_str *field_name = NULL;
3167 xs_dict *new_fields = xs_dict_new();
3168 while (xs_dict_next(args, &k, &v, &c)) {
3169 if (strcmp(k, "display_name") == 0) {
3170 if (v != NULL)
3171 snac.config = xs_dict_set(snac.config, "name", v);
3172 }
3173 else
3174 if (strcmp(k, "note") == 0) {
3175 if (v != NULL)
3176 snac.config = xs_dict_set(snac.config, "bio", v);
3177 }
3178 else
3179 if (strcmp(k, "bot") == 0) {
3180 if (v != NULL)
3181 snac.config = xs_dict_set(snac.config, "bot",
3182 strcmp(v, "true") == 0 ? xs_stock(XSTYPE_TRUE) : xs_stock(XSTYPE_FALSE));
3183 }
3184 else
3185 if (strcmp(k, "source[sensitive]") == 0) {
3186 if (v != NULL)
3187 snac.config = xs_dict_set(snac.config, "cw",
3188 strcmp(v, "true") == 0 ? "open" : "");
3189 }
3190 else
3191 if (strcmp(k, "source[privacy]") == 0) {
3192 if (v != NULL)
3193 snac.config = xs_dict_set(snac.config, "private",
3194 strcmp(v, "private") == 0 ? xs_stock(XSTYPE_TRUE) : xs_stock(XSTYPE_FALSE));
3195 }
3196 else
3197 if (strcmp(k, "header") == 0) {
3198 persist_image("header", v, payload, &snac);
3199 }
3200 else
3201 if (strcmp(k, "avatar") == 0) {
3202 persist_image("avatar", v, payload, &snac);
3203 }
3204 else
3205 if (xs_starts_and_ends("fields_attributes", k, "[name]")) {
3206 field_name = strcmp(v, "") != 0 ? v : NULL;
3207 }
3208 else
3209 if (xs_starts_and_ends("fields_attributes", k, "[value]")) {
3210 if (field_name != NULL) {
3211 new_fields = xs_dict_set(new_fields, field_name, v);
3212 snac.config = xs_dict_set(snac.config, "metadata", new_fields);
3213 }
3214 }
3215 }
3216
3217 /* Persist profile */
3218 if (user_persist(&snac) == 0)
3219 credentials_get(body, ctype, &status, snac);
3220 else
3221 status = HTTP_STATUS_INTERNAL_SERVER_ERROR;
3222 }
3223 else
3224 status = HTTP_STATUS_UNAUTHORIZED;
3225 }
3226
3227 /* user cleanup */
3228 if (logged_in)
3229 user_free(&snac);
3230
3231 srv_debug(1, xs_fmt("mastoapi_patch_handler %s %d", q_path, status));
3232
3233 return status;
3234}
3235
3080 3236
3081void mastoapi_purge(void) 3237void mastoapi_purge(void)
3082{ 3238{