2
2
// Use of this source code is governed by a MIT-style
3
3
// license that can be found in the LICENSE file.
4
4
5
- package models
5
+ package avatars
6
6
7
7
import (
8
- "crypto/md5"
9
- "fmt"
10
8
"net/url"
11
9
"path"
12
10
"strconv"
@@ -19,7 +17,16 @@ import (
19
17
"code.gitea.io/gitea/modules/setting"
20
18
)
21
19
22
- // EmailHash represents a pre-generated hash map
20
+ // DefaultAvatarSize is a sentinel value for the default avatar size, as determined by the avatar-hosting service.
21
+ const DefaultAvatarSize = - 1
22
+
23
+ // DefaultAvatarPixelSize is the default size in pixels of a rendered avatar
24
+ const DefaultAvatarPixelSize = 28
25
+
26
+ // AvatarRenderedSizeFactor is the factor by which the default size is increased for finer rendering
27
+ const AvatarRenderedSizeFactor = 4
28
+
29
+ // EmailHash represents a pre-generated hash map (mainly used by LibravatarURL, it queries email server's DNS records)
23
30
type EmailHash struct {
24
31
Hash string `xorm:"pk varchar(32)"`
25
32
Email string `xorm:"UNIQUE NOT NULL"`
@@ -41,18 +48,7 @@ func DefaultAvatarLink() string {
41
48
return u .String ()
42
49
}
43
50
44
- // DefaultAvatarSize is a sentinel value for the default avatar size, as
45
- // determined by the avatar-hosting service.
46
- const DefaultAvatarSize = - 1
47
-
48
- // DefaultAvatarPixelSize is the default size in pixels of a rendered avatar
49
- const DefaultAvatarPixelSize = 28
50
-
51
- // AvatarRenderedSizeFactor is the factor by which the default size is increased for finer rendering
52
- const AvatarRenderedSizeFactor = 4
53
-
54
- // HashEmail hashes email address to MD5 string.
55
- // https://en.gravatar.com/site/implement/hash/
51
+ // HashEmail hashes email address to MD5 string. https://en.gravatar.com/site/implement/hash/
56
52
func HashEmail (email string ) string {
57
53
return base .EncodeMD5 (strings .ToLower (strings .TrimSpace (email )))
58
54
}
@@ -69,8 +65,8 @@ func GetEmailForHash(md5Sum string) (string, error) {
69
65
})
70
66
}
71
67
72
- // LibravatarURL returns the URL for the given email. This function should only
73
- // be called if a federated avatar service is enabled.
68
+ // LibravatarURL returns the URL for the given email. Slow due to the DNS lookup.
69
+ // This function should only be called if a federated avatar service is enabled.
74
70
func LibravatarURL (email string ) (* url.URL , error ) {
75
71
urlStr , err := setting .LibravatarService .FromEmail (email )
76
72
if err != nil {
@@ -85,14 +81,15 @@ func LibravatarURL(email string) (*url.URL, error) {
85
81
return u , nil
86
82
}
87
83
88
- // HashedAvatarLink returns an avatar link for a provided email
89
- func HashedAvatarLink (email string , size int ) string {
84
+ // saveEmailHash returns an avatar link for a provided email,
85
+ // the email and hash are saved into database, which will be used by GetEmailForHash later
86
+ func saveEmailHash (email string ) string {
90
87
lowerEmail := strings .ToLower (strings .TrimSpace (email ))
91
- sum := fmt . Sprintf ( "%x" , md5 . Sum ([] byte ( lowerEmail )) )
92
- _ , _ = cache .GetString ("Avatar:" + sum , func () (string , error ) {
88
+ emailHash := HashEmail ( lowerEmail )
89
+ _ , _ = cache .GetString ("Avatar:" + emailHash , func () (string , error ) {
93
90
emailHash := & EmailHash {
94
91
Email : lowerEmail ,
95
- Hash : sum ,
92
+ Hash : emailHash ,
96
93
}
97
94
// OK we're going to open a session just because I think that that might hide away any problems with postgres reporting errors
98
95
if err := db .WithTx (func (ctx * db.Context ) error {
@@ -109,39 +106,68 @@ func HashedAvatarLink(email string, size int) string {
109
106
}
110
107
return lowerEmail , nil
111
108
})
112
- if size > 0 {
113
- return setting .AppSubURL + "/avatar/" + url .PathEscape (sum ) + "?size=" + strconv .Itoa (size )
114
- }
115
- return setting .AppSubURL + "/avatar/" + url .PathEscape (sum )
109
+ return emailHash
116
110
}
117
111
118
- // MakeFinalAvatarURL constructs the final avatar URL string
119
- func MakeFinalAvatarURL (u * url.URL , size int ) string {
120
- vals := u .Query ()
121
- vals .Set ("d" , "identicon" )
122
- if size != DefaultAvatarSize {
123
- vals .Set ("s" , strconv .Itoa (size ))
112
+ // GenerateUserAvatarFastLink returns a fast link to the user's avatar via the local explore page.
113
+ func GenerateUserAvatarFastLink (userName string , size int ) string {
114
+ if size < 0 {
115
+ size = 0
124
116
}
125
- u . RawQuery = vals . Encode ( )
126
- return u . String ()
117
+ link := setting . AppSubURL + "/user/avatar/" + userName + "/" + strconv . Itoa ( size )
118
+ return setting . AppURL + strings . TrimPrefix ( link , setting . AppSubURL )[ 1 :]
127
119
}
128
120
129
- // SizedAvatarLink returns a sized link to the avatar for the given email address.
130
- func SizedAvatarLink (email string , size int ) string {
121
+ // generateEmailAvatarLink returns a email avatar link.
122
+ // if final is true, it may use a slow path (eg: query DNS).
123
+ // if final is false, it always uses a fast path.
124
+ func generateEmailAvatarLink (email string , size int , final bool ) string {
125
+ if size <= 0 {
126
+ size = DefaultAvatarSize
127
+ }
128
+
129
+ email = strings .TrimSpace (email )
130
+ if email == "" {
131
+ return DefaultAvatarLink ()
132
+ }
133
+
131
134
var avatarURL * url.URL
135
+ var err error
136
+
132
137
if setting .EnableFederatedAvatar && setting .LibravatarService != nil {
133
- // This is the slow path that would need to call LibravatarURL() which
134
- // does DNS lookups. Avoid it by issuing a redirect so we don't block
135
- // the template render with network requests.
136
- return HashedAvatarLink (email , size )
138
+ emailHash := saveEmailHash (email )
139
+ if final {
140
+ if avatarURL , err = LibravatarURL (email ); err != nil {
141
+ return DefaultAvatarLink ()
142
+ }
143
+ } else {
144
+ if size > 0 {
145
+ return setting .AppSubURL + "/avatar/" + emailHash + "?size=" + strconv .Itoa (size )
146
+ }
147
+ return setting .AppSubURL + "/avatar/" + emailHash
148
+ }
137
149
} else if ! setting .DisableGravatar {
138
- // copy GravatarSourceURL, because we will modify its Path.
139
- copyOfGravatarSourceURL := * setting .GravatarSourceURL
140
- avatarURL = & copyOfGravatarSourceURL
150
+ avatarURLDummy := * setting .GravatarSourceURL // copy GravatarSourceURL, because we will modify its Path.
151
+ avatarURL = & avatarURLDummy
141
152
avatarURL .Path = path .Join (avatarURL .Path , HashEmail (email ))
142
153
} else {
143
154
return DefaultAvatarLink ()
144
155
}
145
156
146
- return MakeFinalAvatarURL (avatarURL , size )
157
+ avatarURL .Query ().Set ("d" , "identicon" )
158
+ if size > 0 {
159
+ avatarURL .Query ().Set ("s" , strconv .Itoa (size ))
160
+ }
161
+ avatarURL .RawQuery = avatarURL .Query ().Encode ()
162
+ return avatarURL .String ()
163
+ }
164
+
165
+ //GenerateEmailAvatarFastLink returns a avatar link (fast, the link may be a delegated one)
166
+ func GenerateEmailAvatarFastLink (email string , size int ) string {
167
+ return generateEmailAvatarLink (email , size , false )
168
+ }
169
+
170
+ //GenerateEmailAvatarFinalLink returns a avatar final link (maybe slow)
171
+ func GenerateEmailAvatarFinalLink (email string , size int ) string {
172
+ return generateEmailAvatarLink (email , size , true )
147
173
}
0 commit comments