ee65d19ff3
1. Proper DELETE of federated statuses (not yet deleting all the media and stuff -- i still have to implement this -- but the actual status is toast). 2. Proper UPDATE of profiles. When you change your profile picture on your remote instance, that will now register properly in GoToSocial. 3. Scrolling down the home timeline - it no longer just sort of ends, and will keep loading older statuses as you scroll. 4. Little bugfixes -- still had some nil pointer errors when dereferencing remote accounts.
257 lines
10 KiB
Go
257 lines
10 KiB
Go
/*
|
|
GoToSocial
|
|
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package util
|
|
|
|
import (
|
|
"fmt"
|
|
"net/url"
|
|
"strings"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
const (
|
|
// UsersPath is for serving users info
|
|
UsersPath = "users"
|
|
// ActorsPath is for serving actors info
|
|
ActorsPath = "actors"
|
|
// StatusesPath is for serving statuses
|
|
StatusesPath = "statuses"
|
|
// InboxPath represents the webfinger inbox location
|
|
InboxPath = "inbox"
|
|
// OutboxPath represents the webfinger outbox location
|
|
OutboxPath = "outbox"
|
|
// FollowersPath represents the webfinger followers location
|
|
FollowersPath = "followers"
|
|
// FollowingPath represents the webfinger following location
|
|
FollowingPath = "following"
|
|
// LikedPath represents the webfinger liked location
|
|
LikedPath = "liked"
|
|
// CollectionsPath represents the webfinger collections location
|
|
CollectionsPath = "collections"
|
|
// FeaturedPath represents the webfinger featured location
|
|
FeaturedPath = "featured"
|
|
// PublicKeyPath is for serving an account's public key
|
|
PublicKeyPath = "main-key"
|
|
// FollowPath used to generate the URI for an individual follow or follow request
|
|
FollowPath = "follow"
|
|
)
|
|
|
|
// APContextKey is a type used specifically for settings values on contexts within go-fed AP request chains
|
|
type APContextKey string
|
|
|
|
const (
|
|
// APActivity can be used to set and retrieve the actual go-fed pub.Activity within a context.
|
|
APActivity APContextKey = "activity"
|
|
// APAccount can be used the set and retrieve the account being interacted with
|
|
APAccount APContextKey = "account"
|
|
// APRequestingAccount can be used to set and retrieve the account of an incoming federation request.
|
|
// This will often be the actor of the instance that's posting the request.
|
|
APRequestingAccount APContextKey = "requestingAccount"
|
|
// APRequestingActorIRI can be used to set and retrieve the actor of an incoming federation request.
|
|
// This will usually be the owner of whatever activity is being posted.
|
|
APRequestingActorIRI APContextKey = "requestingActorIRI"
|
|
// APRequestingPublicKeyID can be used to set and retrieve the public key ID of an incoming federation request.
|
|
APRequestingPublicKeyID APContextKey = "requestingPublicKeyID"
|
|
// APFromFederatorChanKey can be used to pass a pointer to the fromFederator channel into the federator for use in callbacks.
|
|
APFromFederatorChanKey APContextKey = "fromFederatorChan"
|
|
)
|
|
|
|
type ginContextKey struct{}
|
|
|
|
// GinContextKey is used solely for setting and retrieving the gin context from a context.Context
|
|
var GinContextKey = &ginContextKey{}
|
|
|
|
// UserURIs contains a bunch of UserURIs and URLs for a user, host, account, etc.
|
|
type UserURIs struct {
|
|
// The web URL of the instance host, eg https://example.org
|
|
HostURL string
|
|
// The web URL of the user, eg., https://example.org/@example_user
|
|
UserURL string
|
|
// The web URL for statuses of this user, eg., https://example.org/@example_user/statuses
|
|
StatusesURL string
|
|
|
|
// The webfinger URI of this user, eg., https://example.org/users/example_user
|
|
UserURI string
|
|
// The webfinger URI for this user's statuses, eg., https://example.org/users/example_user/statuses
|
|
StatusesURI string
|
|
// The webfinger URI for this user's activitypub inbox, eg., https://example.org/users/example_user/inbox
|
|
InboxURI string
|
|
// The webfinger URI for this user's activitypub outbox, eg., https://example.org/users/example_user/outbox
|
|
OutboxURI string
|
|
// The webfinger URI for this user's followers, eg., https://example.org/users/example_user/followers
|
|
FollowersURI string
|
|
// The webfinger URI for this user's following, eg., https://example.org/users/example_user/following
|
|
FollowingURI string
|
|
// The webfinger URI for this user's liked posts eg., https://example.org/users/example_user/liked
|
|
LikedURI string
|
|
// The webfinger URI for this user's featured collections, eg., https://example.org/users/example_user/collections/featured
|
|
CollectionURI string
|
|
// The URI for this user's public key, eg., https://example.org/users/example_user/publickey
|
|
PublicKeyURI string
|
|
}
|
|
|
|
// GenerateURIForFollow returns the AP URI for a new follow -- something like:
|
|
// https://example.org/users/whatever_user/follow/41c7f33f-1060-48d9-84df-38dcb13cf0d8
|
|
func GenerateURIForFollow(username string, protocol string, host string) string {
|
|
return fmt.Sprintf("%s://%s/%s/%s/%s", protocol, host, UsersPath, FollowPath, uuid.NewString())
|
|
}
|
|
|
|
// GenerateURIsForAccount throws together a bunch of URIs for the given username, with the given protocol and host.
|
|
func GenerateURIsForAccount(username string, protocol string, host string) *UserURIs {
|
|
// The below URLs are used for serving web requests
|
|
hostURL := fmt.Sprintf("%s://%s", protocol, host)
|
|
userURL := fmt.Sprintf("%s/@%s", hostURL, username)
|
|
statusesURL := fmt.Sprintf("%s/%s", userURL, StatusesPath)
|
|
|
|
// the below URIs are used in ActivityPub and Webfinger
|
|
userURI := fmt.Sprintf("%s/%s/%s", hostURL, UsersPath, username)
|
|
statusesURI := fmt.Sprintf("%s/%s", userURI, StatusesPath)
|
|
inboxURI := fmt.Sprintf("%s/%s", userURI, InboxPath)
|
|
outboxURI := fmt.Sprintf("%s/%s", userURI, OutboxPath)
|
|
followersURI := fmt.Sprintf("%s/%s", userURI, FollowersPath)
|
|
followingURI := fmt.Sprintf("%s/%s", userURI, FollowingPath)
|
|
likedURI := fmt.Sprintf("%s/%s", userURI, LikedPath)
|
|
collectionURI := fmt.Sprintf("%s/%s/%s", userURI, CollectionsPath, FeaturedPath)
|
|
publicKeyURI := fmt.Sprintf("%s#%s", userURI, PublicKeyPath)
|
|
|
|
return &UserURIs{
|
|
HostURL: hostURL,
|
|
UserURL: userURL,
|
|
StatusesURL: statusesURL,
|
|
|
|
UserURI: userURI,
|
|
StatusesURI: statusesURI,
|
|
InboxURI: inboxURI,
|
|
OutboxURI: outboxURI,
|
|
FollowersURI: followersURI,
|
|
FollowingURI: followingURI,
|
|
LikedURI: likedURI,
|
|
CollectionURI: collectionURI,
|
|
PublicKeyURI: publicKeyURI,
|
|
}
|
|
}
|
|
|
|
// IsUserPath returns true if the given URL path corresponds to eg /users/example_username
|
|
func IsUserPath(id *url.URL) bool {
|
|
return userPathRegex.MatchString(strings.ToLower(id.Path))
|
|
}
|
|
|
|
// IsInboxPath returns true if the given URL path corresponds to eg /users/example_username/inbox
|
|
func IsInboxPath(id *url.URL) bool {
|
|
return inboxPathRegex.MatchString(strings.ToLower(id.Path))
|
|
}
|
|
|
|
// IsOutboxPath returns true if the given URL path corresponds to eg /users/example_username/outbox
|
|
func IsOutboxPath(id *url.URL) bool {
|
|
return outboxPathRegex.MatchString(strings.ToLower(id.Path))
|
|
}
|
|
|
|
// IsInstanceActorPath returns true if the given URL path corresponds to eg /actors/example_username
|
|
func IsInstanceActorPath(id *url.URL) bool {
|
|
return actorPathRegex.MatchString(strings.ToLower(id.Path))
|
|
}
|
|
|
|
// IsFollowersPath returns true if the given URL path corresponds to eg /users/example_username/followers
|
|
func IsFollowersPath(id *url.URL) bool {
|
|
return followersPathRegex.MatchString(strings.ToLower(id.Path))
|
|
}
|
|
|
|
// IsFollowingPath returns true if the given URL path corresponds to eg /users/example_username/following
|
|
func IsFollowingPath(id *url.URL) bool {
|
|
return followingPathRegex.MatchString(strings.ToLower(id.Path))
|
|
}
|
|
|
|
// IsLikedPath returns true if the given URL path corresponds to eg /users/example_username/liked
|
|
func IsLikedPath(id *url.URL) bool {
|
|
return likedPathRegex.MatchString(strings.ToLower(id.Path))
|
|
}
|
|
|
|
// IsStatusesPath returns true if the given URL path corresponds to eg /users/example_username/statuses/SOME_UUID_OF_A_STATUS
|
|
func IsStatusesPath(id *url.URL) bool {
|
|
return statusesPathRegex.MatchString(strings.ToLower(id.Path))
|
|
}
|
|
|
|
// ParseStatusesPath returns the username and uuid from a path such as /users/example_username/statuses/SOME_UUID_OF_A_STATUS
|
|
func ParseStatusesPath(id *url.URL) (username string, uuid string, err error) {
|
|
matches := statusesPathRegex.FindStringSubmatch(id.Path)
|
|
if len(matches) != 3 {
|
|
err = fmt.Errorf("expected 3 matches but matches length was %d", len(matches))
|
|
return
|
|
}
|
|
username = matches[1]
|
|
uuid = matches[2]
|
|
return
|
|
}
|
|
|
|
// ParseUserPath returns the username from a path such as /users/example_username
|
|
func ParseUserPath(id *url.URL) (username string, err error) {
|
|
matches := userPathRegex.FindStringSubmatch(id.Path)
|
|
if len(matches) != 2 {
|
|
err = fmt.Errorf("expected 2 matches but matches length was %d", len(matches))
|
|
return
|
|
}
|
|
username = matches[1]
|
|
return
|
|
}
|
|
|
|
// ParseInboxPath returns the username from a path such as /users/example_username/inbox
|
|
func ParseInboxPath(id *url.URL) (username string, err error) {
|
|
matches := inboxPathRegex.FindStringSubmatch(id.Path)
|
|
if len(matches) != 2 {
|
|
err = fmt.Errorf("expected 2 matches but matches length was %d", len(matches))
|
|
return
|
|
}
|
|
username = matches[1]
|
|
return
|
|
}
|
|
|
|
// ParseOutboxPath returns the username from a path such as /users/example_username/outbox
|
|
func ParseOutboxPath(id *url.URL) (username string, err error) {
|
|
matches := outboxPathRegex.FindStringSubmatch(id.Path)
|
|
if len(matches) != 2 {
|
|
err = fmt.Errorf("expected 2 matches but matches length was %d", len(matches))
|
|
return
|
|
}
|
|
username = matches[1]
|
|
return
|
|
}
|
|
|
|
// ParseFollowersPath returns the username from a path such as /users/example_username/followers
|
|
func ParseFollowersPath(id *url.URL) (username string, err error) {
|
|
matches := followersPathRegex.FindStringSubmatch(id.Path)
|
|
if len(matches) != 2 {
|
|
err = fmt.Errorf("expected 2 matches but matches length was %d", len(matches))
|
|
return
|
|
}
|
|
username = matches[1]
|
|
return
|
|
}
|
|
|
|
// ParseFollowingPath returns the username from a path such as /users/example_username/following
|
|
func ParseFollowingPath(id *url.URL) (username string, err error) {
|
|
matches := followingPathRegex.FindStringSubmatch(id.Path)
|
|
if len(matches) != 2 {
|
|
err = fmt.Errorf("expected 2 matches but matches length was %d", len(matches))
|
|
return
|
|
}
|
|
username = matches[1]
|
|
return
|
|
}
|