summaryrefslogtreecommitdiff
path: root/html.c
diff options
context:
space:
mode:
Diffstat (limited to 'html.c')
-rw-r--r--html.c217
1 files changed, 195 insertions, 22 deletions
diff --git a/html.c b/html.c
index 78a9854..d807f4b 100644
--- a/html.c
+++ b/html.c
@@ -14,6 +14,7 @@
14#include "xs_curl.h" 14#include "xs_curl.h"
15#include "xs_unicode.h" 15#include "xs_unicode.h"
16#include "xs_url.h" 16#include "xs_url.h"
17#include "xs_random.h"
17 18
18#include "snac.h" 19#include "snac.h"
19 20
@@ -72,6 +73,9 @@ xs_str *replace_shortnames(xs_str *s, const xs_list *tag, int ems, const char *p
72 const xs_dict *v; 73 const xs_dict *v;
73 int c = 0; 74 int c = 0;
74 75
76 xs_set rep_emoji;
77 xs_set_init(&rep_emoji);
78
75 while (xs_list_next(tag_list, &v, &c)) { 79 while (xs_list_next(tag_list, &v, &c)) {
76 const char *t = xs_dict_get(v, "type"); 80 const char *t = xs_dict_get(v, "type");
77 81
@@ -79,6 +83,10 @@ xs_str *replace_shortnames(xs_str *s, const xs_list *tag, int ems, const char *p
79 const char *n = xs_dict_get(v, "name"); 83 const char *n = xs_dict_get(v, "name");
80 const xs_dict *i = xs_dict_get(v, "icon"); 84 const xs_dict *i = xs_dict_get(v, "icon");
81 85
86 /* avoid repeated emojis (Misskey seems to return this) */
87 if (xs_set_add(&rep_emoji, n) == 0)
88 continue;
89
82 if (xs_is_string(n) && xs_is_dict(i)) { 90 if (xs_is_string(n) && xs_is_dict(i)) {
83 const char *u = xs_dict_get(i, "url"); 91 const char *u = xs_dict_get(i, "url");
84 const char *mt = xs_dict_get(i, "mediaType"); 92 const char *mt = xs_dict_get(i, "mediaType");
@@ -93,6 +101,8 @@ xs_str *replace_shortnames(xs_str *s, const xs_list *tag, int ems, const char *p
93 xs_html_attr("loading", "lazy"), 101 xs_html_attr("loading", "lazy"),
94 xs_html_attr("src", url), 102 xs_html_attr("src", url),
95 xs_html_attr("alt", n), 103 xs_html_attr("alt", n),
104 xs_html_attr("title", n),
105 xs_html_attr("class", "snac-emoji"),
96 xs_html_attr("style", style)); 106 xs_html_attr("style", style));
97 107
98 xs *s1 = xs_html_render(img); 108 xs *s1 = xs_html_render(img);
@@ -104,6 +114,8 @@ xs_str *replace_shortnames(xs_str *s, const xs_list *tag, int ems, const char *p
104 } 114 }
105 } 115 }
106 } 116 }
117
118 xs_set_free(&rep_emoji);
107 } 119 }
108 120
109 return s; 121 return s;
@@ -339,7 +351,7 @@ xs_html *html_note(snac *user, const char *summary,
339 const xs_val *mnt_only, const char *redir, 351 const xs_val *mnt_only, const char *redir,
340 const char *in_reply_to, int poll, 352 const char *in_reply_to, int poll,
341 const xs_list *att_files, const xs_list *att_alt_texts, 353 const xs_list *att_files, const xs_list *att_alt_texts,
342 int is_draft) 354 int is_draft, const char *published)
343/* Yes, this is a FUCKTON of arguments and I'm a bit embarrased */ 355/* Yes, this is a FUCKTON of arguments and I'm a bit embarrased */
344{ 356{
345 xs *action = xs_fmt("%s/admin/note", user->actor); 357 xs *action = xs_fmt("%s/admin/note", user->actor);
@@ -429,6 +441,42 @@ xs_html *html_note(snac *user, const char *summary,
429 xs_html_attr("name", "is_draft"), 441 xs_html_attr("name", "is_draft"),
430 xs_html_attr(is_draft ? "checked" : "", NULL)))); 442 xs_html_attr(is_draft ? "checked" : "", NULL))));
431 443
444 /* post date and time */
445 xs *post_date = NULL;
446 xs *post_time = NULL;
447
448 if (xs_is_string(published)) {
449 time_t t = xs_parse_iso_date(published, 0);
450
451 if (t > 0) {
452 post_date = xs_str_time(t, "%Y-%m-%d", 1);
453 post_time = xs_str_time(t, "%H:%M:%S", 1);
454 }
455 }
456
457 if (edit_id == NULL || is_draft || is_scheduled(user, edit_id)) {
458 xs *pdat = xs_fmt(L("Post date and time (timezone: %s):"), user->tz);
459
460 xs_html_add(form,
461 xs_html_tag("p",
462 xs_html_tag("details",
463 xs_html_tag("summary",
464 xs_html_text(L("Scheduled post..."))),
465 xs_html_tag("p",
466 xs_html_text(pdat),
467 xs_html_sctag("br", NULL),
468 xs_html_sctag("input",
469 xs_html_attr("type", "date"),
470 xs_html_attr("value", post_date ? post_date : ""),
471 xs_html_attr("name", "post_date")),
472 xs_html_text(" "),
473 xs_html_sctag("input",
474 xs_html_attr("type", "time"),
475 xs_html_attr("value", post_time ? post_time : ""),
476 xs_html_attr("step", "1"),
477 xs_html_attr("name", "post_time"))))));
478 }
479
432 if (edit_id) 480 if (edit_id)
433 xs_html_add(form, 481 xs_html_add(form,
434 xs_html_sctag("input", 482 xs_html_sctag("input",
@@ -1116,7 +1164,7 @@ xs_html *html_top_controls(snac *user)
1116 NULL, NULL, 1164 NULL, NULL,
1117 xs_stock(XSTYPE_FALSE), "", 1165 xs_stock(XSTYPE_FALSE), "",
1118 xs_stock(XSTYPE_FALSE), NULL, 1166 xs_stock(XSTYPE_FALSE), NULL,
1119 NULL, 1, NULL, NULL, 0), 1167 NULL, 1, NULL, NULL, 0, NULL),
1120 1168
1121 /** operations **/ 1169 /** operations **/
1122 xs_html_tag("details", 1170 xs_html_tag("details",
@@ -1270,6 +1318,27 @@ xs_html *html_top_controls(snac *user)
1270 xs_html_attr("value", lang))); 1318 xs_html_attr("value", lang)));
1271 } 1319 }
1272 1320
1321 /* timezone */
1322 xs_html *tz_select = xs_html_tag("select",
1323 xs_html_attr("name", "tz"));
1324
1325 xs *tzs = xs_tz_list();
1326 const char *tz;
1327
1328 xs_list_foreach(tzs, tz) {
1329 if (strcmp(tz, user->tz) == 0)
1330 xs_html_add(tz_select,
1331 xs_html_tag("option",
1332 xs_html_text(tz),
1333 xs_html_attr("value", tz),
1334 xs_html_attr("selected", "selected")));
1335 else
1336 xs_html_add(tz_select,
1337 xs_html_tag("option",
1338 xs_html_text(tz),
1339 xs_html_attr("value", tz)));
1340 }
1341
1273 xs *user_setup_action = xs_fmt("%s/admin/user-setup", user->actor); 1342 xs *user_setup_action = xs_fmt("%s/admin/user-setup", user->actor);
1274 1343
1275 xs_html_add(top_controls, 1344 xs_html_add(top_controls,
@@ -1466,6 +1535,11 @@ xs_html *html_top_controls(snac *user)
1466 lang_select), 1535 lang_select),
1467 1536
1468 xs_html_tag("p", 1537 xs_html_tag("p",
1538 xs_html_text(L("Time zone:")),
1539 xs_html_sctag("br", NULL),
1540 tz_select),
1541
1542 xs_html_tag("p",
1469 xs_html_text(L("New password:")), 1543 xs_html_text(L("New password:")),
1470 xs_html_sctag("br", NULL), 1544 xs_html_sctag("br", NULL),
1471 xs_html_sctag("input", 1545 xs_html_sctag("input",
@@ -1774,7 +1848,8 @@ xs_html *html_entry_controls(snac *user, const char *actor,
1774 id, NULL, 1848 id, NULL,
1775 xs_dict_get(msg, "sensitive"), xs_dict_get(msg, "summary"), 1849 xs_dict_get(msg, "sensitive"), xs_dict_get(msg, "summary"),
1776 xs_stock(is_msg_public(msg) ? XSTYPE_FALSE : XSTYPE_TRUE), redir, 1850 xs_stock(is_msg_public(msg) ? XSTYPE_FALSE : XSTYPE_TRUE), redir,
1777 NULL, 0, att_files, att_alt_texts, is_draft(user, id))), 1851 NULL, 0, att_files, att_alt_texts, is_draft(user, id),
1852 xs_dict_get(msg, "published"))),
1778 xs_html_tag("p", NULL)); 1853 xs_html_tag("p", NULL));
1779 } 1854 }
1780 1855
@@ -1793,7 +1868,7 @@ xs_html *html_entry_controls(snac *user, const char *actor,
1793 NULL, NULL, 1868 NULL, NULL,
1794 xs_dict_get(msg, "sensitive"), xs_dict_get(msg, "summary"), 1869 xs_dict_get(msg, "sensitive"), xs_dict_get(msg, "summary"),
1795 xs_stock(is_msg_public(msg) ? XSTYPE_FALSE : XSTYPE_TRUE), redir, 1870 xs_stock(is_msg_public(msg) ? XSTYPE_FALSE : XSTYPE_TRUE), redir,
1796 id, 0, NULL, NULL, 0)), 1871 id, 0, NULL, NULL, 0, NULL)),
1797 xs_html_tag("p", NULL)); 1872 xs_html_tag("p", NULL));
1798 } 1873 }
1799 1874
@@ -2396,13 +2471,19 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
2396 name = NULL; 2471 name = NULL;
2397 } 2472 }
2398 else { 2473 else {
2474 xs *d_href = xs_dup(o_href);
2475 if (strlen(d_href) > 64) {
2476 d_href[64] = '\0';
2477 d_href = xs_str_cat(d_href, "...");
2478 }
2479
2399 xs_html_add(content_attachments, 2480 xs_html_add(content_attachments,
2400 xs_html_tag("p", 2481 xs_html_tag("p",
2401 xs_html_tag("a", 2482 xs_html_tag("a",
2402 xs_html_attr("href", o_href), 2483 xs_html_attr("href", o_href),
2403 xs_html_text(L("Attachment")), 2484 xs_html_text(L("Attachment")),
2404 xs_html_text(": "), 2485 xs_html_text(": "),
2405 xs_html_text(o_href)))); 2486 xs_html_text(d_href))));
2406 2487
2407 /* do not generate an Alt... */ 2488 /* do not generate an Alt... */
2408 name = NULL; 2489 name = NULL;
@@ -2689,6 +2770,11 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
2689 } 2770 }
2690 } 2771 }
2691 2772
2773 /* add an invisible hr, to help differentiate between posts in text browsers */
2774 xs_html_add(entry_top,
2775 xs_html_sctag("hr",
2776 xs_html_attr("hidden", NULL)));
2777
2692 return entry_top; 2778 return entry_top;
2693} 2779}
2694 2780
@@ -2830,6 +2916,18 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only,
2830 xs_html_text(L("drafts"))))); 2916 xs_html_text(L("drafts")))));
2831 } 2917 }
2832 2918
2919 {
2920 /* show the list of scheduled posts */
2921 xs *url = xs_fmt("%s/sched", user->actor);
2922 xs_html_add(lol,
2923 xs_html_tag("li",
2924 xs_html_tag("a",
2925 xs_html_attr("href", url),
2926 xs_html_attr("class", "snac-list-link"),
2927 xs_html_attr("title", L("Scheduled posts")),
2928 xs_html_text(L("scheduled posts")))));
2929 }
2930
2833 /* the list of followed hashtags */ 2931 /* the list of followed hashtags */
2834 const char *followed_hashtags = xs_dict_get(user->config, "followed_hashtags"); 2932 const char *followed_hashtags = xs_dict_get(user->config, "followed_hashtags");
2835 2933
@@ -3130,7 +3228,7 @@ xs_html *html_people_list(snac *user, xs_list *list, const char *header, const c
3130 NULL, actor_id, 3228 NULL, actor_id,
3131 xs_stock(XSTYPE_FALSE), "", 3229 xs_stock(XSTYPE_FALSE), "",
3132 xs_stock(XSTYPE_FALSE), NULL, 3230 xs_stock(XSTYPE_FALSE), NULL,
3133 NULL, 0, NULL, NULL, 0), 3231 NULL, 0, NULL, NULL, 0, NULL),
3134 xs_html_tag("p", NULL)); 3232 xs_html_tag("p", NULL));
3135 3233
3136 xs_html_add(snac_post, snac_controls); 3234 xs_html_add(snac_post, snac_controls);
@@ -3879,6 +3977,21 @@ int html_get_handler(const xs_dict *req, const char *q_path,
3879 } 3977 }
3880 } 3978 }
3881 else 3979 else
3980 if (strcmp(p_path, "sched") == 0) { /** list of scheduled posts **/
3981 if (!login(&snac, req)) {
3982 *body = xs_dup(uid);
3983 status = HTTP_STATUS_UNAUTHORIZED;
3984 }
3985 else {
3986 xs *list = scheduled_list(&snac);
3987
3988 *body = html_timeline(&snac, list, 0, skip, show,
3989 0, L("Scheduled posts"), "", 0, error);
3990 *b_size = strlen(*body);
3991 status = HTTP_STATUS_OK;
3992 }
3993 }
3994 else
3882 if (xs_startswith(p_path, "list/")) { /** list timelines **/ 3995 if (xs_startswith(p_path, "list/")) { /** list timelines **/
3883 if (!login(&snac, req)) { 3996 if (!login(&snac, req)) {
3884 *body = xs_dup(uid); 3997 *body = xs_dup(uid);
@@ -4175,12 +4288,14 @@ int html_post_handler(const xs_dict *req, const char *q_path,
4175 snac_debug(&snac, 1, xs_fmt("web action '%s' received", p_path)); 4288 snac_debug(&snac, 1, xs_fmt("web action '%s' received", p_path));
4176 4289
4177 /* post note */ 4290 /* post note */
4178 const xs_str *content = xs_dict_get(p_vars, "content"); 4291 const char *content = xs_dict_get(p_vars, "content");
4179 const xs_str *in_reply_to = xs_dict_get(p_vars, "in_reply_to"); 4292 const char *in_reply_to = xs_dict_get(p_vars, "in_reply_to");
4180 const xs_str *to = xs_dict_get(p_vars, "to"); 4293 const char *to = xs_dict_get(p_vars, "to");
4181 const xs_str *sensitive = xs_dict_get(p_vars, "sensitive"); 4294 const char *sensitive = xs_dict_get(p_vars, "sensitive");
4182 const xs_str *summary = xs_dict_get(p_vars, "summary"); 4295 const char *summary = xs_dict_get(p_vars, "summary");
4183 const xs_str *edit_id = xs_dict_get(p_vars, "edit_id"); 4296 const char *edit_id = xs_dict_get(p_vars, "edit_id");
4297 const char *post_date = xs_dict_get_def(p_vars, "post_date", "");
4298 const char *post_time = xs_dict_get_def(p_vars, "post_time", "");
4184 int priv = !xs_is_null(xs_dict_get(p_vars, "mentioned_only")); 4299 int priv = !xs_is_null(xs_dict_get(p_vars, "mentioned_only"));
4185 int store_as_draft = !xs_is_null(xs_dict_get(p_vars, "is_draft")); 4300 int store_as_draft = !xs_is_null(xs_dict_get(p_vars, "is_draft"));
4186 xs *attach_list = xs_list_new(); 4301 xs *attach_list = xs_list_new();
@@ -4210,9 +4325,12 @@ int html_post_handler(const xs_dict *req, const char *q_path,
4210 const char *fn = xs_list_get(attach_file, 0); 4325 const char *fn = xs_list_get(attach_file, 0);
4211 4326
4212 if (xs_is_string(fn) && *fn != '\0') { 4327 if (xs_is_string(fn) && *fn != '\0') {
4213 char *ext = strrchr(fn, '.'); 4328 char rnd[32];
4214 xs *hash = xs_md5_hex(fn, strlen(fn)); 4329 xs_rnd_buf(rnd, sizeof(rnd));
4215 xs *id = xs_fmt("%s%s", hash, ext); 4330
4331 const char *ext = strrchr(fn, '.');
4332 xs *hash = xs_md5_hex(rnd, strlen(rnd));
4333 xs *id = xs_fmt("post-%s%s", hash, ext ? ext : "");
4216 xs *url = xs_fmt("%s/s/%s", snac.actor, id); 4334 xs *url = xs_fmt("%s/s/%s", snac.actor, id);
4217 int fo = xs_number_get(xs_list_get(attach_file, 1)); 4335 int fo = xs_number_get(xs_list_get(attach_file, 1));
4218 int fs = xs_number_get(xs_list_get(attach_file, 2)); 4336 int fs = xs_number_get(xs_list_get(attach_file, 2));
@@ -4268,6 +4386,31 @@ int html_post_handler(const xs_dict *req, const char *q_path,
4268 msg = xs_dict_set(msg, "summary", xs_is_null(summary) ? "..." : summary); 4386 msg = xs_dict_set(msg, "summary", xs_is_null(summary) ? "..." : summary);
4269 } 4387 }
4270 4388
4389 if (xs_is_string(post_date) && *post_date) {
4390 xs *post_pubdate = xs_fmt("%sT%s", post_date,
4391 xs_is_string(post_time) && *post_time ? post_time : "00:00:00");
4392
4393 time_t t = xs_parse_iso_date(post_pubdate, 0);
4394
4395 if (t != 0) {
4396 t -= xs_tz_offset(snac.tz);
4397
4398 xs *iso_date = xs_str_iso_date(t);
4399 msg = xs_dict_set(msg, "published", iso_date);
4400
4401 snac_debug(&snac, 1, xs_fmt("Published date: [%s]", iso_date));
4402 }
4403 else
4404 snac_log(&snac, xs_fmt("Invalid post date: [%s]", post_pubdate));
4405 }
4406
4407 /* is the published date from the future? */
4408 int future_post = 0;
4409 xs *right_now = xs_str_utctime(0, ISO_DATE_SPEC);
4410
4411 if (strcmp(xs_dict_get(msg, "published"), right_now) > 0)
4412 future_post = 1;
4413
4271 if (xs_is_null(edit_id)) { 4414 if (xs_is_null(edit_id)) {
4272 /* new message */ 4415 /* new message */
4273 const char *id = xs_dict_get(msg, "id"); 4416 const char *id = xs_dict_get(msg, "id");
@@ -4275,6 +4418,10 @@ int html_post_handler(const xs_dict *req, const char *q_path,
4275 if (store_as_draft) { 4418 if (store_as_draft) {
4276 draft_add(&snac, id, msg); 4419 draft_add(&snac, id, msg);
4277 } 4420 }
4421 else
4422 if (future_post) {
4423 schedule_add(&snac, id, msg);
4424 }
4278 else { 4425 else {
4279 c_msg = msg_create(&snac, msg); 4426 c_msg = msg_create(&snac, msg);
4280 timeline_add(&snac, id, msg); 4427 timeline_add(&snac, id, msg);
@@ -4286,7 +4433,7 @@ int html_post_handler(const xs_dict *req, const char *q_path,
4286 4433
4287 if (valid_status(object_get(edit_id, &p_msg))) { 4434 if (valid_status(object_get(edit_id, &p_msg))) {
4288 /* copy relevant fields from previous version */ 4435 /* copy relevant fields from previous version */
4289 char *fields[] = { "id", "context", "url", "published", 4436 char *fields[] = { "id", "context", "url",
4290 "to", "inReplyTo", NULL }; 4437 "to", "inReplyTo", NULL };
4291 int n; 4438 int n;
4292 4439
@@ -4302,18 +4449,34 @@ int html_post_handler(const xs_dict *req, const char *q_path,
4302 if (is_draft(&snac, edit_id)) { 4449 if (is_draft(&snac, edit_id)) {
4303 /* message was previously a draft; it's a create activity */ 4450 /* message was previously a draft; it's a create activity */
4304 4451
4305 /* set the published field to now */ 4452 /* if the date is from the past, overwrite it with right_now */
4306 xs *published = xs_str_utctime(0, ISO_DATE_SPEC); 4453 if (strcmp(xs_dict_get(msg, "published"), right_now) < 0) {
4307 msg = xs_dict_set(msg, "published", published); 4454 snac_debug(&snac, 1, xs_fmt("setting draft ancient date to %s", right_now));
4455 msg = xs_dict_set(msg, "published", right_now);
4456 }
4308 4457
4309 /* overwrite object */ 4458 /* overwrite object */
4310 object_add_ow(edit_id, msg); 4459 object_add_ow(edit_id, msg);
4311 4460
4312 c_msg = msg_create(&snac, msg); 4461 if (future_post) {
4313 timeline_add(&snac, edit_id, msg); 4462 schedule_add(&snac, edit_id, msg);
4463 }
4464 else {
4465 c_msg = msg_create(&snac, msg);
4466 timeline_add(&snac, edit_id, msg);
4467 }
4468
4314 draft_del(&snac, edit_id); 4469 draft_del(&snac, edit_id);
4315 } 4470 }
4471 else
4472 if (is_scheduled(&snac, edit_id)) {
4473 /* editing an scheduled post; just update it */
4474 schedule_add(&snac, edit_id, msg);
4475 }
4316 else { 4476 else {
4477 /* ignore the (possibly changed) published date */
4478 msg = xs_dict_set(msg, "published", xs_dict_get(p_msg, "published"));
4479
4317 /* set the updated field */ 4480 /* set the updated field */
4318 xs *updated = xs_str_utctime(0, ISO_DATE_SPEC); 4481 xs *updated = xs_str_utctime(0, ISO_DATE_SPEC);
4319 msg = xs_dict_set(msg, "updated", updated); 4482 msg = xs_dict_set(msg, "updated", updated);
@@ -4398,6 +4561,9 @@ int html_post_handler(const xs_dict *req, const char *q_path,
4398 if (is_draft(&snac, id)) 4561 if (is_draft(&snac, id))
4399 draft_del(&snac, id); 4562 draft_del(&snac, id);
4400 else 4563 else
4564 if (is_scheduled(&snac, id))
4565 schedule_del(&snac, id);
4566 else
4401 hide(&snac, id); 4567 hide(&snac, id);
4402 } 4568 }
4403 else 4569 else
@@ -4493,6 +4659,8 @@ int html_post_handler(const xs_dict *req, const char *q_path,
4493 4659
4494 draft_del(&snac, id); 4660 draft_del(&snac, id);
4495 4661
4662 schedule_del(&snac, id);
4663
4496 snac_log(&snac, xs_fmt("deleted entry %s", id)); 4664 snac_log(&snac, xs_fmt("deleted entry %s", id));
4497 } 4665 }
4498 } 4666 }
@@ -4613,6 +4781,8 @@ int html_post_handler(const xs_dict *req, const char *q_path,
4613 snac.config = xs_dict_set(snac.config, "show_contact_metrics", xs_stock(XSTYPE_FALSE)); 4781 snac.config = xs_dict_set(snac.config, "show_contact_metrics", xs_stock(XSTYPE_FALSE));
4614 if ((v = xs_dict_get(p_vars, "web_ui_lang")) != NULL) 4782 if ((v = xs_dict_get(p_vars, "web_ui_lang")) != NULL)
4615 snac.config = xs_dict_set(snac.config, "lang", v); 4783 snac.config = xs_dict_set(snac.config, "lang", v);
4784 if ((v = xs_dict_get(p_vars, "tz")) != NULL)
4785 snac.config = xs_dict_set(snac.config, "tz", v);
4616 4786
4617 snac.config = xs_dict_set(snac.config, "latitude", xs_dict_get_def(p_vars, "latitude", "")); 4787 snac.config = xs_dict_set(snac.config, "latitude", xs_dict_get_def(p_vars, "latitude", ""));
4618 snac.config = xs_dict_set(snac.config, "longitude", xs_dict_get_def(p_vars, "longitude", "")); 4788 snac.config = xs_dict_set(snac.config, "longitude", xs_dict_get_def(p_vars, "longitude", ""));
@@ -4636,7 +4806,7 @@ int html_post_handler(const xs_dict *req, const char *q_path,
4636 if (xs_startswith(mimetype, "image/")) { 4806 if (xs_startswith(mimetype, "image/")) {
4637 const char *ext = strrchr(fn, '.'); 4807 const char *ext = strrchr(fn, '.');
4638 xs *hash = xs_md5_hex(fn, strlen(fn)); 4808 xs *hash = xs_md5_hex(fn, strlen(fn));
4639 xs *id = xs_fmt("%s%s", hash, ext); 4809 xs *id = xs_fmt("%s-%s%s", uploads[n], hash, ext ? ext : "");
4640 xs *url = xs_fmt("%s/s/%s", snac.actor, id); 4810 xs *url = xs_fmt("%s/s/%s", snac.actor, id);
4641 int fo = xs_number_get(xs_list_get(uploaded_file, 1)); 4811 int fo = xs_number_get(xs_list_get(uploaded_file, 1));
4642 int fs = xs_number_get(xs_list_get(uploaded_file, 2)); 4812 int fs = xs_number_get(xs_list_get(uploaded_file, 2));
@@ -4705,6 +4875,9 @@ int html_post_handler(const xs_dict *req, const char *q_path,
4705 /* set the option */ 4875 /* set the option */
4706 msg = xs_dict_append(msg, "name", v); 4876 msg = xs_dict_append(msg, "name", v);
4707 4877
4878 /* delete the content */
4879 msg = xs_dict_del(msg, "content");
4880
4708 xs *c_msg = msg_create(&snac, msg); 4881 xs *c_msg = msg_create(&snac, msg);
4709 4882
4710 enqueue_message(&snac, c_msg); 4883 enqueue_message(&snac, c_msg);