diff options
Diffstat (limited to 'html.c')
| -rw-r--r-- | html.c | 393 |
1 files changed, 282 insertions, 111 deletions
@@ -13,6 +13,7 @@ #include "xs_html.h" #include "xs_curl.h" #include "xs_unicode.h" +#include "xs_url.h" #include "snac.h" @@ -115,7 +116,8 @@ xs_str *actor_name(xs_dict *actor, const char *proxy) xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date, const char *udate, const char *url, int priv, - int in_people, const char *proxy, const char *lang) + int in_people, const char *proxy, const char *lang, + const char *md5) { xs_html *actor_icon = xs_html_tag("p", NULL); @@ -224,12 +226,31 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date, if (xs_is_string(lang)) date_title = xs_str_cat(date_title, " (", lang, ")"); + xs_html *date_text = xs_html_text(date_label); + + if (user && md5) { + xs *lpost_url = xs_fmt("%s/admin/p/%s#%s_entry", + user->actor, md5, md5); + date_text = xs_html_tag("a", + xs_html_attr("href", lpost_url), + xs_html_attr("class", "snac-pubdate"), + date_text); + } + else if (user && url) { + xs *lpost_url = xs_fmt("%s/admin?q=%s", + user->actor, xs_url_enc(url)); + date_text = xs_html_tag("a", + xs_html_attr("href", lpost_url), + xs_html_attr("class", "snac-pubdate"), + date_text); + } + xs_html_add(actor_icon, xs_html_text(" "), xs_html_tag("time", xs_html_attr("class", "dt-published snac-pubdate"), xs_html_attr("title", date_title), - xs_html_text(date_label))); + date_text)); } { @@ -261,7 +282,7 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date, } -xs_html *html_msg_icon(snac *user, const char *actor_id, const xs_dict *msg, const char *proxy) +xs_html *html_msg_icon(snac *user, const char *actor_id, const xs_dict *msg, const char *proxy, const char *md5) { xs *actor = NULL; xs_html *actor_icon = NULL; @@ -292,7 +313,7 @@ xs_html *html_msg_icon(snac *user, const char *actor_id, const xs_dict *msg, con else lang = NULL; - actor_icon = html_actor_icon(user, actor, date, udate, url, priv, 0, proxy, lang); + actor_icon = html_actor_icon(user, actor, date, udate, url, priv, 0, proxy, lang, md5); } return actor_icon; @@ -306,7 +327,7 @@ xs_html *html_note(snac *user, const char *summary, const xs_val *cw_yn, const char *cw_text, const xs_val *mnt_only, const char *redir, const char *in_reply_to, int poll, - const char *att_file, const char *att_alt_text, + const xs_list *att_files, const xs_list *att_alt_texts, int is_draft) /* Yes, this is a FUCKTON of arguments and I'm a bit embarrased */ { @@ -411,30 +432,71 @@ xs_html *html_note(snac *user, const char *summary, xs_html_tag("p", NULL), att = xs_html_tag("details", xs_html_tag("summary", - xs_html_text(L("Attachment..."))), + xs_html_text(L("Attachments..."))), xs_html_tag("p", NULL))); - if (att_file && *att_file) + int max_attachments = xs_number_get(xs_dict_get_def(srv_config, "max_attachments", "4")); + int att_n = 0; + + /* fields for the currently existing attachments */ + if (xs_is_list(att_files) && xs_is_list(att_alt_texts)) { + while (att_n < max_attachments) { + const char *att_file = xs_list_get(att_files, att_n); + const char *att_alt_text = xs_list_get(att_alt_texts, att_n); + + if (!xs_is_string(att_file) || !xs_is_string(att_alt_text)) + break; + + xs *att_lbl = xs_fmt("attach_url_%d", att_n); + xs *alt_lbl = xs_fmt("alt_text_%d", att_n); + + if (att_n) + xs_html_add(att, + xs_html_sctag("br", NULL)); + + xs_html_add(att, + xs_html_text(L("File:")), + xs_html_sctag("input", + xs_html_attr("type", "text"), + xs_html_attr("name", att_lbl), + xs_html_attr("title", L("Clear this field to delete the attachment")), + xs_html_attr("value", att_file))); + + xs_html_add(att, + xs_html_text(" "), + xs_html_sctag("input", + xs_html_attr("type", "text"), + xs_html_attr("name", alt_lbl), + xs_html_attr("value", att_alt_text), + xs_html_attr("placeholder", L("Attachment description")))); + + att_n++; + } + } + + /* the rest of possible attachments */ + while (att_n < max_attachments) { + xs *att_lbl = xs_fmt("attach_%d", att_n); + xs *alt_lbl = xs_fmt("alt_text_%d", att_n); + + if (att_n) + xs_html_add(att, + xs_html_sctag("br", NULL)); + xs_html_add(att, - xs_html_text(L("File:")), xs_html_sctag("input", - xs_html_attr("type", "text"), - xs_html_attr("name", "attach_url"), - xs_html_attr("title", L("Clear this field to delete the attachment")), - xs_html_attr("value", att_file))); - else + xs_html_attr("type", "file"), + xs_html_attr("name", att_lbl))); + xs_html_add(att, + xs_html_text(" "), xs_html_sctag("input", - xs_html_attr("type", "file"), - xs_html_attr("name", "attach"))); + xs_html_attr("type", "text"), + xs_html_attr("name", alt_lbl), + xs_html_attr("placeholder", L("Attachment description")))); - xs_html_add(att, - xs_html_text(" "), - xs_html_sctag("input", - xs_html_attr("type", "text"), - xs_html_attr("name", "alt_text"), - xs_html_attr("value", att_alt_text), - xs_html_attr("placeholder", L("Attachment description")))); + att_n++; + } /* add poll controls */ if (poll) { @@ -553,10 +615,11 @@ xs_html *html_instance_head(void) static xs_html *html_instance_body(void) { - const char *host = xs_dict_get(srv_config, "host"); - const char *sdesc = xs_dict_get(srv_config, "short_description"); - const char *email = xs_dict_get(srv_config, "admin_email"); - const char *acct = xs_dict_get(srv_config, "admin_account"); + const char *host = xs_dict_get(srv_config, "host"); + const char *sdesc = xs_dict_get(srv_config, "short_description"); + const char *sdescraw = xs_dict_get(srv_config, "short_description_raw"); + const char *email = xs_dict_get(srv_config, "admin_email"); + const char *acct = xs_dict_get(srv_config, "admin_account"); xs *blurb = xs_replace(snac_blurb, "%host%", host); @@ -569,12 +632,21 @@ static xs_html *html_instance_body(void) dl = xs_html_tag("dl", NULL))); if (sdesc && *sdesc) { - xs_html_add(dl, - xs_html_tag("di", - xs_html_tag("dt", - xs_html_text(L("Site description"))), - xs_html_tag("dd", - xs_html_text(sdesc)))); + if (!xs_is_null(sdescraw) && xs_type(sdescraw) == XSTYPE_TRUE) { + xs_html_add(dl, + xs_html_tag("di", + xs_html_tag("dt", + xs_html_text(L("Site description"))), + xs_html_tag("dd", + xs_html_raw(sdesc)))); + } else { + xs_html_add(dl, + xs_html_tag("di", + xs_html_tag("dt", + xs_html_text(L("Site description"))), + xs_html_tag("dd", + xs_html_text(sdesc)))); + } } if (email && *email) { xs *mailto = xs_fmt("mailto:%s", email); @@ -1028,7 +1100,7 @@ xs_html *html_top_controls(snac *snac) NULL, NULL, xs_stock(XSTYPE_FALSE), "", xs_stock(XSTYPE_FALSE), NULL, - NULL, 1, "", "", 0), + NULL, 1, NULL, NULL, 0), /** operations **/ xs_html_tag("details", @@ -1600,17 +1672,22 @@ xs_html *html_entry_controls(snac *snac, const char *actor, xs *form_id = xs_fmt("%s_edit_form", md5); xs *redir = xs_fmt("%s_entry", md5); - const char *att_file = ""; - const char *att_alt_text = ""; + xs *att_files = xs_list_new(); + xs *att_alt_texts = xs_list_new(); + const xs_list *att_list = xs_dict_get(msg, "attachment"); - /* does it have an attachment? */ - if (xs_type(att_list) == XSTYPE_LIST && xs_list_len(att_list)) { - const xs_dict *d = xs_list_get(att_list, 0); + if (xs_is_list(att_list)) { + const xs_dict *d; + + xs_list_foreach(att_list, d) { + const char *att_file = xs_dict_get(d, "url"); + const char *att_alt_text = xs_dict_get(d, "name"); - if (xs_type(d) == XSTYPE_DICT) { - att_file = xs_dict_get_def(d, "url", ""); - att_alt_text = xs_dict_get_def(d, "name", ""); + if (xs_is_string(att_file) && xs_is_string(att_alt_text)) { + att_files = xs_list_append(att_files, att_file); + att_alt_texts = xs_list_append(att_alt_texts, att_alt_text); + } } } @@ -1622,7 +1699,7 @@ xs_html *html_entry_controls(snac *snac, const char *actor, id, NULL, xs_dict_get(msg, "sensitive"), xs_dict_get(msg, "summary"), xs_stock(is_msg_public(msg) ? XSTYPE_FALSE : XSTYPE_TRUE), redir, - NULL, 0, att_file, att_alt_text, is_draft(snac, id))), + NULL, 0, att_files, att_alt_texts, is_draft(snac, id))), xs_html_tag("p", NULL)); } @@ -1641,7 +1718,7 @@ xs_html *html_entry_controls(snac *snac, const char *actor, NULL, NULL, xs_dict_get(msg, "sensitive"), xs_dict_get(msg, "summary"), xs_stock(is_msg_public(msg) ? XSTYPE_FALSE : XSTYPE_TRUE), redir, - id, 0, "", "", 0)), + id, 0, NULL, NULL, 0)), xs_html_tag("p", NULL)); } @@ -1696,7 +1773,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, xs_html_tag("div", xs_html_attr("class", "snac-origin"), xs_html_text(L("follows you"))), - html_msg_icon(read_only ? NULL : user, xs_dict_get(msg, "actor"), msg, proxy))); + html_msg_icon(read_only ? NULL : user, xs_dict_get(msg, "actor"), msg, proxy, NULL))); } else if (!xs_match(type, POSTLIKE_OBJECT_TYPE)) { @@ -1877,7 +1954,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, } xs_html_add(post_header, - html_msg_icon(read_only ? NULL : user, actor, msg, proxy)); + html_msg_icon(read_only ? NULL : user, actor, msg, proxy, md5)); /** post content **/ @@ -2022,16 +2099,17 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, const char *name = xs_dict_get(v, "name"); const xs_dict *replies = xs_dict_get(v, "replies"); - if (name && replies) { - char *ti = (char *)xs_number_str(xs_dict_get(replies, "totalItems")); + if (xs_is_string(name) && xs_is_dict(replies)) { + const char *ti = xs_number_str(xs_dict_get(replies, "totalItems")); - xs_html_add(poll_result, - xs_html_tag("tr", - xs_html_tag("td", - xs_html_text(name), - xs_html_text(":")), - xs_html_tag("td", - xs_html_text(ti)))); + if (xs_is_string(ti)) + xs_html_add(poll_result, + xs_html_tag("tr", + xs_html_tag("td", + xs_html_text(name), + xs_html_text(":")), + xs_html_tag("td", + xs_html_text(ti)))); } } @@ -2629,6 +2707,29 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, xs_html_attr("title", L("Post drafts")), xs_html_text("drafts")))); } + + /* the list of followed hashtags */ + const char *followed_hashtags = xs_dict_get(user->config, "followed_hashtags"); + + if (xs_is_list(followed_hashtags) && xs_list_len(followed_hashtags)) { + xs_html *loht = xs_html_tag("ul", + xs_html_attr("class", "snac-list-of-lists")); + xs_html_add(body, loht); + + const char *ht; + + xs_list_foreach(followed_hashtags, ht) { + xs *url = xs_fmt("%s/admin?q=%s", user->actor, ht); + url = xs_replace_i(url, "#", "%23"); + + xs_html_add(loht, + xs_html_tag("li", + xs_html_tag("a", + xs_html_attr("href", url), + xs_html_attr("class", "snac-list-link"), + xs_html_text(ht)))); + } + } } xs_html_add(body, @@ -2648,10 +2749,32 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, xs_html_add(body, posts); + int mark_shown = 0; + while (xs_list_iter(&p, &v)) { xs *msg = NULL; int status; + /* "already seen" mark? */ + if (strcmp(v, MD5_ALREADY_SEEN_MARK) == 0) { + if (skip == 0 && !mark_shown) { + xs *s = xs_fmt("%s/admin", user->actor); + + xs_html_add(posts, + xs_html_tag("div", + xs_html_attr("class", "snac-no-more-unseen-posts"), + xs_html_text(L("No more unseen posts")), + xs_html_text(" - "), + xs_html_tag("a", + xs_html_attr("href", s), + xs_html_text(L("Back to top"))))); + } + + mark_shown = 1; + + continue; + } + if (utl && user && !is_pinned_by_md5(user, v)) status = timeline_get_by_md5(user, v, &msg); else @@ -2788,7 +2911,7 @@ xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t, cons xs_html_tag("div", xs_html_attr("class", "snac-post-header"), html_actor_icon(snac, actor, xs_dict_get(actor, "published"), - NULL, NULL, 0, 1, proxy, NULL))); + NULL, NULL, 0, 1, proxy, NULL, NULL))); /* content (user bio) */ const char *c = xs_dict_get(actor, "summary"); @@ -2885,7 +3008,7 @@ xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t, cons NULL, actor_id, xs_stock(XSTYPE_FALSE), "", xs_stock(XSTYPE_FALSE), NULL, - NULL, 0, "", "", 0), + NULL, 0, NULL, NULL, 0), xs_html_tag("p", NULL)); xs_html_add(snac_post, snac_controls); @@ -2970,6 +3093,9 @@ xs_str *html_notifications(snac *user, int skip, int show) xs_set rep; xs_set_init(&rep); + /* dict to store previous notification labels */ + xs *admiration_labels = xs_dict_new(); + const xs_str *v; xs_list_foreach(n_list, v) { @@ -2983,6 +3109,7 @@ xs_str *html_notifications(snac *user, int skip, int show) const char *utype = xs_dict_get(noti, "utype"); const char *id = xs_dict_get(noti, "objid"); const char *date = xs_dict_get(noti, "date"); + const char *id2 = xs_dict_get_path(noti, "msg.id"); xs *wrk = NULL; if (xs_is_null(id)) @@ -2991,12 +3118,15 @@ xs_str *html_notifications(snac *user, int skip, int show) if (is_hidden(user, id)) continue; + if (xs_is_string(id2) && xs_set_add(&rep, id2) != 1) + continue; + object_get(id, &obj); const char *msg_id = NULL; - if (xs_is_dict(obj) && (msg_id = xs_dict_get(obj, "id")) && xs_set_add(&rep, msg_id) != 1) - continue; + if (xs_is_dict(obj)) + msg_id = xs_dict_get(obj, "id"); const char *actor_id = xs_dict_get(noti, "actor"); xs *actor = NULL; @@ -3030,9 +3160,7 @@ xs_str *html_notifications(snac *user, int skip, int show) xs *s_date = xs_crop_i(xs_dup(date), 0, 10); - xs_html *entry = xs_html_tag("div", - xs_html_attr("class", "snac-post-with-desc"), - xs_html_tag("p", + xs_html *this_html_label = xs_html_container( xs_html_tag("b", xs_html_text(label), xs_html_text(" by "), @@ -3043,13 +3171,45 @@ xs_str *html_notifications(snac *user, int skip, int show) xs_html_tag("time", xs_html_attr("class", "dt-published snac-pubdate"), xs_html_attr("title", date), - xs_html_text(s_date)))); + xs_html_text(s_date))); + + xs_html *html_label = NULL; + + if (xs_is_string(msg_id)) { + const xs_val *prev_label = xs_dict_get(admiration_labels, msg_id); + + if (xs_type(prev_label) == XSTYPE_DATA) { + /* there is a previous list of admiration labels! */ + xs_data_get(&html_label, prev_label); + + xs_html_add(html_label, + xs_html_sctag("br", NULL), + this_html_label); + + continue; + } + } + + xs_html *entry = NULL; + + html_label = xs_html_tag("p", + this_html_label); + + /* store in the admiration labels dict */ + xs *pl = xs_data_new(&html_label, sizeof(html_label)); + + if (xs_is_string(msg_id)) + admiration_labels = xs_dict_set(admiration_labels, msg_id, pl); + + entry = xs_html_tag("div", + xs_html_attr("class", "snac-post-with-desc"), + html_label); if (strcmp(type, "Follow") == 0 || strcmp(utype, "Follow") == 0 || strcmp(type, "Block") == 0) { xs_html_add(entry, xs_html_tag("div", xs_html_attr("class", "snac-post"), - html_actor_icon(user, actor, NULL, NULL, NULL, 0, 0, proxy, NULL))); + html_actor_icon(user, actor, NULL, NULL, NULL, 0, 0, proxy, NULL, NULL))); } else if (strcmp(type, "Move") == 0) { @@ -3063,18 +3223,23 @@ xs_str *html_notifications(snac *user, int skip, int show) xs_html_add(entry, xs_html_tag("div", xs_html_attr("class", "snac-post"), - html_actor_icon(user, old_actor, NULL, NULL, NULL, 0, 0, proxy, NULL))); + html_actor_icon(user, old_actor, NULL, NULL, NULL, 0, 0, proxy, NULL, NULL))); } } } else if (obj != NULL) { xs *md5 = xs_md5_hex(id, strlen(id)); + xs *ctxt = xs_fmt("%s/admin/p/%s#%s_entry", user->actor, md5, md5); xs_html *h = html_entry(user, obj, 0, 0, md5, 1); if (h != NULL) { xs_html_add(entry, + xs_html_tag("p", + xs_html_tag("a", + xs_html_attr("href", ctxt), + xs_html_text(L("Context")))), h); } } @@ -3111,8 +3276,6 @@ xs_str *html_notifications(snac *user, int skip, int show) } } - xs_set_free(&rep); - if (noti_new == NULL && noti_seen == NULL) xs_html_add(body, xs_html_tag("h2", @@ -3132,6 +3295,8 @@ xs_str *html_notifications(snac *user, int skip, int show) xs_html_text(L("More..."))))); } + xs_set_free(&rep); + xs_html_add(body, html_footer()); @@ -3232,7 +3397,8 @@ int html_get_handler(const xs_dict *req, const char *q_path, cache = 0; int skip = 0; - int def_show = xs_number_get(xs_dict_get(srv_config, "max_timeline_entries")); + int def_show = xs_number_get(xs_dict_get_def(srv_config, "def_timeline_entries", + xs_dict_get_def(srv_config, "max_timeline_entries", "50"))); int show = def_show; if ((v = xs_dict_get(q_vars, "skip")) != NULL) @@ -3277,21 +3443,17 @@ int html_get_handler(const xs_dict *req, const char *q_path, } else { xs *list = NULL; - xs *next = NULL; + int more = 0; - if (xs_is_true(xs_dict_get(srv_config, "strict_public_timelines"))) { - list = timeline_simple_list(&snac, "public", skip, show); - next = timeline_simple_list(&snac, "public", skip + show, 1); - } - else { - list = timeline_list(&snac, "public", skip, show); - next = timeline_list(&snac, "public", skip + show, 1); - } + if (xs_is_true(xs_dict_get(srv_config, "strict_public_timelines"))) + list = timeline_simple_list(&snac, "public", skip, show, &more); + else + list = timeline_list(&snac, "public", skip, show, &more); xs *pins = pinned_list(&snac); pins = xs_list_cat(pins, list); - *body = html_timeline(&snac, pins, 1, skip, show, xs_list_len(next), NULL, "", 1, error); + *body = html_timeline(&snac, pins, 1, skip, show, more, NULL, "", 1, error); *b_size = strlen(*body); status = HTTP_STATUS_OK; @@ -3440,6 +3602,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, } } else { + /** the private timeline **/ double t = history_mtime(&snac, "timeline.html_"); /* if enabled by admin, return a cached page if its timestamp is: @@ -3453,19 +3616,22 @@ int html_get_handler(const xs_dict *req, const char *q_path, xs_dict_get(req, "if-none-match"), etag); } else { + int more = 0; + snac_debug(&snac, 1, xs_fmt("building timeline")); - xs *list = timeline_list(&snac, "private", skip, show); - xs *next = timeline_list(&snac, "private", skip + show, 1); + xs *list = timeline_list(&snac, "private", skip, show, &more); *body = html_timeline(&snac, list, 0, skip, show, - xs_list_len(next), NULL, "/admin", 1, error); + more, NULL, "/admin", 1, error); *b_size = strlen(*body); status = HTTP_STATUS_OK; if (save) history_add(&snac, "timeline.html_", *body, *b_size, etag); + + timeline_add_mark(&snac); } } } @@ -3481,7 +3647,8 @@ int html_get_handler(const xs_dict *req, const char *q_path, const char *md5 = xs_list_get(l, -1); if (md5 && *md5 && timeline_here(&snac, md5)) { - xs *list = xs_list_append(xs_list_new(), md5); + xs *list0 = xs_list_append(xs_list_new(), md5); + xs *list = timeline_top_level(&snac, list0); *body = html_timeline(&snac, list, 0, 0, 0, 0, NULL, "/admin", 1, error); *b_size = strlen(*body); @@ -3665,7 +3832,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, int cnt = xs_number_get(xs_dict_get_def(srv_config, "max_public_entries", "20")); - xs *elems = timeline_simple_list(&snac, "public", 0, cnt); + xs *elems = timeline_simple_list(&snac, "public", 0, cnt, NULL); xs *bio = xs_dup(xs_dict_get(snac.config, "bio")); xs *rss_title = xs_fmt("%s (@%s@%s)", @@ -3869,52 +4036,56 @@ int html_post_handler(const xs_dict *req, const char *q_path, /* post note */ const xs_str *content = xs_dict_get(p_vars, "content"); const xs_str *in_reply_to = xs_dict_get(p_vars, "in_reply_to"); - const xs_str *attach_url = xs_dict_get(p_vars, "attach_url"); - const xs_list *attach_file = xs_dict_get(p_vars, "attach"); const xs_str *to = xs_dict_get(p_vars, "to"); const xs_str *sensitive = xs_dict_get(p_vars, "sensitive"); const xs_str *summary = xs_dict_get(p_vars, "summary"); const xs_str *edit_id = xs_dict_get(p_vars, "edit_id"); - const xs_str *alt_text = xs_dict_get(p_vars, "alt_text"); int priv = !xs_is_null(xs_dict_get(p_vars, "mentioned_only")); int store_as_draft = !xs_is_null(xs_dict_get(p_vars, "is_draft")); xs *attach_list = xs_list_new(); - /* default alt text */ - if (xs_is_null(alt_text)) - alt_text = ""; + /* iterate the attachments */ + int max_attachments = xs_number_get(xs_dict_get_def(srv_config, "max_attachments", "4")); - /* is attach_url set? */ - if (!xs_is_null(attach_url) && *attach_url != '\0') { - xs *l = xs_list_new(); + for (int att_n = 0; att_n < max_attachments; att_n++) { + xs *url_lbl = xs_fmt("attach_url_%d", att_n); + xs *att_lbl = xs_fmt("attach_%d", att_n); + xs *alt_lbl = xs_fmt("alt_text_%d", att_n); - l = xs_list_append(l, attach_url); - l = xs_list_append(l, alt_text); + const char *attach_url = xs_dict_get(p_vars, url_lbl); + const xs_list *attach_file = xs_dict_get(p_vars, att_lbl); + const char *alt_text = xs_dict_get_def(p_vars, alt_lbl, ""); - attach_list = xs_list_append(attach_list, l); - } + if (xs_is_string(attach_url) && *attach_url != '\0') { + xs *l = xs_list_new(); - /* is attach_file set? */ - if (!xs_is_null(attach_file) && xs_type(attach_file) == XSTYPE_LIST) { - const char *fn = xs_list_get(attach_file, 0); + l = xs_list_append(l, attach_url); + l = xs_list_append(l, alt_text); - if (*fn != '\0') { - char *ext = strrchr(fn, '.'); - xs *hash = xs_md5_hex(fn, strlen(fn)); - xs *id = xs_fmt("%s%s", hash, ext); - xs *url = xs_fmt("%s/s/%s", snac.actor, id); - int fo = xs_number_get(xs_list_get(attach_file, 1)); - int fs = xs_number_get(xs_list_get(attach_file, 2)); + attach_list = xs_list_append(attach_list, l); + } + else + if (xs_is_list(attach_file)) { + const char *fn = xs_list_get(attach_file, 0); - /* store */ - static_put(&snac, id, payload + fo, fs); + if (xs_is_string(fn) && *fn != '\0') { + char *ext = strrchr(fn, '.'); + xs *hash = xs_md5_hex(fn, strlen(fn)); + xs *id = xs_fmt("%s%s", hash, ext); + xs *url = xs_fmt("%s/s/%s", snac.actor, id); + int fo = xs_number_get(xs_list_get(attach_file, 1)); + int fs = xs_number_get(xs_list_get(attach_file, 2)); - xs *l = xs_list_new(); + /* store */ + static_put(&snac, id, payload + fo, fs); - l = xs_list_append(l, url); - l = xs_list_append(l, alt_text); + xs *l = xs_list_new(); - attach_list = xs_list_append(attach_list, l); + l = xs_list_append(l, url); + l = xs_list_append(l, alt_text); + + attach_list = xs_list_append(attach_list, l); + } } } |