mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	Refactor legacy unknwon/com package, improve golangci lint (#19284)
				
					
				
			The main purpose is to refactor the legacy `unknwon/com` package. 1. Remove most imports of `unknwon/com`, only `util/legacy.go` imports the legacy `unknwon/com` 2. Use golangci's depguard to process denied packages 3. Fix some incorrect values in golangci.yml, eg, the version should be quoted string `"1.18"` 4. Use correctly escaped content for `go-import` and `go-source` meta tags 5. Refactor `com.Expand` to our stable (and the same fast) `vars.Expand`, our `vars.Expand` can still return partially rendered content even if the template is not good (eg: key mistach).
This commit is contained in:
		@@ -18,6 +18,7 @@ linters:
 | 
				
			|||||||
    - ineffassign
 | 
					    - ineffassign
 | 
				
			||||||
    - revive
 | 
					    - revive
 | 
				
			||||||
    - gofumpt
 | 
					    - gofumpt
 | 
				
			||||||
 | 
					    - depguard
 | 
				
			||||||
  enable-all: false
 | 
					  enable-all: false
 | 
				
			||||||
  disable-all: true
 | 
					  disable-all: true
 | 
				
			||||||
  fast: false
 | 
					  fast: false
 | 
				
			||||||
@@ -65,7 +66,15 @@ linters-settings:
 | 
				
			|||||||
      - name: modifies-value-receiver
 | 
					      - name: modifies-value-receiver
 | 
				
			||||||
  gofumpt:
 | 
					  gofumpt:
 | 
				
			||||||
    extra-rules: true
 | 
					    extra-rules: true
 | 
				
			||||||
    lang-version: 1.18
 | 
					    lang-version: "1.18"
 | 
				
			||||||
 | 
					  depguard:
 | 
				
			||||||
 | 
					    # TODO: use depguard to replace import checks in gitea-vet
 | 
				
			||||||
 | 
					    list-type: denylist
 | 
				
			||||||
 | 
					    # Check the list against standard lib.
 | 
				
			||||||
 | 
					    include-go-root: true
 | 
				
			||||||
 | 
					    packages-with-error-message:
 | 
				
			||||||
 | 
					      - encoding/json: "use gitea's modules/json instead of encoding/json"
 | 
				
			||||||
 | 
					      - github.com/unknwon/com: "use gitea's util and replacements"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
issues:
 | 
					issues:
 | 
				
			||||||
  exclude-rules:
 | 
					  exclude-rules:
 | 
				
			||||||
@@ -153,5 +162,6 @@ issues:
 | 
				
			|||||||
    - path: models/user/openid.go
 | 
					    - path: models/user/openid.go
 | 
				
			||||||
      linters:
 | 
					      linters:
 | 
				
			||||||
        - golint
 | 
					        - golint
 | 
				
			||||||
    - linters: staticcheck
 | 
					    - linters:
 | 
				
			||||||
 | 
					        - staticcheck
 | 
				
			||||||
      text: "strings.Title is deprecated: The rule Title uses for word boundaries does not handle Unicode punctuation properly. Use golang.org/x/text/cases instead."
 | 
					      text: "strings.Title is deprecated: The rule Title uses for word boundaries does not handle Unicode punctuation properly. Use golang.org/x/text/cases instead."
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,8 +29,7 @@ func TestGoGet(t *testing.T) {
 | 
				
			|||||||
	<body>
 | 
						<body>
 | 
				
			||||||
		go get --insecure %[1]s:%[2]s/blah/glah
 | 
							go get --insecure %[1]s:%[2]s/blah/glah
 | 
				
			||||||
	</body>
 | 
						</body>
 | 
				
			||||||
</html>
 | 
					</html>`, setting.Domain, setting.HTTPPort, setting.AppURL)
 | 
				
			||||||
`, setting.Domain, setting.HTTPPort, setting.AppURL)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert.Equal(t, expected, resp.Body.String())
 | 
						assert.Equal(t, expected, resp.Body.String())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,7 +24,6 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/modules/util"
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
	"github.com/unknwon/com"
 | 
					 | 
				
			||||||
	"xorm.io/xorm"
 | 
						"xorm.io/xorm"
 | 
				
			||||||
	"xorm.io/xorm/names"
 | 
						"xorm.io/xorm/names"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -204,7 +203,7 @@ func prepareTestEnv(t *testing.T, skip int, syncModels ...interface{}) (*xorm.En
 | 
				
			|||||||
	deferFn := PrintCurrentTest(t, ourSkip)
 | 
						deferFn := PrintCurrentTest(t, ourSkip)
 | 
				
			||||||
	assert.NoError(t, os.RemoveAll(setting.RepoRootPath))
 | 
						assert.NoError(t, os.RemoveAll(setting.RepoRootPath))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert.NoError(t, com.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"),
 | 
						assert.NoError(t, util.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"),
 | 
				
			||||||
		setting.RepoRootPath))
 | 
							setting.RepoRootPath))
 | 
				
			||||||
	ownerDirs, err := os.ReadDir(setting.RepoRootPath)
 | 
						ownerDirs, err := os.ReadDir(setting.RepoRootPath)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										8
									
								
								modules/cache/cache_redis.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								modules/cache/cache_redis.go
									
									
									
									
										vendored
									
									
								
							@@ -10,10 +10,10 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/graceful"
 | 
						"code.gitea.io/gitea/modules/graceful"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/nosql"
 | 
						"code.gitea.io/gitea/modules/nosql"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gitea.com/go-chi/cache"
 | 
						"gitea.com/go-chi/cache"
 | 
				
			||||||
	"github.com/go-redis/redis/v8"
 | 
						"github.com/go-redis/redis/v8"
 | 
				
			||||||
	"github.com/unknwon/com"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RedisCacher represents a redis cache adapter implementation.
 | 
					// RedisCacher represents a redis cache adapter implementation.
 | 
				
			||||||
@@ -29,15 +29,15 @@ type RedisCacher struct {
 | 
				
			|||||||
func (c *RedisCacher) Put(key string, val interface{}, expire int64) error {
 | 
					func (c *RedisCacher) Put(key string, val interface{}, expire int64) error {
 | 
				
			||||||
	key = c.prefix + key
 | 
						key = c.prefix + key
 | 
				
			||||||
	if expire == 0 {
 | 
						if expire == 0 {
 | 
				
			||||||
		if err := c.c.Set(graceful.GetManager().HammerContext(), key, com.ToStr(val), 0).Err(); err != nil {
 | 
							if err := c.c.Set(graceful.GetManager().HammerContext(), key, util.ToStr(val), 0).Err(); err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		dur, err := time.ParseDuration(com.ToStr(expire) + "s")
 | 
							dur, err := time.ParseDuration(util.ToStr(expire) + "s")
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if err = c.c.Set(graceful.GetManager().HammerContext(), key, com.ToStr(val), dur).Err(); err != nil {
 | 
							if err = c.c.Set(graceful.GetManager().HammerContext(), key, util.ToStr(val), dur).Err(); err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,13 +31,13 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/templates"
 | 
						"code.gitea.io/gitea/modules/templates"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/translation"
 | 
						"code.gitea.io/gitea/modules/translation"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/web/middleware"
 | 
						"code.gitea.io/gitea/modules/web/middleware"
 | 
				
			||||||
	"code.gitea.io/gitea/services/auth"
 | 
						"code.gitea.io/gitea/services/auth"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gitea.com/go-chi/cache"
 | 
						"gitea.com/go-chi/cache"
 | 
				
			||||||
	"gitea.com/go-chi/session"
 | 
						"gitea.com/go-chi/session"
 | 
				
			||||||
	chi "github.com/go-chi/chi/v5"
 | 
						chi "github.com/go-chi/chi/v5"
 | 
				
			||||||
	"github.com/unknwon/com"
 | 
					 | 
				
			||||||
	"github.com/unrolled/render"
 | 
						"github.com/unrolled/render"
 | 
				
			||||||
	"golang.org/x/crypto/pbkdf2"
 | 
						"golang.org/x/crypto/pbkdf2"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -475,7 +475,7 @@ func (ctx *Context) CookieDecrypt(secret, val string) (string, bool) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	key := pbkdf2.Key([]byte(secret), []byte(secret), 1000, 16, sha256.New)
 | 
						key := pbkdf2.Key([]byte(secret), []byte(secret), 1000, 16, sha256.New)
 | 
				
			||||||
	text, err = com.AESGCMDecrypt(key, text)
 | 
						text, err = util.AESGCMDecrypt(key, text)
 | 
				
			||||||
	return string(text), err == nil
 | 
						return string(text), err == nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -489,7 +489,7 @@ func (ctx *Context) SetSuperSecureCookie(secret, name, value string, expiry int)
 | 
				
			|||||||
// CookieEncrypt encrypts a given value using the provided secret
 | 
					// CookieEncrypt encrypts a given value using the provided secret
 | 
				
			||||||
func (ctx *Context) CookieEncrypt(secret, value string) string {
 | 
					func (ctx *Context) CookieEncrypt(secret, value string) string {
 | 
				
			||||||
	key := pbkdf2.Key([]byte(secret), []byte(secret), 1000, 16, sha256.New)
 | 
						key := pbkdf2.Key([]byte(secret), []byte(secret), 1000, 16, sha256.New)
 | 
				
			||||||
	text, err := com.AESGCMEncrypt(key, []byte(value))
 | 
						text, err := util.AESGCMEncrypt(key, []byte(value))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		panic("error encrypting cookie: " + err.Error())
 | 
							panic("error encrypting cookie: " + err.Error())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,13 +19,14 @@
 | 
				
			|||||||
package context
 | 
					package context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/base32"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/web/middleware"
 | 
						"code.gitea.io/gitea/modules/web/middleware"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/unknwon/com"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CSRF represents a CSRF service and is used to get the current token and validate a suspect token.
 | 
					// CSRF represents a CSRF service and is used to get the current token and validate a suspect token.
 | 
				
			||||||
@@ -162,7 +163,12 @@ func prepareOptions(options []CsrfOptions) CsrfOptions {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// Defaults.
 | 
						// Defaults.
 | 
				
			||||||
	if len(opt.Secret) == 0 {
 | 
						if len(opt.Secret) == 0 {
 | 
				
			||||||
		opt.Secret = string(com.RandomCreateBytes(10))
 | 
							randBytes, err := util.CryptoRandomBytes(8)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								// this panic can be handled by the recover() in http handlers
 | 
				
			||||||
 | 
								panic(fmt.Errorf("failed to generate random bytes: %w", err))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							opt.Secret = base32.StdEncoding.EncodeToString(randBytes)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if len(opt.Header) == 0 {
 | 
						if len(opt.Header) == 0 {
 | 
				
			||||||
		opt.Header = "X-CSRFToken"
 | 
							opt.Header = "X-CSRFToken"
 | 
				
			||||||
@@ -211,7 +217,7 @@ func Csrfer(opt CsrfOptions, ctx *Context) CSRF {
 | 
				
			|||||||
	x.ID = "0"
 | 
						x.ID = "0"
 | 
				
			||||||
	uid := ctx.Session.Get(opt.SessionKey)
 | 
						uid := ctx.Session.Get(opt.SessionKey)
 | 
				
			||||||
	if uid != nil {
 | 
						if uid != nil {
 | 
				
			||||||
		x.ID = com.ToStr(uid)
 | 
							x.ID = util.ToStr(uid)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	needsNew := false
 | 
						needsNew := false
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,6 +8,7 @@ package context
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"html"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
@@ -29,7 +30,6 @@ import (
 | 
				
			|||||||
	asymkey_service "code.gitea.io/gitea/services/asymkey"
 | 
						asymkey_service "code.gitea.io/gitea/services/asymkey"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/editorconfig/editorconfig-core-go/v2"
 | 
						"github.com/editorconfig/editorconfig-core-go/v2"
 | 
				
			||||||
	"github.com/unknwon/com"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IssueTemplateDirCandidates issue templates directory
 | 
					// IssueTemplateDirCandidates issue templates directory
 | 
				
			||||||
@@ -308,11 +308,9 @@ func EarlyResponseForGoGetMeta(ctx *Context) {
 | 
				
			|||||||
		ctx.PlainText(http.StatusBadRequest, "invalid repository path")
 | 
							ctx.PlainText(http.StatusBadRequest, "invalid repository path")
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ctx.PlainText(http.StatusOK, com.Expand(`<meta name="go-import" content="{GoGetImport} git {CloneLink}">`,
 | 
						goImportContent := fmt.Sprintf("%s git %s", ComposeGoGetImport(username, reponame), repo_model.ComposeHTTPSCloneURL(username, reponame))
 | 
				
			||||||
		map[string]string{
 | 
						htmlMeta := fmt.Sprintf(`<meta name="go-import" content="%s">`, html.EscapeString(goImportContent))
 | 
				
			||||||
			"GoGetImport": ComposeGoGetImport(username, reponame),
 | 
						ctx.PlainText(http.StatusOK, htmlMeta)
 | 
				
			||||||
			"CloneLink":   repo_model.ComposeHTTPSCloneURL(username, reponame),
 | 
					 | 
				
			||||||
		}))
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RedirectToRepo redirect to a differently-named repository
 | 
					// RedirectToRepo redirect to a differently-named repository
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,7 @@ package json
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
	"encoding/binary"
 | 
						"encoding/binary"
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json" //nolint:depguard
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	jsoniter "github.com/json-iterator/go"
 | 
						jsoniter "github.com/json-iterator/go"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,9 +21,9 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/modules/markup/common"
 | 
						"code.gitea.io/gitea/modules/markup/common"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/references"
 | 
						"code.gitea.io/gitea/modules/references"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/templates/vars"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/util"
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/unknwon/com"
 | 
					 | 
				
			||||||
	"golang.org/x/net/html"
 | 
						"golang.org/x/net/html"
 | 
				
			||||||
	"golang.org/x/net/html/atom"
 | 
						"golang.org/x/net/html/atom"
 | 
				
			||||||
	"mvdan.cc/xurls/v2"
 | 
						"mvdan.cc/xurls/v2"
 | 
				
			||||||
@@ -838,7 +838,14 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
 | 
				
			|||||||
		reftext := node.Data[ref.RefLocation.Start:ref.RefLocation.End]
 | 
							reftext := node.Data[ref.RefLocation.Start:ref.RefLocation.End]
 | 
				
			||||||
		if exttrack && !ref.IsPull {
 | 
							if exttrack && !ref.IsPull {
 | 
				
			||||||
			ctx.Metas["index"] = ref.Issue
 | 
								ctx.Metas["index"] = ref.Issue
 | 
				
			||||||
			link = createLink(com.Expand(ctx.Metas["format"], ctx.Metas), reftext, "ref-issue ref-external-issue")
 | 
					
 | 
				
			||||||
 | 
								res, err := vars.Expand(ctx.Metas["format"], ctx.Metas)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									// here we could just log the error and continue the rendering
 | 
				
			||||||
 | 
									log.Error("unable to expand template vars for ref %s, err: %v", ref.Issue, err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								link = createLink(res, reftext, "ref-issue ref-external-issue")
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			// Path determines the type of link that will be rendered. It's unknown at this point whether
 | 
								// Path determines the type of link that will be rendered. It's unknown at this point whether
 | 
				
			||||||
			// the linked item is actually a PR or an issue. Luckily it's of no real consequence because
 | 
								// the linked item is actually a PR or an issue. Luckily it's of no real consequence because
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,10 +22,9 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/options"
 | 
						"code.gitea.io/gitea/modules/options"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/templates/vars"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/util"
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
	asymkey_service "code.gitea.io/gitea/services/asymkey"
 | 
						asymkey_service "code.gitea.io/gitea/services/asymkey"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/unknwon/com"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
@@ -250,8 +249,13 @@ func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir,
 | 
				
			|||||||
		"CloneURL.HTTPS": cloneLink.HTTPS,
 | 
							"CloneURL.HTTPS": cloneLink.HTTPS,
 | 
				
			||||||
		"OwnerName":      repo.OwnerName,
 | 
							"OwnerName":      repo.OwnerName,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						res, err := vars.Expand(string(data), match)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							// here we could just log the error and continue the rendering
 | 
				
			||||||
 | 
							log.Error("unable to expand template vars for repo README: %s, err: %v", opts.Readme, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if err = os.WriteFile(filepath.Join(tmpDir, "README.md"),
 | 
						if err = os.WriteFile(filepath.Join(tmpDir, "README.md"),
 | 
				
			||||||
		[]byte(com.Expand(string(data), match)), 0o644); err != nil {
 | 
							[]byte(res), 0o644); err != nil {
 | 
				
			||||||
		return fmt.Errorf("write README.md: %v", err)
 | 
							return fmt.Errorf("write README.md: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,7 +27,6 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/modules/user"
 | 
						"code.gitea.io/gitea/modules/user"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/util"
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/unknwon/com"
 | 
					 | 
				
			||||||
	gossh "golang.org/x/crypto/ssh"
 | 
						gossh "golang.org/x/crypto/ssh"
 | 
				
			||||||
	ini "gopkg.in/ini.v1"
 | 
						ini "gopkg.in/ini.v1"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -612,7 +611,7 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	Cfg.NameMapper = ini.SnackCase
 | 
						Cfg.NameMapper = ini.SnackCase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	homeDir, err := com.HomeDir()
 | 
						homeDir, err := util.HomeDir()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Fatal("Failed to get home directory: %v", err)
 | 
							log.Fatal("Failed to get home directory: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,9 +5,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package sync
 | 
					package sync
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import "code.gitea.io/gitea/modules/util"
 | 
				
			||||||
	"github.com/unknwon/com"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UniqueQueue is a queue which guarantees only one instance of same
 | 
					// UniqueQueue is a queue which guarantees only one instance of same
 | 
				
			||||||
// identity is in the line. Instances with same identity will be
 | 
					// identity is in the line. Instances with same identity will be
 | 
				
			||||||
@@ -73,13 +71,13 @@ func (q *UniqueQueue) Queue() <-chan string {
 | 
				
			|||||||
// Exist returns true if there is an instance with given identity
 | 
					// Exist returns true if there is an instance with given identity
 | 
				
			||||||
// exists in the queue.
 | 
					// exists in the queue.
 | 
				
			||||||
func (q *UniqueQueue) Exist(id interface{}) bool {
 | 
					func (q *UniqueQueue) Exist(id interface{}) bool {
 | 
				
			||||||
	return q.table.IsRunning(com.ToStr(id))
 | 
						return q.table.IsRunning(util.ToStr(id))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AddFunc adds new instance to the queue with a custom runnable function,
 | 
					// AddFunc adds new instance to the queue with a custom runnable function,
 | 
				
			||||||
// the queue is blocked until the function exits.
 | 
					// the queue is blocked until the function exits.
 | 
				
			||||||
func (q *UniqueQueue) AddFunc(id interface{}, fn func()) {
 | 
					func (q *UniqueQueue) AddFunc(id interface{}, fn func()) {
 | 
				
			||||||
	idStr := com.ToStr(id)
 | 
						idStr := util.ToStr(id)
 | 
				
			||||||
	q.table.lock.Lock()
 | 
						q.table.lock.Lock()
 | 
				
			||||||
	if _, ok := q.table.pool[idStr]; ok {
 | 
						if _, ok := q.table.pool[idStr]; ok {
 | 
				
			||||||
		q.table.lock.Unlock()
 | 
							q.table.lock.Unlock()
 | 
				
			||||||
@@ -105,5 +103,5 @@ func (q *UniqueQueue) Add(id interface{}) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Remove removes instance from the queue.
 | 
					// Remove removes instance from the queue.
 | 
				
			||||||
func (q *UniqueQueue) Remove(id interface{}) {
 | 
					func (q *UniqueQueue) Remove(id interface{}) {
 | 
				
			||||||
	q.table.Stop(com.ToStr(id))
 | 
						q.table.Stop(util.ToStr(id))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										93
									
								
								modules/templates/vars/vars.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								modules/templates/vars/vars.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,93 @@
 | 
				
			|||||||
 | 
					// Copyright 2022 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 vars
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"unicode"
 | 
				
			||||||
 | 
						"unicode/utf8"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ErrWrongSyntax represents a wrong syntax with a template
 | 
				
			||||||
 | 
					type ErrWrongSyntax struct {
 | 
				
			||||||
 | 
						Template string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (err ErrWrongSyntax) Error() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("wrong syntax found in %s", err.Template)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ErrVarMissing represents an error that no matched variable
 | 
				
			||||||
 | 
					type ErrVarMissing struct {
 | 
				
			||||||
 | 
						Template string
 | 
				
			||||||
 | 
						Var      string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (err ErrVarMissing) Error() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("the variable %s is missing for %s", err.Var, err.Template)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Expand replaces all variables like {var} by `vars` map, it always returns the expanded string regardless of errors
 | 
				
			||||||
 | 
					// if error occurs, the error part doesn't change and is returned as it is.
 | 
				
			||||||
 | 
					func Expand(template string, vars map[string]string) (string, error) {
 | 
				
			||||||
 | 
						// in the future, if necessary, we can introduce some escape-char,
 | 
				
			||||||
 | 
						// for example: it will use `#' as a reversed char, templates will use `{#{}` to do escape and output char '{'.
 | 
				
			||||||
 | 
						var buf strings.Builder
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						posBegin := 0
 | 
				
			||||||
 | 
						strLen := len(template)
 | 
				
			||||||
 | 
						for posBegin < strLen {
 | 
				
			||||||
 | 
							// find the next `{`
 | 
				
			||||||
 | 
							pos := strings.IndexByte(template[posBegin:], '{')
 | 
				
			||||||
 | 
							if pos == -1 {
 | 
				
			||||||
 | 
								buf.WriteString(template[posBegin:])
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// copy texts between vars
 | 
				
			||||||
 | 
							buf.WriteString(template[posBegin : posBegin+pos])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// find the var between `{` and `}`/end
 | 
				
			||||||
 | 
							posBegin += pos
 | 
				
			||||||
 | 
							posEnd := posBegin + 1
 | 
				
			||||||
 | 
							for posEnd < strLen {
 | 
				
			||||||
 | 
								if template[posEnd] == '}' {
 | 
				
			||||||
 | 
									posEnd++
 | 
				
			||||||
 | 
									break
 | 
				
			||||||
 | 
								} // in the future, if we need to support escape chars, we can do: if (isEscapeChar) { posEnd+=2 }
 | 
				
			||||||
 | 
								posEnd++
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// the var part, it can be "{", "{}", "{..." or or "{...}"
 | 
				
			||||||
 | 
							part := template[posBegin:posEnd]
 | 
				
			||||||
 | 
							posBegin = posEnd
 | 
				
			||||||
 | 
							if part == "{}" || part[len(part)-1] != '}' {
 | 
				
			||||||
 | 
								// treat "{}" or "{..." as error
 | 
				
			||||||
 | 
								err = ErrWrongSyntax{Template: template}
 | 
				
			||||||
 | 
								buf.WriteString(part)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								// now we get a valid key "{...}"
 | 
				
			||||||
 | 
								key := part[1 : len(part)-1]
 | 
				
			||||||
 | 
								keyFirst, _ := utf8.DecodeRuneInString(key)
 | 
				
			||||||
 | 
								if unicode.IsSpace(keyFirst) || unicode.IsPunct(keyFirst) || unicode.IsControl(keyFirst) {
 | 
				
			||||||
 | 
									// the if key doesn't start with a letter, then we do not treat it as a var now
 | 
				
			||||||
 | 
									buf.WriteString(part)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									// look up in the map
 | 
				
			||||||
 | 
									if val, ok := vars[key]; ok {
 | 
				
			||||||
 | 
										buf.WriteString(val)
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										// write the non-existing var as it is
 | 
				
			||||||
 | 
										buf.WriteString(part)
 | 
				
			||||||
 | 
										err = ErrVarMissing{Template: template, Var: key}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return buf.String(), err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										72
									
								
								modules/templates/vars/vars_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								modules/templates/vars/vars_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
				
			|||||||
 | 
					// Copyright 2022 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 vars
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestExpandVars(t *testing.T) {
 | 
				
			||||||
 | 
						kases := []struct {
 | 
				
			||||||
 | 
							tmpl  string
 | 
				
			||||||
 | 
							data  map[string]string
 | 
				
			||||||
 | 
							out   string
 | 
				
			||||||
 | 
							error bool
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								tmpl: "{a}",
 | 
				
			||||||
 | 
								data: map[string]string{
 | 
				
			||||||
 | 
									"a": "1",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								out: "1",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								tmpl: "expand {a}, {b} and {c}, with non-var { } {#}",
 | 
				
			||||||
 | 
								data: map[string]string{
 | 
				
			||||||
 | 
									"a": "1",
 | 
				
			||||||
 | 
									"b": "2",
 | 
				
			||||||
 | 
									"c": "3",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								out: "expand 1, 2 and 3, with non-var { } {#}",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								tmpl: "中文内容 {一}, {二} 和 {三} 中文结尾",
 | 
				
			||||||
 | 
								data: map[string]string{
 | 
				
			||||||
 | 
									"一": "11",
 | 
				
			||||||
 | 
									"二": "22",
 | 
				
			||||||
 | 
									"三": "33",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								out: "中文内容 11, 22 和 33 中文结尾",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								tmpl: "expand {{a}, {b} and {c}",
 | 
				
			||||||
 | 
								data: map[string]string{
 | 
				
			||||||
 | 
									"a": "foo",
 | 
				
			||||||
 | 
									"b": "bar",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								out:   "expand {{a}, bar and {c}",
 | 
				
			||||||
 | 
								error: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								tmpl:  "expand } {} and {",
 | 
				
			||||||
 | 
								out:   "expand } {} and {",
 | 
				
			||||||
 | 
								error: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, kase := range kases {
 | 
				
			||||||
 | 
							t.Run(kase.tmpl, func(t *testing.T) {
 | 
				
			||||||
 | 
								res, err := Expand(kase.tmpl, kase.data)
 | 
				
			||||||
 | 
								assert.EqualValues(t, kase.out, res)
 | 
				
			||||||
 | 
								if kase.error {
 | 
				
			||||||
 | 
									assert.Error(t, err)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									assert.NoError(t, err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,20 +0,0 @@
 | 
				
			|||||||
// 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 util
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"github.com/unknwon/com"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// CopyFile copies file from source to target path.
 | 
					 | 
				
			||||||
func CopyFile(src, dest string) error {
 | 
					 | 
				
			||||||
	return com.Copy(src, dest)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// CopyDir copy files recursively from source to target directory.
 | 
					 | 
				
			||||||
// It returns error when error occurs in underlying functions.
 | 
					 | 
				
			||||||
func CopyDir(srcPath, destPath string) error {
 | 
					 | 
				
			||||||
	return com.CopyDir(srcPath, destPath)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -9,7 +9,7 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ReadAtMost reads at most len(buf) bytes from r into buf.
 | 
					// ReadAtMost reads at most len(buf) bytes from r into buf.
 | 
				
			||||||
// It returns the number of bytes copied. n is only less then len(buf) if r provides fewer bytes.
 | 
					// It returns the number of bytes copied. n is only less than len(buf) if r provides fewer bytes.
 | 
				
			||||||
// If EOF occurs while reading, err will be nil.
 | 
					// If EOF occurs while reading, err will be nil.
 | 
				
			||||||
func ReadAtMost(r io.Reader, buf []byte) (n int, err error) {
 | 
					func ReadAtMost(r io.Reader, buf []byte) (n int, err error) {
 | 
				
			||||||
	n, err = io.ReadFull(r, buf)
 | 
						n, err = io.ReadFull(r, buf)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										84
									
								
								modules/util/legacy.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								modules/util/legacy.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,84 @@
 | 
				
			|||||||
 | 
					// Copyright 2022 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 util
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"crypto/aes"
 | 
				
			||||||
 | 
						"crypto/cipher"
 | 
				
			||||||
 | 
						"crypto/rand"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/unknwon/com" //nolint:depguard
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CopyFile copies file from source to target path.
 | 
				
			||||||
 | 
					func CopyFile(src, dest string) error {
 | 
				
			||||||
 | 
						return com.Copy(src, dest)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CopyDir copy files recursively from source to target directory.
 | 
				
			||||||
 | 
					// It returns error when error occurs in underlying functions.
 | 
				
			||||||
 | 
					func CopyDir(srcPath, destPath string) error {
 | 
				
			||||||
 | 
						return com.CopyDir(srcPath, destPath)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ToStr converts any interface to string. should be replaced.
 | 
				
			||||||
 | 
					func ToStr(value interface{}, args ...int) string {
 | 
				
			||||||
 | 
						return com.ToStr(value, args...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ToSnakeCase converts a string to snake_case. should be replaced.
 | 
				
			||||||
 | 
					func ToSnakeCase(str string) string {
 | 
				
			||||||
 | 
						return com.ToSnakeCase(str)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AESGCMEncrypt (from legacy package): encrypts plaintext with the given key using AES in GCM mode. should be replaced.
 | 
				
			||||||
 | 
					func AESGCMEncrypt(key, plaintext []byte) ([]byte, error) {
 | 
				
			||||||
 | 
						block, err := aes.NewCipher(key)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						gcm, err := cipher.NewGCM(block)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nonce := make([]byte, gcm.NonceSize())
 | 
				
			||||||
 | 
						if _, err := rand.Read(nonce); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ciphertext := gcm.Seal(nil, nonce, plaintext, nil)
 | 
				
			||||||
 | 
						return append(nonce, ciphertext...), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AESGCMDecrypt (from legacy package): decrypts ciphertext with the given key using AES in GCM mode. should be replaced.
 | 
				
			||||||
 | 
					func AESGCMDecrypt(key, ciphertext []byte) ([]byte, error) {
 | 
				
			||||||
 | 
						block, err := aes.NewCipher(key)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						gcm, err := cipher.NewGCM(block)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size := gcm.NonceSize()
 | 
				
			||||||
 | 
						if len(ciphertext)-size <= 0 {
 | 
				
			||||||
 | 
							return nil, errors.New("ciphertext is empty")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nonce := ciphertext[:size]
 | 
				
			||||||
 | 
						ciphertext = ciphertext[size:]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						plainText, err := gcm.Open(nil, nonce, ciphertext, nil)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return plainText, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										37
									
								
								modules/util/legacy_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								modules/util/legacy_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					// Copyright 2022 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 util
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"crypto/aes"
 | 
				
			||||||
 | 
						"crypto/rand"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
 | 
						"github.com/unknwon/com" //nolint:depguard
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestAESGCM(t *testing.T) {
 | 
				
			||||||
 | 
						t.Parallel()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						key := make([]byte, aes.BlockSize)
 | 
				
			||||||
 | 
						_, err := rand.Read(key)
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						plaintext := []byte("this will be encrypted")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ciphertext, err := AESGCMEncrypt(key, plaintext)
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						decrypted, err := AESGCMDecrypt(key, ciphertext)
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert.Equal(t, plaintext, decrypted)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// at the moment, we make sure the result is the same as the legacy package, this assertion can be removed in next round refactoring
 | 
				
			||||||
 | 
						legacy, err := com.AESGCMDecrypt(key, ciphertext)
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						assert.Equal(t, legacy, plaintext)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -154,6 +154,10 @@ func StatDir(rootPath string, includeDir ...bool) ([]string, error) {
 | 
				
			|||||||
	return statDir(rootPath, "", isIncludeDir, false, false)
 | 
						return statDir(rootPath, "", isIncludeDir, false, false)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func isOSWindows() bool {
 | 
				
			||||||
 | 
						return runtime.GOOS == "windows"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// FileURLToPath extracts the path information from a file://... url.
 | 
					// FileURLToPath extracts the path information from a file://... url.
 | 
				
			||||||
func FileURLToPath(u *url.URL) (string, error) {
 | 
					func FileURLToPath(u *url.URL) (string, error) {
 | 
				
			||||||
	if u.Scheme != "file" {
 | 
						if u.Scheme != "file" {
 | 
				
			||||||
@@ -162,7 +166,7 @@ func FileURLToPath(u *url.URL) (string, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	path := u.Path
 | 
						path := u.Path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if runtime.GOOS != "windows" {
 | 
						if !isOSWindows() {
 | 
				
			||||||
		return path, nil
 | 
							return path, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -173,3 +177,24 @@ func FileURLToPath(u *url.URL) (string, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return path, nil
 | 
						return path, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// HomeDir returns path of '~'(in Linux) on Windows,
 | 
				
			||||||
 | 
					// it returns error when the variable does not exist.
 | 
				
			||||||
 | 
					func HomeDir() (home string, err error) {
 | 
				
			||||||
 | 
						// TODO: some users run Gitea with mismatched uid  and "HOME=xxx" (they set HOME=xxx by environment manually)
 | 
				
			||||||
 | 
						// so at the moment we can not use `user.Current().HomeDir`
 | 
				
			||||||
 | 
						if isOSWindows() {
 | 
				
			||||||
 | 
							home = os.Getenv("USERPROFILE")
 | 
				
			||||||
 | 
							if home == "" {
 | 
				
			||||||
 | 
								home = os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							home = os.Getenv("HOME")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if home == "" {
 | 
				
			||||||
 | 
							return "", errors.New("cannot get home directory")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return home, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,10 +10,10 @@ import (
 | 
				
			|||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/translation"
 | 
						"code.gitea.io/gitea/modules/translation"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/validation"
 | 
						"code.gitea.io/gitea/modules/validation"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gitea.com/go-chi/binding"
 | 
						"gitea.com/go-chi/binding"
 | 
				
			||||||
	"github.com/unknwon/com"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Form form binding interface
 | 
					// Form form binding interface
 | 
				
			||||||
@@ -22,7 +22,7 @@ type Form interface {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
	binding.SetNameMapper(com.ToSnakeCase)
 | 
						binding.SetNameMapper(util.ToSnakeCase)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AssignForm assign form values back to the template data.
 | 
					// AssignForm assign form values back to the template data.
 | 
				
			||||||
@@ -43,7 +43,7 @@ func AssignForm(form interface{}, data map[string]interface{}) {
 | 
				
			|||||||
		if fieldName == "-" {
 | 
							if fieldName == "-" {
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		} else if len(fieldName) == 0 {
 | 
							} else if len(fieldName) == 0 {
 | 
				
			||||||
			fieldName = com.ToSnakeCase(field.Name)
 | 
								fieldName = util.ToSnakeCase(field.Name)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		data[fieldName] = val.Field(i).Interface()
 | 
							data[fieldName] = val.Field(i).Interface()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,8 @@
 | 
				
			|||||||
package web
 | 
					package web
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"html"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
	"path"
 | 
						"path"
 | 
				
			||||||
@@ -14,8 +16,6 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/modules/context"
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/util"
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/unknwon/com"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func goGet(ctx *context.Context) {
 | 
					func goGet(ctx *context.Context) {
 | 
				
			||||||
@@ -65,23 +65,23 @@ func goGet(ctx *context.Context) {
 | 
				
			|||||||
	if appURL.Scheme == string(setting.HTTP) {
 | 
						if appURL.Scheme == string(setting.HTTP) {
 | 
				
			||||||
		insecure = "--insecure "
 | 
							insecure = "--insecure "
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ctx.RespHeader().Set("Content-Type", "text/html")
 | 
					
 | 
				
			||||||
	ctx.Status(http.StatusOK)
 | 
						goGetImport := context.ComposeGoGetImport(ownerName, trimmedRepoName)
 | 
				
			||||||
	_, _ = ctx.Write([]byte(com.Expand(`<!doctype html>
 | 
						goImportContent := fmt.Sprintf("%s git %s", goGetImport, repo_model.ComposeHTTPSCloneURL(ownerName, repoName) /*CloneLink*/)
 | 
				
			||||||
 | 
						goSourceContent := fmt.Sprintf("%s _ %s %s", goGetImport, prefix+"{/dir}" /*GoDocDirectory*/, prefix+"{/dir}/{file}#L{line}" /*GoDocFile*/)
 | 
				
			||||||
 | 
						goGetCli := fmt.Sprintf("go get %s%s", insecure, goGetImport)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						res := fmt.Sprintf(`<!doctype html>
 | 
				
			||||||
<html>
 | 
					<html>
 | 
				
			||||||
	<head>
 | 
						<head>
 | 
				
			||||||
		<meta name="go-import" content="{GoGetImport} git {CloneLink}">
 | 
							<meta name="go-import" content="%s">
 | 
				
			||||||
		<meta name="go-source" content="{GoGetImport} _ {GoDocDirectory} {GoDocFile}">
 | 
							<meta name="go-source" content="%s">
 | 
				
			||||||
	</head>
 | 
						</head>
 | 
				
			||||||
	<body>
 | 
						<body>
 | 
				
			||||||
		go get {Insecure}{GoGetImport}
 | 
							%s
 | 
				
			||||||
	</body>
 | 
						</body>
 | 
				
			||||||
</html>
 | 
					</html>`, html.EscapeString(goImportContent), html.EscapeString(goSourceContent), html.EscapeString(goGetCli))
 | 
				
			||||||
`, map[string]string{
 | 
					
 | 
				
			||||||
		"GoGetImport":    context.ComposeGoGetImport(ownerName, trimmedRepoName),
 | 
						ctx.RespHeader().Set("Content-Type", "text/html")
 | 
				
			||||||
		"CloneLink":      repo_model.ComposeHTTPSCloneURL(ownerName, repoName),
 | 
						_, _ = ctx.Write([]byte(res))
 | 
				
			||||||
		"GoDocDirectory": prefix + "{/dir}",
 | 
					 | 
				
			||||||
		"GoDocFile":      prefix + "{/dir}/{file}#L{line}",
 | 
					 | 
				
			||||||
		"Insecure":       insecure,
 | 
					 | 
				
			||||||
	})))
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,6 +35,7 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/modules/markup/markdown"
 | 
						"code.gitea.io/gitea/modules/markup/markdown"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
	api "code.gitea.io/gitea/modules/structs"
 | 
						api "code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/templates/vars"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/upload"
 | 
						"code.gitea.io/gitea/modules/upload"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/util"
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/web"
 | 
						"code.gitea.io/gitea/modules/web"
 | 
				
			||||||
@@ -43,8 +44,6 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/services/forms"
 | 
						"code.gitea.io/gitea/services/forms"
 | 
				
			||||||
	issue_service "code.gitea.io/gitea/services/issue"
 | 
						issue_service "code.gitea.io/gitea/services/issue"
 | 
				
			||||||
	pull_service "code.gitea.io/gitea/services/pull"
 | 
						pull_service "code.gitea.io/gitea/services/pull"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/unknwon/com"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
@@ -1113,7 +1112,13 @@ func ViewIssue(ctx *context.Context) {
 | 
				
			|||||||
			if extIssueUnit.ExternalTrackerConfig().ExternalTrackerStyle == markup.IssueNameStyleNumeric || extIssueUnit.ExternalTrackerConfig().ExternalTrackerStyle == "" {
 | 
								if extIssueUnit.ExternalTrackerConfig().ExternalTrackerStyle == markup.IssueNameStyleNumeric || extIssueUnit.ExternalTrackerConfig().ExternalTrackerStyle == "" {
 | 
				
			||||||
				metas := ctx.Repo.Repository.ComposeMetas()
 | 
									metas := ctx.Repo.Repository.ComposeMetas()
 | 
				
			||||||
				metas["index"] = ctx.Params(":index")
 | 
									metas["index"] = ctx.Params(":index")
 | 
				
			||||||
				ctx.Redirect(com.Expand(extIssueUnit.ExternalTrackerConfig().ExternalTrackerFormat, metas))
 | 
									res, err := vars.Expand(extIssueUnit.ExternalTrackerConfig().ExternalTrackerFormat, metas)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										log.Error("unable to expand template vars for issue url. issue: %s, err: %v", metas["index"], err)
 | 
				
			||||||
 | 
										ctx.ServerError("Expand", err)
 | 
				
			||||||
 | 
										return
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									ctx.Redirect(res)
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else if err != nil && !repo_model.IsErrUnitTypeNotExist(err) {
 | 
							} else if err != nil && !repo_model.IsErrUnitTypeNotExist(err) {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user