Skip to content

Commit e3a5b83

Browse files
masudur-rahmanzeripath
authored andcommitted
Fix user avatar name (#8547)
Migrate avatar names to include user ID and the md5 hash.
1 parent 145c1ea commit e3a5b83

File tree

3 files changed

+119
-1
lines changed

3 files changed

+119
-1
lines changed

models/migrations/migrations.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,8 @@ var migrations = []Migration{
284284
NewMigration("new feature: change target branch of pull requests", featureChangeTargetBranch),
285285
// v114 -> v115
286286
NewMigration("Remove authentication credentials from stored URL", sanitizeOriginalURL),
287+
// v115 -> v116
288+
NewMigration("add user_id prefix to existing user avatar name", renameExistingUserAvatarName),
287289
}
288290

289291
// Migrate database to current version

models/migrations/v115.go

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// Copyright 2019 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package migrations
6+
7+
import (
8+
"crypto/md5"
9+
"fmt"
10+
"io/ioutil"
11+
"os"
12+
"path/filepath"
13+
14+
"code.gitea.io/gitea/modules/log"
15+
"code.gitea.io/gitea/modules/setting"
16+
17+
"xorm.io/xorm"
18+
)
19+
20+
func renameExistingUserAvatarName(x *xorm.Engine) error {
21+
sess := x.NewSession()
22+
defer sess.Close()
23+
24+
type User struct {
25+
ID int64 `xorm:"pk autoincr"`
26+
LowerName string `xorm:"UNIQUE NOT NULL"`
27+
Avatar string
28+
}
29+
deleteList := make(map[string]struct{})
30+
start := 0
31+
for {
32+
if err := sess.Begin(); err != nil {
33+
return fmt.Errorf("session.Begin: %v", err)
34+
}
35+
users := make([]*User, 0, 50)
36+
if err := sess.Table("user").Asc("id").Limit(50, start).Find(&users); err != nil {
37+
return fmt.Errorf("select users from id [%d]: %v", start, err)
38+
}
39+
if len(users) == 0 {
40+
_ = sess.Rollback()
41+
break
42+
}
43+
44+
log.Info("select users [%d - %d]", start, start+len(users))
45+
start += 50
46+
47+
for _, user := range users {
48+
oldAvatar := user.Avatar
49+
50+
if _, err := os.Stat(filepath.Join(setting.AvatarUploadPath, oldAvatar)); err != nil {
51+
log.Warn("[user: %s] os.Stat: %v", user.LowerName, err)
52+
// avatar doesn't exist in the storage
53+
// no need to move avatar and update database
54+
// we can just skip this
55+
continue
56+
}
57+
58+
newAvatar, err := copyOldAvatarToNewLocation(user.ID, oldAvatar)
59+
if err != nil {
60+
_ = sess.Rollback()
61+
return fmt.Errorf("[user: %s] %v", user.LowerName, err)
62+
} else if newAvatar == oldAvatar {
63+
continue
64+
}
65+
66+
user.Avatar = newAvatar
67+
if _, err := sess.ID(user.ID).Cols("avatar").Update(user); err != nil {
68+
_ = sess.Rollback()
69+
return fmt.Errorf("[user: %s] user table update: %v", user.LowerName, err)
70+
}
71+
72+
deleteList[filepath.Join(setting.AvatarUploadPath, oldAvatar)] = struct{}{}
73+
}
74+
if err := sess.Commit(); err != nil {
75+
_ = sess.Rollback()
76+
return fmt.Errorf("commit session: %v", err)
77+
}
78+
}
79+
80+
for file := range deleteList {
81+
if err := os.Remove(file); err != nil {
82+
log.Warn("os.Remove: %v", err)
83+
}
84+
}
85+
return nil
86+
}
87+
88+
// copyOldAvatarToNewLocation copies oldAvatar to newAvatarLocation
89+
// and returns newAvatar location
90+
func copyOldAvatarToNewLocation(userID int64, oldAvatar string) (string, error) {
91+
fr, err := os.Open(filepath.Join(setting.AvatarUploadPath, oldAvatar))
92+
if err != nil {
93+
return "", fmt.Errorf("os.Open: %v", err)
94+
}
95+
defer fr.Close()
96+
97+
data, err := ioutil.ReadAll(fr)
98+
if err != nil {
99+
return "", fmt.Errorf("ioutil.ReadAll: %v", err)
100+
}
101+
102+
newAvatar := fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%d-%x", userID, md5.Sum(data)))))
103+
if newAvatar == oldAvatar {
104+
return newAvatar, nil
105+
}
106+
107+
if err := ioutil.WriteFile(filepath.Join(setting.AvatarUploadPath, newAvatar), data, 0666); err != nil {
108+
return "", fmt.Errorf("ioutil.WriteFile: %v", err)
109+
}
110+
111+
return newAvatar, nil
112+
}

models/user.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,11 @@ func (u *User) UploadAvatar(data []byte) error {
521521
}
522522

523523
u.UseCustomAvatar = true
524-
u.Avatar = fmt.Sprintf("%x", md5.Sum(data))
524+
// Different users can upload same image as avatar
525+
// If we prefix it with u.ID, it will be separated
526+
// Otherwise, if any of the users delete his avatar
527+
// Other users will lose their avatars too.
528+
u.Avatar = fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%d-%x", u.ID, md5.Sum(data)))))
525529
if err = updateUser(sess, u); err != nil {
526530
return fmt.Errorf("updateUser: %v", err)
527531
}

0 commit comments

Comments
 (0)