/* snac - A simple, minimalistic ActivityPub instance */ /* copyright (c) 2022 - 2023 grunfink / MIT license */ #include "xs.h" #include "xs_io.h" #include "xs_json.h" #include "xs_regex.h" #include "xs_set.h" #include "xs_openssl.h" #include "xs_time.h" #include "xs_mime.h" #include "snac.h" int login(snac *snac, const xs_dict *headers) /* tries a login */ { int logged_in = 0; const char *auth = xs_dict_get(headers, "authorization"); if (auth && xs_startswith(auth, "Basic ")) { int sz; xs *s1 = xs_crop_i(xs_dup(auth), 6, 0); xs *s2 = xs_base64_dec(s1, &sz); xs *l1 = xs_split_n(s2, ":", 1); if (xs_list_len(l1) == 2) { logged_in = check_password( xs_list_get(l1, 0), xs_list_get(l1, 1), xs_dict_get(snac->config, "passwd")); } } if (logged_in) lastlog_write(snac, "web"); return logged_in; } xs_str *actor_name(xs_dict *actor) /* gets the actor name */ { xs_list *p; char *v; xs_str *name; if (xs_is_null((v = xs_dict_get(actor, "name"))) || *v == '\0') { if (xs_is_null(v = xs_dict_get(actor, "preferredUsername")) || *v == '\0') { v = "anonymous"; } } name = xs_dup(v); /* replace the :shortnames: */ if (!xs_is_null(p = xs_dict_get(actor, "tag"))) { /* iterate the tags */ while (xs_list_iter(&p, &v)) { char *t = xs_dict_get(v, "type"); if (t && strcmp(t, "Emoji") == 0) { char *n = xs_dict_get(v, "name"); char *i = xs_dict_get(v, "icon"); if (n && i) { char *u = xs_dict_get(i, "url"); xs *img = xs_fmt("", u); name = xs_replace_i(name, n, img); } } } } return name; } xs_str *html_actor_icon(xs_str *os, char *actor, const char *date, const char *udate, const char *url, int priv) { xs *s = xs_str_new(NULL); xs *avatar = NULL; char *v; xs *name = actor_name(actor); /* get the avatar */ if ((v = xs_dict_get(actor, "icon")) != NULL && (v = xs_dict_get(v, "url")) != NULL) { avatar = xs_dup(v); } if (avatar == NULL) avatar = xs_fmt("data:image/png;base64, %s", default_avatar_base64()); { xs *s1 = xs_fmt("
\n", avatar);
s = xs_str_cat(s, s1);
}
{
xs *s1 = xs_fmt("%s",
xs_dict_get(actor, "id"), name);
s = xs_str_cat(s, s1);
}
if (!xs_is_null(url)) {
xs *s1 = xs_fmt(" »", url);
s = xs_str_cat(s, s1);
}
if (priv)
s = xs_str_cat(s, " 🔒");
if (xs_is_null(date)) {
s = xs_str_cat(s, "
\n \n");
}
else {
xs *date_label = xs_crop_i(xs_dup(date), 0, 10);
xs *date_title = xs_dup(date);
if (!xs_is_null(udate)) {
xs *sd = xs_crop_i(xs_dup(udate), 0, 10);
date_label = xs_str_cat(date_label, " / ");
date_label = xs_str_cat(date_label, sd);
date_title = xs_str_cat(date_title, " / ");
date_title = xs_str_cat(date_title, udate);
}
xs *s1 = xs_fmt(
"\n\n",
date_title, date_label);
s = xs_str_cat(s, s1);
}
{
char *username, *id;
xs *s1;
if (xs_is_null(username = xs_dict_get(actor, "preferredUsername")) || *username == '\0') {
/* This should never be reached */
username = "anonymous";
}
if (xs_is_null(id = xs_dict_get(actor, "id")) || *id == '\0') {
/* This should never be reached */
id = "https://social.example.org/anonymous";
}
/* "LIKE AN ANIMAL" */
xs *domain = xs_split(id, "/");
xs *user = xs_fmt("@%s@%s", username, xs_list_get(domain, 2));
s1 = xs_fmt(
"
%s",
xs_dict_get(actor, "id"), user);
s = xs_str_cat(s, s1);
}
return xs_str_cat(os, s);
}
xs_str *html_msg_icon(snac *snac, xs_str *os, const xs_dict *msg)
{
char *actor_id;
xs *actor = NULL;
if ((actor_id = xs_dict_get(msg, "attributedTo")) == NULL)
actor_id = xs_dict_get(msg, "actor");
if (actor_id && valid_status(actor_get(snac, actor_id, &actor))) {
char *date = NULL;
char *udate = NULL;
char *url = NULL;
int priv = 0;
if (strcmp(xs_dict_get(msg, "type"), "Note") == 0)
url = xs_dict_get(msg, "id");
priv = !is_msg_public(snac, msg);
date = xs_dict_get(msg, "published");
udate = xs_dict_get(msg, "updated");
os = html_actor_icon(os, actor, date, udate, url, priv);
}
return os;
}
d_char *html_user_header(snac *snac, d_char *s, int local)
/* creates the HTML header */
{
char *p, *v;
s = xs_str_cat(s, "\n\n
%s
\n" "@%s@%s
\n"; xs *s1 = xs_fmt(_tmpl, xs_dict_get(snac->config, "name"), xs_dict_get(snac->config, "uid"), xs_dict_get(srv_config, "host") ); s = xs_str_cat(s, s1); if (local) { xs *bio = not_really_markdown(xs_dict_get(snac->config, "bio"), NULL); xs *s1 = xs_fmt("\n" "
\n" "
\n"
"%s
\n"
"
%s
\n"
"
" "\n", L("Edit..."), md5, snac->actor, md5, prev_src, id, L("Sensitive content"), L("Only for mentioned people"), L("Attach..."), L("File"), L("File description"), md5, L("Post") ); s = xs_str_cat(s, s1); } { /* the post textarea */ xs *ct = build_mentions(snac, msg); xs *s1 = xs_fmt( "
%s
\n"
"
" "\n", L("Reply..."), md5, snac->actor, md5, ct, id, L("Sensitive content"), L("Only for mentioned people"), L("Attach..."), L("File"), L("File description"), md5, L("Post") ); s = xs_str_cat(s, s1); } s = xs_str_cat(s, "
%s
"); if (!xs_startswith(c, "
")) { xs *s1 = c; c = xs_fmt("
%s
", s1); } /* replace the :shortnames: */ if (!xs_is_null(p = xs_dict_get(msg, "tag"))) { /* iterate the tags */ while (xs_list_iter(&p, &v)) { char *t = xs_dict_get(v, "type"); if (t && strcmp(t, "Emoji") == 0) { char *n = xs_dict_get(v, "name"); char *i = xs_dict_get(v, "icon"); if (n && i) { char *u = xs_dict_get(i, "url"); xs *img = xs_fmt("", u); c = xs_replace_i(c, n, img); } } } } if (strcmp(type, "Question") == 0) { /** question content **/ xs_list *oo = xs_dict_get(msg, "oneOf"); xs_list *ao = xs_dict_get(msg, "anyOf"); xs_list *p; xs_dict *v; int closed = 0; if (xs_dict_get(msg, "closed")) closed = 2; else if (xs_startswith(id, snac->actor)) closed = 1; /* we questioned; closed for us */ else { /* not yet closed? check if we already voted for this */ xs *children = object_children(id); p = children; while (!closed && xs_list_iter(&p, &v)) { xs *msg = NULL; if (valid_status(object_get_by_md5(v, &msg))) { const char *atto = xs_dict_get(msg, "attributedTo"); if (atto && strcmp(atto, snac->actor) == 0) closed = 1; /* we already voted; closed for us */ } } } /* get the appropriate list of options */ p = oo != NULL ? oo : ao; if (closed) { /* closed poll */ c = xs_str_cat(c, "%s: | %d |
%s
\n", L("Closed")); c = xs_str_cat(c, s1); } } else { /* poll still active */ xs *s1 = xs_fmt("\n\n", L("Vote")); s1 = xs_str_cat(s1, s2); c = xs_str_cat(c, s1); } } s = xs_str_cat(s, c); } s = xs_str_cat(s, "\n"); /* add the attachments */ char *attach; if ((attach = xs_dict_get(msg, "attachment")) != NULL) { /** **/ char *v; while (xs_list_iter(&attach, &v)) { char *t = xs_dict_get(v, "mediaType"); if (xs_is_null(t)) continue; if (xs_startswith(t, "image/")) { char *url = xs_dict_get(v, "url"); char *name = xs_dict_get(v, "name"); if (url != NULL) { xs *s1 = xs_fmt("\n", url, xs_is_null(name) ? "" : name); s = xs_str_cat(s, s1); } } else if (xs_startswith(t, "video/")) { char *url = xs_dict_get(v, "url"); if (url != NULL) { xs *s1 = xs_fmt("\n", url); s = xs_str_cat(s, s1); } } } } if (sensitive) s = xs_str_cat(s, "\n"); s = xs_str_cat(s, "
\n"); if (level < 4) ss = xs_str_cat(ss, "
%s
" "%s" "
\n", snac->actor, local ? "" : "/admin", skip + show, show, L("Load more...")); s = xs_str_cat(s, s1); } s = html_user_footer(s); s = xs_str_cat(s, "\n\n"); return s; } d_char *html_people_list(snac *snac, d_char *os, d_char *list, const char *header, const char *t) { xs *s = xs_str_new(NULL); xs *h = xs_fmt("")) s = xs_str_cat(s, sc); else { xs *s1 = xs_fmt("
%s
", sc); s = xs_str_cat(s, s1); } s = xs_str_cat(s, "%s
\n"
"
\n", L("Direct Message..."), md5, t, snac->actor, md5, actor_id, L("Post") ); s = xs_str_cat(s, s2); s = xs_str_cat(s, "