diff options
| author | 2024-05-27 12:24:17 +0200 | |
|---|---|---|
| committer | 2024-05-27 12:24:17 +0200 | |
| commit | 81cf309e4d0ba6c2debccc21ea4f85e1e6245dc5 (patch) | |
| tree | 10528a18c859964fa11eabe15955c85dce7ecf25 | |
| parent | Use enum instead of numeric status codes for HTTP statuses (diff) | |
| download | penes-snac2-81cf309e4d0ba6c2debccc21ea4f85e1e6245dc5.tar.gz penes-snac2-81cf309e4d0ba6c2debccc21ea4f85e1e6245dc5.tar.xz penes-snac2-81cf309e4d0ba6c2debccc21ea4f85e1e6245dc5.zip | |
Implement Mastodon PATCH endpoint for account profile updates
| -rw-r--r-- | data.c | 27 | ||||
| -rw-r--r-- | html.c | 22 | ||||
| -rw-r--r-- | httpd.c | 10 | ||||
| -rw-r--r-- | mastoapi.c | 316 | ||||
| -rw-r--r-- | snac.h | 5 |
5 files changed, 279 insertions, 101 deletions
| @@ -303,6 +303,33 @@ int user_open_by_md5(snac *snac, const char *md5) | |||
| 303 | return 0; | 303 | return 0; |
| 304 | } | 304 | } |
| 305 | 305 | ||
| 306 | int user_persist(snac *snac) | ||
| 307 | /* store user */ | ||
| 308 | { | ||
| 309 | xs *fn = xs_fmt("%s/user.json", snac->basedir); | ||
| 310 | xs *bfn = xs_fmt("%s.bak", fn); | ||
| 311 | FILE *f; | ||
| 312 | |||
| 313 | rename(fn, bfn); | ||
| 314 | |||
| 315 | if ((f = fopen(fn, "w")) != NULL) { | ||
| 316 | xs_json_dump(snac->config, 4, f); | ||
| 317 | fclose(f); | ||
| 318 | } | ||
| 319 | else | ||
| 320 | rename(bfn, fn); | ||
| 321 | |||
| 322 | history_del(snac, "timeline.html_"); | ||
| 323 | |||
| 324 | xs *a_msg = msg_actor(snac); | ||
| 325 | xs *u_msg = msg_update(snac, a_msg); | ||
| 326 | |||
| 327 | enqueue_message(snac, u_msg); | ||
| 328 | enqueue_verify_links(snac); | ||
| 329 | |||
| 330 | return 0; | ||
| 331 | } | ||
| 332 | |||
| 306 | 333 | ||
| 307 | double mtime_nl(const char *fn, int *n_link) | 334 | double mtime_nl(const char *fn, int *n_link) |
| 308 | /* returns the mtime and number of links of a file or directory, or 0.0 */ | 335 | /* returns the mtime and number of links of a file or directory, or 0.0 */ |
| @@ -3334,27 +3334,7 @@ int html_post_handler(const xs_dict *req, const char *q_path, | |||
| 3334 | snac.config = xs_dict_set(snac.config, "passwd", pw); | 3334 | snac.config = xs_dict_set(snac.config, "passwd", pw); |
| 3335 | } | 3335 | } |
| 3336 | 3336 | ||
| 3337 | xs *fn = xs_fmt("%s/user.json", snac.basedir); | 3337 | user_persist(&snac); |
| 3338 | xs *bfn = xs_fmt("%s.bak", fn); | ||
| 3339 | FILE *f; | ||
| 3340 | |||
| 3341 | rename(fn, bfn); | ||
| 3342 | |||
| 3343 | if ((f = fopen(fn, "w")) != NULL) { | ||
| 3344 | xs_json_dump(snac.config, 4, f); | ||
| 3345 | fclose(f); | ||
| 3346 | } | ||
| 3347 | else | ||
| 3348 | rename(bfn, fn); | ||
| 3349 | |||
| 3350 | history_del(&snac, "timeline.html_"); | ||
| 3351 | |||
| 3352 | xs *a_msg = msg_actor(&snac); | ||
| 3353 | xs *u_msg = msg_update(&snac, a_msg); | ||
| 3354 | |||
| 3355 | enqueue_message(&snac, u_msg); | ||
| 3356 | |||
| 3357 | enqueue_verify_links(&snac); | ||
| 3358 | 3338 | ||
| 3359 | status = HTTP_STATUS_SEE_OTHER; | 3339 | status = HTTP_STATUS_SEE_OTHER; |
| 3360 | } | 3340 | } |
| @@ -362,6 +362,16 @@ void httpd_connection(FILE *f) | |||
| 362 | 362 | ||
| 363 | } | 363 | } |
| 364 | else | 364 | else |
| 365 | if (strcmp(method, "PATCH") == 0) { | ||
| 366 | |||
| 367 | #ifndef NO_MASTODON_API | ||
| 368 | if (status == 0) | ||
| 369 | status = mastoapi_patch_handler(req, q_path, | ||
| 370 | payload, p_size, &body, &b_size, &ctype); | ||
| 371 | #endif | ||
| 372 | |||
| 373 | } | ||
| 374 | else | ||
| 365 | if (strcmp(method, "OPTIONS") == 0) { | 375 | if (strcmp(method, "OPTIONS") == 0) { |
| 366 | status = HTTP_STATUS_OK; | 376 | status = HTTP_STATUS_OK; |
| 367 | } | 377 | } |
| @@ -1150,106 +1150,120 @@ int process_auth_token(snac *snac, const xs_dict *req) | |||
| 1150 | return logged_in; | 1150 | return logged_in; |
| 1151 | } | 1151 | } |
| 1152 | 1152 | ||
| 1153 | 1153 | void credentials_get(char **body, char **ctype, int *status, snac snac) | |
| 1154 | int 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 = HTTP_STATUS_NOT_FOUND; | 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 = HTTP_STATUS_OK; | 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 | |||
| 1249 | int 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 = HTTP_STATUS_UNPROCESSABLE_CONTENT; // (no login) | 1269 | status = HTTP_STATUS_UNPROCESSABLE_CONTENT; // (no login) |
| @@ -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 | ||
| 3094 | void 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 | |||
| 3126 | int 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 | ||
| 3081 | void mastoapi_purge(void) | 3237 | void mastoapi_purge(void) |
| 3082 | { | 3238 | { |
| @@ -76,6 +76,7 @@ int user_open(snac *snac, const char *uid); | |||
| 76 | void user_free(snac *snac); | 76 | void user_free(snac *snac); |
| 77 | xs_list *user_list(void); | 77 | xs_list *user_list(void); |
| 78 | int user_open_by_md5(snac *snac, const char *md5); | 78 | int user_open_by_md5(snac *snac, const char *md5); |
| 79 | int user_persist(snac *snac); | ||
| 79 | 80 | ||
| 80 | int validate_uid(const char *uid); | 81 | int validate_uid(const char *uid); |
| 81 | 82 | ||
| @@ -358,6 +359,10 @@ int mastoapi_delete_handler(const xs_dict *req, const char *q_path, | |||
| 358 | int mastoapi_put_handler(const xs_dict *req, const char *q_path, | 359 | int mastoapi_put_handler(const xs_dict *req, const char *q_path, |
| 359 | const char *payload, int p_size, | 360 | const char *payload, int p_size, |
| 360 | char **body, int *b_size, char **ctype); | 361 | char **body, int *b_size, char **ctype); |
| 362 | void persist_image(const char *key, const xs_val *data, const char *payload, snac *snac); | ||
| 363 | int mastoapi_patch_handler(const xs_dict *req, const char *q_path, | ||
| 364 | const char *payload, int p_size, | ||
| 365 | char **body, int *b_size, char **ctype); | ||
| 361 | void mastoapi_purge(void); | 366 | void mastoapi_purge(void); |
| 362 | 367 | ||
| 363 | void verify_links(snac *user); | 368 | void verify_links(snac *user); |