Implement image uploads for Tokodon

This commit is contained in:
Louis Brauer 2024-05-29 11:53:34 +02:00
parent 45a2aab66c
commit af8f1ef273
2 changed files with 62 additions and 30 deletions

View file

@ -3116,15 +3116,16 @@ void persist_image(const char *key, const xs_val *data, const char *payload, sna
if (fn && *fn) { if (fn && *fn) {
const char *ext = strrchr(fn, '.'); const char *ext = strrchr(fn, '.');
/* Mona iOS sends JPG file as application/octet-stream with filename "header"
* Make sure we have a unique file name, otherwise updated images will not be /* Mona iOS sends always jpg as application/octet-stream with no filename */
* loaded by clients.
*/
if (ext == NULL || strcmp(fn, key) == 0) { if (ext == NULL || strcmp(fn, key) == 0) {
fn = random_str();
ext = ".jpg"; ext = ".jpg";
} }
xs *hash = xs_md5_hex(fn, strlen(fn));
/* Make sure we have a unique file name, otherwise updated images will not be
* re-loaded by clients. */
xs *rnd = random_str();
xs *hash = xs_md5_hex(rnd, strlen(rnd));
xs *id = xs_fmt("%s%s", hash, ext); xs *id = xs_fmt("%s%s", hash, ext);
xs *url = xs_fmt("%s/s/%s", snac->actor, id); xs *url = xs_fmt("%s/s/%s", snac->actor, id);
int fo = xs_number_get(xs_list_get(data, 1)); int fo = xs_number_get(xs_list_get(data, 1));
@ -3158,6 +3159,14 @@ int mastoapi_patch_handler(const xs_dict *req, const char *q_path,
if (!xs_is_null(payload)) if (!xs_is_null(payload))
args = xs_json_loads(payload); args = xs_json_loads(payload);
} }
else if (i_ctype && xs_startswith(i_ctype, "application/x-www-form-urlencoded"))
{
// Some apps send form data instead of json so we should cater for those
if (!xs_is_null(payload)) {
xs *upl = xs_url_dec(payload);
args = xs_url_vars(upl);
}
}
else else
args = xs_dup(xs_dict_get(req, "p_vars")); args = xs_dup(xs_dict_get(req, "p_vars"));
@ -3172,10 +3181,6 @@ int mastoapi_patch_handler(const xs_dict *req, const char *q_path,
if (xs_startswith(cmd, "/v1/accounts/update_credentials")) { if (xs_startswith(cmd, "/v1/accounts/update_credentials")) {
/* Update user profile fields */ /* Update user profile fields */
if (logged_in) { if (logged_in) {
/*
xs_str *dump = xs_json_dumps(args, 4);
printf("%s\n\n", dump);
*/
int c = 0; int c = 0;
const xs_str *k; const xs_str *k;
const xs_val *v; const xs_val *v;
@ -3195,7 +3200,8 @@ int mastoapi_patch_handler(const xs_dict *req, const char *q_path,
if (strcmp(k, "bot") == 0) { if (strcmp(k, "bot") == 0) {
if (v != NULL) if (v != NULL)
snac.config = xs_dict_set(snac.config, "bot", snac.config = xs_dict_set(snac.config, "bot",
strcmp(v, "true") == 0 ? xs_stock(XSTYPE_TRUE) : xs_stock(XSTYPE_FALSE)); (strcmp(v, "true") == 0 ||
strcmp(v, "1") == 0) ? xs_stock(XSTYPE_TRUE) : xs_stock(XSTYPE_FALSE));
} }
else else
if (strcmp(k, "source[sensitive]") == 0) { if (strcmp(k, "source[sensitive]") == 0) {
@ -3228,6 +3234,10 @@ int mastoapi_patch_handler(const xs_dict *req, const char *q_path,
snac.config = xs_dict_set(snac.config, "metadata", new_fields); snac.config = xs_dict_set(snac.config, "metadata", new_fields);
} }
} }
/* we don't have support for the following options, yet
- discoverable (0/1)
- locked (0/1)
*/
} }
/* Persist profile */ /* Persist profile */

View file

@ -8,7 +8,6 @@ xs_str *xs_url_dec(const char *str);
xs_dict *xs_url_vars(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); xs_dict *xs_multipart_form_data(const char *payload, int p_size, const char *header);
#ifdef XS_IMPLEMENTATION #ifdef XS_IMPLEMENTATION
xs_str *xs_url_dec(const char *str) xs_str *xs_url_dec(const char *str)
@ -126,6 +125,7 @@ xs_dict *xs_multipart_form_data(const char *payload, int p_size, const char *hea
xs *l1 = NULL; xs *l1 = NULL;
const char *vn = NULL; const char *vn = NULL;
const char *fn = NULL; const char *fn = NULL;
const char *ct = NULL;
char *q; char *q;
int po, ps; int po, ps;
@ -138,32 +138,47 @@ xs_dict *xs_multipart_form_data(const char *payload, int p_size, const char *hea
/* skip the \r\n */ /* skip the \r\n */
p += 2; p += 2;
/* now on a Content-Disposition... line; get it */ /* Tokodon sends also a Content-Type headers,
q = strchr(p, '\r'); let's use it to determine the file type */
s1 = xs_realloc(NULL, q - p + 1); do {
memcpy(s1, p, q - p); if (p[0] == 13 && p[1] == 10)
s1[q - p] = '\0'; 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) */ if (xs_startswith(s1, "Content-Disposition")) {
p = q; /* split by " like a primitive man */
l1 = xs_split(s1, "\"");
/* split by " like a primitive man */ /* get the variable name */
l1 = xs_split(s1, "\""); vn = xs_list_get(l1, 1);
/* get the variable name */ /* is it an attached file? */
vn = xs_list_get(l1, 1); 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) >= 2) {
if (xs_list_len(l1) >= 4 && strcmp(xs_list_get(l1, 2), "; filename=") == 0) { ct = xs_lstrip_chars_i(xs_dup(xs_list_get(l1, 1)), " ");
/* get the file name */ }
fn = xs_list_get(l1, 3); }
}
p += (q - p);
p += 2; // Skip /r/n
} while (1);
/* find the start of the part content */ /* 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; break;
p += 4; p += 2; // Skip empty line
/* find the next boundary */ /* find the next boundary */
if ((q = xs_memmem(p, p_size - (p - payload), boundary, bsz)) == NULL) if ((q = xs_memmem(p, p_size - (p - payload), boundary, bsz)) == NULL)
@ -175,6 +190,13 @@ xs_dict *xs_multipart_form_data(const char *payload, int p_size, const char *hea
/* is it a filename? */ /* is it a filename? */
if (fn != NULL) { if (fn != NULL) {
/* p_var value is a list */ /* 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 *l1 = xs_list_new();
xs *vpo = xs_number_new(po); xs *vpo = xs_number_new(po);
xs *vps = xs_number_new(ps); xs *vps = xs_number_new(ps);