parent
4fe04f1adc
commit
f05a3353f4
2
go.mod
2
go.mod
|
@ -121,7 +121,7 @@ require (
|
||||||
gopkg.in/stretchr/testify.v1 v1.2.2 // indirect
|
gopkg.in/stretchr/testify.v1 v1.2.2 // indirect
|
||||||
gopkg.in/testfixtures.v2 v2.5.0
|
gopkg.in/testfixtures.v2 v2.5.0
|
||||||
mvdan.cc/xurls/v2 v2.0.0
|
mvdan.cc/xurls/v2 v2.0.0
|
||||||
strk.kbt.io/projects/go/libravatar v0.0.0-20160628055650-5eed7bff870a
|
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
|
||||||
xorm.io/builder v0.3.6
|
xorm.io/builder v0.3.6
|
||||||
xorm.io/core v0.7.2
|
xorm.io/core v0.7.2
|
||||||
)
|
)
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -815,8 +815,8 @@ honnef.co/go/tools v0.0.1-2019.2.2/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt
|
||||||
mvdan.cc/xurls/v2 v2.0.0 h1:r1zSOSNS/kqtpmATyMMMvaZ4/djsesbYz5kr0+qMRWc=
|
mvdan.cc/xurls/v2 v2.0.0 h1:r1zSOSNS/kqtpmATyMMMvaZ4/djsesbYz5kr0+qMRWc=
|
||||||
mvdan.cc/xurls/v2 v2.0.0/go.mod h1:2/webFPYOXN9jp/lzuj0zuAVlF+9g4KPFJANH1oJhRU=
|
mvdan.cc/xurls/v2 v2.0.0/go.mod h1:2/webFPYOXN9jp/lzuj0zuAVlF+9g4KPFJANH1oJhRU=
|
||||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
strk.kbt.io/projects/go/libravatar v0.0.0-20160628055650-5eed7bff870a h1:8q33ShxKXRwQ7JVd1ZnhIU3hZhwwn0Le+4fTeAackuM=
|
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 h1:mUcz5b3FJbP5Cvdq7Khzn6J9OCUQJaBwgBkCR+MOwSs=
|
||||||
strk.kbt.io/projects/go/libravatar v0.0.0-20160628055650-5eed7bff870a/go.mod h1:FJGmPh3vz9jSos1L/F91iAgnC/aejc0wIIrF2ZwJxdY=
|
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:FJGmPh3vz9jSos1L/F91iAgnC/aejc0wIIrF2ZwJxdY=
|
||||||
xorm.io/builder v0.3.6 h1:ha28mQ2M+TFx96Hxo+iq6tQgnkC9IZkM6D8w9sKHHF8=
|
xorm.io/builder v0.3.6 h1:ha28mQ2M+TFx96Hxo+iq6tQgnkC9IZkM6D8w9sKHHF8=
|
||||||
xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU=
|
xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU=
|
||||||
xorm.io/core v0.7.2-0.20190928055935-90aeac8d08eb h1:msX3zG3BPl8Ti+LDzP33/9K7BzO/WqFXk610K1kYKfo=
|
xorm.io/core v0.7.2-0.20190928055935-90aeac8d08eb h1:msX3zG3BPl8Ti+LDzP33/9K7BzO/WqFXk610K1kYKfo=
|
||||||
|
|
|
@ -612,7 +612,7 @@ gopkg.in/warnings.v0
|
||||||
gopkg.in/yaml.v2
|
gopkg.in/yaml.v2
|
||||||
# mvdan.cc/xurls/v2 v2.0.0
|
# mvdan.cc/xurls/v2 v2.0.0
|
||||||
mvdan.cc/xurls/v2
|
mvdan.cc/xurls/v2
|
||||||
# strk.kbt.io/projects/go/libravatar v0.0.0-20160628055650-5eed7bff870a
|
# strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
|
||||||
strk.kbt.io/projects/go/libravatar
|
strk.kbt.io/projects/go/libravatar
|
||||||
# xorm.io/builder v0.3.6
|
# xorm.io/builder v0.3.6
|
||||||
xorm.io/builder
|
xorm.io/builder
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
PACKAGES ?= $(shell go list ./...)
|
||||||
|
|
||||||
|
.PHONY: check
|
||||||
|
check: lint
|
||||||
|
go test
|
||||||
|
|
||||||
|
.PHONY: lint
|
||||||
|
lint:
|
||||||
|
@which golint > /dev/null; if [ $$? -ne 0 ]; then \
|
||||||
|
go get -u github.com/golang/lint/golint; \
|
||||||
|
fi
|
||||||
|
@for PKG in $(PACKAGES); do golint -set_exit_status $$PKG || exit 1; done;
|
|
@ -1,12 +1,14 @@
|
||||||
Simple [golang](https://www.golang.org) library for serving
|
Simple [golang](https://www.golang.org) library for serving
|
||||||
[federated avatars](https://www.libravatar.org)
|
[federated avatars](https://www.libravatar.org)
|
||||||
|
|
||||||
|
[![trunk](https://goreportcard.com/badge/strk.kbt.io/projects/go/libravatar)]
|
||||||
|
(https://goreportcard.com/report/strk.kbt.io/projects/go/libravatar)
|
||||||
|
|
||||||
# Use
|
# Use
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
go get strk.kbt.io/projects/go/libravatar
|
go get strk.kbt.io/projects/go/libravatar
|
||||||
cd $GOPATH/src/strk.kbt.io/projects/go/libravatar
|
go doc strk.kbt.io/projects/go/libravatar
|
||||||
go doc
|
|
||||||
```
|
```
|
||||||
|
|
||||||
# Contribute
|
# Contribute
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
// Implements support for federated avatars lookup.
|
// Implements support for federated avatars lookup.
|
||||||
// See https://wiki.libravatar.org/api/
|
// See https://wiki.libravatar.org/api/
|
||||||
|
|
||||||
package libravatar
|
package libravatar // import "strk.kbt.io/projects/go/libravatar"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
|
@ -16,6 +16,7 @@ import (
|
||||||
"net/mail"
|
"net/mail"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -38,7 +39,8 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Default object, enabling object-less function calls
|
// DefaultLibravatar is a default Libravatar object,
|
||||||
|
// enabling object-less function calls
|
||||||
DefaultLibravatar = New()
|
DefaultLibravatar = New()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -53,14 +55,16 @@ type cacheValue struct {
|
||||||
checkedAt time.Time
|
checkedAt time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Libravatar is an opaque structure holding service configuration
|
||||||
type Libravatar struct {
|
type Libravatar struct {
|
||||||
defUrl string // default url
|
defURL string // default url
|
||||||
picSize int // picture size
|
picSize int // picture size
|
||||||
fallbackHost string // default fallback URL
|
fallbackHost string // default fallback URL
|
||||||
secureFallbackHost string // default fallback URL for secure connections
|
secureFallbackHost string // default fallback URL for secure connections
|
||||||
useHTTPS bool
|
useHTTPS bool
|
||||||
nameCache map[cacheKey]cacheValue
|
nameCache map[cacheKey]cacheValue
|
||||||
nameCacheDuration time.Duration
|
nameCacheDuration time.Duration
|
||||||
|
nameCacheMutex *sync.Mutex
|
||||||
minSize uint // smallest image dimension allowed
|
minSize uint // smallest image dimension allowed
|
||||||
maxSize uint // largest image dimension allowed
|
maxSize uint // largest image dimension allowed
|
||||||
size uint // what dimension should be used
|
size uint // what dimension should be used
|
||||||
|
@ -68,7 +72,7 @@ type Libravatar struct {
|
||||||
secureServiceBase string // SRV record to be queried for federation with secure servers
|
secureServiceBase string // SRV record to be queried for federation with secure servers
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instanciate a library handle
|
// New instanciates a new Libravatar object (handle)
|
||||||
func New() *Libravatar {
|
func New() *Libravatar {
|
||||||
// According to https://wiki.libravatar.org/running_your_own/
|
// According to https://wiki.libravatar.org/running_your_own/
|
||||||
// the time-to-live (cache expiry) should be set to at least 1 day.
|
// the time-to-live (cache expiry) should be set to at least 1 day.
|
||||||
|
@ -82,27 +86,28 @@ func New() *Libravatar {
|
||||||
secureServiceBase: `avatars-sec`,
|
secureServiceBase: `avatars-sec`,
|
||||||
nameCache: make(map[cacheKey]cacheValue),
|
nameCache: make(map[cacheKey]cacheValue),
|
||||||
nameCacheDuration: 24 * time.Hour,
|
nameCacheDuration: 24 * time.Hour,
|
||||||
|
nameCacheMutex: &sync.Mutex{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the hostname for fallbacks in case no avatar service is defined
|
// SetFallbackHost sets the hostname for fallbacks in case no avatar
|
||||||
// for a domain
|
// service is defined for a domain
|
||||||
func (v *Libravatar) SetFallbackHost(host string) {
|
func (v *Libravatar) SetFallbackHost(host string) {
|
||||||
v.fallbackHost = host
|
v.fallbackHost = host
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the hostname for fallbacks in case no avatar service is defined
|
// SetSecureFallbackHost sets the hostname for fallbacks in case no
|
||||||
// for a domain, when requiring secure domains
|
// avatar service is defined for a domain, when requiring secure domains
|
||||||
func (v *Libravatar) SetSecureFallbackHost(host string) {
|
func (v *Libravatar) SetSecureFallbackHost(host string) {
|
||||||
v.secureFallbackHost = host
|
v.secureFallbackHost = host
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set useHTTPS flag
|
// SetUseHTTPS sets flag requesting use of https for fetching avatars
|
||||||
func (v *Libravatar) SetUseHTTPS(use bool) {
|
func (v *Libravatar) SetUseHTTPS(use bool) {
|
||||||
v.useHTTPS = use
|
v.useHTTPS = use
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set Avatars image dimension (0 for default)
|
// SetAvatarSize sets avatars image dimension (0 for default)
|
||||||
func (v *Libravatar) SetAvatarSize(size uint) {
|
func (v *Libravatar) SetAvatarSize(size uint) {
|
||||||
v.size = size
|
v.size = size
|
||||||
}
|
}
|
||||||
|
@ -150,8 +155,8 @@ func (v *Libravatar) process(email *mail.Address, openid *url.URL) (string, erro
|
||||||
res := fmt.Sprintf("%s/avatar/%s", URL, v.genHash(email, openid))
|
res := fmt.Sprintf("%s/avatar/%s", URL, v.genHash(email, openid))
|
||||||
|
|
||||||
values := make(url.Values)
|
values := make(url.Values)
|
||||||
if v.defUrl != "" {
|
if v.defURL != "" {
|
||||||
values.Add("d", v.defUrl)
|
values.Add("d", v.defURL)
|
||||||
}
|
}
|
||||||
if v.size > 0 {
|
if v.size > 0 {
|
||||||
values.Add("s", fmt.Sprintf("%d", v.size))
|
values.Add("s", fmt.Sprintf("%d", v.size))
|
||||||
|
@ -181,7 +186,9 @@ func (v *Libravatar) baseURL(email *mail.Address, openid *url.URL) (string, erro
|
||||||
host := v.getDomain(email, openid)
|
host := v.getDomain(email, openid)
|
||||||
key := cacheKey{service, host}
|
key := cacheKey{service, host}
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
v.nameCacheMutex.Lock()
|
||||||
val, found := v.nameCache[key]
|
val, found := v.nameCache[key]
|
||||||
|
v.nameCacheMutex.Unlock()
|
||||||
if found && now.Sub(val.checkedAt) <= v.nameCacheDuration {
|
if found && now.Sub(val.checkedAt) <= v.nameCacheDuration {
|
||||||
return protocol + val.target, nil
|
return protocol + val.target, nil
|
||||||
}
|
}
|
||||||
|
@ -204,53 +211,55 @@ func (v *Libravatar) baseURL(email *mail.Address, openid *url.URL) (string, erro
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
total_weight uint16
|
totalWeight uint16
|
||||||
records []record
|
records []record
|
||||||
top_priority = addrs[0].Priority
|
topPriority = addrs[0].Priority
|
||||||
top_record *net.SRV
|
topRecord *net.SRV
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, rr := range addrs {
|
for _, rr := range addrs {
|
||||||
if rr.Priority > top_priority {
|
if rr.Priority > topPriority {
|
||||||
continue
|
continue
|
||||||
} else if rr.Priority < top_priority {
|
} else if rr.Priority < topPriority {
|
||||||
// won't happen, because net sorts
|
// won't happen, because net sorts
|
||||||
// by priority, but just in case
|
// by priority, but just in case
|
||||||
total_weight = 0
|
totalWeight = 0
|
||||||
records = nil
|
records = nil
|
||||||
top_priority = rr.Priority
|
topPriority = rr.Priority
|
||||||
}
|
}
|
||||||
|
|
||||||
total_weight += rr.Weight
|
totalWeight += rr.Weight
|
||||||
|
|
||||||
if rr.Weight > 0 {
|
if rr.Weight > 0 {
|
||||||
records = append(records, record{rr, total_weight})
|
records = append(records, record{rr, totalWeight})
|
||||||
} else if rr.Weight == 0 {
|
} else if rr.Weight == 0 {
|
||||||
records = append([]record{record{srv: rr, weight: total_weight}}, records...)
|
records = append([]record{record{srv: rr, weight: totalWeight}}, records...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(records) == 1 {
|
if len(records) == 1 {
|
||||||
top_record = records[0].srv
|
topRecord = records[0].srv
|
||||||
} else {
|
} else {
|
||||||
randnum := uint16(rand.Intn(int(total_weight)))
|
randnum := uint16(rand.Intn(int(totalWeight)))
|
||||||
|
|
||||||
for _, rr := range records {
|
for _, rr := range records {
|
||||||
if rr.weight >= randnum {
|
if rr.weight >= randnum {
|
||||||
top_record = rr.srv
|
topRecord = rr.srv
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
domain = fmt.Sprintf("%s:%d", top_record.Target, top_record.Port)
|
domain = fmt.Sprintf("%s:%d", topRecord.Target, topRecord.Port)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v.nameCacheMutex.Lock()
|
||||||
v.nameCache[key] = cacheValue{checkedAt: now, target: domain}
|
v.nameCache[key] = cacheValue{checkedAt: now, target: domain}
|
||||||
|
v.nameCacheMutex.Unlock()
|
||||||
return protocol + domain, nil
|
return protocol + domain, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return url of the avatar for the given email
|
// FromEmail returns the url of the avatar for the given email
|
||||||
func (v *Libravatar) FromEmail(email string) (string, error) {
|
func (v *Libravatar) FromEmail(email string) (string, error) {
|
||||||
addr, err := mail.ParseAddress(email)
|
addr, err := mail.ParseAddress(email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -265,12 +274,13 @@ func (v *Libravatar) FromEmail(email string) (string, error) {
|
||||||
return link, nil
|
return link, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Object-less call to DefaultLibravatar for an email adders
|
// FromEmail is the object-less call to DefaultLibravatar for an email adders
|
||||||
func FromEmail(email string) (string, error) {
|
func FromEmail(email string) (string, error) {
|
||||||
return DefaultLibravatar.FromEmail(email)
|
return DefaultLibravatar.FromEmail(email)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return url of the avatar for the given url (typically for OpenID)
|
// FromURL returns the url of the avatar for the given url (typically
|
||||||
|
// for OpenID)
|
||||||
func (v *Libravatar) FromURL(openid string) (string, error) {
|
func (v *Libravatar) FromURL(openid string) (string, error) {
|
||||||
ourl, err := url.Parse(openid)
|
ourl, err := url.Parse(openid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -291,7 +301,7 @@ func (v *Libravatar) FromURL(openid string) (string, error) {
|
||||||
return link, nil
|
return link, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Object-less call to DefaultLibravatar for a URL
|
// FromURL is the object-less call to DefaultLibravatar for a URL
|
||||||
func FromURL(openid string) (string, error) {
|
func FromURL(openid string) (string, error) {
|
||||||
return DefaultLibravatar.FromURL(openid)
|
return DefaultLibravatar.FromURL(openid)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue