summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--RELEASE_NOTES.md4
-rw-r--r--TODO.md14
-rw-r--r--activitypub.c19
-rw-r--r--data.c6
-rw-r--r--html.c83
-rw-r--r--httpd.c6
-rw-r--r--snac.h7
7 files changed, 95 insertions, 44 deletions
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index b8ee714..72e1d77 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -2,12 +2,16 @@
## 2.48
+New instance page, that show all posts by users in the same instance (like the public instance timeline, but interactive). This will help building communities.
+
Follower-only replies to unknown users are not shown in timelines.
Added verification of metadata links: if the linked page contains a link back to the snac user with a rel="me" attribute, it's marked as verified.
Added a profile-page relation to links in webfinger responses (contributed by khm).
+Fixed some regressions and a crash.
+
## 2.47
Added pagination to the notification page.
diff --git a/TODO.md b/TODO.md
index 51faf7c..3193974 100644
--- a/TODO.md
+++ b/TODO.md
@@ -14,14 +14,16 @@ Important: deleting a follower should do more that just delete the object, see h
## Wishlist
-Add support for rel="me" links, see https://codeberg.org/grunfink/snac2/issues/124 and https://streetpass.social
-
-Hide followers-only replies to unknown accounts, see https://codeberg.org/grunfink/snac2/issues/123
-
Integrate "Ability to federate with hidden networks" see https://codeberg.org/grunfink/snac2/issues/93
Integrate "Added handling for International Domain Names" PR https://codeberg.org/grunfink/snac2/pulls/104
+Consider implementing the rejection of activities from recently-created accounts to mitigate spam, see https://akkoma.dev/AkkomaGang/akkoma/src/branch/develop/lib/pleroma/web/activity_pub/mrf/reject_newly_created_account_note_policy.ex
+
+Consider discarding posts by content using string or regex to mitigate spam.
+
+Consider adding milter-like support to reject posts to mitigate spam.
+
Do something about Akkoma and Misskey's quoted replies (they use the `quoteUrl` field instead of `inReplyTo`).
Add more CSS classes according to https://comam.es/snac/grunfink/p/1705598619.090050
@@ -301,3 +303,7 @@ Add a flag to make accounts private, i.e., they don't expose any content from th
Fix duplicate mentions, see https://codeberg.org/grunfink/snac2/issues/115 (2024-02-14T09:51:01+0100).
Change HTML metadata information to the post info instead of user info, see https://codeberg.org/grunfink/snac2/issues/116 (2024-02-14T09:51:22+0100).
+
+Add support for rel="me" links, see https://codeberg.org/grunfink/snac2/issues/124 and https://streetpass.social (2024-02-22T12:40:58+0100).
+
+Hide followers-only replies to unknown accounts, see https://codeberg.org/grunfink/snac2/issues/123 (2024-02-22T12:40:58+0100).
diff --git a/activitypub.c b/activitypub.c
index e389915..d8f748e 100644
--- a/activitypub.c
+++ b/activitypub.c
@@ -1955,9 +1955,12 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
if (xs_type(object) == XSTYPE_DICT)
object = xs_dict_get(object, "id");
- timeline_admire(snac, object, actor, 1);
- snac_log(snac, xs_fmt("new 'Like' %s %s", actor, object));
- do_notify = 1;
+ if (timeline_admire(snac, object, actor, 1) == 201) {
+ snac_log(snac, xs_fmt("new 'Like' %s %s", actor, object));
+ do_notify = 1;
+ }
+ else
+ snac_log(snac, xs_fmt("repeated 'Like' from %s to %s", actor, object));
}
else
if (strcmp(type, "Announce") == 0) { /** **/
@@ -1983,9 +1986,13 @@ int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
xs *who_o = NULL;
if (valid_status(actor_request(snac, who, &who_o))) {
- timeline_admire(snac, object, actor, 0);
- snac_log(snac, xs_fmt("new 'Announce' %s %s", actor, object));
- do_notify = 1;
+ if (timeline_admire(snac, object, actor, 0) == 201) {
+ snac_log(snac, xs_fmt("new 'Announce' %s %s", actor, object));
+ do_notify = 1;
+ }
+ else
+ snac_log(snac, xs_fmt("repeated 'Announce' from %s to %s",
+ actor, object));
}
else
snac_debug(snac, 1, xs_fmt("dropped 'Announce' on actor request error %s", who));
diff --git a/data.c b/data.c
index 4c2a4a8..1312790 100644
--- a/data.c
+++ b/data.c
@@ -1107,7 +1107,7 @@ int timeline_add(snac *snac, const char *id, const xs_dict *o_msg)
}
-void timeline_admire(snac *snac, const char *id, const char *admirer, int like)
+int timeline_admire(snac *snac, const char *id, const char *admirer, int like)
/* updates a timeline entry with a new admiration */
{
/* if we are admiring this, add to both timelines */
@@ -1116,10 +1116,12 @@ void timeline_admire(snac *snac, const char *id, const char *admirer, int like)
object_user_cache_add(snac, id, "private");
}
- object_admire(id, admirer, like);
+ int ret = object_admire(id, admirer, like);
snac_debug(snac, 1, xs_fmt("timeline_admire (%s) %s %s",
like ? "Like" : "Announce", id, admirer));
+
+ return ret;
}
diff --git a/html.c b/html.c
index 5407e22..e1df7e9 100644
--- a/html.c
+++ b/html.c
@@ -646,7 +646,7 @@ xs_html *html_user_head(snac *user, char *desc)
}
-static xs_html *html_user_body(snac *user, int local)
+static xs_html *html_user_body(snac *user, int read_only)
{
xs_html *body = xs_html_tag("body", NULL);
@@ -667,7 +667,7 @@ static xs_html *html_user_body(snac *user, int local)
xs_html_attr("class", "snac-avatar"),
xs_html_attr("alt", "")));
- if (local) {
+ if (read_only) {
xs *rss_url = xs_fmt("%s.rss", user->actor);
xs *admin_url = xs_fmt("%s/admin", user->actor);
@@ -698,6 +698,8 @@ static xs_html *html_user_body(snac *user, int local)
xs *admin_url = xs_fmt("%s/admin", user->actor);
xs *notify_url = xs_fmt("%s/notifications", user->actor);
xs *people_url = xs_fmt("%s/people", user->actor);
+ xs *instance_url = xs_fmt("%s/instance", user->actor);
+
xs_html_add(top_nav,
xs_html_tag("a",
xs_html_attr("href", user->actor),
@@ -714,7 +716,11 @@ static xs_html *html_user_body(snac *user, int local)
xs_html_text(" - "),
xs_html_tag("a",
xs_html_attr("href", people_url),
- xs_html_text(L("people"))));
+ xs_html_text(L("people"))),
+ xs_html_text(" - "),
+ xs_html_tag("a",
+ xs_html_attr("href", instance_url),
+ xs_html_text(L("instance"))));
}
xs_html_add(body,
@@ -724,7 +730,7 @@ static xs_html *html_user_body(snac *user, int local)
xs_html *top_user = xs_html_tag("div",
xs_html_attr("class", "h-card snac-top-user"));
- if (local) {
+ if (read_only) {
char *header = xs_dict_get(user->config, "header");
if (header && *header) {
xs_html_add(top_user,
@@ -749,7 +755,7 @@ static xs_html *html_user_body(snac *user, int local)
xs_html_attr("class", "snac-top-user-id"),
xs_html_text(handle)));
- if (local) {
+ if (read_only) {
xs *es1 = encode_html(xs_dict_get(user->config, "bio"));
xs *bio1 = not_really_markdown(es1, NULL);
xs *tags = xs_list_new();
@@ -1306,7 +1312,7 @@ xs_html *html_entry_controls(snac *snac, char *actor, const xs_dict *msg, const
}
-xs_html *html_entry(snac *user, xs_dict *msg, int local,
+xs_html *html_entry(snac *user, xs_dict *msg, int read_only,
int level, char *md5, int hide_children)
{
char *id = xs_dict_get(msg, "id");
@@ -1315,7 +1321,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int local,
char *v;
/* do not show non-public messages in the public timeline */
- if ((local || !user) && !is_msg_public(msg))
+ if ((read_only || !user) && !is_msg_public(msg))
return NULL;
/* hidden? do nothing more for this conversation */
@@ -1334,7 +1340,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int local,
xs_html_tag("div",
xs_html_attr("class", "snac-origin"),
xs_html_text(L("follows you"))),
- html_msg_icon(local ? NULL : user, xs_dict_get(msg, "actor"), msg)));
+ html_msg_icon(read_only ? NULL : user, xs_dict_get(msg, "actor"), msg)));
}
else
if (!xs_match(type, "Note|Question|Page|Article|Video")) {
@@ -1446,7 +1452,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int local,
if (!xs_is_null(name)) {
xs *href = NULL;
- if (!local && user != NULL)
+ if (!read_only && user != NULL)
href = xs_fmt("%s/people#%s", user->actor, p);
else
href = xs_dup(xs_dict_get(actor_r, "id"));
@@ -1482,7 +1488,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int local,
}
xs_html_add(post_header,
- html_msg_icon(local ? NULL : user, actor, msg));
+ html_msg_icon(read_only ? NULL : user, actor, msg));
/** post content **/
@@ -1510,7 +1516,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int local,
/* only show it when not in the public timeline and the config setting is "open" */
char *cw = xs_dict_get(user->config, "cw");
- if (xs_is_null(cw) || local)
+ if (xs_is_null(cw) || read_only)
cw = "";
snac_content = xs_html_tag("details",
@@ -1574,7 +1580,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int local,
xs_html *poll = xs_html_tag("div", NULL);
- if (local)
+ if (read_only)
closed = 1; /* non-identified page; show as closed */
else
if (xs_dict_get(msg, "closed"))
@@ -1795,7 +1801,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int local,
/** controls **/
- if (!local && user) {
+ if (!read_only && user) {
xs_html_add(entry,
html_entry_controls(user, actor, msg, md5));
}
@@ -1839,7 +1845,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int local,
object_get_by_md5(cmd5, &chd);
if (chd != NULL && xs_is_null(xs_dict_get(chd, "name"))) {
- xs_html *che = html_entry(user, chd, local, level + 1, cmd5, hide_children);
+ xs_html *che = html_entry(user, chd, read_only, level + 1, cmd5, hide_children);
if (che != NULL) {
if (left > 3)
@@ -1879,8 +1885,9 @@ xs_html *html_footer(void)
}
-xs_str *html_timeline(snac *user, const xs_list *list, int local,
- int skip, int show, int show_more, char *tag)
+xs_str *html_timeline(snac *user, const xs_list *list, int read_only,
+ int skip, int show, int show_more,
+ char *tag, char *page, int utl)
/* returns the HTML for the timeline */
{
xs_list *p = (xs_list *)list;
@@ -1903,7 +1910,7 @@ xs_str *html_timeline(snac *user, const xs_list *list, int local,
if (user) {
head = html_user_head(user, desc);
- body = html_user_body(user, local);
+ body = html_user_body(user, read_only);
}
else {
head = html_instance_head();
@@ -1914,7 +1921,7 @@ xs_str *html_timeline(snac *user, const xs_list *list, int local,
head,
body);
- if (user && !local)
+ if (user && !read_only)
xs_html_add(body,
html_top_controls(user));
@@ -1932,7 +1939,7 @@ xs_str *html_timeline(snac *user, const xs_list *list, int local,
xs *msg = NULL;
int status;
- if (user && !is_pinned_by_md5(user, v))
+ if (utl && user && !is_pinned_by_md5(user, v))
status = timeline_get_by_md5(user, v, &msg);
else
status = object_get_by_md5(v, &msg);
@@ -1954,14 +1961,15 @@ xs_str *html_timeline(snac *user, const xs_list *list, int local,
}
}
- xs_html *entry = html_entry(user, msg, local, 0, v, user ? 0 : 1);
+ xs_html *entry = html_entry(user, msg, read_only, 0, v, user ? 0 : 1);
if (entry != NULL)
xs_html_add(posts,
entry);
}
- if (list && user && local) {
+ if (list && user && read_only) {
+ /** history **/
if (xs_type(xs_dict_get(srv_config, "disable_history")) != XSTYPE_TRUE) {
xs_html *ul = xs_html_tag("ul", NULL);
@@ -2003,12 +2011,15 @@ xs_str *html_timeline(snac *user, const xs_list *list, int local,
xs *m = NULL;
xs *ss = xs_fmt("skip=%d&show=%d", skip + show, show);
+ xs *url = page == NULL || user == NULL ?
+ xs_dup(srv_baseurl) : xs_fmt("%s%s", user->actor, page);
+
if (tag) {
- t = xs_fmt("%s?t=%s", srv_baseurl, tag);
+ t = xs_fmt("%s?t=%s", url, tag);
m = xs_fmt("%s&%s", t, ss);
}
else {
- t = xs_fmt("%s%s", user ? user->actor : srv_baseurl, local ? "" : "/admin");
+ t = xs_dup(url);
m = xs_fmt("%s?%s", t, ss);
}
@@ -2401,7 +2412,8 @@ int html_get_handler(const xs_dict *req, const char *q_path,
xs *h = xs_str_localtime(0, "%Y-%m.html");
if (xs_type(xs_dict_get(snac.config, "private")) == XSTYPE_TRUE) {
- *body = html_timeline(&snac, NULL, 1, 0, 0, 0, NULL);
+ /** empty public timeline for private users **/
+ *body = html_timeline(&snac, NULL, 1, 0, 0, 0, NULL, "", 1);
*b_size = strlen(*body);
status = 200;
}
@@ -2419,7 +2431,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
xs *pins = pinned_list(&snac);
pins = xs_list_cat(pins, list);
- *body = html_timeline(&snac, pins, 1, skip, show, xs_list_len(next), NULL);
+ *body = html_timeline(&snac, pins, 1, skip, show, xs_list_len(next), NULL, "", 1);
*b_size = strlen(*body);
status = 200;
@@ -2456,7 +2468,8 @@ int html_get_handler(const xs_dict *req, const char *q_path,
xs *pins = pinned_list(&snac);
pins = xs_list_cat(pins, list);
- *body = html_timeline(&snac, pins, 0, skip, show, xs_list_len(next), NULL);
+ *body = html_timeline(&snac, pins, 0, skip, show,
+ xs_list_len(next), NULL, "/admin", 1);
*b_size = strlen(*body);
status = 200;
@@ -2491,6 +2504,22 @@ int html_get_handler(const xs_dict *req, const char *q_path,
}
}
else
+ if (strcmp(p_path, "instance") == 0) { /** instance timeline **/
+ if (!login(&snac, req)) {
+ *body = xs_dup(uid);
+ status = 401;
+ }
+ else {
+ xs *list = timeline_instance_list(skip, show);
+ xs *next = timeline_instance_list(skip + show, 1);
+
+ *body = html_timeline(&snac, list, 0, skip, show,
+ xs_list_len(next), NULL, "/instance", 0);
+ *b_size = strlen(*body);
+ status = 200;
+ }
+ }
+ else
if (xs_startswith(p_path, "p/")) { /** a timeline with just one entry **/
if (xs_type(xs_dict_get(snac.config, "private")) == XSTYPE_TRUE)
return 403;
@@ -2504,7 +2533,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
list = xs_list_append(list, md5);
- *body = html_timeline(&snac, list, 1, 0, 0, 0, NULL);
+ *body = html_timeline(&snac, list, 1, 0, 0, 0, NULL, "", 1);
*b_size = strlen(*body);
status = 200;
}
diff --git a/httpd.c b/httpd.c
index fde40cb..319a7b3 100644
--- a/httpd.c
+++ b/httpd.c
@@ -177,6 +177,7 @@ int server_get_handler(xs_dict *req, const char *q_path,
char *t = NULL;
if (xs_type(q_vars) == XSTYPE_DICT && (t = xs_dict_get(q_vars, "t"))) {
+ /** search by tag **/
int skip = 0;
int show = xs_number_get(xs_dict_get(srv_config, "max_timeline_entries"));
char *v;
@@ -194,12 +195,13 @@ int server_get_handler(xs_dict *req, const char *q_path,
more = 1;
}
- *body = html_timeline(NULL, tl, 0, skip, show, more, t);
+ *body = html_timeline(NULL, tl, 0, skip, show, more, t, NULL, 0);
}
else
if (xs_type(xs_dict_get(srv_config, "show_instance_timeline")) == XSTYPE_TRUE) {
+ /** instance timeline **/
xs *tl = timeline_instance_list(0, 30);
- *body = html_timeline(NULL, tl, 0, 0, 0, 0, NULL);
+ *body = html_timeline(NULL, tl, 0, 0, 0, 0, NULL, NULL, 0);
}
else
*body = greeting_html();
diff --git a/snac.h b/snac.h
index 8de3a83..85f8b44 100644
--- a/snac.h
+++ b/snac.h
@@ -133,7 +133,7 @@ int timeline_del(snac *snac, char *id);
xs_list *timeline_simple_list(snac *snac, const char *idx_name, int skip, int show);
xs_list *timeline_list(snac *snac, const char *idx_name, int skip, int show);
int timeline_add(snac *snac, const char *id, const xs_dict *o_msg);
-void timeline_admire(snac *snac, const char *id, const char *admirer, int like);
+int timeline_admire(snac *snac, const char *id, const char *admirer, int like);
xs_list *timeline_top_level(snac *snac, xs_list *list);
xs_list *local_list(snac *snac, int max);
@@ -296,8 +296,9 @@ xs_str *not_really_markdown(const char *content, xs_list **attach);
xs_str *sanitize(const char *content);
xs_str *encode_html(const char *str);
-xs_str *html_timeline(snac *user, const xs_list *list, int local,
- int skip, int show, int show_more, char *tag);
+xs_str *html_timeline(snac *user, const xs_list *list, int read_only,
+ int skip, int show, int show_more,
+ char *tag, char *page, int utl);
int html_get_handler(const xs_dict *req, const char *q_path,
char **body, int *b_size, char **ctype, xs_str **etag);