diff options
author | 2021-05-15 11:58:11 +0200 | |
---|---|---|
committer | 2021-05-15 11:58:11 +0200 | |
commit | cc48294c31a76e94fa879ad0d8d5dbd7e94c651b (patch) | |
tree | 7c26d33b41bab33bbdfbba540958444f4c296602 /internal/message | |
parent | Mediahandler (#21) (diff) | |
download | gotosocial-cc48294c31a76e94fa879ad0d8d5dbd7e94c651b.tar.xz |
Inbox post (#22)
Inbox POST from federated servers now working for statuses and follow requests.
Follow request client API added.
Start work on federating outgoing messages.
Other fixes and changes/tidying up.
Diffstat (limited to 'internal/message')
-rw-r--r-- | internal/message/fediprocess.go | 8 | ||||
-rw-r--r-- | internal/message/frprocess.go | 42 | ||||
-rw-r--r-- | internal/message/processor.go | 75 | ||||
-rw-r--r-- | internal/message/processorutil.go | 6 | ||||
-rw-r--r-- | internal/message/statusprocess.go | 7 |
5 files changed, 135 insertions, 3 deletions
diff --git a/internal/message/fediprocess.go b/internal/message/fediprocess.go index dad1e848c..133e7dbaa 100644 --- a/internal/message/fediprocess.go +++ b/internal/message/fediprocess.go @@ -1,6 +1,7 @@ package message import ( + "context" "fmt" "net/http" @@ -8,6 +9,7 @@ import ( apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" + "github.com/superseriousbusiness/gotosocial/internal/util" ) // authenticateAndDereferenceFediRequest authenticates the HTTP signature of an incoming federation request, using the given @@ -130,3 +132,9 @@ func (p *processor) GetWebfingerAccount(requestedUsername string, request *http. }, }, nil } + +func (p *processor) InboxPost(ctx context.Context, w http.ResponseWriter, r *http.Request) (bool, error) { + contextWithChannel := context.WithValue(ctx, util.APFromFederatorChanKey, p.fromFederator) + posted, err := p.federator.FederatingActor().PostInbox(contextWithChannel, w, r) + return posted, err +} diff --git a/internal/message/frprocess.go b/internal/message/frprocess.go new file mode 100644 index 000000000..c96b83dec --- /dev/null +++ b/internal/message/frprocess.go @@ -0,0 +1,42 @@ +package message + +import ( + apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" + "github.com/superseriousbusiness/gotosocial/internal/db" + "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" + "github.com/superseriousbusiness/gotosocial/internal/oauth" +) + +func (p *processor) FollowRequestsGet(auth *oauth.Auth) ([]apimodel.Account, ErrorWithCode) { + frs := []gtsmodel.FollowRequest{} + if err := p.db.GetFollowRequestsForAccountID(auth.Account.ID, &frs); err != nil { + if _, ok := err.(db.ErrNoEntries); !ok { + return nil, NewErrorInternalError(err) + } + } + + accts := []apimodel.Account{} + for _, fr := range frs { + acct := >smodel.Account{} + if err := p.db.GetByID(fr.AccountID, acct); err != nil { + return nil, NewErrorInternalError(err) + } + mastoAcct, err := p.tc.AccountToMastoPublic(acct) + if err != nil { + return nil, NewErrorInternalError(err) + } + accts = append(accts, *mastoAcct) + } + return accts, nil +} + +func (p *processor) FollowRequestAccept(auth *oauth.Auth, accountID string) ErrorWithCode { + if err := p.db.AcceptFollowRequest(accountID, auth.Account.ID); err != nil { + return NewErrorNotFound(err) + } + return nil +} + +func (p *processor) FollowRequestDeny(auth *oauth.Auth) ErrorWithCode { + return nil +} diff --git a/internal/message/processor.go b/internal/message/processor.go index c8d58a346..7fc850e37 100644 --- a/internal/message/processor.go +++ b/internal/message/processor.go @@ -19,7 +19,11 @@ package message import ( + "context" + "errors" + "fmt" "net/http" + "net/url" "github.com/sirupsen/logrus" apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" @@ -77,6 +81,11 @@ type Processor interface { // FileGet handles the fetching of a media attachment file via the fileserver. FileGet(authed *oauth.Auth, form *apimodel.GetContentRequestForm) (*apimodel.Content, error) + // FollowRequestsGet handles the getting of the authed account's incoming follow requests + FollowRequestsGet(auth *oauth.Auth) ([]apimodel.Account, ErrorWithCode) + // FollowRequestAccept handles the acceptance of a follow request from the given account ID + FollowRequestAccept(auth *oauth.Auth, accountID string) ErrorWithCode + // InstanceGet retrieves instance information for serving at api/v1/instance InstanceGet(domain string) (*apimodel.Instance, ErrorWithCode) @@ -116,6 +125,18 @@ type Processor interface { // GetWebfingerAccount handles the GET for a webfinger resource. Most commonly, it will be used for returning account lookups. GetWebfingerAccount(requestedUsername string, request *http.Request) (*apimodel.WebfingerAccountResponse, ErrorWithCode) + + // InboxPost handles POST requests to a user's inbox for new activitypub messages. + // + // InboxPost returns true if the request was handled as an ActivityPub POST to an actor's inbox. + // If false, the request was not an ActivityPub request and may still be handled by the caller in another way, such as serving a web page. + // + // If the error is nil, then the ResponseWriter's headers and response has already been written. If a non-nil error is returned, then no response has been written. + // + // If the Actor was constructed with the Federated Protocol enabled, side effects will occur. + // + // If the Federated Protocol is not enabled, writes the http.StatusMethodNotAllowed status code in the response. No side effects occur. + InboxPost(ctx context.Context, w http.ResponseWriter, r *http.Request) (bool, error) } // processor just implements the Processor interface @@ -181,6 +202,9 @@ func (p *processor) Start() error { p.log.Infof("received message TO client API: %+v", clientMsg) case clientMsg := <-p.fromClientAPI: p.log.Infof("received message FROM client API: %+v", clientMsg) + if err := p.processFromClientAPI(clientMsg); err != nil { + p.log.Error(err) + } case federatorMsg := <-p.toFederator: p.log.Infof("received message TO federator: %+v", federatorMsg) case federatorMsg := <-p.fromFederator: @@ -227,3 +251,54 @@ type FromFederator struct { APActivityType gtsmodel.ActivityStreamsActivity Activity interface{} } + +func (p *processor) processFromClientAPI(clientMsg FromClientAPI) error { + switch clientMsg.APObjectType { + case gtsmodel.ActivityStreamsNote: + status, ok := clientMsg.Activity.(*gtsmodel.Status) + if !ok { + return errors.New("note was not parseable as *gtsmodel.Status") + } + + if err := p.notifyStatus(status); err != nil { + return err + } + + if status.VisibilityAdvanced.Federated { + return p.federateStatus(status) + } + return nil + } + return fmt.Errorf("message type unprocessable: %+v", clientMsg) +} + +func (p *processor) federateStatus(status *gtsmodel.Status) error { + // derive the sending account -- it might be attached to the status already + sendingAcct := >smodel.Account{} + if status.GTSAccount != nil { + sendingAcct = status.GTSAccount + } else { + // it wasn't attached so get it from the db instead + if err := p.db.GetByID(status.AccountID, sendingAcct); err != nil { + return err + } + } + + outboxURI, err := url.Parse(sendingAcct.OutboxURI) + if err != nil { + return err + } + + // convert the status to AS format Note + note, err := p.tc.StatusToAS(status) + if err != nil { + return err + } + + _, err = p.federator.FederatingActor().Send(context.Background(), outboxURI, note) + return err +} + +func (p *processor) notifyStatus(status *gtsmodel.Status) error { + return nil +} diff --git a/internal/message/processorutil.go b/internal/message/processorutil.go index c928eec1a..233a18ad8 100644 --- a/internal/message/processorutil.go +++ b/internal/message/processorutil.go @@ -179,7 +179,7 @@ func (p *processor) processLanguage(form *apimodel.AdvancedStatusCreateForm, acc func (p *processor) processMentions(form *apimodel.AdvancedStatusCreateForm, accountID string, status *gtsmodel.Status) error { menchies := []string{} - gtsMenchies, err := p.db.MentionStringsToMentions(util.DeriveMentions(form.Status), accountID, status.ID) + gtsMenchies, err := p.db.MentionStringsToMentions(util.DeriveMentionsFromStatus(form.Status), accountID, status.ID) if err != nil { return fmt.Errorf("error generating mentions from status: %s", err) } @@ -198,7 +198,7 @@ func (p *processor) processMentions(form *apimodel.AdvancedStatusCreateForm, acc func (p *processor) processTags(form *apimodel.AdvancedStatusCreateForm, accountID string, status *gtsmodel.Status) error { tags := []string{} - gtsTags, err := p.db.TagStringsToTags(util.DeriveHashtags(form.Status), accountID, status.ID) + gtsTags, err := p.db.TagStringsToTags(util.DeriveHashtagsFromStatus(form.Status), accountID, status.ID) if err != nil { return fmt.Errorf("error generating hashtags from status: %s", err) } @@ -217,7 +217,7 @@ func (p *processor) processTags(form *apimodel.AdvancedStatusCreateForm, account func (p *processor) processEmojis(form *apimodel.AdvancedStatusCreateForm, accountID string, status *gtsmodel.Status) error { emojis := []string{} - gtsEmojis, err := p.db.EmojiStringsToEmojis(util.DeriveEmojis(form.Status), accountID, status.ID) + gtsEmojis, err := p.db.EmojiStringsToEmojis(util.DeriveEmojisFromStatus(form.Status), accountID, status.ID) if err != nil { return fmt.Errorf("error generating emojis from status: %s", err) } diff --git a/internal/message/statusprocess.go b/internal/message/statusprocess.go index bd4329082..d9d115aec 100644 --- a/internal/message/statusprocess.go +++ b/internal/message/statusprocess.go @@ -81,6 +81,13 @@ func (p *processor) StatusCreate(auth *oauth.Auth, form *apimodel.AdvancedStatus } } + // put the new status in the appropriate channel for async processing + p.fromClientAPI <- FromClientAPI{ + APObjectType: newStatus.ActivityStreamsType, + APActivityType: gtsmodel.ActivityStreamsCreate, + Activity: newStatus, + } + // return the frontend representation of the new status to the submitter return p.tc.StatusToMasto(newStatus, auth.Account, auth.Account, nil, newStatus.GTSReplyToAccount, nil) } |