mirror of
https://codeberg.org/grunfink/snac2.git
synced 2024-11-09 19:50:26 +03:00
Merge pull request 'Mastodon PATCH API for user profile updates' (#169) from louis77/snac2:master into master
Reviewed-on: https://codeberg.org/grunfink/snac2/pulls/169
This commit is contained in:
commit
d56d4beb90
2
Makefile
2
Makefile
@ -52,7 +52,7 @@ mastoapi.o: mastoapi.c xs.h xs_hex.h xs_openssl.h xs_json.h xs_io.h \
|
||||
snac.o: snac.c xs.h xs_hex.h xs_io.h xs_unicode.h xs_json.h xs_curl.h \
|
||||
xs_openssl.h xs_socket.h xs_url.h xs_httpd.h xs_mime.h xs_regex.h \
|
||||
xs_set.h xs_time.h xs_glob.h xs_random.h xs_match.h xs_fcgi.h xs_html.h \
|
||||
snac.h
|
||||
snac.h http_codes.h
|
||||
upgrade.o: upgrade.c xs.h xs_io.h xs_json.h xs_glob.h snac.h
|
||||
utils.o: utils.c xs.h xs_io.h xs_json.h xs_time.h xs_openssl.h \
|
||||
xs_random.h xs_glob.h xs_curl.h xs_regex.h snac.h
|
||||
|
@ -96,19 +96,19 @@ int activitypub_request(snac *user, const char *url, xs_dict **data)
|
||||
ctype = xs_dict_get(response, "content-type");
|
||||
|
||||
if (xs_is_null(ctype))
|
||||
status = 400;
|
||||
status = HTTP_STATUS_BAD_REQUEST;
|
||||
else
|
||||
if (xs_str_in(ctype, "application/activity+json") != -1 ||
|
||||
xs_str_in(ctype, "application/ld+json") != -1) {
|
||||
|
||||
/* if there is no payload, fail */
|
||||
if (xs_is_null(payload))
|
||||
status = 400;
|
||||
status = HTTP_STATUS_BAD_REQUEST;
|
||||
else
|
||||
*data = xs_json_loads(payload);
|
||||
}
|
||||
else
|
||||
status = 500;
|
||||
status = HTTP_STATUS_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
return status;
|
||||
@ -443,7 +443,7 @@ int send_to_actor(snac *snac, const char *actor, const xs_dict *msg,
|
||||
xs_val **payload, int *p_size, int timeout)
|
||||
/* sends a message to an actor */
|
||||
{
|
||||
int status = 400;
|
||||
int status = HTTP_STATUS_BAD_REQUEST;
|
||||
xs *inbox = get_actor_inbox(actor);
|
||||
|
||||
if (!xs_is_null(inbox))
|
||||
@ -1762,7 +1762,9 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req)
|
||||
a_status = actor_request(snac, actor, &actor_o);
|
||||
|
||||
/* do not retry permanent failures */
|
||||
if (a_status == 404 || a_status == 410 || a_status < 0) {
|
||||
if (a_status == HTTP_STATUS_NOT_FOUND
|
||||
|| a_status == HTTP_STATUS_GONE
|
||||
|| a_status < 0) {
|
||||
srv_debug(1, xs_fmt("dropping message due to actor error %s %d", actor, a_status));
|
||||
return -1;
|
||||
}
|
||||
@ -1905,7 +1907,7 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req)
|
||||
}
|
||||
else
|
||||
if (strcmp(utype, "Announce") == 0) { /** **/
|
||||
int status = 200;
|
||||
int status = HTTP_STATUS_OK;
|
||||
|
||||
/* commented out: if a followed user boosts something that
|
||||
is requested and then unboosts, the post remains here,
|
||||
@ -2015,7 +2017,7 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req)
|
||||
if (xs_type(object) == XSTYPE_DICT)
|
||||
object = xs_dict_get(object, "id");
|
||||
|
||||
if (timeline_admire(snac, object, actor, 1) == 201)
|
||||
if (timeline_admire(snac, object, actor, 1) == HTTP_STATUS_CREATED)
|
||||
snac_log(snac, xs_fmt("new 'Like' %s %s", actor, object));
|
||||
else
|
||||
snac_log(snac, xs_fmt("repeated 'Like' from %s to %s", actor, object));
|
||||
@ -2046,7 +2048,7 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req)
|
||||
xs *who_o = NULL;
|
||||
|
||||
if (valid_status(actor_request(snac, who, &who_o))) {
|
||||
if (timeline_admire(snac, object, actor, 0) == 201)
|
||||
if (timeline_admire(snac, object, actor, 0) == HTTP_STATUS_CREATED)
|
||||
snac_log(snac, xs_fmt("new 'Announce' %s %s", actor, object));
|
||||
else
|
||||
snac_log(snac, xs_fmt("repeated 'Announce' from %s to %s",
|
||||
@ -2383,11 +2385,15 @@ void process_queue_item(xs_dict *q_item)
|
||||
|
||||
/* if it's not the first time it fails with a timeout,
|
||||
penalize the server by skipping one retry */
|
||||
if (p_status == status && status == 499)
|
||||
if (p_status == status && status == HTTP_STATUS_CLIENT_CLOSED_REQUEST)
|
||||
retries++;
|
||||
|
||||
/* error sending; requeue? */
|
||||
if (status == 400 || status == 404 || status == 405 || status == 410 || status < 0)
|
||||
if (status == HTTP_STATUS_BAD_REQUEST
|
||||
|| status == HTTP_STATUS_NOT_FOUND
|
||||
|| status == HTTP_STATUS_METHOD_NOT_ALLOWED
|
||||
|| status == HTTP_STATUS_GONE
|
||||
|| status < 0)
|
||||
/* explicit error: discard */
|
||||
srv_log(xs_fmt("output message: fatal error %s %d", inbox, status));
|
||||
else
|
||||
@ -2574,7 +2580,7 @@ int process_queue(void)
|
||||
int activitypub_get_handler(const xs_dict *req, const char *q_path,
|
||||
char **body, int *b_size, char **ctype)
|
||||
{
|
||||
int status = 200;
|
||||
int status = HTTP_STATUS_OK;
|
||||
const char *accept = xs_dict_get(req, "accept");
|
||||
snac snac;
|
||||
xs *msg = NULL;
|
||||
@ -2594,7 +2600,7 @@ int activitypub_get_handler(const xs_dict *req, const char *q_path,
|
||||
if (!user_open(&snac, uid)) {
|
||||
/* invalid user */
|
||||
srv_debug(1, xs_fmt("activitypub_get_handler bad user %s", uid));
|
||||
return 404;
|
||||
return HTTP_STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
p_path = xs_list_get(l, 2);
|
||||
@ -2652,12 +2658,12 @@ int activitypub_get_handler(const xs_dict *req, const char *q_path,
|
||||
|
||||
/* don't return non-public objects */
|
||||
if (valid_status(status) && !is_msg_public(msg))
|
||||
status = 404;
|
||||
status = HTTP_STATUS_NOT_FOUND;
|
||||
}
|
||||
else
|
||||
status = 404;
|
||||
status = HTTP_STATUS_NOT_FOUND;
|
||||
|
||||
if (status == 200 && msg != NULL) {
|
||||
if (status == HTTP_STATUS_OK && msg != NULL) {
|
||||
*body = xs_json_dumps(msg, 4);
|
||||
*b_size = strlen(*body);
|
||||
}
|
||||
@ -2677,7 +2683,7 @@ int activitypub_post_handler(const xs_dict *req, const char *q_path,
|
||||
{
|
||||
(void)b_size;
|
||||
|
||||
int status = 202; /* accepted */
|
||||
int status = HTTP_STATUS_ACCEPTED;
|
||||
const char *i_ctype = xs_dict_get(req, "content-type");
|
||||
snac snac;
|
||||
const char *v;
|
||||
@ -2685,13 +2691,13 @@ int activitypub_post_handler(const xs_dict *req, const char *q_path,
|
||||
if (i_ctype == NULL) {
|
||||
*body = xs_str_new("no content-type");
|
||||
*ctype = "text/plain";
|
||||
return 400;
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
}
|
||||
|
||||
if (xs_is_null(payload)) {
|
||||
*body = xs_str_new("no payload");
|
||||
*ctype = "text/plain";
|
||||
return 400;
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
}
|
||||
|
||||
if (xs_str_in(i_ctype, "application/activity+json") == -1 &&
|
||||
@ -2709,7 +2715,7 @@ int activitypub_post_handler(const xs_dict *req, const char *q_path,
|
||||
|
||||
*body = xs_str_new("JSON error");
|
||||
*ctype = "text/plain";
|
||||
return 400;
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
}
|
||||
|
||||
if (id && is_instance_blocked(id)) {
|
||||
@ -2717,7 +2723,7 @@ int activitypub_post_handler(const xs_dict *req, const char *q_path,
|
||||
|
||||
*body = xs_str_new("blocked");
|
||||
*ctype = "text/plain";
|
||||
return 403;
|
||||
return HTTP_STATUS_FORBIDDEN;
|
||||
}
|
||||
|
||||
/* get the user and path */
|
||||
@ -2725,20 +2731,20 @@ int activitypub_post_handler(const xs_dict *req, const char *q_path,
|
||||
|
||||
if (xs_list_len(l) == 2 && strcmp(xs_list_get(l, 1), "shared-inbox") == 0) {
|
||||
enqueue_shared_input(msg, req, 0);
|
||||
return 202;
|
||||
return HTTP_STATUS_ACCEPTED;
|
||||
}
|
||||
|
||||
if (xs_list_len(l) != 3 || strcmp(xs_list_get(l, 2), "inbox") != 0) {
|
||||
/* strange q_path */
|
||||
srv_debug(1, xs_fmt("activitypub_post_handler unsupported path %s", q_path));
|
||||
return 404;
|
||||
return HTTP_STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
const char *uid = xs_list_get(l, 1);
|
||||
if (!user_open(&snac, uid)) {
|
||||
/* invalid user */
|
||||
srv_debug(1, xs_fmt("activitypub_post_handler bad user %s", uid));
|
||||
return 404;
|
||||
return HTTP_STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
/* if it has a digest, check it now, because
|
||||
@ -2752,7 +2758,7 @@ int activitypub_post_handler(const xs_dict *req, const char *q_path,
|
||||
|
||||
*body = xs_str_new("bad digest");
|
||||
*ctype = "text/plain";
|
||||
status = 400;
|
||||
status = HTTP_STATUS_BAD_REQUEST;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2763,7 +2769,7 @@ int activitypub_post_handler(const xs_dict *req, const char *q_path,
|
||||
|
||||
*body = xs_str_new("rejected");
|
||||
*ctype = "text/plain";
|
||||
status = 403;
|
||||
status = HTTP_STATUS_FORBIDDEN;
|
||||
}
|
||||
}
|
||||
|
||||
|
95
data.c
95
data.c
@ -303,6 +303,33 @@ int user_open_by_md5(snac *snac, const char *md5)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int user_persist(snac *snac)
|
||||
/* store user */
|
||||
{
|
||||
xs *fn = xs_fmt("%s/user.json", snac->basedir);
|
||||
xs *bfn = xs_fmt("%s.bak", fn);
|
||||
FILE *f;
|
||||
|
||||
rename(fn, bfn);
|
||||
|
||||
if ((f = fopen(fn, "w")) != NULL) {
|
||||
xs_json_dump(snac->config, 4, f);
|
||||
fclose(f);
|
||||
}
|
||||
else
|
||||
rename(bfn, fn);
|
||||
|
||||
history_del(snac, "timeline.html_");
|
||||
|
||||
xs *a_msg = msg_actor(snac);
|
||||
xs *u_msg = msg_update(snac, a_msg);
|
||||
|
||||
enqueue_message(snac, u_msg);
|
||||
enqueue_verify_links(snac);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
double mtime_nl(const char *fn, int *n_link)
|
||||
/* returns the mtime and number of links of a file or directory, or 0.0 */
|
||||
@ -355,12 +382,12 @@ int is_md5_hex(const char *md5)
|
||||
int index_add_md5(const char *fn, const char *md5)
|
||||
/* adds an md5 to an index */
|
||||
{
|
||||
int status = 201; /* Created */
|
||||
int status = HTTP_STATUS_CREATED;
|
||||
FILE *f;
|
||||
|
||||
if (!is_md5_hex(md5)) {
|
||||
srv_log(xs_fmt("index_add_md5: bad md5 %s %s", fn, md5));
|
||||
return 400;
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&data_mutex);
|
||||
@ -375,7 +402,7 @@ int index_add_md5(const char *fn, const char *md5)
|
||||
fclose(f);
|
||||
}
|
||||
else
|
||||
status = 500;
|
||||
status = HTTP_STATUS_INTERNAL_SERVER_ERROR;
|
||||
|
||||
pthread_mutex_unlock(&data_mutex);
|
||||
|
||||
@ -394,7 +421,7 @@ int index_add(const char *fn, const char *id)
|
||||
int index_del_md5(const char *fn, const char *md5)
|
||||
/* deletes an md5 from an index */
|
||||
{
|
||||
int status = 404;
|
||||
int status = HTTP_STATUS_NOT_FOUND;
|
||||
FILE *f;
|
||||
|
||||
pthread_mutex_lock(&data_mutex);
|
||||
@ -411,7 +438,7 @@ int index_del_md5(const char *fn, const char *md5)
|
||||
[yes: this breaks index_len()] */
|
||||
fseek(f, -33, SEEK_CUR);
|
||||
fwrite("-", 1, 1, f);
|
||||
status = 200;
|
||||
status = HTTP_STATUS_OK;
|
||||
|
||||
break;
|
||||
}
|
||||
@ -420,7 +447,7 @@ int index_del_md5(const char *fn, const char *md5)
|
||||
fclose(f);
|
||||
}
|
||||
else
|
||||
status = 410;
|
||||
status = HTTP_STATUS_GONE;
|
||||
|
||||
pthread_mutex_unlock(&data_mutex);
|
||||
|
||||
@ -660,7 +687,7 @@ int object_here(const char *id)
|
||||
int object_get_by_md5(const char *md5, xs_dict **obj)
|
||||
/* returns a stored object, optionally of the requested type */
|
||||
{
|
||||
int status = 404;
|
||||
int status = HTTP_STATUS_NOT_FOUND;
|
||||
xs *fn = _object_fn_by_md5(md5, "object_get_by_md5");
|
||||
FILE *f;
|
||||
|
||||
@ -669,7 +696,7 @@ int object_get_by_md5(const char *md5, xs_dict **obj)
|
||||
fclose(f);
|
||||
|
||||
if (*obj)
|
||||
status = 200;
|
||||
status = HTTP_STATUS_OK;
|
||||
}
|
||||
else
|
||||
*obj = NULL;
|
||||
@ -689,7 +716,7 @@ int object_get(const char *id, xs_dict **obj)
|
||||
int _object_add(const char *id, const xs_dict *obj, int ow)
|
||||
/* stores an object */
|
||||
{
|
||||
int status = 201; /* Created */
|
||||
int status = HTTP_STATUS_CREATED; /* Created */
|
||||
xs *fn = _object_fn(id);
|
||||
FILE *f;
|
||||
|
||||
@ -697,10 +724,10 @@ int _object_add(const char *id, const xs_dict *obj, int ow)
|
||||
if (!ow) {
|
||||
/* object already here */
|
||||
srv_debug(1, xs_fmt("object_add object already here %s", id));
|
||||
return 204; /* No content */
|
||||
return HTTP_STATUS_NO_CONTENT;
|
||||
}
|
||||
else
|
||||
status = 200;
|
||||
status = HTTP_STATUS_OK;
|
||||
}
|
||||
|
||||
if ((f = fopen(fn, "w")) != NULL) {
|
||||
@ -736,7 +763,7 @@ int _object_add(const char *id, const xs_dict *obj, int ow)
|
||||
}
|
||||
else {
|
||||
srv_log(xs_fmt("object_add error writing %s (errno: %d)", fn, errno));
|
||||
status = 500;
|
||||
status = HTTP_STATUS_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
srv_debug(1, xs_fmt("object_add %s %s %d", id, fn, status));
|
||||
@ -762,11 +789,11 @@ int object_add_ow(const char *id, const xs_dict *obj)
|
||||
int object_del_by_md5(const char *md5)
|
||||
/* deletes an object by its md5 */
|
||||
{
|
||||
int status = 404;
|
||||
int status = HTTP_STATUS_NOT_FOUND;
|
||||
xs *fn = _object_fn_by_md5(md5, "object_del_by_md5");
|
||||
|
||||
if (unlink(fn) != -1) {
|
||||
status = 200;
|
||||
status = HTTP_STATUS_OK;
|
||||
|
||||
/* also delete associated indexes */
|
||||
xs *spec = xs_dup(fn);
|
||||
@ -907,7 +934,7 @@ int object_parent(const char *md5, char *buf, int size)
|
||||
int object_admire(const char *id, const char *actor, int like)
|
||||
/* actor likes or announces this object */
|
||||
{
|
||||
int status = 200;
|
||||
int status = HTTP_STATUS_OK;
|
||||
xs *fn = _object_fn(id);
|
||||
|
||||
fn = xs_replace_i(fn, ".json", like ? "_l.idx" : "_a.idx");
|
||||
@ -1007,7 +1034,7 @@ int follower_add(snac *snac, const char *actor)
|
||||
|
||||
snac_debug(snac, 2, xs_fmt("follower_add %s", actor));
|
||||
|
||||
return ret == -1 ? 500 : 200;
|
||||
return ret == -1 ? HTTP_STATUS_INTERNAL_SERVER_ERROR : HTTP_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
@ -1018,7 +1045,7 @@ int follower_del(snac *snac, const char *actor)
|
||||
|
||||
snac_debug(snac, 2, xs_fmt("follower_del %s", actor));
|
||||
|
||||
return ret == -1 ? 404 : 200;
|
||||
return ret == -1 ? HTTP_STATUS_NOT_FOUND : HTTP_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
@ -1109,7 +1136,7 @@ int timeline_here(snac *snac, const char *md5)
|
||||
int timeline_get_by_md5(snac *snac, const char *md5, xs_dict **msg)
|
||||
/* gets a message from the timeline */
|
||||
{
|
||||
int status = 404;
|
||||
int status = HTTP_STATUS_NOT_FOUND;
|
||||
FILE *f = NULL;
|
||||
|
||||
xs *fn = timeline_fn_by_md5(snac, md5);
|
||||
@ -1119,7 +1146,7 @@ int timeline_get_by_md5(snac *snac, const char *md5, xs_dict **msg)
|
||||
fclose(f);
|
||||
|
||||
if (*msg != NULL)
|
||||
status = 200;
|
||||
status = HTTP_STATUS_OK;
|
||||
}
|
||||
|
||||
return status;
|
||||
@ -1282,7 +1309,7 @@ xs_str *_following_fn(snac *snac, const char *actor)
|
||||
int following_add(snac *snac, const char *actor, const xs_dict *msg)
|
||||
/* adds to the following list */
|
||||
{
|
||||
int ret = 201; /* created */
|
||||
int ret = HTTP_STATUS_CREATED;
|
||||
xs *fn = _following_fn(snac, actor);
|
||||
FILE *f;
|
||||
xs *p_object = NULL;
|
||||
@ -1295,7 +1322,7 @@ int following_add(snac *snac, const char *actor, const xs_dict *msg)
|
||||
|
||||
if (!xs_is_null(type) && strcmp(type, "Accept") == 0) {
|
||||
snac_debug(snac, 1, xs_fmt("following_add actor already confirmed %s", actor));
|
||||
return 200;
|
||||
return HTTP_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1311,7 +1338,7 @@ int following_add(snac *snac, const char *actor, const xs_dict *msg)
|
||||
link(actor_fn, fn);
|
||||
}
|
||||
else
|
||||
ret = 500;
|
||||
ret = HTTP_STATUS_INTERNAL_SERVER_ERROR;
|
||||
|
||||
snac_debug(snac, 2, xs_fmt("following_add %s %s", actor, fn));
|
||||
|
||||
@ -1332,7 +1359,7 @@ int following_del(snac *snac, const char *actor)
|
||||
fn = xs_replace_i(fn, ".json", "_a.json");
|
||||
unlink(fn);
|
||||
|
||||
return 200;
|
||||
return HTTP_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
@ -1350,14 +1377,14 @@ int following_get(snac *snac, const char *actor, xs_dict **data)
|
||||
{
|
||||
xs *fn = _following_fn(snac, actor);
|
||||
FILE *f;
|
||||
int status = 200;
|
||||
int status = HTTP_STATUS_OK;
|
||||
|
||||
if ((f = fopen(fn, "r")) != NULL) {
|
||||
*data = xs_json_load(f);
|
||||
fclose(f);
|
||||
}
|
||||
else
|
||||
status = 404;
|
||||
status = HTTP_STATUS_NOT_FOUND;
|
||||
|
||||
return status;
|
||||
}
|
||||
@ -1576,7 +1603,7 @@ int actor_add(const char *actor, const xs_dict *msg)
|
||||
int actor_get(const char *actor, xs_dict **data)
|
||||
/* returns an already downloaded actor */
|
||||
{
|
||||
int status = 200;
|
||||
int status = HTTP_STATUS_OK;
|
||||
xs_dict *d = NULL;
|
||||
|
||||
if (xs_startswith(actor, srv_baseurl)) {
|
||||
@ -1590,10 +1617,10 @@ int actor_get(const char *actor, xs_dict **data)
|
||||
*data = msg_actor(&user);
|
||||
|
||||
user_free(&user);
|
||||
return 200;
|
||||
return HTTP_STATUS_OK;
|
||||
}
|
||||
else
|
||||
return 404;
|
||||
return HTTP_STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
/* read the object */
|
||||
@ -1606,7 +1633,7 @@ int actor_get(const char *actor, xs_dict **data)
|
||||
if (xs_is_null(xs_dict_get(d, "id")) || xs_is_null(xs_dict_get(d, "type"))) {
|
||||
srv_debug(1, xs_fmt("corrupted actor object %s", actor));
|
||||
d = xs_free(d);
|
||||
return 404;
|
||||
return HTTP_STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (data)
|
||||
@ -1622,7 +1649,7 @@ int actor_get(const char *actor, xs_dict **data)
|
||||
|
||||
if (mtime(fn) + max_time < (double) time(NULL)) {
|
||||
/* actor data exists but also stinks */
|
||||
status = 205; /* "205: Reset Content" "110: Response Is Stale" */
|
||||
status = HTTP_STATUS_RESET_CONTENT; /* "110: Response Is Stale" */
|
||||
}
|
||||
|
||||
return status;
|
||||
@ -1634,7 +1661,7 @@ int actor_get_refresh(snac *user, const char *actor, xs_dict **data)
|
||||
{
|
||||
int status = actor_get(actor, data);
|
||||
|
||||
if (status == 205 && user && !xs_startswith(actor, srv_baseurl))
|
||||
if (status == HTTP_STATUS_RESET_CONTENT && user && !xs_startswith(actor, srv_baseurl))
|
||||
enqueue_actor_refresh(user, actor, 0);
|
||||
|
||||
return status;
|
||||
@ -1953,7 +1980,7 @@ static int _load_raw_file(const char *fn, xs_val **data, int *size,
|
||||
const char *inm, xs_str **etag)
|
||||
/* loads a cached file */
|
||||
{
|
||||
int status = 404;
|
||||
int status = HTTP_STATUS_NOT_FOUND;
|
||||
|
||||
if (fn) {
|
||||
double tm = mtime(fn);
|
||||
@ -1965,7 +1992,7 @@ static int _load_raw_file(const char *fn, xs_val **data, int *size,
|
||||
/* if if-none-match is set, check if it's the same */
|
||||
if (!xs_is_null(inm) && strcmp(e, inm) == 0) {
|
||||
/* client has the newest version */
|
||||
status = 304;
|
||||
status = HTTP_STATUS_NOT_MODIFIED;
|
||||
}
|
||||
else {
|
||||
/* newer or never downloaded; read the full file */
|
||||
@ -1976,7 +2003,7 @@ static int _load_raw_file(const char *fn, xs_val **data, int *size,
|
||||
*data = xs_read(f, size);
|
||||
fclose(f);
|
||||
|
||||
status = 200;
|
||||
status = HTTP_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
|
98
html.c
98
html.c
@ -2540,7 +2540,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
|
||||
char **body, int *b_size, char **ctype, xs_str **etag)
|
||||
{
|
||||
const char *accept = xs_dict_get(req, "accept");
|
||||
int status = 404;
|
||||
int status = HTTP_STATUS_NOT_FOUND;
|
||||
snac snac;
|
||||
xs *uid = NULL;
|
||||
const char *p_path;
|
||||
@ -2553,7 +2553,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
|
||||
|
||||
if (xs_is_null(v)) {
|
||||
srv_log(xs_fmt("html_get_handler bad query '%s'", q_path));
|
||||
return 404;
|
||||
return HTTP_STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
uid = xs_dup(v);
|
||||
@ -2569,7 +2569,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
|
||||
if (!uid || !user_open(&snac, uid)) {
|
||||
/* invalid user */
|
||||
srv_debug(1, xs_fmt("html_get_handler bad user %s", uid));
|
||||
return 404;
|
||||
return HTTP_STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
/* return the RSS if requested by Accept header */
|
||||
@ -2598,7 +2598,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
|
||||
/** empty public timeline for private users **/
|
||||
*body = html_timeline(&snac, NULL, 1, 0, 0, 0, NULL, "", 1);
|
||||
*b_size = strlen(*body);
|
||||
status = 200;
|
||||
status = HTTP_STATUS_OK;
|
||||
}
|
||||
else
|
||||
if (cache && history_mtime(&snac, h) > timeline_mtime(&snac)) {
|
||||
@ -2617,7 +2617,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
|
||||
*body = html_timeline(&snac, pins, 1, skip, show, xs_list_len(next), NULL, "", 1);
|
||||
|
||||
*b_size = strlen(*body);
|
||||
status = 200;
|
||||
status = HTTP_STATUS_OK;
|
||||
|
||||
if (save)
|
||||
history_add(&snac, h, *body, *b_size, etag);
|
||||
@ -2627,7 +2627,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
|
||||
if (strcmp(p_path, "admin") == 0) { /** private timeline **/
|
||||
if (!login(&snac, req)) {
|
||||
*body = xs_dup(uid);
|
||||
status = 401;
|
||||
status = HTTP_STATUS_UNAUTHORIZED;
|
||||
}
|
||||
else {
|
||||
const char *q = xs_dict_get(q_vars, "q");
|
||||
@ -2649,7 +2649,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
|
||||
|
||||
*body = html_timeline(&snac, tl, 0, skip, show, more, title, page, 0);
|
||||
*b_size = strlen(*body);
|
||||
status = 200;
|
||||
status = HTTP_STATUS_OK;
|
||||
}
|
||||
else {
|
||||
/** search by content **/
|
||||
@ -2670,7 +2670,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
|
||||
|
||||
*body = html_timeline(&snac, tl, 0, skip, tl_len, to || tl_len == show, title, page, 0);
|
||||
*b_size = strlen(*body);
|
||||
status = 200;
|
||||
status = HTTP_STATUS_OK;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -2699,7 +2699,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
|
||||
xs_list_len(next), NULL, "/admin", 1);
|
||||
|
||||
*b_size = strlen(*body);
|
||||
status = 200;
|
||||
status = HTTP_STATUS_OK;
|
||||
|
||||
if (save)
|
||||
history_add(&snac, "timeline.html_", *body, *b_size, etag);
|
||||
@ -2711,7 +2711,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
|
||||
if (xs_startswith(p_path, "admin/p/")) { /** unique post by md5 **/
|
||||
if (!login(&snac, req)) {
|
||||
*body = xs_dup(uid);
|
||||
status = 401;
|
||||
status = HTTP_STATUS_UNAUTHORIZED;
|
||||
}
|
||||
else {
|
||||
xs *l = xs_split(p_path, "/");
|
||||
@ -2722,7 +2722,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
|
||||
|
||||
*body = html_timeline(&snac, list, 0, 0, 0, 0, NULL, "/admin", 1);
|
||||
*b_size = strlen(*body);
|
||||
status = 200;
|
||||
status = HTTP_STATUS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2730,31 +2730,31 @@ int html_get_handler(const xs_dict *req, const char *q_path,
|
||||
if (strcmp(p_path, "people") == 0) { /** the list of people **/
|
||||
if (!login(&snac, req)) {
|
||||
*body = xs_dup(uid);
|
||||
status = 401;
|
||||
status = HTTP_STATUS_UNAUTHORIZED;
|
||||
}
|
||||
else {
|
||||
*body = html_people(&snac);
|
||||
*b_size = strlen(*body);
|
||||
status = 200;
|
||||
status = HTTP_STATUS_OK;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (strcmp(p_path, "notifications") == 0) { /** the list of notifications **/
|
||||
if (!login(&snac, req)) {
|
||||
*body = xs_dup(uid);
|
||||
status = 401;
|
||||
status = HTTP_STATUS_UNAUTHORIZED;
|
||||
}
|
||||
else {
|
||||
*body = html_notifications(&snac, skip, show);
|
||||
*b_size = strlen(*body);
|
||||
status = 200;
|
||||
status = HTTP_STATUS_OK;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (strcmp(p_path, "instance") == 0) { /** instance timeline **/
|
||||
if (!login(&snac, req)) {
|
||||
*body = xs_dup(uid);
|
||||
status = 401;
|
||||
status = HTTP_STATUS_UNAUTHORIZED;
|
||||
}
|
||||
else {
|
||||
xs *list = timeline_instance_list(skip, show);
|
||||
@ -2763,14 +2763,14 @@ int html_get_handler(const xs_dict *req, const char *q_path,
|
||||
*body = html_timeline(&snac, list, 0, skip, show,
|
||||
xs_list_len(next), L("Showing instance timeline"), "/instance", 0);
|
||||
*b_size = strlen(*body);
|
||||
status = 200;
|
||||
status = HTTP_STATUS_OK;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (xs_startswith(p_path, "list/")) { /** list timelines **/
|
||||
if (!login(&snac, req)) {
|
||||
*body = xs_dup(uid);
|
||||
status = 401;
|
||||
status = HTTP_STATUS_UNAUTHORIZED;
|
||||
}
|
||||
else {
|
||||
xs *l = xs_split(p_path, "/");
|
||||
@ -2787,14 +2787,14 @@ int html_get_handler(const xs_dict *req, const char *q_path,
|
||||
*body = html_timeline(&snac, list, 0, skip, show,
|
||||
xs_list_len(next), title, base, 1);
|
||||
*b_size = strlen(*body);
|
||||
status = 200;
|
||||
status = HTTP_STATUS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
if (xs_startswith(p_path, "p/")) { /** a timeline with just one entry **/
|
||||
if (xs_type(xs_dict_get(snac.config, "private")) == XSTYPE_TRUE)
|
||||
return 403;
|
||||
return HTTP_STATUS_FORBIDDEN;
|
||||
|
||||
xs *id = xs_fmt("%s/%s", snac.actor, p_path);
|
||||
xs *msg = NULL;
|
||||
@ -2807,7 +2807,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
|
||||
|
||||
*body = html_timeline(&snac, list, 1, 0, 0, 0, NULL, "", 1);
|
||||
*b_size = strlen(*body);
|
||||
status = 200;
|
||||
status = HTTP_STATUS_OK;
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -2829,10 +2829,10 @@ int html_get_handler(const xs_dict *req, const char *q_path,
|
||||
else
|
||||
if (xs_startswith(p_path, "h/")) { /** an entry from the history **/
|
||||
if (xs_type(xs_dict_get(snac.config, "private")) == XSTYPE_TRUE)
|
||||
return 403;
|
||||
return HTTP_STATUS_FORBIDDEN;
|
||||
|
||||
if (xs_type(xs_dict_get(srv_config, "disable_history")) == XSTYPE_TRUE)
|
||||
return 403;
|
||||
return HTTP_STATUS_FORBIDDEN;
|
||||
|
||||
xs *l = xs_split(p_path, "/");
|
||||
const char *id = xs_list_get(l, 1);
|
||||
@ -2841,7 +2841,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
|
||||
if (xs_endswith(id, "timeline.html_")) {
|
||||
/* Don't let them in */
|
||||
*b_size = 0;
|
||||
status = 404;
|
||||
status = HTTP_STATUS_NOT_FOUND;
|
||||
}
|
||||
else
|
||||
status = history_get(&snac, id, body, b_size,
|
||||
@ -2851,7 +2851,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
|
||||
else
|
||||
if (strcmp(p_path, ".rss") == 0) { /** public timeline in RSS format **/
|
||||
if (xs_type(xs_dict_get(snac.config, "private")) == XSTYPE_TRUE)
|
||||
return 403;
|
||||
return HTTP_STATUS_FORBIDDEN;
|
||||
|
||||
xs *elems = timeline_simple_list(&snac, "public", 0, 20);
|
||||
xs *bio = not_really_markdown(xs_dict_get(snac.config, "bio"), NULL, NULL);
|
||||
@ -2865,12 +2865,12 @@ int html_get_handler(const xs_dict *req, const char *q_path,
|
||||
*body = timeline_to_rss(&snac, elems, rss_title, rss_link, bio);
|
||||
*b_size = strlen(*body);
|
||||
*ctype = "application/rss+xml; charset=utf-8";
|
||||
status = 200;
|
||||
status = HTTP_STATUS_OK;
|
||||
|
||||
snac_debug(&snac, 1, xs_fmt("serving RSS"));
|
||||
}
|
||||
else
|
||||
status = 404;
|
||||
status = HTTP_STATUS_NOT_FOUND;
|
||||
|
||||
user_free(&snac);
|
||||
|
||||
@ -2901,7 +2901,7 @@ int html_post_handler(const xs_dict *req, const char *q_path,
|
||||
if (!uid || !user_open(&snac, uid)) {
|
||||
/* invalid user */
|
||||
srv_debug(1, xs_fmt("html_post_handler bad user %s", uid));
|
||||
return 404;
|
||||
return HTTP_STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
p_path = xs_list_get(l, 2);
|
||||
@ -2910,7 +2910,7 @@ int html_post_handler(const xs_dict *req, const char *q_path,
|
||||
if (!login(&snac, req)) {
|
||||
user_free(&snac);
|
||||
*body = xs_dup(uid);
|
||||
return 401;
|
||||
return HTTP_STATUS_UNAUTHORIZED;
|
||||
}
|
||||
|
||||
p_vars = xs_dict_get(req, "p_vars");
|
||||
@ -3049,7 +3049,7 @@ int html_post_handler(const xs_dict *req, const char *q_path,
|
||||
history_del(&snac, "timeline.html_");
|
||||
}
|
||||
|
||||
status = 303;
|
||||
status = HTTP_STATUS_SEE_OTHER;
|
||||
}
|
||||
else
|
||||
if (p_path && strcmp(p_path, "admin/action") == 0) { /** **/
|
||||
@ -3060,11 +3060,11 @@ int html_post_handler(const xs_dict *req, const char *q_path,
|
||||
const char *group = xs_dict_get(p_vars, "group");
|
||||
|
||||
if (action == NULL)
|
||||
return 404;
|
||||
return HTTP_STATUS_NOT_FOUND;
|
||||
|
||||
snac_debug(&snac, 1, xs_fmt("web action '%s' received", action));
|
||||
|
||||
status = 303;
|
||||
status = HTTP_STATUS_SEE_OTHER;
|
||||
|
||||
if (strcmp(action, L("Like")) == 0) { /** **/
|
||||
xs *msg = msg_admiration(&snac, id, "Like");
|
||||
@ -3216,10 +3216,10 @@ int html_post_handler(const xs_dict *req, const char *q_path,
|
||||
timeline_touch(&snac);
|
||||
}
|
||||
else
|
||||
status = 404;
|
||||
status = HTTP_STATUS_NOT_FOUND;
|
||||
|
||||
/* delete the cached timeline */
|
||||
if (status == 303)
|
||||
if (status == HTTP_STATUS_SEE_OTHER)
|
||||
history_del(&snac, "timeline.html_");
|
||||
}
|
||||
else
|
||||
@ -3334,36 +3334,16 @@ int html_post_handler(const xs_dict *req, const char *q_path,
|
||||
snac.config = xs_dict_set(snac.config, "passwd", pw);
|
||||
}
|
||||
|
||||
xs *fn = xs_fmt("%s/user.json", snac.basedir);
|
||||
xs *bfn = xs_fmt("%s.bak", fn);
|
||||
FILE *f;
|
||||
user_persist(&snac);
|
||||
|
||||
rename(fn, bfn);
|
||||
|
||||
if ((f = fopen(fn, "w")) != NULL) {
|
||||
xs_json_dump(snac.config, 4, f);
|
||||
fclose(f);
|
||||
}
|
||||
else
|
||||
rename(bfn, fn);
|
||||
|
||||
history_del(&snac, "timeline.html_");
|
||||
|
||||
xs *a_msg = msg_actor(&snac);
|
||||
xs *u_msg = msg_update(&snac, a_msg);
|
||||
|
||||
enqueue_message(&snac, u_msg);
|
||||
|
||||
enqueue_verify_links(&snac);
|
||||
|
||||
status = 303;
|
||||
status = HTTP_STATUS_SEE_OTHER;
|
||||
}
|
||||
else
|
||||
if (p_path && strcmp(p_path, "admin/clear-notifications") == 0) { /** **/
|
||||
notify_clear(&snac);
|
||||
timeline_touch(&snac);
|
||||
|
||||
status = 303;
|
||||
status = HTTP_STATUS_SEE_OTHER;
|
||||
}
|
||||
else
|
||||
if (p_path && strcmp(p_path, "admin/vote") == 0) { /** **/
|
||||
@ -3416,10 +3396,10 @@ int html_post_handler(const xs_dict *req, const char *q_path,
|
||||
}
|
||||
}
|
||||
|
||||
status = 303;
|
||||
status = HTTP_STATUS_SEE_OTHER;
|
||||
}
|
||||
|
||||
if (status == 303) {
|
||||
if (status == HTTP_STATUS_SEE_OTHER) {
|
||||
const char *redir = xs_dict_get(p_vars, "redir");
|
||||
|
||||
if (xs_is_null(redir))
|
||||
|
45
http_codes.h
Normal file
45
http_codes.h
Normal file
@ -0,0 +1,45 @@
|
||||
HTTP_STATUS(100, CONTINUE, Continue)
|
||||
HTTP_STATUS(101, SWITCHING_PROTOCOLS, Switching Protocols)
|
||||
HTTP_STATUS(102, PROCESSING, Processing)
|
||||
HTTP_STATUS(103, EARLY_HINTS, Early Hints)
|
||||
HTTP_STATUS(200, OK, OK)
|
||||
HTTP_STATUS(201, CREATED, Created)
|
||||
HTTP_STATUS(202, ACCEPTED, Accepted)
|
||||
HTTP_STATUS(203, NON_AUTHORITATIVE_INFORMATION, Non Authoritative Information)
|
||||
HTTP_STATUS(204, NO_CONTENT, No Content)
|
||||
HTTP_STATUS(205, RESET_CONTENT, Reset Content)
|
||||
HTTP_STATUS(206, PARTIAL_CONTENT, Partial Content)
|
||||
HTTP_STATUS(207, MULTI_STATUS, Multi Status)
|
||||
HTTP_STATUS(208, ALREADY_REPORTED, Already Reported)
|
||||
HTTP_STATUS(218, THIS_IS_FINE, This Is Fine)
|
||||
HTTP_STATUS(226, IM_USED, IM Used)
|
||||
HTTP_STATUS(300, MULTIPLE_CHOICES, Multiple Choices)
|
||||
HTTP_STATUS(301, MOVED_PERMANENTLY, Moved Permanently)
|
||||
HTTP_STATUS(302, FOUND, Found)
|
||||
HTTP_STATUS(303, SEE_OTHER, See Other)
|
||||
HTTP_STATUS(304, NOT_MODIFIED, Not Modified)
|
||||
HTTP_STATUS(305, USE_PROXY, Use Proxy)
|
||||
HTTP_STATUS(306, SWITCH_PROXY, Switch Proxy)
|
||||
HTTP_STATUS(307, TEMPORARY_REDIRECT, Temporary Redirect)
|
||||
HTTP_STATUS(308, PERMANENT_REDIRECT, Permanent Redirect)
|
||||
HTTP_STATUS(400, BAD_REQUEST, Bad Request)
|
||||
HTTP_STATUS(401, UNAUTHORIZED, Unauthorized)
|
||||
HTTP_STATUS(402, PAYMENT_REQUIRED, Payment Required)
|
||||
HTTP_STATUS(403, FORBIDDEN, Forbidden)
|
||||
HTTP_STATUS(404, NOT_FOUND, Not Found)
|
||||
HTTP_STATUS(405, METHOD_NOT_ALLOWED, Method Not Allowed)
|
||||
HTTP_STATUS(406, NOT_ACCEPTABLE, Not Acceptable)
|
||||
HTTP_STATUS(407, PROXY_AUTHENTICATION_REQUIRED, Proxy Authentication Required)
|
||||
HTTP_STATUS(408, REQUEST_TIMEOUT, Request Timeout)
|
||||
HTTP_STATUS(409, CONFLICT, Conflict)
|
||||
HTTP_STATUS(410, GONE, Gone)
|
||||
HTTP_STATUS(421, MISDIRECTED_REQUEST, Misdirected Request)
|
||||
HTTP_STATUS(422, UNPROCESSABLE_CONTENT, Unprocessable Content)
|
||||
HTTP_STATUS(499, CLIENT_CLOSED_REQUEST, Client Closed Request)
|
||||
HTTP_STATUS(500, INTERNAL_SERVER_ERROR, Internal Server Error)
|
||||
HTTP_STATUS(501, NOT_IMPLEMENTED, Not Implemented)
|
||||
HTTP_STATUS(502, BAD_GATEWAY, Bad Gateway)
|
||||
HTTP_STATUS(503, SERVICE_UNAVAILABLE, Service Unavailable)
|
||||
HTTP_STATUS(504, GATEWAY_TIMEOUT, Gateway Timeout)
|
||||
HTTP_STATUS(505, HTTP_VERSION_NOT_SUPPORTED, HTTP Version Not Supported)
|
||||
HTTP_STATUS(507, INSUFFICIENT_STORAGE, Insufficient Storage)
|
38
httpd.c
38
httpd.c
@ -217,17 +217,17 @@ int server_get_handler(xs_dict *req, const char *q_path,
|
||||
*body = greeting_html();
|
||||
|
||||
if (*body)
|
||||
status = 200;
|
||||
status = HTTP_STATUS_OK;
|
||||
}
|
||||
else
|
||||
if (strcmp(q_path, "/susie.png") == 0 || strcmp(q_path, "/favicon.ico") == 0 ) {
|
||||
status = 200;
|
||||
status = HTTP_STATUS_OK;
|
||||
*body = xs_base64_dec(default_avatar_base64(), b_size);
|
||||
*ctype = "image/png";
|
||||
}
|
||||
else
|
||||
if (strcmp(q_path, "/.well-known/nodeinfo") == 0) {
|
||||
status = 200;
|
||||
status = HTTP_STATUS_OK;
|
||||
*ctype = "application/json; charset=utf-8";
|
||||
*body = xs_fmt("{\"links\":["
|
||||
"{\"rel\":\"http:/" "/nodeinfo.diaspora.software/ns/schema/2.0\","
|
||||
@ -236,7 +236,7 @@ int server_get_handler(xs_dict *req, const char *q_path,
|
||||
}
|
||||
else
|
||||
if (strcmp(q_path, "/.well-known/host-meta") == 0) {
|
||||
status = 200;
|
||||
status = HTTP_STATUS_OK;
|
||||
*ctype = "application/xrd+xml";
|
||||
*body = xs_fmt("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
"<XRD>"
|
||||
@ -245,13 +245,13 @@ int server_get_handler(xs_dict *req, const char *q_path,
|
||||
}
|
||||
else
|
||||
if (strcmp(q_path, "/nodeinfo_2_0") == 0) {
|
||||
status = 200;
|
||||
status = HTTP_STATUS_OK;
|
||||
*ctype = "application/json; charset=utf-8";
|
||||
*body = nodeinfo_2_0();
|
||||
}
|
||||
else
|
||||
if (strcmp(q_path, "/robots.txt") == 0) {
|
||||
status = 200;
|
||||
status = HTTP_STATUS_OK;
|
||||
*ctype = "text/plain";
|
||||
*body = xs_str_new("User-agent: *\n"
|
||||
"Disallow: /\n");
|
||||
@ -360,10 +360,20 @@ void httpd_connection(FILE *f)
|
||||
payload, p_size, &body, &b_size, &ctype);
|
||||
#endif
|
||||
|
||||
}
|
||||
else
|
||||
if (strcmp(method, "PATCH") == 0) {
|
||||
|
||||
#ifndef NO_MASTODON_API
|
||||
if (status == 0)
|
||||
status = mastoapi_patch_handler(req, q_path,
|
||||
payload, p_size, &body, &b_size, &ctype);
|
||||
#endif
|
||||
|
||||
}
|
||||
else
|
||||
if (strcmp(method, "OPTIONS") == 0) {
|
||||
status = 200;
|
||||
status = HTTP_STATUS_OK;
|
||||
}
|
||||
else
|
||||
if (strcmp(method, "DELETE") == 0) {
|
||||
@ -378,22 +388,22 @@ void httpd_connection(FILE *f)
|
||||
if (status == 0) {
|
||||
srv_archive_error("unattended_method", "unattended method", req, payload);
|
||||
srv_debug(1, xs_fmt("httpd_connection unattended %s %s", method, q_path));
|
||||
status = 404;
|
||||
status = HTTP_STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (status == 403)
|
||||
if (status == HTTP_STATUS_FORBIDDEN)
|
||||
body = xs_str_new("<h1>403 Forbidden</h1>");
|
||||
|
||||
if (status == 404)
|
||||
if (status == HTTP_STATUS_NOT_FOUND)
|
||||
body = xs_str_new("<h1>404 Not Found</h1>");
|
||||
|
||||
if (status == 400 && body != NULL)
|
||||
if (status == HTTP_STATUS_BAD_REQUEST && body != NULL)
|
||||
body = xs_str_new("<h1>400 Bad Request</h1>");
|
||||
|
||||
if (status == 303)
|
||||
if (status == HTTP_STATUS_SEE_OTHER)
|
||||
headers = xs_dict_append(headers, "location", body);
|
||||
|
||||
if (status == 401) {
|
||||
if (status == HTTP_STATUS_UNAUTHORIZED) {
|
||||
xs *www_auth = xs_fmt("Basic realm=\"@%s@%s snac login\"",
|
||||
body, xs_dict_get(srv_config, "host"));
|
||||
|
||||
@ -432,7 +442,7 @@ void httpd_connection(FILE *f)
|
||||
if (p_state->use_fcgi)
|
||||
xs_fcgi_response(f, status, headers, body, b_size, fcgi_id);
|
||||
else
|
||||
xs_httpd_response(f, status, headers, body, b_size);
|
||||
xs_httpd_response(f, status, http_status_text(status), headers, body, b_size);
|
||||
|
||||
fclose(f);
|
||||
|
||||
|
532
mastoapi.c
532
mastoapi.c
File diff suppressed because it is too large
Load Diff
12
snac.c
12
snac.c
@ -170,3 +170,15 @@ int check_password(const char *uid, const char *passwd, const char *hash)
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
const char *http_status_text(int status)
|
||||
/* translate status codes to canonical status texts */
|
||||
{
|
||||
switch (status) {
|
||||
#define HTTP_STATUS(code, name, text) case HTTP_STATUS_ ## name: return #text;
|
||||
#include "http_codes.h"
|
||||
#undef HTTP_STATUS
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
14
snac.h
14
snac.h
@ -76,6 +76,7 @@ int user_open(snac *snac, const char *uid);
|
||||
void user_free(snac *snac);
|
||||
xs_list *user_list(void);
|
||||
int user_open_by_md5(snac *snac, const char *md5);
|
||||
int user_persist(snac *snac);
|
||||
|
||||
int validate_uid(const char *uid);
|
||||
|
||||
@ -358,6 +359,19 @@ int mastoapi_delete_handler(const xs_dict *req, const char *q_path,
|
||||
int mastoapi_put_handler(const xs_dict *req, const char *q_path,
|
||||
const char *payload, int p_size,
|
||||
char **body, int *b_size, char **ctype);
|
||||
void persist_image(const char *key, const xs_val *data, const char *payload, snac *snac);
|
||||
int mastoapi_patch_handler(const xs_dict *req, const char *q_path,
|
||||
const char *payload, int p_size,
|
||||
char **body, int *b_size, char **ctype);
|
||||
void mastoapi_purge(void);
|
||||
|
||||
void verify_links(snac *user);
|
||||
|
||||
|
||||
typedef enum {
|
||||
#define HTTP_STATUS(code, name, text) HTTP_STATUS_ ## name = code,
|
||||
#include "http_codes.h"
|
||||
#undef HTTP_STATUS
|
||||
} http_status;
|
||||
|
||||
const char *http_status_text(int status);
|
||||
|
@ -42,7 +42,7 @@ int webfinger_request_signed(snac *snac, const char *qs, char **actor, char **us
|
||||
}
|
||||
|
||||
if (host == NULL || resource == NULL)
|
||||
return 400;
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
|
||||
headers = xs_dict_append(headers, "accept", "application/json");
|
||||
headers = xs_dict_append(headers, "user-agent", USER_AGENT);
|
||||
@ -139,7 +139,7 @@ int webfinger_get_handler(xs_dict *req, char *q_path,
|
||||
const char *resource = xs_dict_get(q_vars, "resource");
|
||||
|
||||
if (resource == NULL)
|
||||
return 400;
|
||||
return HTTP_STATUS_BAD_REQUEST;
|
||||
|
||||
snac snac;
|
||||
int found = 0;
|
||||
@ -220,12 +220,12 @@ int webfinger_get_handler(xs_dict *req, char *q_path,
|
||||
|
||||
user_free(&snac);
|
||||
|
||||
status = 200;
|
||||
status = HTTP_STATUS_OK;
|
||||
*body = j;
|
||||
*ctype = "application/jrd+json";
|
||||
}
|
||||
else
|
||||
status = 404;
|
||||
status = HTTP_STATUS_NOT_FOUND;
|
||||
|
||||
srv_debug(1, xs_fmt("webfinger_get_handler resource=%s %d", resource, status));
|
||||
|
||||
|
2
xs.h
2
xs.h
@ -52,7 +52,7 @@ typedef char xs_data;
|
||||
|
||||
void *xs_free(void *ptr);
|
||||
void *_xs_realloc(void *ptr, size_t size, const char *file, int line, const char *func);
|
||||
#define xs_realloc(ptr, size) _xs_realloc(ptr, size, __FILE__, __LINE__, __FUNCTION__)
|
||||
#define xs_realloc(ptr, size) _xs_realloc(ptr, size, __FILE__, __LINE__, __func__)
|
||||
int _xs_blk_size(int sz);
|
||||
void _xs_destroy(char **var);
|
||||
#define xs_debug() raise(SIGTRAP)
|
||||
|
@ -5,7 +5,7 @@
|
||||
#define _XS_HTTPD_H
|
||||
|
||||
xs_dict *xs_httpd_request(FILE *f, xs_str **payload, int *p_size);
|
||||
void xs_httpd_response(FILE *f, int status, xs_dict *headers, xs_str *body, int b_size);
|
||||
void xs_httpd_response(FILE *f, int status, const char *status_text, xs_dict *headers, xs_str *body, int b_size);
|
||||
|
||||
|
||||
#ifdef XS_IMPLEMENTATION
|
||||
@ -95,14 +95,14 @@ xs_dict *xs_httpd_request(FILE *f, xs_str **payload, int *p_size)
|
||||
}
|
||||
|
||||
|
||||
void xs_httpd_response(FILE *f, int status, xs_dict *headers, xs_str *body, int b_size)
|
||||
void xs_httpd_response(FILE *f, int status, const char *status_text, xs_dict *headers, xs_str *body, int b_size)
|
||||
/* sends an httpd response */
|
||||
{
|
||||
xs *proto;
|
||||
const xs_str *k;
|
||||
const xs_val *v;
|
||||
|
||||
proto = xs_fmt("HTTP/1.1 %d %s", status, status / 100 == 2 ? "OK" : "ERROR");
|
||||
proto = xs_fmt("HTTP/1.1 %d %s", status, status_text);
|
||||
fprintf(f, "%s\r\n", proto);
|
||||
|
||||
int c = 0;
|
||||
|
2
xs_set.h
2
xs_set.h
@ -111,4 +111,4 @@ int xs_set_add(xs_set *s, const xs_val *data)
|
||||
|
||||
#endif /* XS_IMPLEMENTATION */
|
||||
|
||||
#endif /* XS_SET_H */
|
||||
#endif /* XS_SET_H */
|
||||
|
68
xs_url.h
68
xs_url.h
@ -8,7 +8,6 @@ xs_str *xs_url_dec(const char *str);
|
||||
xs_dict *xs_url_vars(const char *str);
|
||||
xs_dict *xs_multipart_form_data(const char *payload, int p_size, const char *header);
|
||||
|
||||
|
||||
#ifdef XS_IMPLEMENTATION
|
||||
|
||||
xs_str *xs_url_dec(const char *str)
|
||||
@ -107,7 +106,13 @@ xs_dict *xs_multipart_form_data(const char *payload, int p_size, const char *hea
|
||||
if (xs_list_len(l1) != 2)
|
||||
return NULL;
|
||||
|
||||
boundary = xs_fmt("--%s", xs_list_get(l1, 1));
|
||||
boundary = xs_dup(xs_list_get(l1, 1));
|
||||
|
||||
/* Tokodon sends the boundary header with double quotes surrounded */
|
||||
if (xs_starts_and_ends("\"", boundary, "\"") != 0)
|
||||
boundary = xs_strip_chars_i(boundary, "\"");
|
||||
|
||||
boundary = xs_fmt("--%s", boundary);
|
||||
}
|
||||
|
||||
bsz = strlen(boundary);
|
||||
@ -120,6 +125,7 @@ xs_dict *xs_multipart_form_data(const char *payload, int p_size, const char *hea
|
||||
xs *l1 = NULL;
|
||||
const char *vn = NULL;
|
||||
const char *fn = NULL;
|
||||
const char *ct = NULL;
|
||||
char *q;
|
||||
int po, ps;
|
||||
|
||||
@ -132,32 +138,47 @@ xs_dict *xs_multipart_form_data(const char *payload, int p_size, const char *hea
|
||||
/* skip the \r\n */
|
||||
p += 2;
|
||||
|
||||
/* now on a Content-Disposition... line; get it */
|
||||
q = strchr(p, '\r');
|
||||
s1 = xs_realloc(NULL, q - p + 1);
|
||||
memcpy(s1, p, q - p);
|
||||
s1[q - p] = '\0';
|
||||
/* Tokodon sends also a Content-Type headers,
|
||||
let's use it to determine the file type */
|
||||
do {
|
||||
if (p[0] == 13 && p[1] == 10)
|
||||
break;
|
||||
q = strchr(p, '\r');
|
||||
s1 = xs_realloc(NULL, q - p + 1);
|
||||
memcpy(s1, p, q - p);
|
||||
s1[q - p] = '\0';
|
||||
|
||||
/* move on (over a \r\n) */
|
||||
p = q;
|
||||
if (xs_startswith(s1, "Content-Disposition")) {
|
||||
/* split by " like a primitive man */
|
||||
l1 = xs_split(s1, "\"");
|
||||
|
||||
/* split by " like a primitive man */
|
||||
l1 = xs_split(s1, "\"");
|
||||
/* get the variable name */
|
||||
vn = xs_list_get(l1, 1);
|
||||
|
||||
/* get the variable name */
|
||||
vn = xs_list_get(l1, 1);
|
||||
/* is it an attached file? */
|
||||
if (xs_list_len(l1) >= 4 && strcmp(xs_list_get(l1, 2), "; filename=") == 0) {
|
||||
/* get the file name */
|
||||
fn = xs_list_get(l1, 3);
|
||||
}
|
||||
}
|
||||
else
|
||||
if (xs_startswith(s1, "Content-Type")) {
|
||||
l1 = xs_split(s1, ":");
|
||||
|
||||
/* is it an attached file? */
|
||||
if (xs_list_len(l1) >= 4 && strcmp(xs_list_get(l1, 2), "; filename=") == 0) {
|
||||
/* get the file name */
|
||||
fn = xs_list_get(l1, 3);
|
||||
}
|
||||
if (xs_list_len(l1) >= 2) {
|
||||
ct = xs_lstrip_chars_i(xs_dup(xs_list_get(l1, 1)), " ");
|
||||
}
|
||||
}
|
||||
|
||||
p += (q - p);
|
||||
p += 2; // Skip /r/n
|
||||
} while (1);
|
||||
|
||||
/* find the start of the part content */
|
||||
if ((p = xs_memmem(p, p_size - (p - payload), "\r\n\r\n", 4)) == NULL)
|
||||
if ((p = xs_memmem(p, p_size - (p - payload), "\r\n", 2)) == NULL)
|
||||
break;
|
||||
|
||||
p += 4;
|
||||
p += 2; // Skip empty line
|
||||
|
||||
/* find the next boundary */
|
||||
if ((q = xs_memmem(p, p_size - (p - payload), boundary, bsz)) == NULL)
|
||||
@ -169,6 +190,13 @@ xs_dict *xs_multipart_form_data(const char *payload, int p_size, const char *hea
|
||||
/* is it a filename? */
|
||||
if (fn != NULL) {
|
||||
/* p_var value is a list */
|
||||
/* if filename has no extension and content-type is image, attach extension to the filename */
|
||||
if (strchr(fn, '.') == NULL && xs_startswith(ct, "image/")) {
|
||||
char *ext = strchr(ct, '/');
|
||||
ext++;
|
||||
fn = xs_str_cat(xs_str_new(""), fn, ".", ext);
|
||||
}
|
||||
|
||||
xs *l1 = xs_list_new();
|
||||
xs *vpo = xs_number_new(po);
|
||||
xs *vps = xs_number_new(ps);
|
||||
|
Loading…
Reference in New Issue
Block a user