diff options
| author | 2025-10-18 21:33:31 +0200 | |
|---|---|---|
| committer | 2025-10-18 21:33:31 +0200 | |
| commit | 2d433afc27afe46c9641cd0b0bd076566cfb67e2 (patch) | |
| tree | 826378411a16df79b16b5b351b7c56d20db3219f /activitypub.c | |
| parent | FEDERATION.md: also document outgoing Webmention. (diff) | |
| download | snac2-2d433afc27afe46c9641cd0b0bd076566cfb67e2.tar.gz snac2-2d433afc27afe46c9641cd0b0bd076566cfb67e2.tar.xz snac2-2d433afc27afe46c9641cd0b0bd076566cfb67e2.zip | |
Enhances Mastodon API compatibility by adding support for displaying remote users' follower/following/post counts and their posts when viewing profiles in Mastodon-compatible apps (Fedilab, Tusky, etc.).
Fixes for Moshidon and improvements for better compatibility with HAProxy
Diffstat (limited to 'activitypub.c')
| -rw-r--r-- | activitypub.c | 78 |
1 files changed, 77 insertions, 1 deletions
diff --git a/activitypub.c b/activitypub.c index f19c6fc..117bbc9 100644 --- a/activitypub.c +++ b/activitypub.c | |||
| @@ -118,6 +118,75 @@ int activitypub_request(snac *user, const char *url, xs_dict **data) | |||
| 118 | } | 118 | } |
| 119 | 119 | ||
| 120 | 120 | ||
| 121 | static xs_dict *actor_get_collections(snac *user, xs_dict *actor, int throttle) | ||
| 122 | /* fetches follower/following/statuses counts from an actor's collections and adds them to the actor object */ | ||
| 123 | { | ||
| 124 | /* only fetch if counts are not already present in the actor object */ | ||
| 125 | const xs_number *existing_followers = xs_dict_get(actor, "followers_count"); | ||
| 126 | const xs_number *existing_following = xs_dict_get(actor, "following_count"); | ||
| 127 | const xs_number *existing_statuses = xs_dict_get(actor, "statuses_count"); | ||
| 128 | |||
| 129 | /* skip if we already have all counts */ | ||
| 130 | if (xs_type(existing_followers) == XSTYPE_NUMBER && | ||
| 131 | xs_type(existing_following) == XSTYPE_NUMBER && | ||
| 132 | xs_type(existing_statuses) == XSTYPE_NUMBER) { | ||
| 133 | return actor; | ||
| 134 | } | ||
| 135 | |||
| 136 | /* CRITICAL: duplicate URLs BEFORE any xs_dict_set calls, as xs_dict_set can reallocate the dict */ | ||
| 137 | xs *followers_url = xs_dup(xs_dict_get(actor, "followers")); | ||
| 138 | xs *following_url = xs_dup(xs_dict_get(actor, "following")); | ||
| 139 | xs *outbox_url = xs_dup(xs_dict_get(actor, "outbox")); | ||
| 140 | |||
| 141 | /* only fetch followers count if not already present */ | ||
| 142 | if (xs_type(existing_followers) != XSTYPE_NUMBER && !xs_is_null(followers_url)) { | ||
| 143 | xs *followers_coll = NULL; | ||
| 144 | if (activitypub_request(user, followers_url, &followers_coll) == 200) { | ||
| 145 | const xs_number *total = xs_dict_get(followers_coll, "totalItems"); | ||
| 146 | if (xs_type(total) == XSTYPE_NUMBER) { | ||
| 147 | xs *total_copy = xs_dup(total); | ||
| 148 | actor = xs_dict_set(actor, "followers_count", total_copy); | ||
| 149 | } | ||
| 150 | } | ||
| 151 | /* throttle to prevent resource exhaustion on low-power devices */ | ||
| 152 | if (throttle) | ||
| 153 | usleep(100000); /* 100ms delay between requests */ | ||
| 154 | } | ||
| 155 | |||
| 156 | /* only fetch following count if not already present */ | ||
| 157 | if (xs_type(existing_following) != XSTYPE_NUMBER && !xs_is_null(following_url)) { | ||
| 158 | xs *following_coll = NULL; | ||
| 159 | if (activitypub_request(user, following_url, &following_coll) == 200) { | ||
| 160 | const xs_number *total = xs_dict_get(following_coll, "totalItems"); | ||
| 161 | if (xs_type(total) == XSTYPE_NUMBER) { | ||
| 162 | xs *total_copy = xs_dup(total); | ||
| 163 | actor = xs_dict_set(actor, "following_count", total_copy); | ||
| 164 | } | ||
| 165 | } | ||
| 166 | /* throttle to prevent resource exhaustion on low-power devices */ | ||
| 167 | if (throttle) | ||
| 168 | usleep(100000); /* 100ms delay between requests */ | ||
| 169 | } | ||
| 170 | |||
| 171 | /* only fetch statuses count if not already present */ | ||
| 172 | if (xs_type(existing_statuses) != XSTYPE_NUMBER && !xs_is_null(outbox_url)) { | ||
| 173 | xs *outbox_coll = NULL; | ||
| 174 | if (activitypub_request(user, outbox_url, &outbox_coll) == 200) { | ||
| 175 | const xs_number *total = xs_dict_get(outbox_coll, "totalItems"); | ||
| 176 | if (xs_type(total) == XSTYPE_NUMBER) { | ||
| 177 | xs *total_copy = xs_dup(total); | ||
| 178 | actor = xs_dict_set(actor, "statuses_count", total_copy); | ||
| 179 | } | ||
| 180 | } | ||
| 181 | /* throttle to prevent resource exhaustion on low-power devices */ | ||
| 182 | if (throttle) | ||
| 183 | usleep(100000); /* 100ms delay between requests */ | ||
| 184 | } | ||
| 185 | |||
| 186 | return actor; | ||
| 187 | } | ||
| 188 | |||
| 189 | |||
| 121 | int actor_request(snac *user, const char *actor, xs_dict **data) | 190 | int actor_request(snac *user, const char *actor, xs_dict **data) |
| 122 | /* request an actor */ | 191 | /* request an actor */ |
| 123 | { | 192 | { |
| @@ -135,6 +204,9 @@ int actor_request(snac *user, const char *actor, xs_dict **data) | |||
| 135 | status = activitypub_request(user, actor, &payload); | 204 | status = activitypub_request(user, actor, &payload); |
| 136 | 205 | ||
| 137 | if (valid_status(status)) { | 206 | if (valid_status(status)) { |
| 207 | /* fetch collection counts when initially fetching an actor (no throttle) */ | ||
| 208 | payload = actor_get_collections(user, payload, 0); | ||
| 209 | |||
| 138 | /* renew data */ | 210 | /* renew data */ |
| 139 | status = actor_add(actor, payload); | 211 | status = actor_add(actor, payload); |
| 140 | 212 | ||
| @@ -3118,8 +3190,12 @@ void process_user_queue_item(snac *user, xs_dict *q_item) | |||
| 3118 | xs *actor_o = NULL; | 3190 | xs *actor_o = NULL; |
| 3119 | int status; | 3191 | int status; |
| 3120 | 3192 | ||
| 3121 | if (valid_status((status = activitypub_request(user, actor, &actor_o)))) | 3193 | if (valid_status((status = activitypub_request(user, actor, &actor_o)))) { |
| 3194 | /* refresh collection counts with throttling to prevent resource exhaustion */ | ||
| 3195 | actor_o = actor_get_collections(user, actor_o, 1); | ||
| 3196 | |||
| 3122 | actor_add(actor, actor_o); | 3197 | actor_add(actor, actor_o); |
| 3198 | } | ||
| 3123 | else { | 3199 | else { |
| 3124 | if (status == HTTP_STATUS_GONE) { | 3200 | if (status == HTTP_STATUS_GONE) { |
| 3125 | actor_failure(actor, 1); | 3201 | actor_failure(actor, 1); |