2020-03-27 15:34:39 +03:00
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
import (
"crypto/md5"
"fmt"
"net/url"
2020-12-03 21:46:11 +03:00
"path"
"strconv"
2020-03-27 15:34:39 +03:00
"strings"
2020-12-03 21:46:11 +03:00
"code.gitea.io/gitea/modules/base"
2020-03-27 15:34:39 +03:00
"code.gitea.io/gitea/modules/cache"
2020-12-03 21:46:11 +03:00
"code.gitea.io/gitea/modules/log"
2020-03-27 15:34:39 +03:00
"code.gitea.io/gitea/modules/setting"
)
// EmailHash represents a pre-generated hash map
type EmailHash struct {
Hash string ` xorm:"pk varchar(32)" `
Email string ` xorm:"UNIQUE NOT NULL" `
}
2020-12-03 21:46:11 +03:00
// DefaultAvatarLink the default avatar link
func DefaultAvatarLink ( ) string {
u , err := url . Parse ( setting . AppSubURL )
if err != nil {
log . Error ( "GetUserByEmail: %v" , err )
return ""
}
u . Path = path . Join ( u . Path , "/img/avatar_default.png" )
return u . String ( )
}
// DefaultAvatarSize is a sentinel value for the default avatar size, as
// determined by the avatar-hosting service.
const DefaultAvatarSize = - 1
2020-12-09 03:12:15 +03:00
// DefaultAvatarPixelSize is the default size in pixels of a rendered avatar
const DefaultAvatarPixelSize = 28
// AvatarRenderedSizeFactor is the factor by which the default size is increased for finer rendering
const AvatarRenderedSizeFactor = 2
2020-12-03 21:46:11 +03:00
// HashEmail hashes email address to MD5 string.
// https://en.gravatar.com/site/implement/hash/
func HashEmail ( email string ) string {
return base . EncodeMD5 ( strings . ToLower ( strings . TrimSpace ( email ) ) )
}
2020-03-27 15:34:39 +03:00
// GetEmailForHash converts a provided md5sum to the email
func GetEmailForHash ( md5Sum string ) ( string , error ) {
return cache . GetString ( "Avatar:" + md5Sum , func ( ) ( string , error ) {
emailHash := EmailHash {
Hash : strings . ToLower ( strings . TrimSpace ( md5Sum ) ) ,
}
_ , err := x . Get ( & emailHash )
return emailHash . Email , err
} )
}
2020-12-03 21:46:11 +03:00
// LibravatarURL returns the URL for the given email. This function should only
// be called if a federated avatar service is enabled.
func LibravatarURL ( email string ) ( * url . URL , error ) {
urlStr , err := setting . LibravatarService . FromEmail ( email )
if err != nil {
log . Error ( "LibravatarService.FromEmail(email=%s): error %v" , email , err )
return nil , err
}
u , err := url . Parse ( urlStr )
if err != nil {
log . Error ( "Failed to parse libravatar url(%s): error %v" , urlStr , err )
return nil , err
}
return u , nil
}
// HashedAvatarLink returns an avatar link for a provided email
func HashedAvatarLink ( email string ) string {
2020-03-27 15:34:39 +03:00
lowerEmail := strings . ToLower ( strings . TrimSpace ( email ) )
sum := fmt . Sprintf ( "%x" , md5 . Sum ( [ ] byte ( lowerEmail ) ) )
_ , _ = cache . GetString ( "Avatar:" + sum , func ( ) ( string , error ) {
emailHash := & EmailHash {
Email : lowerEmail ,
Hash : sum ,
}
2020-08-25 01:39:18 +03:00
// OK we're going to open a session just because I think that that might hide away any problems with postgres reporting errors
sess := x . NewSession ( )
defer sess . Close ( )
if err := sess . Begin ( ) ; err != nil {
// we don't care about any DB problem just return the lowerEmail
return lowerEmail , nil
}
_ , _ = sess . Insert ( emailHash )
if err := sess . Commit ( ) ; err != nil {
// Seriously we don't care about any DB problems just return the lowerEmail - we expect the transaction to fail most of the time
return lowerEmail , nil
}
2020-03-27 15:34:39 +03:00
return lowerEmail , nil
} )
return setting . AppSubURL + "/avatar/" + url . PathEscape ( sum )
}
2020-12-03 21:46:11 +03:00
// MakeFinalAvatarURL constructs the final avatar URL string
func MakeFinalAvatarURL ( u * url . URL , size int ) string {
vals := u . Query ( )
vals . Set ( "d" , "identicon" )
if size != DefaultAvatarSize {
vals . Set ( "s" , strconv . Itoa ( size ) )
}
u . RawQuery = vals . Encode ( )
return u . String ( )
}
// SizedAvatarLink returns a sized link to the avatar for the given email address.
func SizedAvatarLink ( email string , size int ) string {
var avatarURL * url . URL
if setting . EnableFederatedAvatar && setting . LibravatarService != nil {
// This is the slow path that would need to call LibravatarURL() which
// does DNS lookups. Avoid it by issuing a redirect so we don't block
// the template render with network requests.
return HashedAvatarLink ( email )
} else if ! setting . DisableGravatar {
// copy GravatarSourceURL, because we will modify its Path.
copyOfGravatarSourceURL := * setting . GravatarSourceURL
avatarURL = & copyOfGravatarSourceURL
avatarURL . Path = path . Join ( avatarURL . Path , HashEmail ( email ) )
} else {
return DefaultAvatarLink ( )
}
return MakeFinalAvatarURL ( avatarURL , size )
}