pub(crate) async fn receive_create_comment(
create: Create,
context: &LemmyContext,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
- let user = get_actor_as_user(&create, context).await?;
+ let user = get_actor_as_user(&create, context, request_counter).await?;
let note = Note::from_any_base(create.object().to_owned().one().context(location_info!())?)?
.context(location_info!())?;
- let comment = CommentForm::from_apub(¬e, context, Some(user.actor_id()?)).await?;
+ let comment =
+ CommentForm::from_apub(¬e, context, Some(user.actor_id()?), request_counter).await?;
let inserted_comment =
blocking(context.pool(), move |conn| Comment::upsert(conn, &comment)).await??;
websocket_id: None,
});
- announce_if_community_is_local(create, &user, context).await?;
+ announce_if_community_is_local(create, &user, context, request_counter).await?;
Ok(HttpResponse::Ok().finish())
}
pub(crate) async fn receive_update_comment(
update: Update,
context: &LemmyContext,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
let note = Note::from_any_base(update.object().to_owned().one().context(location_info!())?)?
.context(location_info!())?;
- let user = get_actor_as_user(&update, context).await?;
+ let user = get_actor_as_user(&update, context, request_counter).await?;
- let comment = CommentForm::from_apub(¬e, context, Some(user.actor_id()?)).await?;
+ let comment =
+ CommentForm::from_apub(¬e, context, Some(user.actor_id()?), request_counter).await?;
- let original_comment_id = get_or_fetch_and_insert_comment(&comment.get_ap_id()?, context)
- .await?
- .id;
+ let original_comment_id =
+ get_or_fetch_and_insert_comment(&comment.get_ap_id()?, context, request_counter)
+ .await?
+ .id;
let updated_comment = blocking(context.pool(), move |conn| {
Comment::update(conn, original_comment_id, &comment)
websocket_id: None,
});
- announce_if_community_is_local(update, &user, context).await?;
+ announce_if_community_is_local(update, &user, context, request_counter).await?;
Ok(HttpResponse::Ok().finish())
}
pub(crate) async fn receive_like_comment(
like: Like,
context: &LemmyContext,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
let note = Note::from_any_base(like.object().to_owned().one().context(location_info!())?)?
.context(location_info!())?;
- let user = get_actor_as_user(&like, context).await?;
+ let user = get_actor_as_user(&like, context, request_counter).await?;
- let comment = CommentForm::from_apub(¬e, context, None).await?;
+ let comment = CommentForm::from_apub(¬e, context, None, request_counter).await?;
- let comment_id = get_or_fetch_and_insert_comment(&comment.get_ap_id()?, context)
+ let comment_id = get_or_fetch_and_insert_comment(&comment.get_ap_id()?, context, request_counter)
.await?
.id;
websocket_id: None,
});
- announce_if_community_is_local(like, &user, context).await?;
+ announce_if_community_is_local(like, &user, context, request_counter).await?;
Ok(HttpResponse::Ok().finish())
}
pub(crate) async fn receive_dislike_comment(
dislike: Dislike,
context: &LemmyContext,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
let note = Note::from_any_base(
dislike
.context(location_info!())?,
)?
.context(location_info!())?;
- let user = get_actor_as_user(&dislike, context).await?;
+ let user = get_actor_as_user(&dislike, context, request_counter).await?;
- let comment = CommentForm::from_apub(¬e, context, None).await?;
+ let comment = CommentForm::from_apub(¬e, context, None, request_counter).await?;
- let comment_id = get_or_fetch_and_insert_comment(&comment.get_ap_id()?, context)
+ let comment_id = get_or_fetch_and_insert_comment(&comment.get_ap_id()?, context, request_counter)
.await?
.id;
websocket_id: None,
});
- announce_if_community_is_local(dislike, &user, context).await?;
+ announce_if_community_is_local(dislike, &user, context, request_counter).await?;
Ok(HttpResponse::Ok().finish())
}
context: &LemmyContext,
delete: Delete,
comment: Comment,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
let deleted_comment = blocking(context.pool(), move |conn| {
Comment::update_deleted(conn, comment.id, true)
websocket_id: None,
});
- let user = get_actor_as_user(&delete, context).await?;
- announce_if_community_is_local(delete, &user, context).await?;
+ let user = get_actor_as_user(&delete, context, request_counter).await?;
+ announce_if_community_is_local(delete, &user, context, request_counter).await?;
Ok(HttpResponse::Ok().finish())
}
undo: Undo,
like: &Like,
context: &LemmyContext,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
- let user = get_actor_as_user(like, context).await?;
+ let user = get_actor_as_user(like, context, request_counter).await?;
let note = Note::from_any_base(like.object().to_owned().one().context(location_info!())?)?
.context(location_info!())?;
- let comment = CommentForm::from_apub(¬e, context, None).await?;
+ let comment = CommentForm::from_apub(¬e, context, None, request_counter).await?;
- let comment_id = get_or_fetch_and_insert_comment(&comment.get_ap_id()?, context)
+ let comment_id = get_or_fetch_and_insert_comment(&comment.get_ap_id()?, context, request_counter)
.await?
.id;
websocket_id: None,
});
- announce_if_community_is_local(undo, &user, context).await?;
+ announce_if_community_is_local(undo, &user, context, request_counter).await?;
Ok(HttpResponse::Ok().finish())
}
undo: Undo,
dislike: &Dislike,
context: &LemmyContext,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
- let user = get_actor_as_user(dislike, context).await?;
+ let user = get_actor_as_user(dislike, context, request_counter).await?;
let note = Note::from_any_base(
dislike
.object()
)?
.context(location_info!())?;
- let comment = CommentForm::from_apub(¬e, context, None).await?;
+ let comment = CommentForm::from_apub(¬e, context, None, request_counter).await?;
- let comment_id = get_or_fetch_and_insert_comment(&comment.get_ap_id()?, context)
+ let comment_id = get_or_fetch_and_insert_comment(&comment.get_ap_id()?, context, request_counter)
.await?
.id;
websocket_id: None,
});
- announce_if_community_is_local(undo, &user, context).await?;
+ announce_if_community_is_local(undo, &user, context, request_counter).await?;
Ok(HttpResponse::Ok().finish())
}
context: &LemmyContext,
undo: Undo,
comment: Comment,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
let deleted_comment = blocking(context.pool(), move |conn| {
Comment::update_deleted(conn, comment.id, false)
websocket_id: None,
});
- let user = get_actor_as_user(&undo, context).await?;
- announce_if_community_is_local(undo, &user, context).await?;
+ let user = get_actor_as_user(&undo, context, request_counter).await?;
+ announce_if_community_is_local(undo, &user, context, request_counter).await?;
Ok(HttpResponse::Ok().finish())
}
context: &LemmyContext,
undo: Undo,
comment: Comment,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
let removed_comment = blocking(context.pool(), move |conn| {
Comment::update_removed(conn, comment.id, false)
websocket_id: None,
});
- let mod_ = get_actor_as_user(&undo, context).await?;
- announce_if_community_is_local(undo, &mod_, context).await?;
+ let mod_ = get_actor_as_user(&undo, context, request_counter).await?;
+ announce_if_community_is_local(undo, &mod_, context, request_counter).await?;
Ok(HttpResponse::Ok().finish())
}
context: &LemmyContext,
delete: Delete,
community: Community,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
let deleted_community = blocking(context.pool(), move |conn| {
Community::update_deleted(conn, community.id, true)
websocket_id: None,
});
- let user = get_actor_as_user(&delete, context).await?;
- announce_if_community_is_local(delete, &user, context).await?;
+ let user = get_actor_as_user(&delete, context, request_counter).await?;
+ announce_if_community_is_local(delete, &user, context, request_counter).await?;
Ok(HttpResponse::Ok().finish())
}
websocket_id: None,
});
+ // TODO: this should probably also call announce_if_community_is_local()
Ok(HttpResponse::Ok().finish())
}
context: &LemmyContext,
undo: Undo,
community: Community,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
let deleted_community = blocking(context.pool(), move |conn| {
Community::update_deleted(conn, community.id, false)
websocket_id: None,
});
- let user = get_actor_as_user(&undo, context).await?;
- announce_if_community_is_local(undo, &user, context).await?;
+ let user = get_actor_as_user(&undo, context, request_counter).await?;
+ announce_if_community_is_local(undo, &user, context, request_counter).await?;
Ok(HttpResponse::Ok().finish())
}
context: &LemmyContext,
undo: Undo,
community: Community,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
let removed_community = blocking(context.pool(), move |conn| {
Community::update_removed(conn, community.id, false)
websocket_id: None,
});
- let mod_ = get_actor_as_user(&undo, context).await?;
- announce_if_community_is_local(undo, &mod_, context).await?;
+ let mod_ = get_actor_as_user(&undo, context, request_counter).await?;
+ announce_if_community_is_local(undo, &mod_, context, request_counter).await?;
Ok(HttpResponse::Ok().finish())
}
activity: T,
user: &User_,
context: &LemmyContext,
+ request_counter: &mut i32,
) -> Result<(), LemmyError>
where
T: AsObject<Kind>,
.context(location_info!())?
.as_xsd_any_uri()
.context(location_info!())?;
- let community = get_or_fetch_and_upsert_community(&community_uri, context).await?;
+ // TODO: we could just read from the local db here (and ignore if the community is not found)
+ let community =
+ get_or_fetch_and_upsert_community(&community_uri, context, request_counter).await?;
if community.local {
community
pub(crate) async fn get_actor_as_user<T, A>(
activity: &T,
context: &LemmyContext,
+ request_counter: &mut i32,
) -> Result<User_, LemmyError>
where
T: AsBase<A> + ActorAndObjectRef,
{
let actor = activity.actor()?;
let user_uri = actor.as_single_xsd_any_uri().context(location_info!())?;
- get_or_fetch_and_upsert_user(&user_uri, context).await
+ get_or_fetch_and_upsert_user(&user_uri, context, request_counter).await
}
pub(crate) enum FindResults {
pub(crate) async fn receive_create_post(
create: Create,
context: &LemmyContext,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
- let user = get_actor_as_user(&create, context).await?;
+ let user = get_actor_as_user(&create, context, request_counter).await?;
let page = PageExt::from_any_base(create.object().to_owned().one().context(location_info!())?)?
.context(location_info!())?;
- let post = PostForm::from_apub(&page, context, Some(user.actor_id()?)).await?;
+ let post = PostForm::from_apub(&page, context, Some(user.actor_id()?), request_counter).await?;
// Using an upsert, since likes (which fetch the post), sometimes come in before the create
// resulting in double posts.
websocket_id: None,
});
- announce_if_community_is_local(create, &user, context).await?;
+ announce_if_community_is_local(create, &user, context, request_counter).await?;
Ok(HttpResponse::Ok().finish())
}
pub(crate) async fn receive_update_post(
update: Update,
context: &LemmyContext,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
- let user = get_actor_as_user(&update, context).await?;
+ let user = get_actor_as_user(&update, context, request_counter).await?;
let page = PageExt::from_any_base(update.object().to_owned().one().context(location_info!())?)?
.context(location_info!())?;
- let post = PostForm::from_apub(&page, context, Some(user.actor_id()?)).await?;
+ let post = PostForm::from_apub(&page, context, Some(user.actor_id()?), request_counter).await?;
- let original_post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, context)
+ let original_post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, context, request_counter)
.await?
.id;
websocket_id: None,
});
- announce_if_community_is_local(update, &user, context).await?;
+ announce_if_community_is_local(update, &user, context, request_counter).await?;
Ok(HttpResponse::Ok().finish())
}
pub(crate) async fn receive_like_post(
like: Like,
context: &LemmyContext,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
- let user = get_actor_as_user(&like, context).await?;
+ let user = get_actor_as_user(&like, context, request_counter).await?;
let page = PageExt::from_any_base(like.object().to_owned().one().context(location_info!())?)?
.context(location_info!())?;
- let post = PostForm::from_apub(&page, context, None).await?;
+ let post = PostForm::from_apub(&page, context, None, request_counter).await?;
- let post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, context)
+ let post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, context, request_counter)
.await?
.id;
websocket_id: None,
});
- announce_if_community_is_local(like, &user, context).await?;
+ announce_if_community_is_local(like, &user, context, request_counter).await?;
Ok(HttpResponse::Ok().finish())
}
pub(crate) async fn receive_dislike_post(
dislike: Dislike,
context: &LemmyContext,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
- let user = get_actor_as_user(&dislike, context).await?;
+ let user = get_actor_as_user(&dislike, context, request_counter).await?;
let page = PageExt::from_any_base(
dislike
.object()
)?
.context(location_info!())?;
- let post = PostForm::from_apub(&page, context, None).await?;
+ let post = PostForm::from_apub(&page, context, None, request_counter).await?;
- let post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, context)
+ let post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, context, request_counter)
.await?
.id;
websocket_id: None,
});
- announce_if_community_is_local(dislike, &user, context).await?;
+ announce_if_community_is_local(dislike, &user, context, request_counter).await?;
Ok(HttpResponse::Ok().finish())
}
context: &LemmyContext,
delete: Delete,
post: Post,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
let deleted_post = blocking(context.pool(), move |conn| {
Post::update_deleted(conn, post.id, true)
websocket_id: None,
});
- let user = get_actor_as_user(&delete, context).await?;
- announce_if_community_is_local(delete, &user, context).await?;
+ let user = get_actor_as_user(&delete, context, request_counter).await?;
+ announce_if_community_is_local(delete, &user, context, request_counter).await?;
Ok(HttpResponse::Ok().finish())
}
undo: Undo,
like: &Like,
context: &LemmyContext,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
- let user = get_actor_as_user(like, context).await?;
+ let user = get_actor_as_user(like, context, request_counter).await?;
let page = PageExt::from_any_base(like.object().to_owned().one().context(location_info!())?)?
.context(location_info!())?;
- let post = PostForm::from_apub(&page, context, None).await?;
+ let post = PostForm::from_apub(&page, context, None, request_counter).await?;
- let post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, context)
+ let post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, context, request_counter)
.await?
.id;
websocket_id: None,
});
- announce_if_community_is_local(undo, &user, context).await?;
+ announce_if_community_is_local(undo, &user, context, request_counter).await?;
Ok(HttpResponse::Ok().finish())
}
undo: Undo,
dislike: &Dislike,
context: &LemmyContext,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
- let user = get_actor_as_user(dislike, context).await?;
+ let user = get_actor_as_user(dislike, context, request_counter).await?;
let page = PageExt::from_any_base(
dislike
.object()
)?
.context(location_info!())?;
- let post = PostForm::from_apub(&page, context, None).await?;
+ let post = PostForm::from_apub(&page, context, None, request_counter).await?;
- let post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, context)
+ let post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, context, request_counter)
.await?
.id;
websocket_id: None,
});
- announce_if_community_is_local(undo, &user, context).await?;
+ announce_if_community_is_local(undo, &user, context, request_counter).await?;
Ok(HttpResponse::Ok().finish())
}
context: &LemmyContext,
undo: Undo,
post: Post,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
let deleted_post = blocking(context.pool(), move |conn| {
Post::update_deleted(conn, post.id, false)
websocket_id: None,
});
- let user = get_actor_as_user(&undo, context).await?;
- announce_if_community_is_local(undo, &user, context).await?;
+ let user = get_actor_as_user(&undo, context, request_counter).await?;
+ announce_if_community_is_local(undo, &user, context, request_counter).await?;
Ok(HttpResponse::Ok().finish())
}
context: &LemmyContext,
undo: Undo,
post: Post,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
let removed_post = blocking(context.pool(), move |conn| {
Post::update_removed(conn, post.id, false)
websocket_id: None,
});
- let mod_ = get_actor_as_user(&undo, context).await?;
- announce_if_community_is_local(undo, &mod_, context).await?;
+ let mod_ = get_actor_as_user(&undo, context, request_counter).await?;
+ announce_if_community_is_local(undo, &mod_, context, request_counter).await?;
Ok(HttpResponse::Ok().finish())
}
debug!("mention actor_id: {}", actor_id);
addressed_ccs.push(actor_id.to_owned().to_string().parse()?);
- let mention_user = get_or_fetch_and_upsert_user(&actor_id, context).await?;
+ let mention_user = get_or_fetch_and_upsert_user(&actor_id, context, &mut 0).await?;
let shared_inbox = mention_user.get_shared_inbox_url()?;
mention_inboxes.push(shared_inbox);
.actor()?
.as_single_xsd_any_uri()
.context(location_info!())?;
- let user = get_or_fetch_and_upsert_user(actor_uri, context).await?;
+ let user = get_or_fetch_and_upsert_user(actor_uri, context, &mut 0).await?;
let mut accept = Accept::new(self.actor_id.to_owned(), follow.into_any_base()?);
accept
static ACTOR_REFETCH_INTERVAL_SECONDS: i64 = 24 * 60 * 60;
static ACTOR_REFETCH_INTERVAL_SECONDS_DEBUG: i64 = 10;
+/// Maximum number of HTTP requests allowed to handle a single incoming activity (or a single object
+/// fetch through the search).
+///
+/// Tests are passing with a value of 5, so 10 should be safe for production.
+static MAX_REQUEST_NUMBER: i32 = 10;
+
/// Fetch any type of ActivityPub object, handling things like HTTP headers, deserialisation,
/// timeouts etc.
-async fn fetch_remote_object<Response>(client: &Client, url: &Url) -> Result<Response, LemmyError>
+async fn fetch_remote_object<Response>(
+ client: &Client,
+ url: &Url,
+ recursion_counter: &mut i32,
+) -> Result<Response, LemmyError>
where
Response: for<'de> Deserialize<'de>,
{
+ *recursion_counter += 1;
+ if *recursion_counter > MAX_REQUEST_NUMBER {
+ return Err(anyhow!("Maximum recursion depth reached").into());
+ }
check_is_apub_id_valid(&url)?;
let timeout = Duration::from_secs(60);
};
let domain = query_url.domain().context("url has no domain")?;
- let response =
- match fetch_remote_object::<SearchAcceptedObjects>(context.client(), &query_url).await? {
- SearchAcceptedObjects::Person(p) => {
- let user_uri = p.inner.id(domain)?.context("person has no id")?;
+ let recursion_counter = &mut 0;
+ let response = match fetch_remote_object::<SearchAcceptedObjects>(
+ context.client(),
+ &query_url,
+ recursion_counter,
+ )
+ .await?
+ {
+ SearchAcceptedObjects::Person(p) => {
+ let user_uri = p.inner.id(domain)?.context("person has no id")?;
- let user = get_or_fetch_and_upsert_user(&user_uri, context).await?;
+ let user = get_or_fetch_and_upsert_user(&user_uri, context, recursion_counter).await?;
- response.users = vec![
- blocking(context.pool(), move |conn| {
- UserView::get_user_secure(conn, user.id)
- })
- .await??,
- ];
+ response.users = vec![
+ blocking(context.pool(), move |conn| {
+ UserView::get_user_secure(conn, user.id)
+ })
+ .await??,
+ ];
- response
- }
- SearchAcceptedObjects::Group(g) => {
- let community_uri = g.inner.id(domain)?.context("group has no id")?;
+ response
+ }
+ SearchAcceptedObjects::Group(g) => {
+ let community_uri = g.inner.id(domain)?.context("group has no id")?;
- let community = get_or_fetch_and_upsert_community(community_uri, context).await?;
+ let community =
+ get_or_fetch_and_upsert_community(community_uri, context, recursion_counter).await?;
- response.communities = vec![
- blocking(context.pool(), move |conn| {
- CommunityView::read(conn, community.id, None)
- })
- .await??,
- ];
+ response.communities = vec![
+ blocking(context.pool(), move |conn| {
+ CommunityView::read(conn, community.id, None)
+ })
+ .await??,
+ ];
- response
- }
- SearchAcceptedObjects::Page(p) => {
- let post_form = PostForm::from_apub(&p, context, Some(query_url)).await?;
+ response
+ }
+ SearchAcceptedObjects::Page(p) => {
+ let post_form = PostForm::from_apub(&p, context, Some(query_url), recursion_counter).await?;
- let p = blocking(context.pool(), move |conn| Post::upsert(conn, &post_form)).await??;
- response.posts =
- vec![blocking(context.pool(), move |conn| PostView::read(conn, p.id, None)).await??];
+ let p = blocking(context.pool(), move |conn| Post::upsert(conn, &post_form)).await??;
+ response.posts =
+ vec![blocking(context.pool(), move |conn| PostView::read(conn, p.id, None)).await??];
- response
- }
- SearchAcceptedObjects::Comment(c) => {
- let comment_form = CommentForm::from_apub(&c, context, Some(query_url)).await?;
+ response
+ }
+ SearchAcceptedObjects::Comment(c) => {
+ let comment_form =
+ CommentForm::from_apub(&c, context, Some(query_url), recursion_counter).await?;
- let c = blocking(context.pool(), move |conn| {
- Comment::upsert(conn, &comment_form)
+ let c = blocking(context.pool(), move |conn| {
+ Comment::upsert(conn, &comment_form)
+ })
+ .await??;
+ response.comments = vec![
+ blocking(context.pool(), move |conn| {
+ CommentView::read(conn, c.id, None)
})
- .await??;
- response.comments = vec![
- blocking(context.pool(), move |conn| {
- CommentView::read(conn, c.id, None)
- })
- .await??,
- ];
-
- response
- }
- };
+ .await??,
+ ];
+
+ response
+ }
+ };
Ok(response)
}
pub(crate) async fn get_or_fetch_and_upsert_actor(
apub_id: &Url,
context: &LemmyContext,
+ recursion_counter: &mut i32,
) -> Result<Box<dyn ActorType>, LemmyError> {
- let community = get_or_fetch_and_upsert_community(apub_id, context).await;
+ let community = get_or_fetch_and_upsert_community(apub_id, context, recursion_counter).await;
let actor: Box<dyn ActorType> = match community {
Ok(c) => Box::new(c),
- Err(_) => Box::new(get_or_fetch_and_upsert_user(apub_id, context).await?),
+ Err(_) => Box::new(get_or_fetch_and_upsert_user(apub_id, context, recursion_counter).await?),
};
Ok(actor)
}
pub(crate) async fn get_or_fetch_and_upsert_user(
apub_id: &Url,
context: &LemmyContext,
+ recursion_counter: &mut i32,
) -> Result<User_, LemmyError> {
let apub_id_owned = apub_id.to_owned();
let user = blocking(context.pool(), move |conn| {
// If its older than a day, re-fetch it
Ok(u) if !u.local && should_refetch_actor(u.last_refreshed_at) => {
debug!("Fetching and updating from remote user: {}", apub_id);
- let person = fetch_remote_object::<PersonExt>(context.client(), apub_id).await?;
-
- let mut uf = UserForm::from_apub(&person, context, Some(apub_id.to_owned())).await?;
+ let person =
+ fetch_remote_object::<PersonExt>(context.client(), apub_id, recursion_counter).await?;
+
+ let mut uf = UserForm::from_apub(
+ &person,
+ context,
+ Some(apub_id.to_owned()),
+ recursion_counter,
+ )
+ .await?;
uf.last_refreshed_at = Some(naive_now());
let user = blocking(context.pool(), move |conn| User_::update(conn, u.id, &uf)).await??;
Ok(u) => Ok(u),
Err(NotFound {}) => {
debug!("Fetching and creating remote user: {}", apub_id);
- let person = fetch_remote_object::<PersonExt>(context.client(), apub_id).await?;
-
- let uf = UserForm::from_apub(&person, context, Some(apub_id.to_owned())).await?;
+ let person =
+ fetch_remote_object::<PersonExt>(context.client(), apub_id, recursion_counter).await?;
+
+ let uf = UserForm::from_apub(
+ &person,
+ context,
+ Some(apub_id.to_owned()),
+ recursion_counter,
+ )
+ .await?;
let user = blocking(context.pool(), move |conn| User_::upsert(conn, &uf)).await??;
Ok(user)
pub(crate) async fn get_or_fetch_and_upsert_community(
apub_id: &Url,
context: &LemmyContext,
+ recursion_counter: &mut i32,
) -> Result<Community, LemmyError> {
let apub_id_owned = apub_id.to_owned();
let community = blocking(context.pool(), move |conn| {
match community {
Ok(c) if !c.local && should_refetch_actor(c.last_refreshed_at) => {
debug!("Fetching and updating from remote community: {}", apub_id);
- fetch_remote_community(apub_id, context, Some(c.id)).await
+ fetch_remote_community(apub_id, context, Some(c.id), recursion_counter).await
}
Ok(c) => Ok(c),
Err(NotFound {}) => {
debug!("Fetching and creating remote community: {}", apub_id);
- fetch_remote_community(apub_id, context, None).await
+ fetch_remote_community(apub_id, context, None, recursion_counter).await
}
Err(e) => Err(e.into()),
}
apub_id: &Url,
context: &LemmyContext,
community_id: Option<i32>,
+ recursion_counter: &mut i32,
) -> Result<Community, LemmyError> {
- let group = fetch_remote_object::<GroupExt>(context.client(), apub_id).await?;
+ let group = fetch_remote_object::<GroupExt>(context.client(), apub_id, recursion_counter).await?;
- let cf = CommunityForm::from_apub(&group, context, Some(apub_id.to_owned())).await?;
+ let cf =
+ CommunityForm::from_apub(&group, context, Some(apub_id.to_owned()), recursion_counter).await?;
let community = blocking(context.pool(), move |conn| Community::upsert(conn, &cf)).await??;
// Also add the community moderators too
let mut creator_and_moderators = Vec::new();
for uri in creator_and_moderator_uris {
- let c_or_m = get_or_fetch_and_upsert_user(uri, context).await?;
+ let c_or_m = get_or_fetch_and_upsert_user(uri, context, recursion_counter).await?;
creator_and_moderators.push(c_or_m);
}
}
// fetch outbox (maybe make this conditional)
- let outbox =
- fetch_remote_object::<OrderedCollection>(context.client(), &community.get_outbox_url()?)
- .await?;
+ let outbox = fetch_remote_object::<OrderedCollection>(
+ context.client(),
+ &community.get_outbox_url()?,
+ recursion_counter,
+ )
+ .await?;
let outbox_items = outbox.items().context(location_info!())?.clone();
let mut outbox_items = outbox_items.many().context(location_info!())?;
if outbox_items.len() > 20 {
// The post creator may be from a blocked instance,
// if it errors, then continue
- let post = match PostForm::from_apub(&page, context, None).await {
+ let post = match PostForm::from_apub(&page, context, None, recursion_counter).await {
Ok(post) => post,
Err(_) => continue,
};
pub(crate) async fn get_or_fetch_and_insert_post(
post_ap_id: &Url,
context: &LemmyContext,
+ recursion_counter: &mut i32,
) -> Result<Post, LemmyError> {
let post_ap_id_owned = post_ap_id.to_owned();
let post = blocking(context.pool(), move |conn| {
Ok(p) => Ok(p),
Err(NotFound {}) => {
debug!("Fetching and creating remote post: {}", post_ap_id);
- let post = fetch_remote_object::<PageExt>(context.client(), post_ap_id).await?;
- let post_form = PostForm::from_apub(&post, context, Some(post_ap_id.to_owned())).await?;
+ let post =
+ fetch_remote_object::<PageExt>(context.client(), post_ap_id, recursion_counter).await?;
+ let post_form = PostForm::from_apub(
+ &post,
+ context,
+ Some(post_ap_id.to_owned()),
+ recursion_counter,
+ )
+ .await?;
let post = blocking(context.pool(), move |conn| Post::upsert(conn, &post_form)).await??;
pub(crate) async fn get_or_fetch_and_insert_comment(
comment_ap_id: &Url,
context: &LemmyContext,
+ recursion_counter: &mut i32,
) -> Result<Comment, LemmyError> {
let comment_ap_id_owned = comment_ap_id.to_owned();
let comment = blocking(context.pool(), move |conn| {
"Fetching and creating remote comment and its parents: {}",
comment_ap_id
);
- let comment = fetch_remote_object::<Note>(context.client(), comment_ap_id).await?;
- let comment_form =
- CommentForm::from_apub(&comment, context, Some(comment_ap_id.to_owned())).await?;
+ let comment =
+ fetch_remote_object::<Note>(context.client(), comment_ap_id, recursion_counter).await?;
+ let comment_form = CommentForm::from_apub(
+ &comment,
+ context,
+ Some(comment_ap_id.to_owned()),
+ recursion_counter,
+ )
+ .await?;
let comment = blocking(context.pool(), move |conn| {
Comment::upsert(conn, &comment_form)
);
check_is_apub_id_valid(user_uri)?;
- let user = get_or_fetch_and_upsert_user(&user_uri, &context).await?;
+ let request_counter = &mut 0;
+ let user = get_or_fetch_and_upsert_user(&user_uri, &context, request_counter).await?;
verify_signature(&request, &user)?;
check_is_apub_id_valid(&actor_id)?;
- let actor = get_or_fetch_and_upsert_actor(&actor_id, &context).await?;
+ let request_counter = &mut 0;
+ let actor = get_or_fetch_and_upsert_actor(&actor_id, &context, request_counter).await?;
verify_signature(&request, actor.as_ref())?;
let any_base = activity.clone().into_any_base()?;
let kind = activity.kind().context(location_info!())?;
let res = match kind {
- ValidTypes::Announce => receive_announce(&context, any_base, actor.as_ref()).await,
- ValidTypes::Create => receive_create(&context, any_base, actor_id).await,
- ValidTypes::Update => receive_update(&context, any_base, actor_id).await,
- ValidTypes::Like => receive_like(&context, any_base, actor_id).await,
- ValidTypes::Dislike => receive_dislike(&context, any_base, actor_id).await,
+ ValidTypes::Announce => {
+ receive_announce(&context, any_base, actor.as_ref(), request_counter).await
+ }
+ ValidTypes::Create => receive_create(&context, any_base, actor_id, request_counter).await,
+ ValidTypes::Update => receive_update(&context, any_base, actor_id, request_counter).await,
+ ValidTypes::Like => receive_like(&context, any_base, actor_id, request_counter).await,
+ ValidTypes::Dislike => receive_dislike(&context, any_base, actor_id, request_counter).await,
ValidTypes::Remove => receive_remove(&context, any_base, actor_id).await,
- ValidTypes::Delete => receive_delete(&context, any_base, actor_id).await,
- ValidTypes::Undo => receive_undo(&context, any_base, actor_id).await,
+ ValidTypes::Delete => receive_delete(&context, any_base, actor_id, request_counter).await,
+ ValidTypes::Undo => receive_undo(&context, any_base, actor_id, request_counter).await,
};
insert_activity(actor.user_id(), activity.clone(), false, context.pool()).await?;
context: &LemmyContext,
activity: AnyBase,
actor: &dyn ActorType,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
let announce = Announce::from_any_base(activity)?.context(location_info!())?;
verify_activity_domains_valid(&announce, actor.actor_id()?, false)?;
check_is_apub_id_valid(&inner_id)?;
match kind {
- Some("Create") => receive_create(context, object, inner_id).await,
- Some("Update") => receive_update(context, object, inner_id).await,
- Some("Like") => receive_like(context, object, inner_id).await,
- Some("Dislike") => receive_dislike(context, object, inner_id).await,
- Some("Delete") => receive_delete(context, object, inner_id).await,
+ Some("Create") => receive_create(context, object, inner_id, request_counter).await,
+ Some("Update") => receive_update(context, object, inner_id, request_counter).await,
+ Some("Like") => receive_like(context, object, inner_id, request_counter).await,
+ Some("Dislike") => receive_dislike(context, object, inner_id, request_counter).await,
+ Some("Delete") => receive_delete(context, object, inner_id, request_counter).await,
Some("Remove") => receive_remove(context, object, inner_id).await,
- Some("Undo") => receive_undo(context, object, inner_id).await,
+ Some("Undo") => receive_undo(context, object, inner_id, request_counter).await,
_ => receive_unhandled_activity(announce),
}
}
context: &LemmyContext,
activity: AnyBase,
expected_domain: Url,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
let create = Create::from_any_base(activity)?.context(location_info!())?;
verify_activity_domains_valid(&create, expected_domain, true)?;
match create.object().as_single_kind_str() {
- Some("Page") => receive_create_post(create, context).await,
- Some("Note") => receive_create_comment(create, context).await,
+ Some("Page") => receive_create_post(create, context, request_counter).await,
+ Some("Note") => receive_create_comment(create, context, request_counter).await,
_ => receive_unhandled_activity(create),
}
}
context: &LemmyContext,
activity: AnyBase,
expected_domain: Url,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
let update = Update::from_any_base(activity)?.context(location_info!())?;
verify_activity_domains_valid(&update, expected_domain, true)?;
match update.object().as_single_kind_str() {
- Some("Page") => receive_update_post(update, context).await,
- Some("Note") => receive_update_comment(update, context).await,
+ Some("Page") => receive_update_post(update, context, request_counter).await,
+ Some("Note") => receive_update_comment(update, context, request_counter).await,
_ => receive_unhandled_activity(update),
}
}
context: &LemmyContext,
activity: AnyBase,
expected_domain: Url,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
let like = Like::from_any_base(activity)?.context(location_info!())?;
verify_activity_domains_valid(&like, expected_domain, false)?;
match like.object().as_single_kind_str() {
- Some("Page") => receive_like_post(like, context).await,
- Some("Note") => receive_like_comment(like, context).await,
+ Some("Page") => receive_like_post(like, context, request_counter).await,
+ Some("Note") => receive_like_comment(like, context, request_counter).await,
_ => receive_unhandled_activity(like),
}
}
context: &LemmyContext,
activity: AnyBase,
expected_domain: Url,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
let enable_downvotes = blocking(context.pool(), move |conn| {
Site::read(conn, 1).map(|s| s.enable_downvotes)
verify_activity_domains_valid(&dislike, expected_domain, false)?;
match dislike.object().as_single_kind_str() {
- Some("Page") => receive_dislike_post(dislike, context).await,
- Some("Note") => receive_dislike_comment(dislike, context).await,
+ Some("Page") => receive_dislike_post(dislike, context, request_counter).await,
+ Some("Note") => receive_dislike_comment(dislike, context, request_counter).await,
_ => receive_unhandled_activity(dislike),
}
}
context: &LemmyContext,
activity: AnyBase,
expected_domain: Url,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
let delete = Delete::from_any_base(activity)?.context(location_info!())?;
verify_activity_domains_valid(&delete, expected_domain, true)?;
.context(location_info!())?;
match find_by_id(context, object).await {
- Ok(FindResults::Post(p)) => receive_delete_post(context, delete, p).await,
- Ok(FindResults::Comment(c)) => receive_delete_comment(context, delete, c).await,
- Ok(FindResults::Community(c)) => receive_delete_community(context, delete, c).await,
+ Ok(FindResults::Post(p)) => receive_delete_post(context, delete, p, request_counter).await,
+ Ok(FindResults::Comment(c)) => {
+ receive_delete_comment(context, delete, c, request_counter).await
+ }
+ Ok(FindResults::Community(c)) => {
+ receive_delete_community(context, delete, c, request_counter).await
+ }
// if we dont have the object, no need to do anything
Err(_) => Ok(HttpResponse::Ok().finish()),
}
context: &LemmyContext,
activity: AnyBase,
expected_domain: Url,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
let undo = Undo::from_any_base(activity)?.context(location_info!())?;
verify_activity_domains_valid(&undo, expected_domain.to_owned(), true)?;
match undo.object().as_single_kind_str() {
- Some("Delete") => receive_undo_delete(context, undo, expected_domain).await,
- Some("Remove") => receive_undo_remove(context, undo, expected_domain).await,
- Some("Like") => receive_undo_like(context, undo, expected_domain).await,
- Some("Dislike") => receive_undo_dislike(context, undo, expected_domain).await,
+ Some("Delete") => receive_undo_delete(context, undo, expected_domain, request_counter).await,
+ Some("Remove") => receive_undo_remove(context, undo, expected_domain, request_counter).await,
+ Some("Like") => receive_undo_like(context, undo, expected_domain, request_counter).await,
+ Some("Dislike") => receive_undo_dislike(context, undo, expected_domain, request_counter).await,
_ => receive_unhandled_activity(undo),
}
}
context: &LemmyContext,
undo: Undo,
expected_domain: Url,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
let delete = Delete::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
.context(location_info!())?;
.single_xsd_any_uri()
.context(location_info!())?;
match find_by_id(context, object).await {
- Ok(FindResults::Post(p)) => receive_undo_delete_post(context, undo, p).await,
- Ok(FindResults::Comment(c)) => receive_undo_delete_comment(context, undo, c).await,
- Ok(FindResults::Community(c)) => receive_undo_delete_community(context, undo, c).await,
+ Ok(FindResults::Post(p)) => receive_undo_delete_post(context, undo, p, request_counter).await,
+ Ok(FindResults::Comment(c)) => {
+ receive_undo_delete_comment(context, undo, c, request_counter).await
+ }
+ Ok(FindResults::Community(c)) => {
+ receive_undo_delete_community(context, undo, c, request_counter).await
+ }
// if we dont have the object, no need to do anything
Err(_) => Ok(HttpResponse::Ok().finish()),
}
context: &LemmyContext,
undo: Undo,
expected_domain: Url,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
let remove = Remove::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
.context(location_info!())?;
.single_xsd_any_uri()
.context(location_info!())?;
match find_by_id(context, object).await {
- Ok(FindResults::Post(p)) => receive_undo_remove_post(context, undo, p).await,
- Ok(FindResults::Comment(c)) => receive_undo_remove_comment(context, undo, c).await,
- Ok(FindResults::Community(c)) => receive_undo_remove_community(context, undo, c).await,
+ Ok(FindResults::Post(p)) => receive_undo_remove_post(context, undo, p, request_counter).await,
+ Ok(FindResults::Comment(c)) => {
+ receive_undo_remove_comment(context, undo, c, request_counter).await
+ }
+ Ok(FindResults::Community(c)) => {
+ receive_undo_remove_community(context, undo, c, request_counter).await
+ }
// if we dont have the object, no need to do anything
Err(_) => Ok(HttpResponse::Ok().finish()),
}
context: &LemmyContext,
undo: Undo,
expected_domain: Url,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
let like = Like::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
.context(location_info!())?;
.as_single_kind_str()
.context(location_info!())?;
match type_ {
- "Note" => receive_undo_like_comment(undo, &like, context).await,
- "Page" => receive_undo_like_post(undo, &like, context).await,
+ "Note" => receive_undo_like_comment(undo, &like, context, request_counter).await,
+ "Page" => receive_undo_like_post(undo, &like, context, request_counter).await,
d => Err(anyhow!("Undo Delete type {} not supported", d).into()),
}
}
context: &LemmyContext,
undo: Undo,
expected_domain: Url,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
let dislike = Dislike::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
.context(location_info!())?;
.as_single_kind_str()
.context(location_info!())?;
match type_ {
- "Note" => receive_undo_dislike_comment(undo, &dislike, context).await,
- "Page" => receive_undo_dislike_post(undo, &dislike, context).await,
+ "Note" => receive_undo_dislike_comment(undo, &dislike, context, request_counter).await,
+ "Page" => receive_undo_dislike_post(undo, &dislike, context, request_counter).await,
d => Err(anyhow!("Undo Delete type {} not supported", d).into()),
}
}
check_is_apub_id_valid(actor_uri)?;
- let actor = get_or_fetch_and_upsert_actor(actor_uri, &context).await?;
+ let request_counter = &mut 0;
+ let actor = get_or_fetch_and_upsert_actor(actor_uri, &context, request_counter).await?;
verify_signature(&request, actor.as_ref())?;
let any_base = activity.clone().into_any_base()?;
let kind = activity.kind().context(location_info!())?;
let res = match kind {
- ValidTypes::Accept => receive_accept(&context, any_base, actor.as_ref(), user).await,
- ValidTypes::Create => receive_create_private_message(&context, any_base, actor.as_ref()).await,
- ValidTypes::Update => receive_update_private_message(&context, any_base, actor.as_ref()).await,
+ ValidTypes::Accept => {
+ receive_accept(&context, any_base, actor.as_ref(), user, request_counter).await
+ }
+ ValidTypes::Create => {
+ receive_create_private_message(&context, any_base, actor.as_ref(), request_counter).await
+ }
+ ValidTypes::Update => {
+ receive_update_private_message(&context, any_base, actor.as_ref(), request_counter).await
+ }
ValidTypes::Delete => receive_delete_private_message(&context, any_base, actor.as_ref()).await,
ValidTypes::Undo => {
receive_undo_delete_private_message(&context, any_base, actor.as_ref()).await
activity: AnyBase,
actor: &dyn ActorType,
user: User_,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
let accept = Accept::from_any_base(activity)?.context(location_info!())?;
verify_activity_domains_valid(&accept, actor.actor_id()?, false)?;
.single_xsd_any_uri()
.context(location_info!())?;
- let community = get_or_fetch_and_upsert_community(&community_uri, context).await?;
+ let community =
+ get_or_fetch_and_upsert_community(&community_uri, context, request_counter).await?;
// Now you need to add this to the community follower
let community_follower_form = CommunityFollowerForm {
context: &LemmyContext,
activity: AnyBase,
actor: &dyn ActorType,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
let create = Create::from_any_base(activity)?.context(location_info!())?;
verify_activity_domains_valid(&create, actor.actor_id()?, true)?;
.context(location_info!())?;
let private_message =
- PrivateMessageForm::from_apub(¬e, context, Some(actor.actor_id()?)).await?;
+ PrivateMessageForm::from_apub(¬e, context, Some(actor.actor_id()?), request_counter).await?;
let inserted_private_message = blocking(&context.pool(), move |conn| {
PrivateMessage::create(conn, &private_message)
context: &LemmyContext,
activity: AnyBase,
actor: &dyn ActorType,
+ request_counter: &mut i32,
) -> Result<HttpResponse, LemmyError> {
let update = Update::from_any_base(activity)?.context(location_info!())?;
verify_activity_domains_valid(&update, actor.actor_id()?, true)?;
let note = Note::from_any_base(object)?.context(location_info!())?;
let private_message_form =
- PrivateMessageForm::from_apub(¬e, context, Some(actor.actor_id()?)).await?;
+ PrivateMessageForm::from_apub(¬e, context, Some(actor.actor_id()?), request_counter).await?;
let private_message_ap_id = private_message_form
.ap_id
apub: &Self::ApubType,
context: &LemmyContext,
expected_domain: Option<Url>,
+ request_counter: &mut i32,
) -> Result<Self, LemmyError>
where
Self: Sized;
note: &Note,
context: &LemmyContext,
expected_domain: Option<Url>,
+ request_counter: &mut i32,
) -> Result<CommentForm, LemmyError> {
let creator_actor_id = ¬e
.attributed_to()
.as_single_xsd_any_uri()
.context(location_info!())?;
- let creator = get_or_fetch_and_upsert_user(creator_actor_id, context).await?;
+ let creator = get_or_fetch_and_upsert_user(creator_actor_id, context, request_counter).await?;
let mut in_reply_tos = note
.in_reply_to()
let post_ap_id = in_reply_tos.next().context(location_info!())??;
// This post, or the parent comment might not yet exist on this server yet, fetch them.
- let post = get_or_fetch_and_insert_post(&post_ap_id, context).await?;
+ let post = get_or_fetch_and_insert_post(&post_ap_id, context, request_counter).await?;
// The 2nd item, if it exists, is the parent comment apub_id
// For deeply nested comments, FromApub automatically gets called recursively
Some(parent_comment_uri) => {
let parent_comment_ap_id = &parent_comment_uri?;
let parent_comment =
- get_or_fetch_and_insert_comment(&parent_comment_ap_id, context).await?;
+ get_or_fetch_and_insert_comment(&parent_comment_ap_id, context, request_counter).await?;
Some(parent_comment.id)
}
group: &GroupExt,
context: &LemmyContext,
expected_domain: Option<Url>,
+ request_counter: &mut i32,
) -> Result<Self, LemmyError> {
let creator_and_moderator_uris = group.inner.attributed_to().context(location_info!())?;
let creator_uri = creator_and_moderator_uris
.as_xsd_any_uri()
.context(location_info!())?;
- let creator = get_or_fetch_and_upsert_user(creator_uri, context).await?;
+ let creator = get_or_fetch_and_upsert_user(creator_uri, context, request_counter).await?;
let name = group
.inner
.preferred_username()
page: &PageExt,
context: &LemmyContext,
expected_domain: Option<Url>,
+ request_counter: &mut i32,
) -> Result<PostForm, LemmyError> {
let ext = &page.ext_one;
let creator_actor_id = page
.as_single_xsd_any_uri()
.context(location_info!())?;
- let creator = get_or_fetch_and_upsert_user(creator_actor_id, context).await?;
+ let creator = get_or_fetch_and_upsert_user(creator_actor_id, context, request_counter).await?;
let community_actor_id = page
.inner
.as_single_xsd_any_uri()
.context(location_info!())?;
- let community = get_or_fetch_and_upsert_community(community_actor_id, context).await?;
+ let community =
+ get_or_fetch_and_upsert_community(community_actor_id, context, request_counter).await?;
let thumbnail_url = match &page.inner.image() {
Some(any_image) => Image::from_any_base(
note: &Note,
context: &LemmyContext,
expected_domain: Option<Url>,
+ request_counter: &mut i32,
) -> Result<PrivateMessageForm, LemmyError> {
let creator_actor_id = note
.attributed_to()
.single_xsd_any_uri()
.context(location_info!())?;
- let creator = get_or_fetch_and_upsert_user(&creator_actor_id, context).await?;
+ let creator = get_or_fetch_and_upsert_user(&creator_actor_id, context, request_counter).await?;
let recipient_actor_id = note
.to()
.context(location_info!())?
.clone()
.single_xsd_any_uri()
.context(location_info!())?;
- let recipient = get_or_fetch_and_upsert_user(&recipient_actor_id, context).await?;
+ let recipient =
+ get_or_fetch_and_upsert_user(&recipient_actor_id, context, request_counter).await?;
let ap_id = note.id_unchecked().context(location_info!())?.to_string();
check_is_apub_id_valid(&Url::parse(&ap_id)?)?;
person: &PersonExt,
_context: &LemmyContext,
expected_domain: Option<Url>,
+ _request_counter: &mut i32,
) -> Result<Self, LemmyError> {
let avatar = match person.icon() {
Some(any_image) => Some(