123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- package libravatar
- import (
- "crypto/md5"
- "crypto/sha256"
- "fmt"
- "math/rand"
- "net"
- "net/mail"
- "net/url"
- "strings"
- "time"
- )
- const (
-
-
- HTTP404 = "404"
-
-
- MysteryMan = "mm"
-
- IdentIcon = "identicon"
-
- MonsterID = "monsterid"
-
- Wavatar = "wavatar"
-
- Retro = "retro"
- )
- var (
-
-
- DefaultLibravatar = New()
- )
- type cacheKey struct {
- service string
- domain string
- }
- type cacheValue struct {
- target string
- checkedAt time.Time
- }
- type Libravatar struct {
- defURL string
- picSize int
- fallbackHost string
- secureFallbackHost string
- useHTTPS bool
- nameCache map[cacheKey]cacheValue
- nameCacheDuration time.Duration
- minSize uint
- maxSize uint
- size uint
- serviceBase string
- secureServiceBase string
- }
- func New() *Libravatar {
-
-
- return &Libravatar{
- fallbackHost: `cdn.libravatar.org`,
- secureFallbackHost: `seccdn.libravatar.org`,
- minSize: 1,
- maxSize: 512,
- size: 0,
- serviceBase: `avatars`,
- secureServiceBase: `avatars-sec`,
- nameCache: make(map[cacheKey]cacheValue),
- nameCacheDuration: 24 * time.Hour,
- }
- }
- func (v *Libravatar) SetFallbackHost(host string) {
- v.fallbackHost = host
- }
- func (v *Libravatar) SetSecureFallbackHost(host string) {
- v.secureFallbackHost = host
- }
- func (v *Libravatar) SetUseHTTPS(use bool) {
- v.useHTTPS = use
- }
- func (v *Libravatar) SetAvatarSize(size uint) {
- v.size = size
- }
- func (v *Libravatar) genHash(email *mail.Address, openid *url.URL) string {
- if email != nil {
- email.Address = strings.ToLower(strings.TrimSpace(email.Address))
- sum := md5.Sum([]byte(email.Address))
- return fmt.Sprintf("%x", sum)
- } else if openid != nil {
- openid.Scheme = strings.ToLower(openid.Scheme)
- openid.Host = strings.ToLower(openid.Host)
- sum := sha256.Sum256([]byte(openid.String()))
- return fmt.Sprintf("%x", sum)
- }
-
- panic("Neither Email or OpenID set")
- }
- func (v *Libravatar) getDomain(email *mail.Address, openid *url.URL) string {
- if email != nil {
- u, err := url.Parse("//" + email.Address)
- if err != nil {
- if v.useHTTPS && v.secureFallbackHost != "" {
- return v.secureFallbackHost
- }
- return v.fallbackHost
- }
- return u.Host
- } else if openid != nil {
- return openid.Host
- }
-
- panic("Neither Email or OpenID set")
- }
- func (v *Libravatar) process(email *mail.Address, openid *url.URL) (string, error) {
- URL, err := v.baseURL(email, openid)
- if err != nil {
- return "", err
- }
- res := fmt.Sprintf("%s/avatar/%s", URL, v.genHash(email, openid))
- values := make(url.Values)
- if v.defURL != "" {
- values.Add("d", v.defURL)
- }
- if v.size > 0 {
- values.Add("s", fmt.Sprintf("%d", v.size))
- }
- if len(values) > 0 {
- return fmt.Sprintf("%s?%s", res, values.Encode()), nil
- }
- return res, nil
- }
- func (v *Libravatar) baseURL(email *mail.Address, openid *url.URL) (string, error) {
- var service, protocol, domain string
- if v.useHTTPS {
- protocol = "https://"
- service = v.secureServiceBase
- domain = v.secureFallbackHost
- } else {
- protocol = "http://"
- service = v.serviceBase
- domain = v.fallbackHost
- }
- host := v.getDomain(email, openid)
- key := cacheKey{service, host}
- now := time.Now()
- val, found := v.nameCache[key]
- if found && now.Sub(val.checkedAt) <= v.nameCacheDuration {
- return protocol + val.target, nil
- }
- _, addrs, err := net.LookupSRV(service, "tcp", host)
- if err != nil && err.(*net.DNSError).IsTimeout {
- return "", err
- }
- if len(addrs) == 1 {
-
- domain = strings.TrimSuffix(addrs[0].Target, ".")
- } else if len(addrs) > 1 {
-
-
- type record struct {
- srv *net.SRV
- weight uint16
- }
- var (
- totalWeight uint16
- records []record
- topPriority = addrs[0].Priority
- topRecord *net.SRV
- )
- for _, rr := range addrs {
- if rr.Priority > topPriority {
- continue
- } else if rr.Priority < topPriority {
-
-
- totalWeight = 0
- records = nil
- topPriority = rr.Priority
- }
- totalWeight += rr.Weight
- if rr.Weight > 0 {
- records = append(records, record{rr, totalWeight})
- } else if rr.Weight == 0 {
- records = append([]record{record{srv: rr, weight: totalWeight}}, records...)
- }
- }
- if len(records) == 1 {
- topRecord = records[0].srv
- } else {
- randnum := uint16(rand.Intn(int(totalWeight)))
- for _, rr := range records {
- if rr.weight >= randnum {
- topRecord = rr.srv
- break
- }
- }
- }
- domain = fmt.Sprintf("%s:%d", topRecord.Target, topRecord.Port)
- }
- v.nameCache[key] = cacheValue{checkedAt: now, target: domain}
- return protocol + domain, nil
- }
- func (v *Libravatar) FromEmail(email string) (string, error) {
- addr, err := mail.ParseAddress(email)
- if err != nil {
- return "", err
- }
- link, err := v.process(addr, nil)
- if err != nil {
- return "", err
- }
- return link, nil
- }
- func FromEmail(email string) (string, error) {
- return DefaultLibravatar.FromEmail(email)
- }
- func (v *Libravatar) FromURL(openid string) (string, error) {
- ourl, err := url.Parse(openid)
- if err != nil {
- return "", err
- }
- if !ourl.IsAbs() {
- return "", fmt.Errorf("Is not an absolute URL")
- } else if ourl.Scheme != "http" && ourl.Scheme != "https" {
- return "", fmt.Errorf("Invalid protocol: %s", ourl.Scheme)
- }
- link, err := v.process(nil, ourl)
- if err != nil {
- return "", err
- }
- return link, nil
- }
- func FromURL(openid string) (string, error) {
- return DefaultLibravatar.FromURL(openid)
- }
|