gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

uri.go (14685B)


      1 // GoToSocial
      2 // Copyright (C) GoToSocial Authors admin@gotosocial.org
      3 // SPDX-License-Identifier: AGPL-3.0-or-later
      4 //
      5 // This program is free software: you can redistribute it and/or modify
      6 // it under the terms of the GNU Affero General Public License as published by
      7 // the Free Software Foundation, either version 3 of the License, or
      8 // (at your option) any later version.
      9 //
     10 // This program is distributed in the hope that it will be useful,
     11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
     12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13 // GNU Affero General Public License for more details.
     14 //
     15 // You should have received a copy of the GNU Affero General Public License
     16 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
     17 
     18 package uris
     19 
     20 import (
     21 	"fmt"
     22 	"net/url"
     23 
     24 	"github.com/superseriousbusiness/gotosocial/internal/config"
     25 	"github.com/superseriousbusiness/gotosocial/internal/regexes"
     26 )
     27 
     28 const (
     29 	UsersPath        = "users"         // UsersPath is for serving users info
     30 	StatusesPath     = "statuses"      // StatusesPath is for serving statuses
     31 	InboxPath        = "inbox"         // InboxPath represents the activitypub inbox location
     32 	OutboxPath       = "outbox"        // OutboxPath represents the activitypub outbox location
     33 	FollowersPath    = "followers"     // FollowersPath represents the activitypub followers location
     34 	FollowingPath    = "following"     // FollowingPath represents the activitypub following location
     35 	LikedPath        = "liked"         // LikedPath represents the activitypub liked location
     36 	CollectionsPath  = "collections"   // CollectionsPath represents the activitypub collections location
     37 	FeaturedPath     = "featured"      // FeaturedPath represents the activitypub featured location
     38 	PublicKeyPath    = "main-key"      // PublicKeyPath is for serving an account's public key
     39 	FollowPath       = "follow"        // FollowPath used to generate the URI for an individual follow or follow request
     40 	UpdatePath       = "updates"       // UpdatePath is used to generate the URI for an account update
     41 	BlocksPath       = "blocks"        // BlocksPath is used to generate the URI for a block
     42 	ReportsPath      = "reports"       // ReportsPath is used to generate the URI for a report/flag
     43 	ConfirmEmailPath = "confirm_email" // ConfirmEmailPath is used to generate the URI for an email confirmation link
     44 	FileserverPath   = "fileserver"    // FileserverPath is a path component for serving attachments + media
     45 	EmojiPath        = "emoji"         // EmojiPath represents the activitypub emoji location
     46 )
     47 
     48 // UserURIs contains a bunch of UserURIs and URLs for a user, host, account, etc.
     49 type UserURIs struct {
     50 	// The web URL of the instance host, eg https://example.org
     51 	HostURL string
     52 	// The web URL of the user, eg., https://example.org/@example_user
     53 	UserURL string
     54 	// The web URL for statuses of this user, eg., https://example.org/@example_user/statuses
     55 	StatusesURL string
     56 
     57 	// The activitypub URI of this user, eg., https://example.org/users/example_user
     58 	UserURI string
     59 	// The activitypub URI for this user's statuses, eg., https://example.org/users/example_user/statuses
     60 	StatusesURI string
     61 	// The activitypub URI for this user's activitypub inbox, eg., https://example.org/users/example_user/inbox
     62 	InboxURI string
     63 	// The activitypub URI for this user's activitypub outbox, eg., https://example.org/users/example_user/outbox
     64 	OutboxURI string
     65 	// The activitypub URI for this user's followers, eg., https://example.org/users/example_user/followers
     66 	FollowersURI string
     67 	// The activitypub URI for this user's following, eg., https://example.org/users/example_user/following
     68 	FollowingURI string
     69 	// The activitypub URI for this user's liked posts eg., https://example.org/users/example_user/liked
     70 	LikedURI string
     71 	// The activitypub URI for this user's featured collections, eg., https://example.org/users/example_user/collections/featured
     72 	FeaturedCollectionURI string
     73 	// The URI for this user's public key, eg., https://example.org/users/example_user/publickey
     74 	PublicKeyURI string
     75 }
     76 
     77 // GenerateURIForFollow returns the AP URI for a new follow -- something like:
     78 // https://example.org/users/whatever_user/follow/01F7XTH1QGBAPMGF49WJZ91XGC
     79 func GenerateURIForFollow(username string, thisFollowID string) string {
     80 	protocol := config.GetProtocol()
     81 	host := config.GetHost()
     82 	return fmt.Sprintf("%s://%s/%s/%s/%s/%s", protocol, host, UsersPath, username, FollowPath, thisFollowID)
     83 }
     84 
     85 // GenerateURIForLike returns the AP URI for a new like/fave -- something like:
     86 // https://example.org/users/whatever_user/liked/01F7XTH1QGBAPMGF49WJZ91XGC
     87 func GenerateURIForLike(username string, thisFavedID string) string {
     88 	protocol := config.GetProtocol()
     89 	host := config.GetHost()
     90 	return fmt.Sprintf("%s://%s/%s/%s/%s/%s", protocol, host, UsersPath, username, LikedPath, thisFavedID)
     91 }
     92 
     93 // GenerateURIForUpdate returns the AP URI for a new update activity -- something like:
     94 // https://example.org/users/whatever_user#updates/01F7XTH1QGBAPMGF49WJZ91XGC
     95 func GenerateURIForUpdate(username string, thisUpdateID string) string {
     96 	protocol := config.GetProtocol()
     97 	host := config.GetHost()
     98 	return fmt.Sprintf("%s://%s/%s/%s#%s/%s", protocol, host, UsersPath, username, UpdatePath, thisUpdateID)
     99 }
    100 
    101 // GenerateURIForBlock returns the AP URI for a new block activity -- something like:
    102 // https://example.org/users/whatever_user/blocks/01F7XTH1QGBAPMGF49WJZ91XGC
    103 func GenerateURIForBlock(username string, thisBlockID string) string {
    104 	protocol := config.GetProtocol()
    105 	host := config.GetHost()
    106 	return fmt.Sprintf("%s://%s/%s/%s/%s/%s", protocol, host, UsersPath, username, BlocksPath, thisBlockID)
    107 }
    108 
    109 // GenerateURIForReport returns the API URI for a new Flag activity -- something like:
    110 // https://example.org/reports/01GP3AWY4CRDVRNZKW0TEAMB5R
    111 //
    112 // This path specifically doesn't contain any info about the user who did the reporting,
    113 // to protect their privacy.
    114 func GenerateURIForReport(thisReportID string) string {
    115 	protocol := config.GetProtocol()
    116 	host := config.GetHost()
    117 	return fmt.Sprintf("%s://%s/%s/%s", protocol, host, ReportsPath, thisReportID)
    118 }
    119 
    120 // GenerateURIForEmailConfirm returns a link for email confirmation -- something like:
    121 // https://example.org/confirm_email?token=490e337c-0162-454f-ac48-4b22bb92a205
    122 func GenerateURIForEmailConfirm(token string) string {
    123 	protocol := config.GetProtocol()
    124 	host := config.GetHost()
    125 	return fmt.Sprintf("%s://%s/%s?token=%s", protocol, host, ConfirmEmailPath, token)
    126 }
    127 
    128 // GenerateURIsForAccount throws together a bunch of URIs for the given username, with the given protocol and host.
    129 func GenerateURIsForAccount(username string) *UserURIs {
    130 	protocol := config.GetProtocol()
    131 	host := config.GetHost()
    132 
    133 	// The below URLs are used for serving web requests
    134 	hostURL := fmt.Sprintf("%s://%s", protocol, host)
    135 	userURL := fmt.Sprintf("%s/@%s", hostURL, username)
    136 	statusesURL := fmt.Sprintf("%s/%s", userURL, StatusesPath)
    137 
    138 	// the below URIs are used in ActivityPub and Webfinger
    139 	userURI := fmt.Sprintf("%s/%s/%s", hostURL, UsersPath, username)
    140 	statusesURI := fmt.Sprintf("%s/%s", userURI, StatusesPath)
    141 	inboxURI := fmt.Sprintf("%s/%s", userURI, InboxPath)
    142 	outboxURI := fmt.Sprintf("%s/%s", userURI, OutboxPath)
    143 	followersURI := fmt.Sprintf("%s/%s", userURI, FollowersPath)
    144 	followingURI := fmt.Sprintf("%s/%s", userURI, FollowingPath)
    145 	likedURI := fmt.Sprintf("%s/%s", userURI, LikedPath)
    146 	collectionURI := fmt.Sprintf("%s/%s/%s", userURI, CollectionsPath, FeaturedPath)
    147 	publicKeyURI := fmt.Sprintf("%s/%s", userURI, PublicKeyPath)
    148 
    149 	return &UserURIs{
    150 		HostURL:     hostURL,
    151 		UserURL:     userURL,
    152 		StatusesURL: statusesURL,
    153 
    154 		UserURI:               userURI,
    155 		StatusesURI:           statusesURI,
    156 		InboxURI:              inboxURI,
    157 		OutboxURI:             outboxURI,
    158 		FollowersURI:          followersURI,
    159 		FollowingURI:          followingURI,
    160 		LikedURI:              likedURI,
    161 		FeaturedCollectionURI: collectionURI,
    162 		PublicKeyURI:          publicKeyURI,
    163 	}
    164 }
    165 
    166 // GenerateURIForAttachment generates a URI for an attachment/emoji/header etc.
    167 // Will produced something like https://example.org/fileserver/01FPST95B8FC3HG3AGCDKPQNQ2/attachment/original/01FPST9QK4V5XWS3F9Z4F2G1X7.gif
    168 func GenerateURIForAttachment(accountID string, mediaType string, mediaSize string, mediaID string, extension string) string {
    169 	protocol := config.GetProtocol()
    170 	host := config.GetHost()
    171 	return fmt.Sprintf("%s://%s/%s/%s/%s/%s/%s.%s", protocol, host, FileserverPath, accountID, mediaType, mediaSize, mediaID, extension)
    172 }
    173 
    174 // GenerateURIForEmoji generates an activitypub uri for a new emoji.
    175 func GenerateURIForEmoji(emojiID string) string {
    176 	protocol := config.GetProtocol()
    177 	host := config.GetHost()
    178 	return fmt.Sprintf("%s://%s/%s/%s", protocol, host, EmojiPath, emojiID)
    179 }
    180 
    181 // IsUserPath returns true if the given URL path corresponds to eg /users/example_username
    182 func IsUserPath(id *url.URL) bool {
    183 	return regexes.UserPath.MatchString(id.Path)
    184 }
    185 
    186 // IsInboxPath returns true if the given URL path corresponds to eg /users/example_username/inbox
    187 func IsInboxPath(id *url.URL) bool {
    188 	return regexes.InboxPath.MatchString(id.Path)
    189 }
    190 
    191 // IsOutboxPath returns true if the given URL path corresponds to eg /users/example_username/outbox
    192 func IsOutboxPath(id *url.URL) bool {
    193 	return regexes.OutboxPath.MatchString(id.Path)
    194 }
    195 
    196 // IsFollowersPath returns true if the given URL path corresponds to eg /users/example_username/followers
    197 func IsFollowersPath(id *url.URL) bool {
    198 	return regexes.FollowersPath.MatchString(id.Path)
    199 }
    200 
    201 // IsFollowingPath returns true if the given URL path corresponds to eg /users/example_username/following
    202 func IsFollowingPath(id *url.URL) bool {
    203 	return regexes.FollowingPath.MatchString(id.Path)
    204 }
    205 
    206 // IsFollowPath returns true if the given URL path corresponds to eg /users/example_username/follow/SOME_ULID_OF_A_FOLLOW
    207 func IsFollowPath(id *url.URL) bool {
    208 	return regexes.FollowPath.MatchString(id.Path)
    209 }
    210 
    211 // IsLikedPath returns true if the given URL path corresponds to eg /users/example_username/liked
    212 func IsLikedPath(id *url.URL) bool {
    213 	return regexes.LikedPath.MatchString(id.Path)
    214 }
    215 
    216 // IsLikePath returns true if the given URL path corresponds to eg /users/example_username/liked/SOME_ULID_OF_A_STATUS
    217 func IsLikePath(id *url.URL) bool {
    218 	return regexes.LikePath.MatchString(id.Path)
    219 }
    220 
    221 // IsStatusesPath returns true if the given URL path corresponds to eg /users/example_username/statuses/SOME_ULID_OF_A_STATUS
    222 func IsStatusesPath(id *url.URL) bool {
    223 	return regexes.StatusesPath.MatchString(id.Path)
    224 }
    225 
    226 // IsPublicKeyPath returns true if the given URL path corresponds to eg /users/example_username/main-key
    227 func IsPublicKeyPath(id *url.URL) bool {
    228 	return regexes.PublicKeyPath.MatchString(id.Path)
    229 }
    230 
    231 // IsBlockPath returns true if the given URL path corresponds to eg /users/example_username/blocks/SOME_ULID_OF_A_BLOCK
    232 func IsBlockPath(id *url.URL) bool {
    233 	return regexes.BlockPath.MatchString(id.Path)
    234 }
    235 
    236 // IsReportPath returns true if the given URL path corresponds to eg /reports/SOME_ULID_OF_A_REPORT
    237 func IsReportPath(id *url.URL) bool {
    238 	return regexes.ReportPath.MatchString(id.Path)
    239 }
    240 
    241 // ParseStatusesPath returns the username and ulid from a path such as /users/example_username/statuses/SOME_ULID_OF_A_STATUS
    242 func ParseStatusesPath(id *url.URL) (username string, ulid string, err error) {
    243 	matches := regexes.StatusesPath.FindStringSubmatch(id.Path)
    244 	if len(matches) != 3 {
    245 		err = fmt.Errorf("expected 3 matches but matches length was %d", len(matches))
    246 		return
    247 	}
    248 	username = matches[1]
    249 	ulid = matches[2]
    250 	return
    251 }
    252 
    253 // ParseUserPath returns the username from a path such as /users/example_username
    254 func ParseUserPath(id *url.URL) (username string, err error) {
    255 	matches := regexes.UserPath.FindStringSubmatch(id.Path)
    256 	if len(matches) != 2 {
    257 		err = fmt.Errorf("expected 2 matches but matches length was %d", len(matches))
    258 		return
    259 	}
    260 	username = matches[1]
    261 	return
    262 }
    263 
    264 // ParseInboxPath returns the username from a path such as /users/example_username/inbox
    265 func ParseInboxPath(id *url.URL) (username string, err error) {
    266 	matches := regexes.InboxPath.FindStringSubmatch(id.Path)
    267 	if len(matches) != 2 {
    268 		err = fmt.Errorf("expected 2 matches but matches length was %d", len(matches))
    269 		return
    270 	}
    271 	username = matches[1]
    272 	return
    273 }
    274 
    275 // ParseOutboxPath returns the username from a path such as /users/example_username/outbox
    276 func ParseOutboxPath(id *url.URL) (username string, err error) {
    277 	matches := regexes.OutboxPath.FindStringSubmatch(id.Path)
    278 	if len(matches) != 2 {
    279 		err = fmt.Errorf("expected 2 matches but matches length was %d", len(matches))
    280 		return
    281 	}
    282 	username = matches[1]
    283 	return
    284 }
    285 
    286 // ParseFollowersPath returns the username from a path such as /users/example_username/followers
    287 func ParseFollowersPath(id *url.URL) (username string, err error) {
    288 	matches := regexes.FollowersPath.FindStringSubmatch(id.Path)
    289 	if len(matches) != 2 {
    290 		err = fmt.Errorf("expected 2 matches but matches length was %d", len(matches))
    291 		return
    292 	}
    293 	username = matches[1]
    294 	return
    295 }
    296 
    297 // ParseFollowingPath returns the username from a path such as /users/example_username/following
    298 func ParseFollowingPath(id *url.URL) (username string, err error) {
    299 	matches := regexes.FollowingPath.FindStringSubmatch(id.Path)
    300 	if len(matches) != 2 {
    301 		err = fmt.Errorf("expected 2 matches but matches length was %d", len(matches))
    302 		return
    303 	}
    304 	username = matches[1]
    305 	return
    306 }
    307 
    308 // ParseLikedPath returns the username and ulid from a path such as /users/example_username/liked/SOME_ULID_OF_A_STATUS
    309 func ParseLikedPath(id *url.URL) (username string, ulid string, err error) {
    310 	matches := regexes.LikePath.FindStringSubmatch(id.Path)
    311 	if len(matches) != 3 {
    312 		err = fmt.Errorf("expected 3 matches but matches length was %d", len(matches))
    313 		return
    314 	}
    315 	username = matches[1]
    316 	ulid = matches[2]
    317 	return
    318 }
    319 
    320 // ParseBlockPath returns the username and ulid from a path such as /users/example_username/blocks/SOME_ULID_OF_A_BLOCK
    321 func ParseBlockPath(id *url.URL) (username string, ulid string, err error) {
    322 	matches := regexes.BlockPath.FindStringSubmatch(id.Path)
    323 	if len(matches) != 3 {
    324 		err = fmt.Errorf("expected 3 matches but matches length was %d", len(matches))
    325 		return
    326 	}
    327 	username = matches[1]
    328 	ulid = matches[2]
    329 	return
    330 }
    331 
    332 // ParseReportPath returns the ulid from a path such as /reports/SOME_ULID_OF_A_REPORT
    333 func ParseReportPath(id *url.URL) (ulid string, err error) {
    334 	matches := regexes.ReportPath.FindStringSubmatch(id.Path)
    335 	if len(matches) != 2 {
    336 		err = fmt.Errorf("expected 2 matches but matches length was %d", len(matches))
    337 		return
    338 	}
    339 	ulid = matches[1]
    340 	return
    341 }