2021-04-11 09:50:27 +02:00
package send
import (
"brainbaking.com/go-jamming/rest"
2021-05-20 16:09:46 +02:00
"fmt"
2021-04-11 09:50:27 +02:00
"github.com/rs/zerolog/log"
2021-04-25 12:18:31 +02:00
"net/url"
"regexp"
2021-04-11 09:50:27 +02:00
"strings"
"willnorris.com/go/microformats"
)
const (
2021-04-13 09:10:32 +02:00
typeWebmention string = "webmention"
typeUnknown string = "unknown"
typePingback string = "pingback"
2021-04-11 09:50:27 +02:00
)
2021-04-25 12:18:31 +02:00
var (
relWebmention = regexp . MustCompile ( ` rel="??'??webmention ` )
2021-05-20 16:09:46 +02:00
possibleFeedEndpoints = [ ] string {
"all/index.xml" ,
"index.xml" ,
"feed" ,
"feed/index.xml" ,
}
2021-04-25 12:18:31 +02:00
)
2021-05-20 16:09:46 +02:00
func ( sndr * Sender ) discoverRssFeed ( domain string ) ( string , error ) {
for _ , endpt := range possibleFeedEndpoints {
feedUrl := fmt . Sprintf ( "https://%s/%s" , domain , endpt )
resp , err := sndr . RestClient . Head ( feedUrl )
if err != nil || ! rest . IsStatusOk ( resp ) || resp . Header . Get ( "Content-Type" ) != "text/xml" {
continue
}
return feedUrl , nil
}
return "" , fmt . Errorf ( "Unable to discover RSS feed for domain %s" , domain )
}
func ( sndr * Sender ) discoverMentionEndpoint ( target string ) ( link string , mentionType string ) {
2021-04-13 09:10:32 +02:00
mentionType = typeUnknown
2021-04-11 09:50:27 +02:00
header , body , err := sndr . RestClient . GetBody ( target )
if err != nil {
log . Warn ( ) . Str ( "target" , target ) . Msg ( "Failed to discover possible endpoint, aborting send" )
return
}
2021-04-25 12:18:31 +02:00
link = header . Get ( rest . RequestUrl ) // default to a possible redirect of the target
2021-04-25 12:48:05 +02:00
baseUrl , _ := url . Parse ( link )
2021-04-11 09:50:27 +02:00
2021-04-25 12:18:31 +02:00
// prefer links in the header over the html itself.
for _ , possibleLink := range header . Values ( "link" ) {
if relWebmention . MatchString ( possibleLink ) {
2021-04-25 12:48:05 +02:00
return buildWebmentionHeaderLink ( possibleLink , baseUrl ) , typeWebmention
2021-04-25 12:18:31 +02:00
}
2021-04-11 09:50:27 +02:00
}
if header . Get ( "X-Pingback" ) != "" {
2021-04-13 09:10:32 +02:00
return header . Get ( "X-Pingback" ) , typePingback
2021-04-11 09:50:27 +02:00
}
// this also complies with w3.org regulations: relative endpoint could be possible
2021-04-25 12:18:31 +02:00
format := microformats . Parse ( strings . NewReader ( body ) , baseUrl )
2021-04-13 09:10:32 +02:00
if len ( format . Rels [ typeWebmention ] ) > 0 {
mentionType = typeWebmention
2021-04-25 12:18:31 +02:00
for _ , possibleWm := range format . Rels [ typeWebmention ] {
if possibleWm != link {
link = possibleWm
return
}
}
2021-04-13 09:10:32 +02:00
} else if len ( format . Rels [ typePingback ] ) > 0 {
mentionType = typePingback
link = format . Rels [ typePingback ] [ 0 ]
2021-04-11 09:50:27 +02:00
}
return
}
2021-04-25 12:18:31 +02:00
// buildWebmentionHeaderLink tries to extract the link from the link header.
2021-04-13 09:10:32 +02:00
// e.g. Link: <http://aaronpk.example/webmention-endpoint>; rel="webmention"
2021-04-25 12:18:31 +02:00
// could also be comma-separated, e.g. <https://webmention.rocks/test/19/webmention/error>; rel="other", <https://webmention.rocks/test/19/webmention?head=true>; rel="webmention"
func buildWebmentionHeaderLink ( link string , baseUrl * url . URL ) ( wm string ) {
if strings . Contains ( link , "," ) {
for _ , possibleLink := range strings . Split ( link , "," ) {
if relWebmention . MatchString ( possibleLink ) {
link = strings . TrimSpace ( possibleLink )
}
}
}
2021-04-11 09:50:27 +02:00
raw := strings . Split ( link , ";" ) [ 0 ] [ 1 : ]
2021-04-25 12:18:31 +02:00
wm = raw [ : len ( raw ) - 1 ]
2021-04-25 12:48:05 +02:00
if ! strings . HasPrefix ( wm , "http" ) {
abs , _ := baseUrl . Parse ( wm )
wm = abs . String ( )
2021-04-25 12:18:31 +02:00
}
return
2021-04-11 09:50:27 +02:00
}