167 lines
3.6 KiB
Go
167 lines
3.6 KiB
Go
package forgefed
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
|
|
"code.gitea.io/gitea/modules/log"
|
|
)
|
|
|
|
// https://datatracker.ietf.org/doc/html/draft-ietf-appsawg-webfinger-14#section-4.4
|
|
|
|
type WebfingerJRD struct {
|
|
Subject string `json:"subject,omitempty"`
|
|
Aliases []string `json:"aliases,omitempty"`
|
|
Properties map[string]any `json:"properties,omitempty"`
|
|
Links []*WebfingerLink `json:"links,omitempty"`
|
|
}
|
|
|
|
func (w WebfingerJRD) GetAvatar() *WebfingerLink {
|
|
for _, link := range w.Links {
|
|
if link.Rel == "http://webfinger.net/rel/avatar" {
|
|
return link
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (w WebfingerJRD) GetProfilePage() *WebfingerLink {
|
|
for _, link := range w.Links {
|
|
if link.Rel == "http://webfinger.net/rel/profile-page" && link.Type == "text/html" {
|
|
return link
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (w WebfingerJRD) GetActorLink() *WebfingerLink {
|
|
for _, link := range w.Links {
|
|
if link.Rel == "self" && link.Type == "application/activity+json" {
|
|
return link
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type WebfingerLink struct {
|
|
Rel string `json:"rel,omitempty"`
|
|
Type string `json:"type,omitempty"`
|
|
Href string `json:"href,omitempty"`
|
|
Titles map[string]string `json:"titles,omitempty"`
|
|
Properties map[string]any `json:"properties,omitempty"`
|
|
}
|
|
|
|
func GetHostnameFromResource(resource string) (string, error) {
|
|
r := resource
|
|
if strings.HasPrefix(resource, "@") {
|
|
resource, _ = strings.CutPrefix(resource, "@")
|
|
}
|
|
actor, err := url.Parse(resource)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
var hostname string
|
|
switch actor.Scheme {
|
|
case "":
|
|
i := strings.Split(resource, "@")
|
|
if len(i) != 2 {
|
|
log.Error("Invalid webfinger query " + r)
|
|
return "", errors.New("Invalid webfinger query " + r)
|
|
}
|
|
hostname = i[1]
|
|
case "mailto":
|
|
i := strings.Split(resource, "@")
|
|
if len(i) != 2 {
|
|
log.Error("Invalid webfinger query " + r)
|
|
return "", errors.New("Invalid webfinger query " + r)
|
|
}
|
|
hostname = i[1]
|
|
case "https":
|
|
hostname = actor.Host
|
|
default:
|
|
log.Error("Invalid webfinger query " + r)
|
|
return "", errors.New("Invalid webfinger query" + r)
|
|
|
|
}
|
|
return hostname, nil
|
|
}
|
|
|
|
// Get Actor object by performing webfinger lookup
|
|
func WebFingerLookup(q string) (*WebfingerJRD, error) {
|
|
if strings.HasPrefix(q, "@") {
|
|
q, _ = strings.CutPrefix(q, "@")
|
|
}
|
|
actor, err := url.Parse(q)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var res string
|
|
switch actor.Scheme {
|
|
case "":
|
|
res = fmt.Sprintf("acct:%s", q)
|
|
case "mailto":
|
|
res = q
|
|
case "https":
|
|
res = q
|
|
default:
|
|
return nil, errors.New("Invalid webfinger query")
|
|
|
|
}
|
|
|
|
hostname, err := GetHostnameFromResource(q)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
link := fmt.Sprintf("https://%s/.well-known/webfinger?resource=%s", hostname, res)
|
|
|
|
r, err := http.Get(link)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer r.Body.Close()
|
|
|
|
webfingerResponse := new(WebfingerJRD)
|
|
err = json.NewDecoder(r.Body).Decode(webfingerResponse)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return webfingerResponse, nil
|
|
}
|
|
|
|
func IsFingerable(resource string) bool {
|
|
if strings.HasPrefix(resource, "@") {
|
|
resource, _ = strings.CutPrefix(resource, "@")
|
|
}
|
|
actor, err := url.Parse(resource)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
switch actor.Scheme {
|
|
case "":
|
|
i := strings.Split(resource, "@")
|
|
if len(i) == 2 {
|
|
_ = i[1] // TODO: do len check before referencing element #2
|
|
return true
|
|
}
|
|
return false
|
|
case "mailto":
|
|
i := strings.Split(resource, "@")
|
|
if len(i) == 2 {
|
|
_ = i[1]
|
|
return true
|
|
}
|
|
return false
|
|
case "https":
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|