1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
use crate::data::Data;
use chrono::NaiveDateTime;
use std::ops::Deref;
use url::Url;

/// Trait which allows verification and reception of incoming activities.
#[async_trait::async_trait(?Send)]
#[enum_delegate::register]
pub trait ActivityHandler {
    type DataType;
    type Error;

    /// `id` field of the activity
    fn id(&self) -> &Url;

    /// `actor` field of activity
    fn actor(&self) -> &Url;

    /// Verify that the activity is valid. If this method returns an error, the activity will be
    /// discarded. This is separate from receive(), so that it can be called recursively on nested
    /// objects, without storing something in the database by accident.
    async fn verify(
        &self,
        data: &Data<Self::DataType>,
        request_counter: &mut i32,
    ) -> Result<(), Self::Error>;

    /// Receives the activity and stores its action in database.
    async fn receive(
        self,
        data: &Data<Self::DataType>,
        request_counter: &mut i32,
    ) -> Result<(), Self::Error>;
}

/// Allow for boxing of enum variants
#[async_trait::async_trait(?Send)]
impl<T> ActivityHandler for Box<T>
where
    T: ActivityHandler,
{
    type DataType = T::DataType;
    type Error = T::Error;

    fn id(&self) -> &Url {
        self.deref().id()
    }

    fn actor(&self) -> &Url {
        self.deref().actor()
    }

    async fn verify(
        &self,
        data: &Data<Self::DataType>,
        request_counter: &mut i32,
    ) -> Result<(), Self::Error> {
        self.deref().verify(data, request_counter).await
    }

    async fn receive(
        self,
        data: &Data<Self::DataType>,
        request_counter: &mut i32,
    ) -> Result<(), Self::Error> {
        (*self).receive(data, request_counter).await
    }
}

#[async_trait::async_trait(?Send)]
pub trait ApubObject {
    type DataType;
    type ApubType;
    type DbType;
    type Error;

    /// If the object is stored in the database, this method should return the fetch time. Used to
    /// update actors after certain interval.
    fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
        None
    }

    /// Try to read the object with given ID from local database. Returns Ok(None) if it doesn't exist.
    async fn read_from_apub_id(
        object_id: Url,
        data: &Self::DataType,
    ) -> Result<Option<Self>, Self::Error>
    where
        Self: Sized;

    /// Marks the object as deleted in local db. Called when a delete activity is received, or if
    /// fetch returns a tombstone.
    async fn delete(self, _data: &Self::DataType) -> Result<(), Self::Error>
    where
        Self: Sized,
    {
        Ok(())
    }

    /// Trait for converting an object or actor into the respective ActivityPub type.
    async fn into_apub(self, data: &Self::DataType) -> Result<Self::ApubType, Self::Error>;

    /// Verify that the object is valid. If this method returns an error, it will be
    /// discarded. This is separate from from_apub(), so that it can be called recursively on nested
    /// objects, without storing something in the database by accident.
    async fn verify(
        apub: &Self::ApubType,
        expected_domain: &Url,
        data: &Self::DataType,
        request_counter: &mut i32,
    ) -> Result<(), Self::Error>;

    /// Converts an object from ActivityPub type to Lemmy internal type.
    ///
    /// * `apub` The object to read from
    /// * `context` LemmyContext which holds DB pool, HTTP client etc
    /// * `expected_domain` Domain where the object was received from. None in case of mod action.
    /// * `mod_action_allowed` True if the object can be a mod activity, ignore `expected_domain` in this case
    async fn from_apub(
        apub: Self::ApubType,
        data: &Self::DataType,
        request_counter: &mut i32,
    ) -> Result<Self, Self::Error>
    where
        Self: Sized;
}

pub trait Actor: ApubObject {
    /// Returns the actor's public key for verification of HTTP signatures
    fn public_key(&self) -> &str;

    /// The inbox where activities for this user should be sent to
    fn inbox(&self) -> Url;

    /// The actor's shared inbox, if any
    fn shared_inbox(&self) -> Option<Url> {
        None
    }

    fn shared_inbox_or_inbox(&self) -> Url {
        self.shared_inbox().unwrap_or_else(|| self.inbox())
    }
}