commit af8f1ef
Louis Brauer
·
2024-05-29 09:53:34 +0000 UTC
parent 45a2aab
Implement image uploads for Tokodon
2 files changed,
+64,
-32
M
xs_url.h
+21,
-11
1@@ -3116,15 +3116,16 @@ void persist_image(const char *key, const xs_val *data, const char *payload, sna
2
3 if (fn && *fn) {
4 const char *ext = strrchr(fn, '.');
5- /* Mona iOS sends JPG file as application/octet-stream with filename "header"
6- * Make sure we have a unique file name, otherwise updated images will not be
7- * loaded by clients.
8- */
9+
10+ /* Mona iOS sends always jpg as application/octet-stream with no filename */
11 if (ext == NULL || strcmp(fn, key) == 0) {
12- fn = random_str();
13 ext = ".jpg";
14 }
15- xs *hash = xs_md5_hex(fn, strlen(fn));
16+
17+ /* Make sure we have a unique file name, otherwise updated images will not be
18+ * re-loaded by clients. */
19+ xs *rnd = random_str();
20+ xs *hash = xs_md5_hex(rnd, strlen(rnd));
21 xs *id = xs_fmt("%s%s", hash, ext);
22 xs *url = xs_fmt("%s/s/%s", snac->actor, id);
23 int fo = xs_number_get(xs_list_get(data, 1));
24@@ -3158,6 +3159,14 @@ int mastoapi_patch_handler(const xs_dict *req, const char *q_path,
25 if (!xs_is_null(payload))
26 args = xs_json_loads(payload);
27 }
28+ else if (i_ctype && xs_startswith(i_ctype, "application/x-www-form-urlencoded"))
29+ {
30+ // Some apps send form data instead of json so we should cater for those
31+ if (!xs_is_null(payload)) {
32+ xs *upl = xs_url_dec(payload);
33+ args = xs_url_vars(upl);
34+ }
35+ }
36 else
37 args = xs_dup(xs_dict_get(req, "p_vars"));
38
39@@ -3172,10 +3181,6 @@ int mastoapi_patch_handler(const xs_dict *req, const char *q_path,
40 if (xs_startswith(cmd, "/v1/accounts/update_credentials")) {
41 /* Update user profile fields */
42 if (logged_in) {
43- /*
44- xs_str *dump = xs_json_dumps(args, 4);
45- printf("%s\n\n", dump);
46- */
47 int c = 0;
48 const xs_str *k;
49 const xs_val *v;
50@@ -3195,7 +3200,8 @@ int mastoapi_patch_handler(const xs_dict *req, const char *q_path,
51 if (strcmp(k, "bot") == 0) {
52 if (v != NULL)
53 snac.config = xs_dict_set(snac.config, "bot",
54- strcmp(v, "true") == 0 ? xs_stock(XSTYPE_TRUE) : xs_stock(XSTYPE_FALSE));
55+ (strcmp(v, "true") == 0 ||
56+ strcmp(v, "1") == 0) ? xs_stock(XSTYPE_TRUE) : xs_stock(XSTYPE_FALSE));
57 }
58 else
59 if (strcmp(k, "source[sensitive]") == 0) {
60@@ -3228,6 +3234,10 @@ int mastoapi_patch_handler(const xs_dict *req, const char *q_path,
61 snac.config = xs_dict_set(snac.config, "metadata", new_fields);
62 }
63 }
64+ /* we don't have support for the following options, yet
65+ - discoverable (0/1)
66+ - locked (0/1)
67+ */
68 }
69
70 /* Persist profile */
M
xs_url.h
+43,
-21
1@@ -8,7 +8,6 @@ xs_str *xs_url_dec(const char *str);
2 xs_dict *xs_url_vars(const char *str);
3 xs_dict *xs_multipart_form_data(const char *payload, int p_size, const char *header);
4
5-
6 #ifdef XS_IMPLEMENTATION
7
8 xs_str *xs_url_dec(const char *str)
9@@ -126,6 +125,7 @@ xs_dict *xs_multipart_form_data(const char *payload, int p_size, const char *hea
10 xs *l1 = NULL;
11 const char *vn = NULL;
12 const char *fn = NULL;
13+ const char *ct = NULL;
14 char *q;
15 int po, ps;
16
17@@ -138,32 +138,47 @@ xs_dict *xs_multipart_form_data(const char *payload, int p_size, const char *hea
18 /* skip the \r\n */
19 p += 2;
20
21- /* now on a Content-Disposition... line; get it */
22- q = strchr(p, '\r');
23- s1 = xs_realloc(NULL, q - p + 1);
24- memcpy(s1, p, q - p);
25- s1[q - p] = '\0';
26-
27- /* move on (over a \r\n) */
28- p = q;
29-
30- /* split by " like a primitive man */
31- l1 = xs_split(s1, "\"");
32+ /* Tokodon sends also a Content-Type headers,
33+ let's use it to determine the file type */
34+ do {
35+ if (p[0] == 13 && p[1] == 10)
36+ break;
37+ q = strchr(p, '\r');
38+ s1 = xs_realloc(NULL, q - p + 1);
39+ memcpy(s1, p, q - p);
40+ s1[q - p] = '\0';
41+
42+ if (xs_startswith(s1, "Content-Disposition")) {
43+ /* split by " like a primitive man */
44+ l1 = xs_split(s1, "\"");
45+
46+ /* get the variable name */
47+ vn = xs_list_get(l1, 1);
48+
49+ /* is it an attached file? */
50+ if (xs_list_len(l1) >= 4 && strcmp(xs_list_get(l1, 2), "; filename=") == 0) {
51+ /* get the file name */
52+ fn = xs_list_get(l1, 3);
53+ }
54+ }
55+ else
56+ if (xs_startswith(s1, "Content-Type")) {
57+ l1 = xs_split(s1, ":");
58
59- /* get the variable name */
60- vn = xs_list_get(l1, 1);
61+ if (xs_list_len(l1) >= 2) {
62+ ct = xs_lstrip_chars_i(xs_dup(xs_list_get(l1, 1)), " ");
63+ }
64+ }
65
66- /* is it an attached file? */
67- if (xs_list_len(l1) >= 4 && strcmp(xs_list_get(l1, 2), "; filename=") == 0) {
68- /* get the file name */
69- fn = xs_list_get(l1, 3);
70- }
71+ p += (q - p);
72+ p += 2; // Skip /r/n
73+ } while (1);
74
75 /* find the start of the part content */
76- if ((p = xs_memmem(p, p_size - (p - payload), "\r\n\r\n", 4)) == NULL)
77+ if ((p = xs_memmem(p, p_size - (p - payload), "\r\n", 2)) == NULL)
78 break;
79
80- p += 4;
81+ p += 2; // Skip empty line
82
83 /* find the next boundary */
84 if ((q = xs_memmem(p, p_size - (p - payload), boundary, bsz)) == NULL)
85@@ -175,6 +190,13 @@ xs_dict *xs_multipart_form_data(const char *payload, int p_size, const char *hea
86 /* is it a filename? */
87 if (fn != NULL) {
88 /* p_var value is a list */
89+ /* if filename has no extension and content-type is image, attach extension to the filename */
90+ if (strchr(fn, '.') == NULL && xs_startswith(ct, "image/")) {
91+ char *ext = strchr(ct, '/');
92+ ext++;
93+ fn = xs_str_cat(xs_str_new(""), fn, ".", ext);
94+ }
95+
96 xs *l1 = xs_list_new();
97 xs *vpo = xs_number_new(po);
98 xs *vps = xs_number_new(ps);