New UI merge in progress
This commit is contained in:
parent
0a739cf9ac
commit
8dd07c0ddd
199 changed files with 15030 additions and 9325 deletions
|
@ -5,12 +5,9 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
"github.com/Unknwon/macaron"
|
||||
"github.com/macaron-contrib/i18n"
|
||||
|
||||
"github.com/go-martini/martini"
|
||||
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/middleware/binding"
|
||||
)
|
||||
|
||||
|
@ -25,17 +22,6 @@ type AdminEditUserForm struct {
|
|||
LoginType int `form:"login_type"`
|
||||
}
|
||||
|
||||
func (f *AdminEditUserForm) Name(field string) string {
|
||||
names := map[string]string{
|
||||
"Email": "E-mail address",
|
||||
"Website": "Website",
|
||||
"Location": "Location",
|
||||
"Avatar": "Gravatar Email",
|
||||
}
|
||||
return names[field]
|
||||
}
|
||||
|
||||
func (f *AdminEditUserForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) {
|
||||
data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData)
|
||||
validate(errors, data, f)
|
||||
func (f *AdminEditUserForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
}
|
||||
|
|
|
@ -5,13 +5,12 @@
|
|||
package apiv1
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
||||
"github.com/go-martini/martini"
|
||||
"github.com/Unknwon/macaron"
|
||||
"github.com/macaron-contrib/i18n"
|
||||
|
||||
"github.com/gogits/gogs/modules/auth"
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/middleware/binding"
|
||||
)
|
||||
|
@ -22,17 +21,16 @@ type MarkdownForm struct {
|
|||
Context string `form:"context"`
|
||||
}
|
||||
|
||||
func (f *MarkdownForm) Validate(errs *binding.Errors, req *http.Request, ctx martini.Context) {
|
||||
data := ctx.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData)
|
||||
validateApiReq(errs, data, f)
|
||||
func (f *MarkdownForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validateApiReq(errs, ctx.Data, f, l)
|
||||
}
|
||||
|
||||
func validateApiReq(errs *binding.Errors, data base.TmplData, f interface{}) {
|
||||
func validateApiReq(errs *binding.Errors, data map[string]interface{}, f interface{}, l i18n.Locale) {
|
||||
if errs.Count() == 0 {
|
||||
return
|
||||
} else if len(errs.Overall) > 0 {
|
||||
for _, err := range errs.Overall {
|
||||
log.Error("%s: %v", reflect.TypeOf(f), err)
|
||||
log.Error(4, "%s: %v", reflect.TypeOf(f), err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
@ -7,49 +7,152 @@ package auth
|
|||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/go-martini/martini"
|
||||
"github.com/macaron-contrib/i18n"
|
||||
"github.com/macaron-contrib/session"
|
||||
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/middleware/binding"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
type AuthenticationForm struct {
|
||||
Id int64 `form:"id"`
|
||||
Type int `form:"type"`
|
||||
AuthName string `form:"name" binding:"Required;MaxSize(50)"`
|
||||
Domain string `form:"domain"`
|
||||
Host string `form:"host"`
|
||||
Port int `form:"port"`
|
||||
UseSSL bool `form:"usessl"`
|
||||
BaseDN string `form:"base_dn"`
|
||||
Attributes string `form:"attributes"`
|
||||
Filter string `form:"filter"`
|
||||
MsAdSA string `form:"ms_ad_sa"`
|
||||
IsActived bool `form:"is_actived"`
|
||||
SmtpAuth string `form:"smtpauth"`
|
||||
SmtpHost string `form:"smtphost"`
|
||||
SmtpPort int `form:"smtpport"`
|
||||
Tls bool `form:"tls"`
|
||||
AllowAutoRegister bool `form:"allowautoregister"`
|
||||
}
|
||||
|
||||
func (f *AuthenticationForm) Name(field string) string {
|
||||
names := map[string]string{
|
||||
"AuthName": "Authentication's name",
|
||||
"Domain": "Domain name",
|
||||
"Host": "Host address",
|
||||
"Port": "Port Number",
|
||||
"UseSSL": "Use SSL",
|
||||
"BaseDN": "Base DN",
|
||||
"Attributes": "Search attributes",
|
||||
"Filter": "Search filter",
|
||||
"MsAdSA": "Ms Ad SA",
|
||||
// SignedInId returns the id of signed in user.
|
||||
func SignedInId(header http.Header, sess session.Store) int64 {
|
||||
if !models.HasEngine {
|
||||
return 0
|
||||
}
|
||||
return names[field]
|
||||
|
||||
if setting.Service.EnableReverseProxyAuth {
|
||||
webAuthUser := header.Get(setting.ReverseProxyAuthUser)
|
||||
if len(webAuthUser) > 0 {
|
||||
u, err := models.GetUserByName(webAuthUser)
|
||||
if err != nil {
|
||||
if err != models.ErrUserNotExist {
|
||||
log.Error(4, "GetUserByName: %v", err)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
return u.Id
|
||||
}
|
||||
}
|
||||
|
||||
uid := sess.Get("uid")
|
||||
if uid == nil {
|
||||
return 0
|
||||
}
|
||||
if id, ok := uid.(int64); ok {
|
||||
if _, err := models.GetUserById(id); err != nil {
|
||||
if err != models.ErrUserNotExist {
|
||||
log.Error(4, "GetUserById: %v", err)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
return id
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (f *AuthenticationForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) {
|
||||
data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData)
|
||||
validate(errors, data, f)
|
||||
// SignedInUser returns the user object of signed user.
|
||||
func SignedInUser(header http.Header, sess session.Store) *models.User {
|
||||
uid := SignedInId(header, sess)
|
||||
if uid <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
u, err := models.GetUserById(uid)
|
||||
if err != nil {
|
||||
log.Error(4, "GetUserById: %v", err)
|
||||
return nil
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// AssignForm assign form values back to the template data.
|
||||
func AssignForm(form interface{}, data map[string]interface{}) {
|
||||
typ := reflect.TypeOf(form)
|
||||
val := reflect.ValueOf(form)
|
||||
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
typ = typ.Elem()
|
||||
val = val.Elem()
|
||||
}
|
||||
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
|
||||
fieldName := field.Tag.Get("form")
|
||||
// Allow ignored fields in the struct
|
||||
if fieldName == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
data[fieldName] = val.Field(i).Interface()
|
||||
}
|
||||
}
|
||||
|
||||
func GetMinMaxSize(field reflect.StructField) string {
|
||||
for _, rule := range strings.Split(field.Tag.Get("binding"), ";") {
|
||||
if strings.HasPrefix(rule, "MinSize(") || strings.HasPrefix(rule, "MaxSize(") {
|
||||
return rule[8 : len(rule)-1]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func validate(errs *binding.Errors, data map[string]interface{}, f interface{}, l i18n.Locale) {
|
||||
if errs.Count() == 0 {
|
||||
return
|
||||
} else if len(errs.Overall) > 0 {
|
||||
for _, err := range errs.Overall {
|
||||
log.Error(4, "%s: %v", reflect.TypeOf(f), err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
data["HasError"] = true
|
||||
AssignForm(f, data)
|
||||
|
||||
typ := reflect.TypeOf(f)
|
||||
val := reflect.ValueOf(f)
|
||||
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
typ = typ.Elem()
|
||||
val = val.Elem()
|
||||
}
|
||||
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
|
||||
fieldName := field.Tag.Get("form")
|
||||
// Allow ignored fields in the struct
|
||||
if fieldName == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
if err, ok := errs.Fields[field.Name]; ok {
|
||||
data["Err_"+field.Name] = true
|
||||
trName := l.Tr("form." + field.Name)
|
||||
switch err {
|
||||
case binding.BindingRequireError:
|
||||
data["ErrorMsg"] = trName + l.Tr("form.require_error")
|
||||
case binding.BindingAlphaDashError:
|
||||
data["ErrorMsg"] = trName + l.Tr("form.alpha_dash_error")
|
||||
case binding.BindingAlphaDashDotError:
|
||||
data["ErrorMsg"] = trName + l.Tr("form.alpha_dash_dot_error")
|
||||
case binding.BindingMinSizeError:
|
||||
data["ErrorMsg"] = trName + l.Tr("form.min_size_error", GetMinMaxSize(field))
|
||||
case binding.BindingMaxSizeError:
|
||||
data["ErrorMsg"] = trName + l.Tr("form.max_size_error", GetMinMaxSize(field))
|
||||
case binding.BindingEmailError:
|
||||
data["ErrorMsg"] = trName + l.Tr("form.email_error")
|
||||
case binding.BindingUrlError:
|
||||
data["ErrorMsg"] = trName + l.Tr("form.url_error")
|
||||
default:
|
||||
data["ErrorMsg"] = l.Tr("form.unknown_error") + " " + err
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
36
modules/auth/auth_form.go
Normal file
36
modules/auth/auth_form.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
// Copyright 2014 The Gogs 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 auth
|
||||
|
||||
import (
|
||||
"github.com/Unknwon/macaron"
|
||||
"github.com/macaron-contrib/i18n"
|
||||
|
||||
"github.com/gogits/gogs/modules/middleware/binding"
|
||||
)
|
||||
|
||||
type AuthenticationForm struct {
|
||||
Id int64 `form:"id"`
|
||||
Type int `form:"type"`
|
||||
AuthName string `form:"name" binding:"Required;MaxSize(50)"`
|
||||
Domain string `form:"domain"`
|
||||
Host string `form:"host"`
|
||||
Port int `form:"port"`
|
||||
UseSSL bool `form:"usessl"`
|
||||
BaseDN string `form:"base_dn"`
|
||||
Attributes string `form:"attributes"`
|
||||
Filter string `form:"filter"`
|
||||
MsAdSA string `form:"ms_ad_sa"`
|
||||
IsActived bool `form:"is_actived"`
|
||||
SmtpAuth string `form:"smtpauth"`
|
||||
SmtpHost string `form:"smtphost"`
|
||||
SmtpPort int `form:"smtpport"`
|
||||
Tls bool `form:"tls"`
|
||||
AllowAutoRegister bool `form:"allowautoregister"`
|
||||
}
|
||||
|
||||
func (f *AuthenticationForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
}
|
|
@ -55,7 +55,7 @@ func LoginUser(name, passwd string) (a string, r bool) {
|
|||
func (ls Ldapsource) SearchEntry(name, passwd string) (string, bool) {
|
||||
l, err := ldapDial(ls)
|
||||
if err != nil {
|
||||
log.Error("LDAP Connect error, %s:%v", ls.Host, err)
|
||||
log.Error(4, "LDAP Connect error, %s:%v", ls.Host, err)
|
||||
ls.Enabled = false
|
||||
return "", false
|
||||
}
|
||||
|
|
|
@ -5,12 +5,9 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
"github.com/Unknwon/macaron"
|
||||
"github.com/macaron-contrib/i18n"
|
||||
|
||||
"github.com/go-martini/martini"
|
||||
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/middleware/binding"
|
||||
)
|
||||
|
||||
|
@ -26,17 +23,8 @@ type CreateOrgForm struct {
|
|||
Email string `form:"email" binding:"Required;Email;MaxSize(50)"`
|
||||
}
|
||||
|
||||
func (f *CreateOrgForm) Name(field string) string {
|
||||
names := map[string]string{
|
||||
"OrgName": "Organization name",
|
||||
"Email": "E-mail address",
|
||||
}
|
||||
return names[field]
|
||||
}
|
||||
|
||||
func (f *CreateOrgForm) Validate(errs *binding.Errors, req *http.Request, ctx martini.Context) {
|
||||
data := ctx.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData)
|
||||
validate(errs, data, f)
|
||||
func (f *CreateOrgForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
}
|
||||
|
||||
type OrgSettingForm struct {
|
||||
|
@ -47,20 +35,8 @@ type OrgSettingForm struct {
|
|||
Location string `form:"location" binding:"MaxSize(50)"`
|
||||
}
|
||||
|
||||
func (f *OrgSettingForm) Name(field string) string {
|
||||
names := map[string]string{
|
||||
"DisplayName": "Display name",
|
||||
"Email": "E-mail address",
|
||||
"Description": "Description",
|
||||
"Website": "Website address",
|
||||
"Location": "Location",
|
||||
}
|
||||
return names[field]
|
||||
}
|
||||
|
||||
func (f *OrgSettingForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) {
|
||||
data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData)
|
||||
validate(errors, data, f)
|
||||
func (f *OrgSettingForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
}
|
||||
|
||||
// ___________
|
||||
|
@ -76,15 +52,6 @@ type CreateTeamForm struct {
|
|||
Permission string `form:"permission"`
|
||||
}
|
||||
|
||||
func (f *CreateTeamForm) Name(field string) string {
|
||||
names := map[string]string{
|
||||
"TeamName": "Team name",
|
||||
"Description": "Team description",
|
||||
}
|
||||
return names[field]
|
||||
}
|
||||
|
||||
func (f *CreateTeamForm) Validate(errs *binding.Errors, req *http.Request, ctx martini.Context) {
|
||||
data := ctx.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData)
|
||||
validate(errs, data, f)
|
||||
func (f *CreateTeamForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
}
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
// Copyright 2014 The Gogs 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 auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
||||
"github.com/go-martini/martini"
|
||||
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/middleware/binding"
|
||||
)
|
||||
|
||||
type AddSSHKeyForm struct {
|
||||
KeyName string `form:"keyname" binding:"Required"`
|
||||
KeyContent string `form:"key_content" binding:"Required"`
|
||||
}
|
||||
|
||||
func (f *AddSSHKeyForm) Name(field string) string {
|
||||
names := map[string]string{
|
||||
"KeyName": "SSH key name",
|
||||
"KeyContent": "SSH key content",
|
||||
}
|
||||
return names[field]
|
||||
}
|
||||
|
||||
func (f *AddSSHKeyForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) {
|
||||
data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData)
|
||||
validate(errors, data, f)
|
||||
}
|
21
modules/auth/publickey_form.go
Normal file
21
modules/auth/publickey_form.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2014 The Gogs 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 auth
|
||||
|
||||
import (
|
||||
"github.com/Unknwon/macaron"
|
||||
"github.com/macaron-contrib/i18n"
|
||||
|
||||
"github.com/gogits/gogs/modules/middleware/binding"
|
||||
)
|
||||
|
||||
type AddSSHKeyForm struct {
|
||||
SSHTitle string `form:"title" binding:"Required"`
|
||||
Content string `form:"content" binding:"Required"`
|
||||
}
|
||||
|
||||
func (f *AddSSHKeyForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
}
|
|
@ -1,252 +0,0 @@
|
|||
// Copyright 2014 The Gogs 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 auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
||||
"github.com/go-martini/martini"
|
||||
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/middleware/binding"
|
||||
)
|
||||
|
||||
// __________ .__ __
|
||||
// \______ \ ____ ______ ____ _____|__|/ |_ ___________ ___.__.
|
||||
// | _// __ \\____ \ / _ \/ ___/ \ __\/ _ \_ __ < | |
|
||||
// | | \ ___/| |_> > <_> )___ \| || | ( <_> ) | \/\___ |
|
||||
// |____|_ /\___ > __/ \____/____ >__||__| \____/|__| / ____|
|
||||
// \/ \/|__| \/ \/
|
||||
|
||||
type CreateRepoForm struct {
|
||||
Uid int64 `form:"uid" binding:"Required"`
|
||||
RepoName string `form:"repo" binding:"Required;AlphaDash;MaxSize(100)"`
|
||||
Private bool `form:"private"`
|
||||
Description string `form:"desc" binding:"MaxSize(255)"`
|
||||
Language string `form:"language"`
|
||||
License string `form:"license"`
|
||||
InitReadme bool `form:"initReadme"`
|
||||
}
|
||||
|
||||
func (f *CreateRepoForm) Name(field string) string {
|
||||
names := map[string]string{
|
||||
"RepoName": "Repository name",
|
||||
"Description": "Description",
|
||||
}
|
||||
return names[field]
|
||||
}
|
||||
|
||||
func (f *CreateRepoForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) {
|
||||
data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData)
|
||||
validate(errors, data, f)
|
||||
}
|
||||
|
||||
type MigrateRepoForm struct {
|
||||
Url string `form:"url" binding:"Url"`
|
||||
AuthUserName string `form:"auth_username"`
|
||||
AuthPasswd string `form:"auth_password"`
|
||||
Uid int64 `form:"uid" binding:"Required"`
|
||||
RepoName string `form:"repo" binding:"Required;AlphaDash;MaxSize(100)"`
|
||||
Mirror bool `form:"mirror"`
|
||||
Private bool `form:"private"`
|
||||
Description string `form:"desc" binding:"MaxSize(255)"`
|
||||
}
|
||||
|
||||
func (f *MigrateRepoForm) Name(field string) string {
|
||||
names := map[string]string{
|
||||
"Url": "Migration URL",
|
||||
"RepoName": "Repository name",
|
||||
"Description": "Description",
|
||||
}
|
||||
return names[field]
|
||||
}
|
||||
|
||||
func (f *MigrateRepoForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) {
|
||||
data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData)
|
||||
validate(errors, data, f)
|
||||
}
|
||||
|
||||
type RepoSettingForm struct {
|
||||
RepoName string `form:"name" binding:"Required;AlphaDash;MaxSize(100)"`
|
||||
Description string `form:"desc" binding:"MaxSize(255)"`
|
||||
Website string `form:"site" binding:"Url;MaxSize(100)"`
|
||||
Branch string `form:"branch"`
|
||||
Interval int `form:"interval"`
|
||||
Private bool `form:"private"`
|
||||
GoGet bool `form:"goget"`
|
||||
}
|
||||
|
||||
func (f *RepoSettingForm) Name(field string) string {
|
||||
names := map[string]string{
|
||||
"RepoName": "Repository name",
|
||||
"Description": "Description",
|
||||
"Website": "Website address",
|
||||
}
|
||||
return names[field]
|
||||
}
|
||||
|
||||
func (f *RepoSettingForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) {
|
||||
data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData)
|
||||
validate(errors, data, f)
|
||||
}
|
||||
|
||||
// __ __ ___. .__ .__ __
|
||||
// / \ / \ ____\_ |__ | |__ | |__ ____ | | __
|
||||
// \ \/\/ // __ \| __ \| | \| | \ / _ \| |/ /
|
||||
// \ /\ ___/| \_\ \ Y \ Y ( <_> ) <
|
||||
// \__/\ / \___ >___ /___| /___| /\____/|__|_ \
|
||||
// \/ \/ \/ \/ \/ \/
|
||||
|
||||
type NewWebhookForm struct {
|
||||
Url string `form:"url" binding:"Required;Url"`
|
||||
ContentType string `form:"content_type" binding:"Required"`
|
||||
Secret string `form:"secret""`
|
||||
PushOnly bool `form:"push_only"`
|
||||
Active bool `form:"active"`
|
||||
}
|
||||
|
||||
func (f *NewWebhookForm) Name(field string) string {
|
||||
names := map[string]string{
|
||||
"Url": "Payload URL",
|
||||
"ContentType": "Content type",
|
||||
}
|
||||
return names[field]
|
||||
}
|
||||
|
||||
func (f *NewWebhookForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) {
|
||||
data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData)
|
||||
validate(errors, data, f)
|
||||
}
|
||||
|
||||
// .___
|
||||
// | | ______ ________ __ ____
|
||||
// | |/ ___// ___/ | \_/ __ \
|
||||
// | |\___ \ \___ \| | /\ ___/
|
||||
// |___/____ >____ >____/ \___ >
|
||||
// \/ \/ \/
|
||||
|
||||
type CreateIssueForm struct {
|
||||
IssueName string `form:"title" binding:"Required;MaxSize(50)"`
|
||||
MilestoneId int64 `form:"milestoneid"`
|
||||
AssigneeId int64 `form:"assigneeid"`
|
||||
Labels string `form:"labels"`
|
||||
Content string `form:"content"`
|
||||
}
|
||||
|
||||
func (f *CreateIssueForm) Name(field string) string {
|
||||
names := map[string]string{
|
||||
"IssueName": "Issue name",
|
||||
}
|
||||
return names[field]
|
||||
}
|
||||
|
||||
func (f *CreateIssueForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) {
|
||||
data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData)
|
||||
validate(errors, data, f)
|
||||
}
|
||||
|
||||
// _____ .__.__ __
|
||||
// / \ |__| | ____ _______/ |_ ____ ____ ____
|
||||
// / \ / \| | | _/ __ \ / ___/\ __\/ _ \ / \_/ __ \
|
||||
// / Y \ | |_\ ___/ \___ \ | | ( <_> ) | \ ___/
|
||||
// \____|__ /__|____/\___ >____ > |__| \____/|___| /\___ >
|
||||
// \/ \/ \/ \/ \/
|
||||
|
||||
type CreateMilestoneForm struct {
|
||||
Title string `form:"title" binding:"Required;MaxSize(50)"`
|
||||
Content string `form:"content"`
|
||||
Deadline string `form:"due_date"`
|
||||
}
|
||||
|
||||
func (f *CreateMilestoneForm) Name(field string) string {
|
||||
names := map[string]string{
|
||||
"Title": "Milestone name",
|
||||
}
|
||||
return names[field]
|
||||
}
|
||||
|
||||
func (f *CreateMilestoneForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) {
|
||||
data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData)
|
||||
validate(errors, data, f)
|
||||
}
|
||||
|
||||
// .____ ___. .__
|
||||
// | | _____ \_ |__ ____ | |
|
||||
// | | \__ \ | __ \_/ __ \| |
|
||||
// | |___ / __ \| \_\ \ ___/| |__
|
||||
// |_______ (____ /___ /\___ >____/
|
||||
// \/ \/ \/ \/
|
||||
|
||||
type CreateLabelForm struct {
|
||||
Title string `form:"title" binding:"Required;MaxSize(50)"`
|
||||
Color string `form:"color" binding:"Required;Size(7)"`
|
||||
}
|
||||
|
||||
func (f *CreateLabelForm) Name(field string) string {
|
||||
names := map[string]string{
|
||||
"Title": "Label name",
|
||||
"Color": "Label color",
|
||||
}
|
||||
return names[field]
|
||||
}
|
||||
|
||||
func (f *CreateLabelForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) {
|
||||
data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData)
|
||||
validate(errors, data, f)
|
||||
}
|
||||
|
||||
// __________ .__
|
||||
// \______ \ ____ | | ____ _____ ______ ____
|
||||
// | _// __ \| | _/ __ \\__ \ / ___// __ \
|
||||
// | | \ ___/| |_\ ___/ / __ \_\___ \\ ___/
|
||||
// |____|_ /\___ >____/\___ >____ /____ >\___ >
|
||||
// \/ \/ \/ \/ \/ \/
|
||||
|
||||
type NewReleaseForm struct {
|
||||
TagName string `form:"tag_name" binding:"Required"`
|
||||
Target string `form:"tag_target" binding:"Required"`
|
||||
Title string `form:"title" binding:"Required"`
|
||||
Content string `form:"content" binding:"Required"`
|
||||
Draft string `form:"draft"`
|
||||
Prerelease bool `form:"prerelease"`
|
||||
}
|
||||
|
||||
func (f *NewReleaseForm) Name(field string) string {
|
||||
names := map[string]string{
|
||||
"TagName": "Tag name",
|
||||
"Target": "Target",
|
||||
"Title": "Release title",
|
||||
"Content": "Release content",
|
||||
}
|
||||
return names[field]
|
||||
}
|
||||
|
||||
func (f *NewReleaseForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) {
|
||||
data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData)
|
||||
validate(errors, data, f)
|
||||
}
|
||||
|
||||
type EditReleaseForm struct {
|
||||
Target string `form:"tag_target" binding:"Required"`
|
||||
Title string `form:"title" binding:"Required"`
|
||||
Content string `form:"content" binding:"Required"`
|
||||
Draft string `form:"draft"`
|
||||
Prerelease bool `form:"prerelease"`
|
||||
}
|
||||
|
||||
func (f *EditReleaseForm) Name(field string) string {
|
||||
names := map[string]string{
|
||||
"Target": "Target",
|
||||
"Title": "Release title",
|
||||
"Content": "Release content",
|
||||
}
|
||||
return names[field]
|
||||
}
|
||||
|
||||
func (f *EditReleaseForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) {
|
||||
data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData)
|
||||
validate(errors, data, f)
|
||||
}
|
165
modules/auth/repo_form.go
Normal file
165
modules/auth/repo_form.go
Normal file
|
@ -0,0 +1,165 @@
|
|||
// Copyright 2014 The Gogs 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 auth
|
||||
|
||||
import (
|
||||
"github.com/Unknwon/macaron"
|
||||
"github.com/macaron-contrib/i18n"
|
||||
|
||||
"github.com/gogits/gogs/modules/middleware/binding"
|
||||
)
|
||||
|
||||
// _______________________________________ _________.______________________ _______________.___.
|
||||
// \______ \_ _____/\______ \_____ \ / _____/| \__ ___/\_____ \\______ \__ | |
|
||||
// | _/| __)_ | ___// | \ \_____ \ | | | | / | \| _// | |
|
||||
// | | \| \ | | / | \/ \| | | | / | \ | \\____ |
|
||||
// |____|_ /_______ / |____| \_______ /_______ /|___| |____| \_______ /____|_ // ______|
|
||||
// \/ \/ \/ \/ \/ \/ \/
|
||||
|
||||
type CreateRepoForm struct {
|
||||
Uid int64 `form:"uid" binding:"Required"`
|
||||
RepoName string `form:"repo_name" binding:"Required;AlphaDash;MaxSize(100)"`
|
||||
Private bool `form:"private"`
|
||||
Description string `form:"desc" binding:"MaxSize(255)"`
|
||||
Gitignore string `form:"gitignore"`
|
||||
License string `form:"license"`
|
||||
InitReadme bool `form:"init_readme"`
|
||||
}
|
||||
|
||||
func (f *CreateRepoForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
}
|
||||
|
||||
type MigrateRepoForm struct {
|
||||
Url string `form:"url" binding:"Url"`
|
||||
AuthUserName string `form:"auth_username"`
|
||||
AuthPasswd string `form:"auth_password"`
|
||||
Uid int64 `form:"uid" binding:"Required"`
|
||||
RepoName string `form:"repo" binding:"Required;AlphaDash;MaxSize(100)"`
|
||||
Mirror bool `form:"mirror"`
|
||||
Private bool `form:"private"`
|
||||
Description string `form:"desc" binding:"MaxSize(255)"`
|
||||
}
|
||||
|
||||
func (f *MigrateRepoForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
}
|
||||
|
||||
type RepoSettingForm struct {
|
||||
RepoName string `form:"name" binding:"Required;AlphaDash;MaxSize(100)"`
|
||||
Description string `form:"desc" binding:"MaxSize(255)"`
|
||||
Website string `form:"site" binding:"Url;MaxSize(100)"`
|
||||
Branch string `form:"branch"`
|
||||
Interval int `form:"interval"`
|
||||
Private bool `form:"private"`
|
||||
GoGet bool `form:"goget"`
|
||||
}
|
||||
|
||||
func (f *RepoSettingForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
}
|
||||
|
||||
// __ __ ___. .__ .__ __
|
||||
// / \ / \ ____\_ |__ | |__ | |__ ____ | | __
|
||||
// \ \/\/ // __ \| __ \| | \| | \ / _ \| |/ /
|
||||
// \ /\ ___/| \_\ \ Y \ Y ( <_> ) <
|
||||
// \__/\ / \___ >___ /___| /___| /\____/|__|_ \
|
||||
// \/ \/ \/ \/ \/ \/
|
||||
|
||||
type NewWebhookForm struct {
|
||||
Url string `form:"url" binding:"Required;Url"`
|
||||
ContentType string `form:"content_type" binding:"Required"`
|
||||
Secret string `form:"secret""`
|
||||
PushOnly bool `form:"push_only"`
|
||||
Active bool `form:"active"`
|
||||
}
|
||||
|
||||
func (f *NewWebhookForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
}
|
||||
|
||||
// .___
|
||||
// | | ______ ________ __ ____
|
||||
// | |/ ___// ___/ | \_/ __ \
|
||||
// | |\___ \ \___ \| | /\ ___/
|
||||
// |___/____ >____ >____/ \___ >
|
||||
// \/ \/ \/
|
||||
|
||||
type CreateIssueForm struct {
|
||||
IssueName string `form:"title" binding:"Required;MaxSize(50)"`
|
||||
MilestoneId int64 `form:"milestoneid"`
|
||||
AssigneeId int64 `form:"assigneeid"`
|
||||
Labels string `form:"labels"`
|
||||
Content string `form:"content"`
|
||||
}
|
||||
|
||||
func (f *CreateIssueForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
}
|
||||
|
||||
// _____ .__.__ __
|
||||
// / \ |__| | ____ _______/ |_ ____ ____ ____
|
||||
// / \ / \| | | _/ __ \ / ___/\ __\/ _ \ / \_/ __ \
|
||||
// / Y \ | |_\ ___/ \___ \ | | ( <_> ) | \ ___/
|
||||
// \____|__ /__|____/\___ >____ > |__| \____/|___| /\___ >
|
||||
// \/ \/ \/ \/ \/
|
||||
|
||||
type CreateMilestoneForm struct {
|
||||
Title string `form:"title" binding:"Required;MaxSize(50)"`
|
||||
Content string `form:"content"`
|
||||
Deadline string `form:"due_date"`
|
||||
}
|
||||
|
||||
func (f *CreateMilestoneForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
}
|
||||
|
||||
// .____ ___. .__
|
||||
// | | _____ \_ |__ ____ | |
|
||||
// | | \__ \ | __ \_/ __ \| |
|
||||
// | |___ / __ \| \_\ \ ___/| |__
|
||||
// |_______ (____ /___ /\___ >____/
|
||||
// \/ \/ \/ \/
|
||||
|
||||
type CreateLabelForm struct {
|
||||
Title string `form:"title" binding:"Required;MaxSize(50)"`
|
||||
Color string `form:"color" binding:"Required;Size(7)"`
|
||||
}
|
||||
|
||||
func (f *CreateLabelForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
}
|
||||
|
||||
// __________ .__
|
||||
// \______ \ ____ | | ____ _____ ______ ____
|
||||
// | _// __ \| | _/ __ \\__ \ / ___// __ \
|
||||
// | | \ ___/| |_\ ___/ / __ \_\___ \\ ___/
|
||||
// |____|_ /\___ >____/\___ >____ /____ >\___ >
|
||||
// \/ \/ \/ \/ \/ \/
|
||||
|
||||
type NewReleaseForm struct {
|
||||
TagName string `form:"tag_name" binding:"Required"`
|
||||
Target string `form:"tag_target" binding:"Required"`
|
||||
Title string `form:"title" binding:"Required"`
|
||||
Content string `form:"content" binding:"Required"`
|
||||
Draft string `form:"draft"`
|
||||
Prerelease bool `form:"prerelease"`
|
||||
}
|
||||
|
||||
func (f *NewReleaseForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
}
|
||||
|
||||
type EditReleaseForm struct {
|
||||
Target string `form:"tag_target" binding:"Required"`
|
||||
Title string `form:"title" binding:"Required"`
|
||||
Content string `form:"content" binding:"Required"`
|
||||
Draft string `form:"draft"`
|
||||
Prerelease bool `form:"prerelease"`
|
||||
}
|
||||
|
||||
func (f *EditReleaseForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
}
|
|
@ -1,299 +0,0 @@
|
|||
// Copyright 2014 The Gogs 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 auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/go-martini/martini"
|
||||
|
||||
"github.com/gogits/session"
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/middleware/binding"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
// Web form interface.
|
||||
type Form interface {
|
||||
Name(field string) string
|
||||
}
|
||||
|
||||
type RegisterForm struct {
|
||||
UserName string `form:"username" binding:"Required;AlphaDashDot;MaxSize(30)"`
|
||||
Email string `form:"email" binding:"Required;Email;MaxSize(50)"`
|
||||
Password string `form:"passwd" binding:"Required;MinSize(6);MaxSize(30)"`
|
||||
RetypePasswd string `form:"retypepasswd"`
|
||||
LoginType string `form:"logintype"`
|
||||
LoginName string `form:"loginname"`
|
||||
}
|
||||
|
||||
func (f *RegisterForm) Name(field string) string {
|
||||
names := map[string]string{
|
||||
"UserName": "Username",
|
||||
"Email": "E-mail address",
|
||||
"Password": "Password",
|
||||
"RetypePasswd": "Re-type password",
|
||||
}
|
||||
return names[field]
|
||||
}
|
||||
|
||||
func (f *RegisterForm) Validate(errs *binding.Errors, req *http.Request, ctx martini.Context) {
|
||||
data := ctx.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData)
|
||||
validate(errs, data, f)
|
||||
}
|
||||
|
||||
type LogInForm struct {
|
||||
UserName string `form:"username" binding:"Required;MaxSize(35)"`
|
||||
Password string `form:"passwd" binding:"Required;MinSize(6);MaxSize(30)"`
|
||||
Remember bool `form:"remember"`
|
||||
}
|
||||
|
||||
func (f *LogInForm) Name(field string) string {
|
||||
names := map[string]string{
|
||||
"UserName": "Username",
|
||||
"Password": "Password",
|
||||
}
|
||||
return names[field]
|
||||
}
|
||||
|
||||
func (f *LogInForm) Validate(errs *binding.Errors, req *http.Request, ctx martini.Context) {
|
||||
data := ctx.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData)
|
||||
validate(errs, data, f)
|
||||
}
|
||||
|
||||
func GetMinMaxSize(field reflect.StructField) string {
|
||||
for _, rule := range strings.Split(field.Tag.Get("binding"), ";") {
|
||||
if strings.HasPrefix(rule, "MinSize(") || strings.HasPrefix(rule, "MaxSize(") {
|
||||
return rule[8 : len(rule)-1]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func validate(errs *binding.Errors, data base.TmplData, f Form) {
|
||||
if errs.Count() == 0 {
|
||||
return
|
||||
} else if len(errs.Overall) > 0 {
|
||||
for _, err := range errs.Overall {
|
||||
log.Error("%s: %v", reflect.TypeOf(f), err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
data["HasError"] = true
|
||||
AssignForm(f, data)
|
||||
|
||||
typ := reflect.TypeOf(f)
|
||||
val := reflect.ValueOf(f)
|
||||
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
typ = typ.Elem()
|
||||
val = val.Elem()
|
||||
}
|
||||
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
|
||||
fieldName := field.Tag.Get("form")
|
||||
// Allow ignored fields in the struct
|
||||
if fieldName == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
if err, ok := errs.Fields[field.Name]; ok {
|
||||
data["Err_"+field.Name] = true
|
||||
switch err {
|
||||
case binding.BindingRequireError:
|
||||
data["ErrorMsg"] = f.Name(field.Name) + " cannot be empty"
|
||||
case binding.BindingAlphaDashError:
|
||||
data["ErrorMsg"] = f.Name(field.Name) + " must be valid alpha or numeric or dash(-_) characters"
|
||||
case binding.BindingAlphaDashDotError:
|
||||
data["ErrorMsg"] = f.Name(field.Name) + " must be valid alpha or numeric or dash(-_) or dot characters"
|
||||
case binding.BindingMinSizeError:
|
||||
data["ErrorMsg"] = f.Name(field.Name) + " must contain at least " + GetMinMaxSize(field) + " characters"
|
||||
case binding.BindingMaxSizeError:
|
||||
data["ErrorMsg"] = f.Name(field.Name) + " must contain at most " + GetMinMaxSize(field) + " characters"
|
||||
case binding.BindingEmailError:
|
||||
data["ErrorMsg"] = f.Name(field.Name) + " is not a valid e-mail address"
|
||||
case binding.BindingUrlError:
|
||||
data["ErrorMsg"] = f.Name(field.Name) + " is not a valid URL"
|
||||
default:
|
||||
data["ErrorMsg"] = "Unknown error: " + err
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AssignForm assign form values back to the template data.
|
||||
func AssignForm(form interface{}, data base.TmplData) {
|
||||
typ := reflect.TypeOf(form)
|
||||
val := reflect.ValueOf(form)
|
||||
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
typ = typ.Elem()
|
||||
val = val.Elem()
|
||||
}
|
||||
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
|
||||
fieldName := field.Tag.Get("form")
|
||||
// Allow ignored fields in the struct
|
||||
if fieldName == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
data[fieldName] = val.Field(i).Interface()
|
||||
}
|
||||
}
|
||||
|
||||
type InstallForm struct {
|
||||
Database string `form:"database" binding:"Required"`
|
||||
Host string `form:"host"`
|
||||
User string `form:"user"`
|
||||
Passwd string `form:"passwd"`
|
||||
DatabaseName string `form:"database_name"`
|
||||
SslMode string `form:"ssl_mode"`
|
||||
DatabasePath string `form:"database_path"`
|
||||
RepoRootPath string `form:"repo_path"`
|
||||
RunUser string `form:"run_user"`
|
||||
Domain string `form:"domain"`
|
||||
AppUrl string `form:"app_url"`
|
||||
AdminName string `form:"admin_name" binding:"Required;AlphaDashDot;MaxSize(30)"`
|
||||
AdminPasswd string `form:"admin_pwd" binding:"Required;MinSize(6);MaxSize(30)"`
|
||||
AdminEmail string `form:"admin_email" binding:"Required;Email;MaxSize(50)"`
|
||||
SmtpHost string `form:"smtp_host"`
|
||||
SmtpEmail string `form:"mailer_user"`
|
||||
SmtpPasswd string `form:"mailer_pwd"`
|
||||
RegisterConfirm string `form:"register_confirm"`
|
||||
MailNotify string `form:"mail_notify"`
|
||||
}
|
||||
|
||||
func (f *InstallForm) Name(field string) string {
|
||||
names := map[string]string{
|
||||
"Database": "Database name",
|
||||
"AdminName": "Admin user name",
|
||||
"AdminPasswd": "Admin password",
|
||||
"AdminEmail": "Admin e-maill address",
|
||||
}
|
||||
return names[field]
|
||||
}
|
||||
|
||||
func (f *InstallForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) {
|
||||
data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData)
|
||||
validate(errors, data, f)
|
||||
}
|
||||
|
||||
// SignedInId returns the id of signed in user.
|
||||
func SignedInId(header http.Header, sess session.SessionStore) int64 {
|
||||
if !models.HasEngine {
|
||||
return 0
|
||||
}
|
||||
|
||||
if setting.Service.EnableReverseProxyAuth {
|
||||
webAuthUser := header.Get(setting.ReverseProxyAuthUser)
|
||||
if len(webAuthUser) > 0 {
|
||||
u, err := models.GetUserByName(webAuthUser)
|
||||
if err != nil {
|
||||
if err != models.ErrUserNotExist {
|
||||
log.Error("auth.user.SignedInId(GetUserByName): %v", err)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
return u.Id
|
||||
}
|
||||
}
|
||||
|
||||
uid := sess.Get("userId")
|
||||
if uid == nil {
|
||||
return 0
|
||||
}
|
||||
if id, ok := uid.(int64); ok {
|
||||
if _, err := models.GetUserById(id); err != nil {
|
||||
if err != models.ErrUserNotExist {
|
||||
log.Error("auth.user.SignedInId(GetUserById): %v", err)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
return id
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// SignedInUser returns the user object of signed user.
|
||||
func SignedInUser(header http.Header, sess session.SessionStore) *models.User {
|
||||
uid := SignedInId(header, sess)
|
||||
if uid <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
u, err := models.GetUserById(uid)
|
||||
if err != nil {
|
||||
log.Error("user.SignedInUser: %v", err)
|
||||
return nil
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// IsSignedIn check if any user has signed in.
|
||||
func IsSignedIn(header http.Header, sess session.SessionStore) bool {
|
||||
return SignedInId(header, sess) > 0
|
||||
}
|
||||
|
||||
type FeedsForm struct {
|
||||
UserId int64 `form:"userid" binding:"Required"`
|
||||
Page int64 `form:"p"`
|
||||
}
|
||||
|
||||
type UpdateProfileForm struct {
|
||||
UserName string `form:"username" binding:"Required;AlphaDash;MaxSize(30)"`
|
||||
FullName string `form:"fullname" binding:"MaxSize(40)"`
|
||||
Email string `form:"email" binding:"Required;Email;MaxSize(50)"`
|
||||
Website string `form:"website" binding:"Url;MaxSize(50)"`
|
||||
Location string `form:"location" binding:"MaxSize(50)"`
|
||||
Avatar string `form:"avatar" binding:"Required;Email;MaxSize(50)"`
|
||||
}
|
||||
|
||||
func (f *UpdateProfileForm) Name(field string) string {
|
||||
names := map[string]string{
|
||||
"UserName": "Username",
|
||||
"Email": "E-mail address",
|
||||
"Website": "Website address",
|
||||
"Location": "Location",
|
||||
"Avatar": "Gravatar Email",
|
||||
}
|
||||
return names[field]
|
||||
}
|
||||
|
||||
func (f *UpdateProfileForm) Validate(errs *binding.Errors, req *http.Request, ctx martini.Context) {
|
||||
data := ctx.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData)
|
||||
validate(errs, data, f)
|
||||
}
|
||||
|
||||
type UpdatePasswdForm struct {
|
||||
OldPasswd string `form:"oldpasswd" binding:"Required;MinSize(6);MaxSize(30)"`
|
||||
NewPasswd string `form:"newpasswd" binding:"Required;MinSize(6);MaxSize(30)"`
|
||||
RetypePasswd string `form:"retypepasswd"`
|
||||
}
|
||||
|
||||
func (f *UpdatePasswdForm) Name(field string) string {
|
||||
names := map[string]string{
|
||||
"OldPasswd": "Old password",
|
||||
"NewPasswd": "New password",
|
||||
"RetypePasswd": "Re-type password",
|
||||
}
|
||||
return names[field]
|
||||
}
|
||||
|
||||
func (f *UpdatePasswdForm) Validate(errs *binding.Errors, req *http.Request, ctx martini.Context) {
|
||||
data := ctx.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData)
|
||||
validate(errs, data, f)
|
||||
}
|
98
modules/auth/user_form.go
Normal file
98
modules/auth/user_form.go
Normal file
|
@ -0,0 +1,98 @@
|
|||
// Copyright 2014 The Gogs 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 auth
|
||||
|
||||
import (
|
||||
"github.com/Unknwon/macaron"
|
||||
"github.com/macaron-contrib/i18n"
|
||||
|
||||
"github.com/gogits/gogs/modules/middleware/binding"
|
||||
)
|
||||
|
||||
type InstallForm struct {
|
||||
Database string `form:"database" binding:"Required"`
|
||||
Host string `form:"host"`
|
||||
User string `form:"user"`
|
||||
Passwd string `form:"passwd"`
|
||||
DatabaseName string `form:"database_name"`
|
||||
SslMode string `form:"ssl_mode"`
|
||||
DatabasePath string `form:"database_path"`
|
||||
RepoRootPath string `form:"repo_path"`
|
||||
RunUser string `form:"run_user"`
|
||||
Domain string `form:"domain"`
|
||||
AppUrl string `form:"app_url"`
|
||||
AdminName string `form:"admin_name" binding:"Required;AlphaDashDot;MaxSize(30)"`
|
||||
AdminPasswd string `form:"admin_pwd" binding:"Required;MinSize(6);MaxSize(30)"`
|
||||
AdminEmail string `form:"admin_email" binding:"Required;Email;MaxSize(50)"`
|
||||
SmtpHost string `form:"smtp_host"`
|
||||
SmtpEmail string `form:"mailer_user"`
|
||||
SmtpPasswd string `form:"mailer_pwd"`
|
||||
RegisterConfirm string `form:"register_confirm"`
|
||||
MailNotify string `form:"mail_notify"`
|
||||
}
|
||||
|
||||
func (f *InstallForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
}
|
||||
|
||||
// _____ ____ _________________ ___
|
||||
// / _ \ | | \__ ___/ | \
|
||||
// / /_\ \| | / | | / ~ \
|
||||
// / | \ | / | | \ Y /
|
||||
// \____|__ /______/ |____| \___|_ /
|
||||
// \/ \/
|
||||
|
||||
type RegisterForm struct {
|
||||
UserName string `form:"uname" binding:"Required;AlphaDashDot;MaxSize(35)"`
|
||||
Email string `form:"email" binding:"Required;Email;MaxSize(50)"`
|
||||
Password string `form:"password" binding:"Required;MinSize(6);MaxSize(30)"`
|
||||
Retype string `form:"retype"`
|
||||
LoginType string `form:"logintype"`
|
||||
LoginName string `form:"loginname"`
|
||||
}
|
||||
|
||||
func (f *RegisterForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
}
|
||||
|
||||
type SignInForm struct {
|
||||
UserName string `form:"uname" binding:"Required;MaxSize(35)"`
|
||||
Password string `form:"password" binding:"Required;MinSize(6);MaxSize(30)"`
|
||||
Remember bool `form:"remember"`
|
||||
}
|
||||
|
||||
func (f *SignInForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
}
|
||||
|
||||
// __________________________________________.___ _______ ________ _________
|
||||
// / _____/\_ _____/\__ ___/\__ ___/| |\ \ / _____/ / _____/
|
||||
// \_____ \ | __)_ | | | | | |/ | \/ \ ___ \_____ \
|
||||
// / \ | \ | | | | | / | \ \_\ \/ \
|
||||
// /_______ //_______ / |____| |____| |___\____|__ /\______ /_______ /
|
||||
// \/ \/ \/ \/ \/
|
||||
|
||||
type UpdateProfileForm struct {
|
||||
UserName string `form:"uname" binding:"Required;MaxSize(35)"`
|
||||
FullName string `form:"fullname" binding:"MaxSize(40)"`
|
||||
Email string `form:"email" binding:"Required;Email;MaxSize(50)"`
|
||||
Website string `form:"website" binding:"Url;MaxSize(50)"`
|
||||
Location string `form:"location" binding:"MaxSize(50)"`
|
||||
Avatar string `form:"avatar" binding:"Required;Email;MaxSize(50)"`
|
||||
}
|
||||
|
||||
func (f *UpdateProfileForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
}
|
||||
|
||||
type ChangePasswordForm struct {
|
||||
OldPassword string `form:"old_password" binding:"Required;MinSize(6);MaxSize(30)"`
|
||||
Password string `form:"password" binding:"Required;MinSize(6);MaxSize(30)"`
|
||||
Retype string `form:"retype"`
|
||||
}
|
||||
|
||||
func (f *ChangePasswordForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) {
|
||||
validate(errs, ctx.Data, f, l)
|
||||
}
|
|
@ -5,9 +5,7 @@
|
|||
package base
|
||||
|
||||
type (
|
||||
// Type TmplData represents data in the templates.
|
||||
TmplData map[string]interface{}
|
||||
TplName string
|
||||
TplName string
|
||||
|
||||
ApiJsonErr struct {
|
||||
Message string `json:"message"`
|
||||
|
|
|
@ -51,7 +51,7 @@ var mailDomains = map[string]string{
|
|||
|
||||
var TemplateFuncs template.FuncMap = map[string]interface{}{
|
||||
"GoVer": func() string {
|
||||
return runtime.Version()
|
||||
return strings.Title(runtime.Version())
|
||||
},
|
||||
"AppName": func() string {
|
||||
return setting.AppName
|
||||
|
@ -69,7 +69,8 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{
|
|||
return fmt.Sprint(time.Since(startTime).Nanoseconds()/1e6) + "ms"
|
||||
},
|
||||
"AvatarLink": AvatarLink,
|
||||
"str2html": Str2html,
|
||||
"str2html": Str2html, // TODO: Legacy
|
||||
"Str2html": Str2html,
|
||||
"TimeSince": TimeSince,
|
||||
"FileSize": FileSize,
|
||||
"Subtract": Subtract,
|
||||
|
@ -98,10 +99,14 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{
|
|||
"DiffTypeToStr": DiffTypeToStr,
|
||||
"DiffLineTypeToStr": DiffLineTypeToStr,
|
||||
"ShortSha": ShortSha,
|
||||
"Oauth2Icon": Oauth2Icon,
|
||||
"Oauth2Name": Oauth2Name,
|
||||
"Md5": EncodeMd5,
|
||||
"ActionContent2Commits": ActionContent2Commits,
|
||||
"Oauth2Icon": Oauth2Icon,
|
||||
"Oauth2Name": Oauth2Name,
|
||||
"CreateCaptcha": func() string { return "" },
|
||||
}
|
||||
|
||||
// TODO: Legacy
|
||||
type Actioner interface {
|
||||
GetOpType() int
|
||||
GetActUserName() string
|
||||
|
@ -117,11 +122,11 @@ type Actioner interface {
|
|||
func ActionIcon(opType int) string {
|
||||
switch opType {
|
||||
case 1: // Create repository.
|
||||
return "plus-circle"
|
||||
return "repo"
|
||||
case 5, 9: // Commit repository.
|
||||
return "arrow-circle-o-right"
|
||||
return "git-commit"
|
||||
case 6: // Create issue.
|
||||
return "exclamation-circle"
|
||||
return "issue-opened"
|
||||
case 8: // Transfer repository.
|
||||
return "share"
|
||||
case 10: // Comment issue.
|
||||
|
@ -131,6 +136,7 @@ func ActionIcon(opType int) string {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Legacy
|
||||
const (
|
||||
TPL_CREATE_REPO = `<a href="/user/%s">%s</a> created repository <a href="/%s">%s</a>`
|
||||
TPL_COMMIT_REPO = `<a href="/user/%s">%s</a> pushed to <a href="/%s/src/%s">%s</a> at <a href="/%s">%s</a>%s`
|
||||
|
@ -155,6 +161,15 @@ type PushCommits struct {
|
|||
Commits []*PushCommit
|
||||
}
|
||||
|
||||
func ActionContent2Commits(act Actioner) *PushCommits {
|
||||
var push *PushCommits
|
||||
if err := json.Unmarshal([]byte(act.GetContent()), &push); err != nil {
|
||||
return nil
|
||||
}
|
||||
return push
|
||||
}
|
||||
|
||||
// TODO: Legacy
|
||||
// ActionDesc accepts int that represents action operation type
|
||||
// and returns the description.
|
||||
func ActionDesc(act Actioner) string {
|
||||
|
|
|
@ -13,10 +13,13 @@ import (
|
|||
"fmt"
|
||||
"hash"
|
||||
"math"
|
||||
"strconv"
|
||||
r "math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/Unknwon/i18n"
|
||||
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
|
@ -42,6 +45,33 @@ func GetRandomString(n int, alphabets ...byte) string {
|
|||
return string(bytes)
|
||||
}
|
||||
|
||||
// RandomCreateBytes generate random []byte by specify chars.
|
||||
func RandomCreateBytes(n int, alphabets ...byte) []byte {
|
||||
const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||
var bytes = make([]byte, n)
|
||||
var randby bool
|
||||
if num, err := rand.Read(bytes); num != n || err != nil {
|
||||
r.Seed(time.Now().UnixNano())
|
||||
randby = true
|
||||
}
|
||||
for i, b := range bytes {
|
||||
if len(alphabets) == 0 {
|
||||
if randby {
|
||||
bytes[i] = alphanum[r.Intn(len(alphanum))]
|
||||
} else {
|
||||
bytes[i] = alphanum[b%byte(len(alphanum))]
|
||||
}
|
||||
} else {
|
||||
if randby {
|
||||
bytes[i] = alphabets[r.Intn(len(alphabets))]
|
||||
} else {
|
||||
bytes[i] = alphabets[b%byte(len(alphabets))]
|
||||
}
|
||||
}
|
||||
}
|
||||
return bytes
|
||||
}
|
||||
|
||||
// http://code.google.com/p/go/source/browse/pbkdf2/pbkdf2.go?repo=crypto
|
||||
func PBKDF2(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte {
|
||||
prf := hmac.New(h, password)
|
||||
|
@ -89,7 +119,7 @@ func VerifyTimeLimitCode(data string, minutes int, code string) bool {
|
|||
// split code
|
||||
start := code[:12]
|
||||
lives := code[12:18]
|
||||
if d, err := StrTo(lives).Int(); err == nil {
|
||||
if d, err := com.StrTo(lives).Int(); err == nil {
|
||||
minutes = d
|
||||
}
|
||||
|
||||
|
@ -133,7 +163,7 @@ func CreateTimeLimitCode(data string, minutes int, startInf interface{}) string
|
|||
|
||||
// create sha1 encode string
|
||||
sh := sha1.New()
|
||||
sh.Write([]byte(data + setting.SecretKey + startStr + endStr + ToStr(minutes)))
|
||||
sh.Write([]byte(data + setting.SecretKey + startStr + endStr + com.ToStr(minutes)))
|
||||
encoded := hex.EncodeToString(sh.Sum(nil))
|
||||
|
||||
code := fmt.Sprintf("%s%06d%s", startStr, minutes, encoded)
|
||||
|
@ -240,53 +270,53 @@ func TimeSincePro(then time.Time) string {
|
|||
}
|
||||
|
||||
// TimeSince calculates the time interval and generate user-friendly string.
|
||||
func TimeSince(then time.Time) string {
|
||||
func TimeSince(then time.Time, lang string) string {
|
||||
now := time.Now()
|
||||
|
||||
lbl := "ago"
|
||||
lbl := i18n.Tr(lang, "tool.ago")
|
||||
diff := now.Unix() - then.Unix()
|
||||
if then.After(now) {
|
||||
lbl = "from now"
|
||||
lbl = i18n.Tr(lang, "tool.from_now")
|
||||
diff = then.Unix() - now.Unix()
|
||||
}
|
||||
|
||||
switch {
|
||||
case diff <= 0:
|
||||
return "now"
|
||||
return i18n.Tr(lang, "tool.now")
|
||||
case diff <= 2:
|
||||
return fmt.Sprintf("1 second %s", lbl)
|
||||
return i18n.Tr(lang, "tool.1s", lbl)
|
||||
case diff < 1*Minute:
|
||||
return fmt.Sprintf("%d seconds %s", diff, lbl)
|
||||
return i18n.Tr(lang, "tool.seconds", diff, lbl)
|
||||
|
||||
case diff < 2*Minute:
|
||||
return fmt.Sprintf("1 minute %s", lbl)
|
||||
return i18n.Tr(lang, "tool.1m", lbl)
|
||||
case diff < 1*Hour:
|
||||
return fmt.Sprintf("%d minutes %s", diff/Minute, lbl)
|
||||
return i18n.Tr(lang, "tool.minutes", diff/Minute, lbl)
|
||||
|
||||
case diff < 2*Hour:
|
||||
return fmt.Sprintf("1 hour %s", lbl)
|
||||
return i18n.Tr(lang, "tool.1h", lbl)
|
||||
case diff < 1*Day:
|
||||
return fmt.Sprintf("%d hours %s", diff/Hour, lbl)
|
||||
return i18n.Tr(lang, "tool.hours", diff/Hour, lbl)
|
||||
|
||||
case diff < 2*Day:
|
||||
return fmt.Sprintf("1 day %s", lbl)
|
||||
return i18n.Tr(lang, "tool.1d", lbl)
|
||||
case diff < 1*Week:
|
||||
return fmt.Sprintf("%d days %s", diff/Day, lbl)
|
||||
return i18n.Tr(lang, "tool.days", diff/Day, lbl)
|
||||
|
||||
case diff < 2*Week:
|
||||
return fmt.Sprintf("1 week %s", lbl)
|
||||
return i18n.Tr(lang, "tool.1w", lbl)
|
||||
case diff < 1*Month:
|
||||
return fmt.Sprintf("%d weeks %s", diff/Week, lbl)
|
||||
return i18n.Tr(lang, "tool.weeks", diff/Week, lbl)
|
||||
|
||||
case diff < 2*Month:
|
||||
return fmt.Sprintf("1 month %s", lbl)
|
||||
return i18n.Tr(lang, "tool.1mon", lbl)
|
||||
case diff < 1*Year:
|
||||
return fmt.Sprintf("%d months %s", diff/Month, lbl)
|
||||
return i18n.Tr(lang, "tool.months", diff/Month, lbl)
|
||||
|
||||
case diff < 2*Year:
|
||||
return fmt.Sprintf("1 year %s", lbl)
|
||||
return i18n.Tr(lang, "tool.1y", lbl)
|
||||
default:
|
||||
return fmt.Sprintf("%d years %s", diff/Year, lbl)
|
||||
return i18n.Tr(lang, "tool.years", diff/Year, lbl)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -439,88 +469,3 @@ func DateFormat(t time.Time, format string) string {
|
|||
format = replacer.Replace(format)
|
||||
return t.Format(format)
|
||||
}
|
||||
|
||||
// convert string to specify type
|
||||
|
||||
type StrTo string
|
||||
|
||||
func (f StrTo) Exist() bool {
|
||||
return string(f) != string(0x1E)
|
||||
}
|
||||
|
||||
func (f StrTo) Int() (int, error) {
|
||||
v, err := strconv.ParseInt(f.String(), 10, 32)
|
||||
return int(v), err
|
||||
}
|
||||
|
||||
func (f StrTo) Int64() (int64, error) {
|
||||
v, err := strconv.ParseInt(f.String(), 10, 64)
|
||||
return int64(v), err
|
||||
}
|
||||
|
||||
func (f StrTo) MustInt() int {
|
||||
v, _ := f.Int()
|
||||
return v
|
||||
}
|
||||
|
||||
func (f StrTo) MustInt64() int64 {
|
||||
v, _ := f.Int64()
|
||||
return v
|
||||
}
|
||||
|
||||
func (f StrTo) String() string {
|
||||
if f.Exist() {
|
||||
return string(f)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// convert any type to string
|
||||
func ToStr(value interface{}, args ...int) (s string) {
|
||||
switch v := value.(type) {
|
||||
case bool:
|
||||
s = strconv.FormatBool(v)
|
||||
case float32:
|
||||
s = strconv.FormatFloat(float64(v), 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 32))
|
||||
case float64:
|
||||
s = strconv.FormatFloat(v, 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 64))
|
||||
case int:
|
||||
s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
|
||||
case int8:
|
||||
s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
|
||||
case int16:
|
||||
s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
|
||||
case int32:
|
||||
s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
|
||||
case int64:
|
||||
s = strconv.FormatInt(v, argInt(args).Get(0, 10))
|
||||
case uint:
|
||||
s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
|
||||
case uint8:
|
||||
s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
|
||||
case uint16:
|
||||
s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
|
||||
case uint32:
|
||||
s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
|
||||
case uint64:
|
||||
s = strconv.FormatUint(v, argInt(args).Get(0, 10))
|
||||
case string:
|
||||
s = v
|
||||
case []byte:
|
||||
s = string(v)
|
||||
default:
|
||||
s = fmt.Sprintf("%v", v)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
type argInt []int
|
||||
|
||||
func (a argInt) Get(i int, args ...int) (r int) {
|
||||
if i >= 0 && i < len(a) {
|
||||
r = a[i]
|
||||
} else if len(args) > 0 {
|
||||
r = args[0]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
3623
modules/bin/conf.go
3623
modules/bin/conf.go
File diff suppressed because it is too large
Load diff
201
modules/captcha/captcha.go
Normal file
201
modules/captcha/captcha.go
Normal file
|
@ -0,0 +1,201 @@
|
|||
// Copyright 2014 The Gogs 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 captcha a middleware that provides captcha service for Macaron.
|
||||
package captcha
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/Unknwon/macaron"
|
||||
|
||||
"github.com/gogits/cache"
|
||||
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultChars = []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
)
|
||||
|
||||
const (
|
||||
// default captcha attributes
|
||||
challengeNums = 6
|
||||
expiration = 600
|
||||
fieldIdName = "captcha_id"
|
||||
fieldCaptchaName = "captcha"
|
||||
cachePrefix = "captcha_"
|
||||
defaultURLPrefix = "/captcha/"
|
||||
)
|
||||
|
||||
// Captcha struct
|
||||
type Captcha struct {
|
||||
store cache.Cache
|
||||
|
||||
// url prefix for captcha image
|
||||
URLPrefix string
|
||||
|
||||
// specify captcha id input field name
|
||||
FieldIdName string
|
||||
// specify captcha result input field name
|
||||
FieldCaptchaName string
|
||||
|
||||
// captcha image width and height
|
||||
StdWidth int
|
||||
StdHeight int
|
||||
|
||||
// captcha chars nums
|
||||
ChallengeNums int
|
||||
|
||||
// captcha expiration seconds
|
||||
Expiration int64
|
||||
|
||||
// cache key prefix
|
||||
CachePrefix string
|
||||
}
|
||||
|
||||
// generate key string
|
||||
func (c *Captcha) key(id string) string {
|
||||
return c.CachePrefix + id
|
||||
}
|
||||
|
||||
// generate rand chars with default chars
|
||||
func (c *Captcha) genRandChars() []byte {
|
||||
return base.RandomCreateBytes(c.ChallengeNums, defaultChars...)
|
||||
}
|
||||
|
||||
// beego filter handler for serve captcha image
|
||||
func (c *Captcha) Handler(ctx *macaron.Context) {
|
||||
var chars []byte
|
||||
|
||||
id := path.Base(ctx.Req.RequestURI)
|
||||
if i := strings.Index(id, "."); i != -1 {
|
||||
id = id[:i]
|
||||
}
|
||||
|
||||
key := c.key(id)
|
||||
|
||||
if v, ok := c.store.Get(key).([]byte); ok {
|
||||
chars = v
|
||||
} else {
|
||||
ctx.Status(404)
|
||||
ctx.Write([]byte("captcha not found"))
|
||||
return
|
||||
}
|
||||
|
||||
// reload captcha
|
||||
if len(ctx.Query("reload")) > 0 {
|
||||
chars = c.genRandChars()
|
||||
if err := c.store.Put(key, chars, c.Expiration); err != nil {
|
||||
ctx.Status(500)
|
||||
ctx.Write([]byte("captcha reload error"))
|
||||
log.Error(4, "Reload Create Captcha Error: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
img := NewImage(chars, c.StdWidth, c.StdHeight)
|
||||
if _, err := img.WriteTo(ctx.RW()); err != nil {
|
||||
log.Error(4, "Write Captcha Image Error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// tempalte func for output html
|
||||
func (c *Captcha) CreateCaptchaHtml() template.HTML {
|
||||
value, err := c.CreateCaptcha()
|
||||
if err != nil {
|
||||
log.Error(4, "Create Captcha Error: %v", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
// create html
|
||||
return template.HTML(fmt.Sprintf(`<input type="hidden" name="%s" value="%s">`+
|
||||
`<a class="captcha" href="javascript:">`+
|
||||
`<img onclick="this.src=('%s%s.png?reload='+(new Date()).getTime())" class="captcha-img" src="%s%s.png">`+
|
||||
`</a>`, c.FieldIdName, value, c.URLPrefix, value, c.URLPrefix, value))
|
||||
}
|
||||
|
||||
// create a new captcha id
|
||||
func (c *Captcha) CreateCaptcha() (string, error) {
|
||||
// generate captcha id
|
||||
id := string(base.RandomCreateBytes(15))
|
||||
|
||||
// get the captcha chars
|
||||
chars := c.genRandChars()
|
||||
|
||||
// save to store
|
||||
if err := c.store.Put(c.key(id), chars, c.Expiration); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// verify from a request
|
||||
func (c *Captcha) VerifyReq(req *http.Request) bool {
|
||||
req.ParseForm()
|
||||
return c.Verify(req.Form.Get(c.FieldIdName), req.Form.Get(c.FieldCaptchaName))
|
||||
}
|
||||
|
||||
// direct verify id and challenge string
|
||||
func (c *Captcha) Verify(id string, challenge string) (success bool) {
|
||||
if len(challenge) == 0 || len(id) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var chars []byte
|
||||
|
||||
key := c.key(id)
|
||||
|
||||
if v, ok := c.store.Get(key).([]byte); ok && len(v) == len(challenge) {
|
||||
chars = v
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
// finally remove it
|
||||
c.store.Delete(key)
|
||||
}()
|
||||
|
||||
// verify challenge
|
||||
for i, c := range chars {
|
||||
if c != challenge[i]-48 {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// create a new captcha.Captcha
|
||||
func NewCaptcha(urlPrefix string, store cache.Cache) *Captcha {
|
||||
cpt := &Captcha{}
|
||||
cpt.store = store
|
||||
cpt.FieldIdName = fieldIdName
|
||||
cpt.FieldCaptchaName = fieldCaptchaName
|
||||
cpt.ChallengeNums = challengeNums
|
||||
cpt.Expiration = expiration
|
||||
cpt.CachePrefix = cachePrefix
|
||||
cpt.StdWidth = stdWidth
|
||||
cpt.StdHeight = stdHeight
|
||||
|
||||
if len(urlPrefix) == 0 {
|
||||
urlPrefix = defaultURLPrefix
|
||||
}
|
||||
|
||||
if urlPrefix[len(urlPrefix)-1] != '/' {
|
||||
urlPrefix += "/"
|
||||
}
|
||||
|
||||
cpt.URLPrefix = urlPrefix
|
||||
|
||||
base.TemplateFuncs["CreateCaptcha"] = cpt.CreateCaptchaHtml
|
||||
return cpt
|
||||
}
|
487
modules/captcha/image.go
Normal file
487
modules/captcha/image.go
Normal file
|
@ -0,0 +1,487 @@
|
|||
// Copyright 2014 The Gogs 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 captcha
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/png"
|
||||
"io"
|
||||
"math"
|
||||
)
|
||||
|
||||
const (
|
||||
fontWidth = 11
|
||||
fontHeight = 18
|
||||
blackChar = 1
|
||||
|
||||
// Standard width and height of a captcha image.
|
||||
stdWidth = 240
|
||||
stdHeight = 80
|
||||
// Maximum absolute skew factor of a single digit.
|
||||
maxSkew = 0.7
|
||||
// Number of background circles.
|
||||
circleCount = 20
|
||||
)
|
||||
|
||||
var font = [][]byte{
|
||||
{ // 0
|
||||
0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
|
||||
0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
|
||||
0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0,
|
||||
0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0,
|
||||
1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0,
|
||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1,
|
||||
0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0,
|
||||
0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0,
|
||||
0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
|
||||
0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
|
||||
},
|
||||
{ // 1
|
||||
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0,
|
||||
0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0,
|
||||
0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0,
|
||||
0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0,
|
||||
0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
},
|
||||
{ // 2
|
||||
0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0,
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
|
||||
1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0,
|
||||
0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0,
|
||||
0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0,
|
||||
0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
},
|
||||
{ // 3
|
||||
0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
|
||||
1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,
|
||||
0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0,
|
||||
0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
|
||||
1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
|
||||
},
|
||||
{ // 4
|
||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,
|
||||
0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0,
|
||||
0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0,
|
||||
0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0,
|
||||
0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0,
|
||||
0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0,
|
||||
0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0,
|
||||
0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0,
|
||||
0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0,
|
||||
1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
|
||||
},
|
||||
{ // 5
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
|
||||
0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
|
||||
1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
|
||||
0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0,
|
||||
},
|
||||
{ // 6
|
||||
0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0,
|
||||
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0,
|
||||
0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0,
|
||||
0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0,
|
||||
1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0,
|
||||
1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1,
|
||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1,
|
||||
0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0,
|
||||
0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
|
||||
0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
|
||||
},
|
||||
{ // 7
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0,
|
||||
},
|
||||
{ // 8
|
||||
0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
|
||||
0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0,
|
||||
0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1,
|
||||
0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0,
|
||||
0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
|
||||
0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
|
||||
0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0,
|
||||
0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0,
|
||||
1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1,
|
||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0,
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
|
||||
0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
|
||||
},
|
||||
{ // 9
|
||||
0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
|
||||
0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0,
|
||||
1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0,
|
||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1,
|
||||
0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1,
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1,
|
||||
0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
|
||||
0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
|
||||
},
|
||||
}
|
||||
|
||||
type Image struct {
|
||||
*image.Paletted
|
||||
numWidth int
|
||||
numHeight int
|
||||
dotSize int
|
||||
}
|
||||
|
||||
var prng = &siprng{}
|
||||
|
||||
// randIntn returns a pseudorandom non-negative int in range [0, n).
|
||||
func randIntn(n int) int {
|
||||
return prng.Intn(n)
|
||||
}
|
||||
|
||||
// randInt returns a pseudorandom int in range [from, to].
|
||||
func randInt(from, to int) int {
|
||||
return prng.Intn(to+1-from) + from
|
||||
}
|
||||
|
||||
// randFloat returns a pseudorandom float64 in range [from, to].
|
||||
func randFloat(from, to float64) float64 {
|
||||
return (to-from)*prng.Float64() + from
|
||||
}
|
||||
|
||||
func randomPalette() color.Palette {
|
||||
p := make([]color.Color, circleCount+1)
|
||||
// Transparent color.
|
||||
p[0] = color.RGBA{0xFF, 0xFF, 0xFF, 0x00}
|
||||
// Primary color.
|
||||
prim := color.RGBA{
|
||||
uint8(randIntn(129)),
|
||||
uint8(randIntn(129)),
|
||||
uint8(randIntn(129)),
|
||||
0xFF,
|
||||
}
|
||||
p[1] = prim
|
||||
// Circle colors.
|
||||
for i := 2; i <= circleCount; i++ {
|
||||
p[i] = randomBrightness(prim, 255)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// NewImage returns a new captcha image of the given width and height with the
|
||||
// given digits, where each digit must be in range 0-9.
|
||||
func NewImage(digits []byte, width, height int) *Image {
|
||||
m := new(Image)
|
||||
m.Paletted = image.NewPaletted(image.Rect(0, 0, width, height), randomPalette())
|
||||
m.calculateSizes(width, height, len(digits))
|
||||
// Randomly position captcha inside the image.
|
||||
maxx := width - (m.numWidth+m.dotSize)*len(digits) - m.dotSize
|
||||
maxy := height - m.numHeight - m.dotSize*2
|
||||
var border int
|
||||
if width > height {
|
||||
border = height / 5
|
||||
} else {
|
||||
border = width / 5
|
||||
}
|
||||
x := randInt(border, maxx-border)
|
||||
y := randInt(border, maxy-border)
|
||||
// Draw digits.
|
||||
for _, n := range digits {
|
||||
m.drawDigit(font[n], x, y)
|
||||
x += m.numWidth + m.dotSize
|
||||
}
|
||||
// Draw strike-through line.
|
||||
m.strikeThrough()
|
||||
// Apply wave distortion.
|
||||
m.distort(randFloat(5, 10), randFloat(100, 200))
|
||||
// Fill image with random circles.
|
||||
m.fillWithCircles(circleCount, m.dotSize)
|
||||
return m
|
||||
}
|
||||
|
||||
// encodedPNG encodes an image to PNG and returns
|
||||
// the result as a byte slice.
|
||||
func (m *Image) encodedPNG() []byte {
|
||||
var buf bytes.Buffer
|
||||
if err := png.Encode(&buf, m.Paletted); err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// WriteTo writes captcha image in PNG format into the given writer.
|
||||
func (m *Image) WriteTo(w io.Writer) (int64, error) {
|
||||
n, err := w.Write(m.encodedPNG())
|
||||
return int64(n), err
|
||||
}
|
||||
|
||||
func (m *Image) calculateSizes(width, height, ncount int) {
|
||||
// Goal: fit all digits inside the image.
|
||||
var border int
|
||||
if width > height {
|
||||
border = height / 4
|
||||
} else {
|
||||
border = width / 4
|
||||
}
|
||||
// Convert everything to floats for calculations.
|
||||
w := float64(width - border*2)
|
||||
h := float64(height - border*2)
|
||||
// fw takes into account 1-dot spacing between digits.
|
||||
fw := float64(fontWidth + 1)
|
||||
fh := float64(fontHeight)
|
||||
nc := float64(ncount)
|
||||
// Calculate the width of a single digit taking into account only the
|
||||
// width of the image.
|
||||
nw := w / nc
|
||||
// Calculate the height of a digit from this width.
|
||||
nh := nw * fh / fw
|
||||
// Digit too high?
|
||||
if nh > h {
|
||||
// Fit digits based on height.
|
||||
nh = h
|
||||
nw = fw / fh * nh
|
||||
}
|
||||
// Calculate dot size.
|
||||
m.dotSize = int(nh / fh)
|
||||
// Save everything, making the actual width smaller by 1 dot to account
|
||||
// for spacing between digits.
|
||||
m.numWidth = int(nw) - m.dotSize
|
||||
m.numHeight = int(nh)
|
||||
}
|
||||
|
||||
func (m *Image) drawHorizLine(fromX, toX, y int, colorIdx uint8) {
|
||||
for x := fromX; x <= toX; x++ {
|
||||
m.SetColorIndex(x, y, colorIdx)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Image) drawCircle(x, y, radius int, colorIdx uint8) {
|
||||
f := 1 - radius
|
||||
dfx := 1
|
||||
dfy := -2 * radius
|
||||
xo := 0
|
||||
yo := radius
|
||||
|
||||
m.SetColorIndex(x, y+radius, colorIdx)
|
||||
m.SetColorIndex(x, y-radius, colorIdx)
|
||||
m.drawHorizLine(x-radius, x+radius, y, colorIdx)
|
||||
|
||||
for xo < yo {
|
||||
if f >= 0 {
|
||||
yo--
|
||||
dfy += 2
|
||||
f += dfy
|
||||
}
|
||||
xo++
|
||||
dfx += 2
|
||||
f += dfx
|
||||
m.drawHorizLine(x-xo, x+xo, y+yo, colorIdx)
|
||||
m.drawHorizLine(x-xo, x+xo, y-yo, colorIdx)
|
||||
m.drawHorizLine(x-yo, x+yo, y+xo, colorIdx)
|
||||
m.drawHorizLine(x-yo, x+yo, y-xo, colorIdx)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Image) fillWithCircles(n, maxradius int) {
|
||||
maxx := m.Bounds().Max.X
|
||||
maxy := m.Bounds().Max.Y
|
||||
for i := 0; i < n; i++ {
|
||||
colorIdx := uint8(randInt(1, circleCount-1))
|
||||
r := randInt(1, maxradius)
|
||||
m.drawCircle(randInt(r, maxx-r), randInt(r, maxy-r), r, colorIdx)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Image) strikeThrough() {
|
||||
maxx := m.Bounds().Max.X
|
||||
maxy := m.Bounds().Max.Y
|
||||
y := randInt(maxy/3, maxy-maxy/3)
|
||||
amplitude := randFloat(5, 20)
|
||||
period := randFloat(80, 180)
|
||||
dx := 2.0 * math.Pi / period
|
||||
for x := 0; x < maxx; x++ {
|
||||
xo := amplitude * math.Cos(float64(y)*dx)
|
||||
yo := amplitude * math.Sin(float64(x)*dx)
|
||||
for yn := 0; yn < m.dotSize; yn++ {
|
||||
r := randInt(0, m.dotSize)
|
||||
m.drawCircle(x+int(xo), y+int(yo)+(yn*m.dotSize), r/2, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Image) drawDigit(digit []byte, x, y int) {
|
||||
skf := randFloat(-maxSkew, maxSkew)
|
||||
xs := float64(x)
|
||||
r := m.dotSize / 2
|
||||
y += randInt(-r, r)
|
||||
for yo := 0; yo < fontHeight; yo++ {
|
||||
for xo := 0; xo < fontWidth; xo++ {
|
||||
if digit[yo*fontWidth+xo] != blackChar {
|
||||
continue
|
||||
}
|
||||
m.drawCircle(x+xo*m.dotSize, y+yo*m.dotSize, r, 1)
|
||||
}
|
||||
xs += skf
|
||||
x = int(xs)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Image) distort(amplude float64, period float64) {
|
||||
w := m.Bounds().Max.X
|
||||
h := m.Bounds().Max.Y
|
||||
|
||||
oldm := m.Paletted
|
||||
newm := image.NewPaletted(image.Rect(0, 0, w, h), oldm.Palette)
|
||||
|
||||
dx := 2.0 * math.Pi / period
|
||||
for x := 0; x < w; x++ {
|
||||
for y := 0; y < h; y++ {
|
||||
xo := amplude * math.Sin(float64(y)*dx)
|
||||
yo := amplude * math.Cos(float64(x)*dx)
|
||||
newm.SetColorIndex(x, y, oldm.ColorIndexAt(x+int(xo), y+int(yo)))
|
||||
}
|
||||
}
|
||||
m.Paletted = newm
|
||||
}
|
||||
|
||||
func randomBrightness(c color.RGBA, max uint8) color.RGBA {
|
||||
minc := min3(c.R, c.G, c.B)
|
||||
maxc := max3(c.R, c.G, c.B)
|
||||
if maxc > max {
|
||||
return c
|
||||
}
|
||||
n := randIntn(int(max-maxc)) - int(minc)
|
||||
return color.RGBA{
|
||||
uint8(int(c.R) + n),
|
||||
uint8(int(c.G) + n),
|
||||
uint8(int(c.B) + n),
|
||||
uint8(c.A),
|
||||
}
|
||||
}
|
||||
|
||||
func min3(x, y, z uint8) (m uint8) {
|
||||
m = x
|
||||
if y < m {
|
||||
m = y
|
||||
}
|
||||
if z < m {
|
||||
m = z
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func max3(x, y, z uint8) (m uint8) {
|
||||
m = x
|
||||
if y > m {
|
||||
m = y
|
||||
}
|
||||
if z > m {
|
||||
m = z
|
||||
}
|
||||
return
|
||||
}
|
42
modules/captcha/image_test.go
Normal file
42
modules/captcha/image_test.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
// Copyright 2014 The Gogs 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 captcha
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/astaxie/beego/utils"
|
||||
)
|
||||
|
||||
type byteCounter struct {
|
||||
n int64
|
||||
}
|
||||
|
||||
func (bc *byteCounter) Write(b []byte) (int, error) {
|
||||
bc.n += int64(len(b))
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
func BenchmarkNewImage(b *testing.B) {
|
||||
b.StopTimer()
|
||||
d := utils.RandomCreateBytes(challengeNums, defaultChars...)
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
NewImage(d, stdWidth, stdHeight)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkImageWriteTo(b *testing.B) {
|
||||
b.StopTimer()
|
||||
d := utils.RandomCreateBytes(challengeNums, defaultChars...)
|
||||
b.StartTimer()
|
||||
counter := &byteCounter{}
|
||||
for i := 0; i < b.N; i++ {
|
||||
img := NewImage(d, stdWidth, stdHeight)
|
||||
img.WriteTo(counter)
|
||||
b.SetBytes(counter.n)
|
||||
counter.n = 0
|
||||
}
|
||||
}
|
267
modules/captcha/siprng.go
Normal file
267
modules/captcha/siprng.go
Normal file
|
@ -0,0 +1,267 @@
|
|||
// Copyright 2014 The Gogs 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 captcha
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// siprng is PRNG based on SipHash-2-4.
|
||||
type siprng struct {
|
||||
mu sync.Mutex
|
||||
k0, k1, ctr uint64
|
||||
}
|
||||
|
||||
// siphash implements SipHash-2-4, accepting a uint64 as a message.
|
||||
func siphash(k0, k1, m uint64) uint64 {
|
||||
// Initialization.
|
||||
v0 := k0 ^ 0x736f6d6570736575
|
||||
v1 := k1 ^ 0x646f72616e646f6d
|
||||
v2 := k0 ^ 0x6c7967656e657261
|
||||
v3 := k1 ^ 0x7465646279746573
|
||||
t := uint64(8) << 56
|
||||
|
||||
// Compression.
|
||||
v3 ^= m
|
||||
|
||||
// Round 1.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
// Round 2.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
v0 ^= m
|
||||
|
||||
// Compress last block.
|
||||
v3 ^= t
|
||||
|
||||
// Round 1.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
// Round 2.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
v0 ^= t
|
||||
|
||||
// Finalization.
|
||||
v2 ^= 0xff
|
||||
|
||||
// Round 1.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
// Round 2.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
// Round 3.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
// Round 4.
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
return v0 ^ v1 ^ v2 ^ v3
|
||||
}
|
||||
|
||||
// rekey sets a new PRNG key, which is read from crypto/rand.
|
||||
func (p *siprng) rekey() {
|
||||
var k [16]byte
|
||||
if _, err := io.ReadFull(rand.Reader, k[:]); err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
p.k0 = binary.LittleEndian.Uint64(k[0:8])
|
||||
p.k1 = binary.LittleEndian.Uint64(k[8:16])
|
||||
p.ctr = 1
|
||||
}
|
||||
|
||||
// Uint64 returns a new pseudorandom uint64.
|
||||
// It rekeys PRNG on the first call and every 64 MB of generated data.
|
||||
func (p *siprng) Uint64() uint64 {
|
||||
p.mu.Lock()
|
||||
if p.ctr == 0 || p.ctr > 8*1024*1024 {
|
||||
p.rekey()
|
||||
}
|
||||
v := siphash(p.k0, p.k1, p.ctr)
|
||||
p.ctr++
|
||||
p.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
|
||||
func (p *siprng) Int63() int64 {
|
||||
return int64(p.Uint64() & 0x7fffffffffffffff)
|
||||
}
|
||||
|
||||
func (p *siprng) Uint32() uint32 {
|
||||
return uint32(p.Uint64())
|
||||
}
|
||||
|
||||
func (p *siprng) Int31() int32 {
|
||||
return int32(p.Uint32() & 0x7fffffff)
|
||||
}
|
||||
|
||||
func (p *siprng) Intn(n int) int {
|
||||
if n <= 0 {
|
||||
panic("invalid argument to Intn")
|
||||
}
|
||||
if n <= 1<<31-1 {
|
||||
return int(p.Int31n(int32(n)))
|
||||
}
|
||||
return int(p.Int63n(int64(n)))
|
||||
}
|
||||
|
||||
func (p *siprng) Int63n(n int64) int64 {
|
||||
if n <= 0 {
|
||||
panic("invalid argument to Int63n")
|
||||
}
|
||||
max := int64((1 << 63) - 1 - (1<<63)%uint64(n))
|
||||
v := p.Int63()
|
||||
for v > max {
|
||||
v = p.Int63()
|
||||
}
|
||||
return v % n
|
||||
}
|
||||
|
||||
func (p *siprng) Int31n(n int32) int32 {
|
||||
if n <= 0 {
|
||||
panic("invalid argument to Int31n")
|
||||
}
|
||||
max := int32((1 << 31) - 1 - (1<<31)%uint32(n))
|
||||
v := p.Int31()
|
||||
for v > max {
|
||||
v = p.Int31()
|
||||
}
|
||||
return v % n
|
||||
}
|
||||
|
||||
func (p *siprng) Float64() float64 { return float64(p.Int63()) / (1 << 63) }
|
23
modules/captcha/siprng_test.go
Normal file
23
modules/captcha/siprng_test.go
Normal file
|
@ -0,0 +1,23 @@
|
|||
// Copyright 2014 The Gogs 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 captcha
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestSiphash(t *testing.T) {
|
||||
good := uint64(0xe849e8bb6ffe2567)
|
||||
cur := siphash(0, 0, 0)
|
||||
if cur != good {
|
||||
t.Fatalf("siphash: expected %x, got %x", good, cur)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSiprng(b *testing.B) {
|
||||
b.SetBytes(8)
|
||||
p := &siprng{}
|
||||
for i := 0; i < b.N; i++ {
|
||||
p.Uint64()
|
||||
}
|
||||
}
|
26
modules/git/blob.go
Normal file
26
modules/git/blob.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
// Copyright 2014 The Gogs 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 git
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
)
|
||||
|
||||
type Blob struct {
|
||||
repo *Repository
|
||||
*TreeEntry
|
||||
}
|
||||
|
||||
func (b *Blob) Data() (io.Reader, error) {
|
||||
stdout, stderr, err := com.ExecCmdDirBytes(b.repo.Path, "git", "show", b.Id.String())
|
||||
if err != nil {
|
||||
return nil, errors.New(string(stderr))
|
||||
}
|
||||
return bytes.NewBuffer(stdout), nil
|
||||
}
|
78
modules/git/commit.go
Normal file
78
modules/git/commit.go
Normal file
|
@ -0,0 +1,78 @@
|
|||
// Copyright 2014 The Gogs 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 git
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Commit represents a git commit.
|
||||
type Commit struct {
|
||||
Tree
|
||||
Id sha1 // The id of this commit object
|
||||
Author *Signature
|
||||
Committer *Signature
|
||||
CommitMessage string
|
||||
|
||||
parents []sha1 // sha1 strings
|
||||
}
|
||||
|
||||
// Return the commit message. Same as retrieving CommitMessage directly.
|
||||
func (c *Commit) Message() string {
|
||||
return c.CommitMessage
|
||||
}
|
||||
|
||||
func (c *Commit) Summary() string {
|
||||
return strings.Split(c.CommitMessage, "\n")[0]
|
||||
}
|
||||
|
||||
// Return oid of the parent number n (0-based index). Return nil if no such parent exists.
|
||||
func (c *Commit) ParentId(n int) (id sha1, err error) {
|
||||
if n >= len(c.parents) {
|
||||
err = IdNotExist
|
||||
return
|
||||
}
|
||||
return c.parents[n], nil
|
||||
}
|
||||
|
||||
// Return parent number n (0-based index)
|
||||
func (c *Commit) Parent(n int) (*Commit, error) {
|
||||
id, err := c.ParentId(n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parent, err := c.repo.getCommit(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return parent, nil
|
||||
}
|
||||
|
||||
// Return the number of parents of the commit. 0 if this is the
|
||||
// root commit, otherwise 1,2,...
|
||||
func (c *Commit) ParentCount() int {
|
||||
return len(c.parents)
|
||||
}
|
||||
|
||||
func (c *Commit) CommitsBefore() (*list.List, error) {
|
||||
return c.repo.getCommitsBefore(c.Id)
|
||||
}
|
||||
|
||||
func (c *Commit) CommitsBeforeUntil(commitId string) (*list.List, error) {
|
||||
ec, err := c.repo.GetCommit(commitId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.repo.CommitsBetween(c, ec)
|
||||
}
|
||||
|
||||
func (c *Commit) CommitsCount() (int, error) {
|
||||
return c.repo.commitsCount(c.Id)
|
||||
}
|
||||
|
||||
func (c *Commit) GetCommitOfRelPath(relPath string) (*Commit, error) {
|
||||
return c.repo.getCommitOfRelPath(c.Id, relPath)
|
||||
}
|
36
modules/git/commit_archive.go
Normal file
36
modules/git/commit_archive.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
// Copyright 2014 The Gogs 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 git
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
)
|
||||
|
||||
type ArchiveType int
|
||||
|
||||
const (
|
||||
ZIP ArchiveType = iota + 1
|
||||
TARGZ
|
||||
)
|
||||
|
||||
func (c *Commit) CreateArchive(path string, archiveType ArchiveType) error {
|
||||
var format string
|
||||
switch archiveType {
|
||||
case ZIP:
|
||||
format = "zip"
|
||||
case TARGZ:
|
||||
format = "tar.gz"
|
||||
default:
|
||||
return fmt.Errorf("unknown format: %v", archiveType)
|
||||
}
|
||||
|
||||
_, stderr, err := com.ExecCmdDir(c.repo.Path, "git", "archive", "--format="+format, "-o", path, c.Id.String())
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s", stderr)
|
||||
}
|
||||
return nil
|
||||
}
|
27
modules/git/repo.go
Normal file
27
modules/git/repo.go
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2014 The Gogs 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 git
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// Repository represents a Git repository.
|
||||
type Repository struct {
|
||||
Path string
|
||||
|
||||
commitCache map[sha1]*Commit
|
||||
tagCache map[sha1]*Tag
|
||||
}
|
||||
|
||||
// OpenRepository opens the repository at the given path.
|
||||
func OpenRepository(repoPath string) (*Repository, error) {
|
||||
repoPath, err := filepath.Abs(repoPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Repository{Path: repoPath}, nil
|
||||
}
|
38
modules/git/repo_branch.go
Normal file
38
modules/git/repo_branch.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Copyright 2014 The Gogs 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 git
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
)
|
||||
|
||||
func IsBranchExist(repoPath, branchName string) bool {
|
||||
_, _, err := com.ExecCmdDir(repoPath, "git", "show-ref", "--verify", "refs/heads/"+branchName)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (repo *Repository) IsBranchExist(branchName string) bool {
|
||||
return IsBranchExist(repo.Path, branchName)
|
||||
}
|
||||
|
||||
func (repo *Repository) GetBranches() ([]string, error) {
|
||||
stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "show-ref", "--heads")
|
||||
if err != nil {
|
||||
return nil, errors.New(stderr)
|
||||
}
|
||||
infos := strings.Split(stdout, "\n")
|
||||
branches := make([]string, len(infos)-1)
|
||||
for i, info := range infos[:len(infos)-1] {
|
||||
parts := strings.Split(info, " ")
|
||||
if len(parts) != 2 {
|
||||
continue // NOTE: I should believe git will not give me wrong string.
|
||||
}
|
||||
branches[i] = strings.TrimPrefix(parts[1], "refs/heads/")
|
||||
}
|
||||
return branches, nil
|
||||
}
|
234
modules/git/repo_commit.go
Normal file
234
modules/git/repo_commit.go
Normal file
|
@ -0,0 +1,234 @@
|
|||
// Copyright 2014 The Gogs 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 git
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"errors"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
)
|
||||
|
||||
func (repo *Repository) getCommitIdOfRef(refpath string) (string, error) {
|
||||
stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "show-ref", "--verify", refpath)
|
||||
if err != nil {
|
||||
return "", errors.New(stderr)
|
||||
}
|
||||
return strings.Split(stdout, " ")[0], nil
|
||||
}
|
||||
|
||||
func (repo *Repository) GetCommitIdOfBranch(branchName string) (string, error) {
|
||||
return repo.getCommitIdOfRef("refs/heads/" + branchName)
|
||||
}
|
||||
|
||||
// get branch's last commit or a special commit by id string
|
||||
func (repo *Repository) GetCommitOfBranch(branchName string) (*Commit, error) {
|
||||
commitId, err := repo.GetCommitIdOfBranch(branchName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return repo.GetCommit(commitId)
|
||||
}
|
||||
|
||||
// Parse commit information from the (uncompressed) raw
|
||||
// data from the commit object.
|
||||
// \n\n separate headers from message
|
||||
func parseCommitData(data []byte) (*Commit, error) {
|
||||
commit := new(Commit)
|
||||
commit.parents = make([]sha1, 0, 1)
|
||||
// we now have the contents of the commit object. Let's investigate...
|
||||
nextline := 0
|
||||
l:
|
||||
for {
|
||||
eol := bytes.IndexByte(data[nextline:], '\n')
|
||||
switch {
|
||||
case eol > 0:
|
||||
line := data[nextline : nextline+eol]
|
||||
spacepos := bytes.IndexByte(line, ' ')
|
||||
reftype := line[:spacepos]
|
||||
switch string(reftype) {
|
||||
case "tree":
|
||||
id, err := NewIdFromString(string(line[spacepos+1:]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
commit.Tree.Id = id
|
||||
case "parent":
|
||||
// A commit can have one or more parents
|
||||
oid, err := NewIdFromString(string(line[spacepos+1:]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
commit.parents = append(commit.parents, oid)
|
||||
case "author":
|
||||
sig, err := newSignatureFromCommitline(line[spacepos+1:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
commit.Author = sig
|
||||
case "committer":
|
||||
sig, err := newSignatureFromCommitline(line[spacepos+1:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
commit.Committer = sig
|
||||
}
|
||||
nextline += eol + 1
|
||||
case eol == 0:
|
||||
commit.CommitMessage = string(data[nextline+1:])
|
||||
break l
|
||||
default:
|
||||
break l
|
||||
}
|
||||
}
|
||||
return commit, nil
|
||||
}
|
||||
|
||||
func (repo *Repository) getCommit(id sha1) (*Commit, error) {
|
||||
if repo.commitCache != nil {
|
||||
if c, ok := repo.commitCache[id]; ok {
|
||||
return c, nil
|
||||
}
|
||||
} else {
|
||||
repo.commitCache = make(map[sha1]*Commit, 10)
|
||||
}
|
||||
|
||||
data, bytErr, err := com.ExecCmdDirBytes(repo.Path, "git", "cat-file", "-p", id.String())
|
||||
if err != nil {
|
||||
return nil, errors.New(string(bytErr))
|
||||
}
|
||||
|
||||
commit, err := parseCommitData(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
commit.repo = repo
|
||||
commit.Id = id
|
||||
|
||||
repo.commitCache[id] = commit
|
||||
return commit, nil
|
||||
}
|
||||
|
||||
// Find the commit object in the repository.
|
||||
func (repo *Repository) GetCommit(commitId string) (*Commit, error) {
|
||||
id, err := NewIdFromString(commitId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return repo.getCommit(id)
|
||||
}
|
||||
|
||||
func (repo *Repository) commitsCount(id sha1) (int, error) {
|
||||
stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "rev-list", "--count", id.String())
|
||||
if err != nil {
|
||||
return 0, errors.New(stderr)
|
||||
}
|
||||
return com.StrTo(strings.TrimSpace(stdout)).Int()
|
||||
}
|
||||
|
||||
// used only for single tree, (]
|
||||
func (repo *Repository) CommitsBetween(last *Commit, before *Commit) (*list.List, error) {
|
||||
l := list.New()
|
||||
if last == nil || last.ParentCount() == 0 {
|
||||
return l, nil
|
||||
}
|
||||
|
||||
var err error
|
||||
cur := last
|
||||
for {
|
||||
if cur.Id.Equal(before.Id) {
|
||||
break
|
||||
}
|
||||
l.PushBack(cur)
|
||||
if cur.ParentCount() == 0 {
|
||||
break
|
||||
}
|
||||
cur, err = cur.Parent(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func (repo *Repository) commitsBefore(lock *sync.Mutex, l *list.List, parent *list.Element, id sha1, limit int) error {
|
||||
commit, err := repo.getCommit(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var e *list.Element
|
||||
if parent == nil {
|
||||
e = l.PushBack(commit)
|
||||
} else {
|
||||
var in = parent
|
||||
for {
|
||||
if in == nil {
|
||||
break
|
||||
} else if in.Value.(*Commit).Id.Equal(commit.Id) {
|
||||
return nil
|
||||
} else {
|
||||
if in.Next() == nil {
|
||||
break
|
||||
}
|
||||
if in.Value.(*Commit).Committer.When.Equal(commit.Committer.When) {
|
||||
break
|
||||
}
|
||||
|
||||
if in.Value.(*Commit).Committer.When.After(commit.Committer.When) &&
|
||||
in.Next().Value.(*Commit).Committer.When.Before(commit.Committer.When) {
|
||||
break
|
||||
}
|
||||
}
|
||||
in = in.Next()
|
||||
}
|
||||
|
||||
e = l.InsertAfter(commit, in)
|
||||
}
|
||||
|
||||
var pr = parent
|
||||
if commit.ParentCount() > 1 {
|
||||
pr = e
|
||||
}
|
||||
|
||||
for i := 0; i < commit.ParentCount(); i++ {
|
||||
id, err := commit.ParentId(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = repo.commitsBefore(lock, l, pr, id, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *Repository) getCommitsBefore(id sha1) (*list.List, error) {
|
||||
l := list.New()
|
||||
lock := new(sync.Mutex)
|
||||
err := repo.commitsBefore(lock, l, nil, id, 0)
|
||||
return l, err
|
||||
}
|
||||
|
||||
func (repo *Repository) getCommitOfRelPath(id sha1, relPath string) (*Commit, error) {
|
||||
stdout, _, err := com.ExecCmdDir(repo.Path, "git", "log", "-1", prettyLogFormat, id.String(), "--", relPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id, err = NewIdFromString(string(stdout))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return repo.getCommit(id)
|
||||
}
|
14
modules/git/repo_object.go
Normal file
14
modules/git/repo_object.go
Normal file
|
@ -0,0 +1,14 @@
|
|||
// Copyright 2014 The Gogs 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 git
|
||||
|
||||
type ObjectType string
|
||||
|
||||
const (
|
||||
COMMIT ObjectType = "commit"
|
||||
TREE ObjectType = "tree"
|
||||
BLOB ObjectType = "blob"
|
||||
TAG ObjectType = "tag"
|
||||
)
|
96
modules/git/repo_tag.go
Normal file
96
modules/git/repo_tag.go
Normal file
|
@ -0,0 +1,96 @@
|
|||
// Copyright 2014 The Gogs 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 git
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
)
|
||||
|
||||
func IsTagExist(repoPath, tagName string) bool {
|
||||
_, _, err := com.ExecCmdDir(repoPath, "git", "show-ref", "--verify", "refs/tags/"+tagName)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (repo *Repository) IsTagExist(tagName string) bool {
|
||||
return IsTagExist(repo.Path, tagName)
|
||||
}
|
||||
|
||||
// GetTags returns all tags of given repository.
|
||||
func (repo *Repository) GetTags() ([]string, error) {
|
||||
stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "tag", "-l")
|
||||
if err != nil {
|
||||
return nil, errors.New(stderr)
|
||||
}
|
||||
tags := strings.Split(stdout, "\n")
|
||||
return tags[:len(tags)-1], nil
|
||||
}
|
||||
|
||||
func (repo *Repository) getTag(id sha1) (*Tag, error) {
|
||||
if repo.tagCache != nil {
|
||||
if t, ok := repo.tagCache[id]; ok {
|
||||
return t, nil
|
||||
}
|
||||
} else {
|
||||
repo.tagCache = make(map[sha1]*Tag, 10)
|
||||
}
|
||||
|
||||
// Get tag type.
|
||||
tp, stderr, err := com.ExecCmdDir(repo.Path, "git", "cat-file", "-t", id.String())
|
||||
if err != nil {
|
||||
return nil, errors.New(stderr)
|
||||
}
|
||||
|
||||
// Tag is a commit.
|
||||
if ObjectType(tp) == COMMIT {
|
||||
tag := &Tag{
|
||||
Id: id,
|
||||
Object: id,
|
||||
Type: string(COMMIT),
|
||||
repo: repo,
|
||||
}
|
||||
repo.tagCache[id] = tag
|
||||
return tag, nil
|
||||
}
|
||||
|
||||
// Tag with message.
|
||||
data, bytErr, err := com.ExecCmdDirBytes(repo.Path, "git", "cat-file", "-p", id.String())
|
||||
if err != nil {
|
||||
return nil, errors.New(string(bytErr))
|
||||
}
|
||||
|
||||
tag, err := parseTagData(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tag.Id = id
|
||||
tag.repo = repo
|
||||
|
||||
repo.tagCache[id] = tag
|
||||
return tag, nil
|
||||
}
|
||||
|
||||
// GetTag returns a Git tag by given name.
|
||||
func (repo *Repository) GetTag(tagName string) (*Tag, error) {
|
||||
stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "show-ref", "--tags", tagName)
|
||||
if err != nil {
|
||||
return nil, errors.New(stderr)
|
||||
}
|
||||
|
||||
id, err := NewIdFromString(strings.Split(stdout, " ")[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tag, err := repo.getTag(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tag.Name = tagName
|
||||
return tag, nil
|
||||
}
|
32
modules/git/repo_tree.go
Normal file
32
modules/git/repo_tree.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
// Copyright 2014 The Gogs 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 git
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
)
|
||||
|
||||
// Find the tree object in the repository.
|
||||
func (repo *Repository) GetTree(idStr string) (*Tree, error) {
|
||||
id, err := NewIdFromString(idStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return repo.getTree(id)
|
||||
}
|
||||
|
||||
func (repo *Repository) getTree(id sha1) (*Tree, error) {
|
||||
treePath := filepathFromSHA1(repo.Path, id.String())
|
||||
if !com.IsFile(treePath) {
|
||||
_, _, err := com.ExecCmdDir(repo.Path, "git", "ls-tree", id.String())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("repo.getTree: %v", ErrNotExist)
|
||||
}
|
||||
}
|
||||
|
||||
return NewTree(repo, id), nil
|
||||
}
|
87
modules/git/sha1.go
Normal file
87
modules/git/sha1.go
Normal file
|
@ -0,0 +1,87 @@
|
|||
// Copyright 2014 The Gogs 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 git
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
IdNotExist = errors.New("sha1 id not exist")
|
||||
)
|
||||
|
||||
type sha1 [20]byte
|
||||
|
||||
// Return true if s has the same sha1 as caller.
|
||||
// Support 40-length-string, []byte, sha1
|
||||
func (id sha1) Equal(s2 interface{}) bool {
|
||||
switch v := s2.(type) {
|
||||
case string:
|
||||
if len(v) != 40 {
|
||||
return false
|
||||
}
|
||||
return v == id.String()
|
||||
case []byte:
|
||||
if len(v) != 20 {
|
||||
return false
|
||||
}
|
||||
for i, v := range v {
|
||||
if id[i] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
case sha1:
|
||||
for i, v := range v {
|
||||
if id[i] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Return string (hex) representation of the Oid
|
||||
func (s sha1) String() string {
|
||||
result := make([]byte, 0, 40)
|
||||
hexvalues := []byte("0123456789abcdef")
|
||||
for i := 0; i < 20; i++ {
|
||||
result = append(result, hexvalues[s[i]>>4])
|
||||
result = append(result, hexvalues[s[i]&0xf])
|
||||
}
|
||||
return string(result)
|
||||
}
|
||||
|
||||
// Create a new sha1 from a 20 byte slice.
|
||||
func NewId(b []byte) (sha1, error) {
|
||||
var id sha1
|
||||
if len(b) != 20 {
|
||||
return id, errors.New("Length must be 20")
|
||||
}
|
||||
|
||||
for i := 0; i < 20; i++ {
|
||||
id[i] = b[i]
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// Create a new sha1 from a Sha1 string of length 40.
|
||||
func NewIdFromString(s string) (sha1, error) {
|
||||
s = strings.TrimSpace(s)
|
||||
var id sha1
|
||||
if len(s) != 40 {
|
||||
return id, fmt.Errorf("Length must be 40")
|
||||
}
|
||||
b, err := hex.DecodeString(s)
|
||||
if err != nil {
|
||||
return id, err
|
||||
}
|
||||
|
||||
return NewId(b)
|
||||
}
|
40
modules/git/signature.go
Normal file
40
modules/git/signature.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
// Copyright 2014 The Gogs 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 git
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Author and Committer information
|
||||
type Signature struct {
|
||||
Email string
|
||||
Name string
|
||||
When time.Time
|
||||
}
|
||||
|
||||
// Helper to get a signature from the commit line, which looks like this:
|
||||
// author Patrick Gundlach <gundlach@speedata.de> 1378823654 +0200
|
||||
// but without the "author " at the beginning (this method should)
|
||||
// be used for author and committer.
|
||||
//
|
||||
// FIXME: include timezone!
|
||||
func newSignatureFromCommitline(line []byte) (*Signature, error) {
|
||||
sig := new(Signature)
|
||||
emailstart := bytes.IndexByte(line, '<')
|
||||
sig.Name = string(line[:emailstart-1])
|
||||
emailstop := bytes.IndexByte(line, '>')
|
||||
sig.Email = string(line[emailstart+1 : emailstop])
|
||||
timestop := bytes.IndexByte(line[emailstop+2:], ' ')
|
||||
timestring := string(line[emailstop+2 : emailstop+2+timestop])
|
||||
seconds, err := strconv.ParseInt(timestring, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sig.When = time.Unix(seconds, 0)
|
||||
return sig, nil
|
||||
}
|
67
modules/git/tag.go
Normal file
67
modules/git/tag.go
Normal file
|
@ -0,0 +1,67 @@
|
|||
// Copyright 2014 The Gogs 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 git
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
)
|
||||
|
||||
// Tag represents a Git tag.
|
||||
type Tag struct {
|
||||
Name string
|
||||
Id sha1
|
||||
repo *Repository
|
||||
Object sha1 // The id of this commit object
|
||||
Type string
|
||||
Tagger *Signature
|
||||
TagMessage string
|
||||
}
|
||||
|
||||
func (tag *Tag) Commit() (*Commit, error) {
|
||||
return tag.repo.getCommit(tag.Object)
|
||||
}
|
||||
|
||||
// Parse commit information from the (uncompressed) raw
|
||||
// data from the commit object.
|
||||
// \n\n separate headers from message
|
||||
func parseTagData(data []byte) (*Tag, error) {
|
||||
tag := new(Tag)
|
||||
// we now have the contents of the commit object. Let's investigate...
|
||||
nextline := 0
|
||||
l:
|
||||
for {
|
||||
eol := bytes.IndexByte(data[nextline:], '\n')
|
||||
switch {
|
||||
case eol > 0:
|
||||
line := data[nextline : nextline+eol]
|
||||
spacepos := bytes.IndexByte(line, ' ')
|
||||
reftype := line[:spacepos]
|
||||
switch string(reftype) {
|
||||
case "object":
|
||||
id, err := NewIdFromString(string(line[spacepos+1:]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tag.Object = id
|
||||
case "type":
|
||||
// A commit can have one or more parents
|
||||
tag.Type = string(line[spacepos+1:])
|
||||
case "tagger":
|
||||
sig, err := newSignatureFromCommitline(line[spacepos+1:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tag.Tagger = sig
|
||||
}
|
||||
nextline += eol + 1
|
||||
case eol == 0:
|
||||
tag.TagMessage = string(data[nextline+1:])
|
||||
break l
|
||||
default:
|
||||
break l
|
||||
}
|
||||
}
|
||||
return tag, nil
|
||||
}
|
124
modules/git/tree.go
Normal file
124
modules/git/tree.go
Normal file
|
@ -0,0 +1,124 @@
|
|||
// Copyright 2014 The Gogs 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 git
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotExist = errors.New("error not exist")
|
||||
)
|
||||
|
||||
// A tree is a flat directory listing.
|
||||
type Tree struct {
|
||||
Id sha1
|
||||
repo *Repository
|
||||
|
||||
// parent tree
|
||||
ptree *Tree
|
||||
|
||||
entries Entries
|
||||
entriesParsed bool
|
||||
}
|
||||
|
||||
// Parse tree information from the (uncompressed) raw
|
||||
// data from the tree object.
|
||||
func parseTreeData(tree *Tree, data []byte) ([]*TreeEntry, error) {
|
||||
entries := make([]*TreeEntry, 0, 10)
|
||||
l := len(data)
|
||||
pos := 0
|
||||
for pos < l {
|
||||
entry := new(TreeEntry)
|
||||
entry.ptree = tree
|
||||
step := 6
|
||||
switch string(data[pos : pos+step]) {
|
||||
case "100644":
|
||||
entry.mode = ModeBlob
|
||||
entry.Type = BLOB
|
||||
case "100755":
|
||||
entry.mode = ModeExec
|
||||
entry.Type = BLOB
|
||||
case "120000":
|
||||
entry.mode = ModeSymlink
|
||||
entry.Type = BLOB
|
||||
case "160000":
|
||||
entry.mode = ModeCommit
|
||||
entry.Type = COMMIT
|
||||
case "040000":
|
||||
entry.mode = ModeTree
|
||||
entry.Type = TREE
|
||||
default:
|
||||
return nil, errors.New("unknown type: " + string(data[pos:pos+step]))
|
||||
}
|
||||
pos += step + 6 // Skip string type of entry type.
|
||||
|
||||
step = 40
|
||||
id, err := NewIdFromString(string(data[pos : pos+step]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entry.Id = id
|
||||
pos += step + 1 // Skip half of sha1.
|
||||
|
||||
step = bytes.IndexByte(data[pos:], '\n')
|
||||
entry.name = string(data[pos : pos+step])
|
||||
pos += step + 1
|
||||
entries = append(entries, entry)
|
||||
}
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
func (t *Tree) SubTree(rpath string) (*Tree, error) {
|
||||
if len(rpath) == 0 {
|
||||
return t, nil
|
||||
}
|
||||
|
||||
paths := strings.Split(rpath, "/")
|
||||
var err error
|
||||
var g = t
|
||||
var p = t
|
||||
var te *TreeEntry
|
||||
for _, name := range paths {
|
||||
te, err = p.GetTreeEntryByPath(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
g, err = t.repo.getTree(te.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
g.ptree = p
|
||||
p = g
|
||||
}
|
||||
return g, nil
|
||||
}
|
||||
|
||||
func (t *Tree) ListEntries(relpath string) (Entries, error) {
|
||||
if t.entriesParsed {
|
||||
return t.entries, nil
|
||||
}
|
||||
t.entriesParsed = true
|
||||
|
||||
stdout, _, err := com.ExecCmdDirBytes(t.repo.Path,
|
||||
"git", "ls-tree", t.Id.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.entries, err = parseTreeData(t, stdout)
|
||||
return t.entries, err
|
||||
}
|
||||
|
||||
func NewTree(repo *Repository, id sha1) *Tree {
|
||||
tree := new(Tree)
|
||||
tree.Id = id
|
||||
tree.repo = repo
|
||||
return tree
|
||||
}
|
46
modules/git/tree_blob.go
Normal file
46
modules/git/tree_blob.go
Normal file
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2014 The Gogs 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 git
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (t *Tree) GetTreeEntryByPath(relpath string) (*TreeEntry, error) {
|
||||
if len(relpath) == 0 {
|
||||
return &TreeEntry{
|
||||
Id: t.Id,
|
||||
Type: TREE,
|
||||
mode: ModeTree,
|
||||
}, nil
|
||||
// return nil, fmt.Errorf("GetTreeEntryByPath(empty relpath): %v", ErrNotExist)
|
||||
}
|
||||
|
||||
relpath = path.Clean(relpath)
|
||||
parts := strings.Split(relpath, "/")
|
||||
var err error
|
||||
tree := t
|
||||
for i, name := range parts {
|
||||
if i == len(parts)-1 {
|
||||
entries, err := tree.ListEntries(path.Dir(relpath))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, v := range entries {
|
||||
if v.name == name {
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tree, err = tree.SubTree(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("GetTreeEntryByPath: %v", ErrNotExist)
|
||||
}
|
109
modules/git/tree_entry.go
Normal file
109
modules/git/tree_entry.go
Normal file
|
@ -0,0 +1,109 @@
|
|||
// Copyright 2014 The Gogs 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 git
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
)
|
||||
|
||||
type EntryMode int
|
||||
|
||||
// There are only a few file modes in Git. They look like unix file modes, but they can only be
|
||||
// one of these.
|
||||
const (
|
||||
ModeBlob EntryMode = 0100644
|
||||
ModeExec EntryMode = 0100755
|
||||
ModeSymlink EntryMode = 0120000
|
||||
ModeCommit EntryMode = 0160000
|
||||
ModeTree EntryMode = 0040000
|
||||
)
|
||||
|
||||
type TreeEntry struct {
|
||||
Id sha1
|
||||
Type ObjectType
|
||||
|
||||
mode EntryMode
|
||||
name string
|
||||
|
||||
ptree *Tree
|
||||
|
||||
commited bool
|
||||
|
||||
size int64
|
||||
sized bool
|
||||
}
|
||||
|
||||
func (te *TreeEntry) Name() string {
|
||||
return te.name
|
||||
}
|
||||
|
||||
func (te *TreeEntry) Size() int64 {
|
||||
if te.IsDir() {
|
||||
return 0
|
||||
}
|
||||
|
||||
if te.sized {
|
||||
return te.size
|
||||
}
|
||||
|
||||
stdout, _, err := com.ExecCmdDir(te.ptree.repo.Path, "git", "cat-file", "-s", te.Id.String())
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
te.sized = true
|
||||
te.size = com.StrTo(strings.TrimSpace(stdout)).MustInt64()
|
||||
return te.size
|
||||
}
|
||||
|
||||
func (te *TreeEntry) IsDir() bool {
|
||||
return te.mode == ModeTree
|
||||
}
|
||||
|
||||
func (te *TreeEntry) EntryMode() EntryMode {
|
||||
return te.mode
|
||||
}
|
||||
|
||||
func (te *TreeEntry) Blob() *Blob {
|
||||
return &Blob{
|
||||
repo: te.ptree.repo,
|
||||
TreeEntry: te,
|
||||
}
|
||||
}
|
||||
|
||||
type Entries []*TreeEntry
|
||||
|
||||
var sorter = []func(t1, t2 *TreeEntry) bool{
|
||||
func(t1, t2 *TreeEntry) bool {
|
||||
return t1.IsDir() && !t2.IsDir()
|
||||
},
|
||||
func(t1, t2 *TreeEntry) bool {
|
||||
return t1.name < t2.name
|
||||
},
|
||||
}
|
||||
|
||||
func (bs Entries) Len() int { return len(bs) }
|
||||
func (bs Entries) Swap(i, j int) { bs[i], bs[j] = bs[j], bs[i] }
|
||||
func (bs Entries) Less(i, j int) bool {
|
||||
t1, t2 := bs[i], bs[j]
|
||||
var k int
|
||||
for k = 0; k < len(sorter)-1; k++ {
|
||||
sort := sorter[k]
|
||||
switch {
|
||||
case sort(t1, t2):
|
||||
return true
|
||||
case sort(t2, t1):
|
||||
return false
|
||||
}
|
||||
}
|
||||
return sorter[k](t1, t2)
|
||||
}
|
||||
|
||||
func (bs Entries) Sort() {
|
||||
sort.Sort(bs)
|
||||
}
|
27
modules/git/utils.go
Normal file
27
modules/git/utils.go
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2014 The Gogs 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 git
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const prettyLogFormat = `--pretty=format:%H`
|
||||
|
||||
func RefEndName(refStr string) string {
|
||||
index := strings.LastIndex(refStr, "/")
|
||||
if index != -1 {
|
||||
return refStr[index+1:]
|
||||
}
|
||||
return refStr
|
||||
}
|
||||
|
||||
// If the object is stored in its own file (i.e not in a pack file),
|
||||
// this function returns the full path to the object file.
|
||||
// It does not test if the file exists.
|
||||
func filepathFromSHA1(rootdir, sha1 string) string {
|
||||
return filepath.Join(rootdir, "objects", sha1[:2], sha1[2:])
|
||||
}
|
43
modules/git/version.go
Normal file
43
modules/git/version.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
// Copyright 2014 The Gogs 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 git
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
)
|
||||
|
||||
// Version represents version of Git.
|
||||
type Version struct {
|
||||
Major, Minor, Patch int
|
||||
}
|
||||
|
||||
// GetVersion returns current Git version installed.
|
||||
func GetVersion() (Version, error) {
|
||||
stdout, stderr, err := com.ExecCmd("git", "version")
|
||||
if err != nil {
|
||||
return Version{}, errors.New(stderr)
|
||||
}
|
||||
|
||||
infos := strings.Split(stdout, " ")
|
||||
if len(infos) < 3 {
|
||||
return Version{}, errors.New("not enough output")
|
||||
}
|
||||
|
||||
v := Version{}
|
||||
for i, s := range strings.Split(strings.TrimSpace(infos[2]), ".") {
|
||||
switch i {
|
||||
case 0:
|
||||
v.Major, _ = com.StrTo(s).Int()
|
||||
case 1:
|
||||
v.Minor, _ = com.StrTo(s).Int()
|
||||
case 2:
|
||||
v.Patch, _ = com.StrTo(s).Int()
|
||||
}
|
||||
}
|
||||
return v, nil
|
||||
}
|
73
modules/log/console.go
Normal file
73
modules/log/console.go
Normal file
|
@ -0,0 +1,73 @@
|
|||
// Copyright 2014 The Gogs 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 log
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
type Brush func(string) string
|
||||
|
||||
func NewBrush(color string) Brush {
|
||||
pre := "\033["
|
||||
reset := "\033[0m"
|
||||
return func(text string) string {
|
||||
return pre + color + "m" + text + reset
|
||||
}
|
||||
}
|
||||
|
||||
var colors = []Brush{
|
||||
NewBrush("1;36"), // Trace cyan
|
||||
NewBrush("1;34"), // Debug blue
|
||||
NewBrush("1;32"), // Info green
|
||||
NewBrush("1;33"), // Warn yellow
|
||||
NewBrush("1;31"), // Error red
|
||||
NewBrush("1;35"), // Critical purple
|
||||
NewBrush("1;31"), // Fatal red
|
||||
}
|
||||
|
||||
// ConsoleWriter implements LoggerInterface and writes messages to terminal.
|
||||
type ConsoleWriter struct {
|
||||
lg *log.Logger
|
||||
Level int `json:"level"`
|
||||
}
|
||||
|
||||
// create ConsoleWriter returning as LoggerInterface.
|
||||
func NewConsole() LoggerInterface {
|
||||
return &ConsoleWriter{
|
||||
lg: log.New(os.Stdout, "", log.Ldate|log.Ltime),
|
||||
Level: TRACE,
|
||||
}
|
||||
}
|
||||
|
||||
func (cw *ConsoleWriter) Init(config string) error {
|
||||
return json.Unmarshal([]byte(config), cw)
|
||||
}
|
||||
|
||||
func (cw *ConsoleWriter) WriteMsg(msg string, skip, level int) error {
|
||||
if cw.Level > level {
|
||||
return nil
|
||||
}
|
||||
if runtime.GOOS == "windows" {
|
||||
cw.lg.Println(msg)
|
||||
} else {
|
||||
cw.lg.Println(colors[level](msg))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *ConsoleWriter) Destroy() {
|
||||
}
|
||||
|
||||
func (_ *ConsoleWriter) Flush() {
|
||||
|
||||
}
|
||||
|
||||
func init() {
|
||||
Register("console", NewConsole)
|
||||
}
|
237
modules/log/file.go
Normal file
237
modules/log/file.go
Normal file
|
@ -0,0 +1,237 @@
|
|||
// Copyright 2014 The Gogs 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 log
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// FileLogWriter implements LoggerInterface.
|
||||
// It writes messages by lines limit, file size limit, or time frequency.
|
||||
type FileLogWriter struct {
|
||||
*log.Logger
|
||||
mw *MuxWriter
|
||||
// The opened file
|
||||
Filename string `json:"filename"`
|
||||
|
||||
Maxlines int `json:"maxlines"`
|
||||
maxlines_curlines int
|
||||
|
||||
// Rotate at size
|
||||
Maxsize int `json:"maxsize"`
|
||||
maxsize_cursize int
|
||||
|
||||
// Rotate daily
|
||||
Daily bool `json:"daily"`
|
||||
Maxdays int64 `json:"maxdays`
|
||||
daily_opendate int
|
||||
|
||||
Rotate bool `json:"rotate"`
|
||||
|
||||
startLock sync.Mutex // Only one log can write to the file
|
||||
|
||||
Level int `json:"level"`
|
||||
}
|
||||
|
||||
// an *os.File writer with locker.
|
||||
type MuxWriter struct {
|
||||
sync.Mutex
|
||||
fd *os.File
|
||||
}
|
||||
|
||||
// write to os.File.
|
||||
func (l *MuxWriter) Write(b []byte) (int, error) {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
return l.fd.Write(b)
|
||||
}
|
||||
|
||||
// set os.File in writer.
|
||||
func (l *MuxWriter) SetFd(fd *os.File) {
|
||||
if l.fd != nil {
|
||||
l.fd.Close()
|
||||
}
|
||||
l.fd = fd
|
||||
}
|
||||
|
||||
// create a FileLogWriter returning as LoggerInterface.
|
||||
func NewFileWriter() LoggerInterface {
|
||||
w := &FileLogWriter{
|
||||
Filename: "",
|
||||
Maxlines: 1000000,
|
||||
Maxsize: 1 << 28, //256 MB
|
||||
Daily: true,
|
||||
Maxdays: 7,
|
||||
Rotate: true,
|
||||
Level: TRACE,
|
||||
}
|
||||
// use MuxWriter instead direct use os.File for lock write when rotate
|
||||
w.mw = new(MuxWriter)
|
||||
// set MuxWriter as Logger's io.Writer
|
||||
w.Logger = log.New(w.mw, "", log.Ldate|log.Ltime)
|
||||
return w
|
||||
}
|
||||
|
||||
// Init file logger with json config.
|
||||
// config like:
|
||||
// {
|
||||
// "filename":"log/gogs.log",
|
||||
// "maxlines":10000,
|
||||
// "maxsize":1<<30,
|
||||
// "daily":true,
|
||||
// "maxdays":15,
|
||||
// "rotate":true
|
||||
// }
|
||||
func (w *FileLogWriter) Init(config string) error {
|
||||
if err := json.Unmarshal([]byte(config), w); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(w.Filename) == 0 {
|
||||
return errors.New("config must have filename")
|
||||
}
|
||||
return w.StartLogger()
|
||||
}
|
||||
|
||||
// start file logger. create log file and set to locker-inside file writer.
|
||||
func (w *FileLogWriter) StartLogger() error {
|
||||
fd, err := w.createLogFile()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.mw.SetFd(fd)
|
||||
if err = w.initFd(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *FileLogWriter) docheck(size int) {
|
||||
w.startLock.Lock()
|
||||
defer w.startLock.Unlock()
|
||||
if w.Rotate && ((w.Maxlines > 0 && w.maxlines_curlines >= w.Maxlines) ||
|
||||
(w.Maxsize > 0 && w.maxsize_cursize >= w.Maxsize) ||
|
||||
(w.Daily && time.Now().Day() != w.daily_opendate)) {
|
||||
if err := w.DoRotate(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
w.maxlines_curlines++
|
||||
w.maxsize_cursize += size
|
||||
}
|
||||
|
||||
// write logger message into file.
|
||||
func (w *FileLogWriter) WriteMsg(msg string, skip, level int) error {
|
||||
if level < w.Level {
|
||||
return nil
|
||||
}
|
||||
n := 24 + len(msg) // 24 stand for the length "2013/06/23 21:00:22 [T] "
|
||||
w.docheck(n)
|
||||
w.Logger.Println(msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *FileLogWriter) createLogFile() (*os.File, error) {
|
||||
// Open the log file
|
||||
return os.OpenFile(w.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
|
||||
}
|
||||
|
||||
func (w *FileLogWriter) initFd() error {
|
||||
fd := w.mw.fd
|
||||
finfo, err := fd.Stat()
|
||||
if err != nil {
|
||||
return fmt.Errorf("get stat: %s\n", err)
|
||||
}
|
||||
w.maxsize_cursize = int(finfo.Size())
|
||||
w.daily_opendate = time.Now().Day()
|
||||
if finfo.Size() > 0 {
|
||||
content, err := ioutil.ReadFile(w.Filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.maxlines_curlines = len(strings.Split(string(content), "\n"))
|
||||
} else {
|
||||
w.maxlines_curlines = 0
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DoRotate means it need to write file in new file.
|
||||
// new file name like xx.log.2013-01-01.2
|
||||
func (w *FileLogWriter) DoRotate() error {
|
||||
_, err := os.Lstat(w.Filename)
|
||||
if err == nil { // file exists
|
||||
// Find the next available number
|
||||
num := 1
|
||||
fname := ""
|
||||
for ; err == nil && num <= 999; num++ {
|
||||
fname = w.Filename + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), num)
|
||||
_, err = os.Lstat(fname)
|
||||
}
|
||||
// return error if the last file checked still existed
|
||||
if err == nil {
|
||||
return fmt.Errorf("rotate: cannot find free log number to rename %s\n", w.Filename)
|
||||
}
|
||||
|
||||
// block Logger's io.Writer
|
||||
w.mw.Lock()
|
||||
defer w.mw.Unlock()
|
||||
|
||||
fd := w.mw.fd
|
||||
fd.Close()
|
||||
|
||||
// close fd before rename
|
||||
// Rename the file to its newfound home
|
||||
if err = os.Rename(w.Filename, fname); err != nil {
|
||||
return fmt.Errorf("Rotate: %s\n", err)
|
||||
}
|
||||
|
||||
// re-start logger
|
||||
if err = w.StartLogger(); err != nil {
|
||||
return fmt.Errorf("Rotate StartLogger: %s\n", err)
|
||||
}
|
||||
|
||||
go w.deleteOldLog()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *FileLogWriter) deleteOldLog() {
|
||||
dir := filepath.Dir(w.Filename)
|
||||
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
if !info.IsDir() && info.ModTime().Unix() < (time.Now().Unix()-60*60*24*w.Maxdays) {
|
||||
if strings.HasPrefix(filepath.Base(path), filepath.Base(w.Filename)) {
|
||||
os.Remove(path)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// destroy file logger, close file writer.
|
||||
func (w *FileLogWriter) Destroy() {
|
||||
w.mw.fd.Close()
|
||||
}
|
||||
|
||||
// flush file logger.
|
||||
// there are no buffering messages in file logger in memory.
|
||||
// flush file means sync file from disk.
|
||||
func (w *FileLogWriter) Flush() {
|
||||
w.mw.fd.Sync()
|
||||
}
|
||||
|
||||
func init() {
|
||||
Register("file", NewFileWriter)
|
||||
}
|
|
@ -2,32 +2,29 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package log is a wrapper of logs for short calling name.
|
||||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/gogits/logs"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
loggers []*logs.BeeLogger
|
||||
GitLogger *logs.BeeLogger
|
||||
loggers []*Logger
|
||||
GitLogger *Logger
|
||||
)
|
||||
|
||||
func init() {
|
||||
NewLogger(0, "console", `{"level": 0}`)
|
||||
}
|
||||
|
||||
func NewLogger(bufLen int64, mode, config string) {
|
||||
logger := logs.NewLogger(bufLen)
|
||||
logger := newLogger(bufLen)
|
||||
|
||||
isExist := false
|
||||
for _, l := range loggers {
|
||||
if l.Adapter == mode {
|
||||
if l.adapter == mode {
|
||||
isExist = true
|
||||
l = logger
|
||||
}
|
||||
|
@ -35,15 +32,14 @@ func NewLogger(bufLen int64, mode, config string) {
|
|||
if !isExist {
|
||||
loggers = append(loggers, logger)
|
||||
}
|
||||
logger.SetLogFuncCallDepth(3)
|
||||
if err := logger.SetLogger(mode, config); err != nil {
|
||||
Fatal("Fail to set logger(%s): %v", mode, err)
|
||||
Fatal(1, "Fail to set logger(%s): %v", mode, err)
|
||||
}
|
||||
}
|
||||
|
||||
func NewGitLogger(logPath string) {
|
||||
os.MkdirAll(path.Dir(logPath), os.ModePerm)
|
||||
GitLogger = logs.NewLogger(0)
|
||||
GitLogger = newLogger(0)
|
||||
GitLogger.SetLogger("file", fmt.Sprintf(`{"level":0,"filename":"%s","rotate":false}`, logPath))
|
||||
}
|
||||
|
||||
|
@ -65,28 +61,237 @@ func Info(format string, v ...interface{}) {
|
|||
}
|
||||
}
|
||||
|
||||
func Error(format string, v ...interface{}) {
|
||||
for _, logger := range loggers {
|
||||
logger.Error(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
func Warn(format string, v ...interface{}) {
|
||||
for _, logger := range loggers {
|
||||
logger.Warn(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
func Critical(format string, v ...interface{}) {
|
||||
func Error(skip int, format string, v ...interface{}) {
|
||||
for _, logger := range loggers {
|
||||
logger.Critical(format, v...)
|
||||
logger.Error(skip, format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
func Fatal(format string, v ...interface{}) {
|
||||
Error(format, v...)
|
||||
func Critical(skip int, format string, v ...interface{}) {
|
||||
for _, logger := range loggers {
|
||||
logger.Critical(skip, format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
func Fatal(skip int, format string, v ...interface{}) {
|
||||
Error(skip, format, v...)
|
||||
for _, l := range loggers {
|
||||
l.Close()
|
||||
}
|
||||
os.Exit(2)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// .___ _______________________________________________________ _________ ___________
|
||||
// | |\ \__ ___/\_ _____/\______ \_ _____/ _ \ \_ ___ \\_ _____/
|
||||
// | |/ | \| | | __)_ | _/| __)/ /_\ \/ \ \/ | __)_
|
||||
// | / | \ | | \ | | \| \/ | \ \____| \
|
||||
// |___\____|__ /____| /_______ / |____|_ /\___ /\____|__ /\______ /_______ /
|
||||
// \/ \/ \/ \/ \/ \/ \/
|
||||
|
||||
type LogLevel int
|
||||
|
||||
const (
|
||||
TRACE = iota
|
||||
DEBUG
|
||||
INFO
|
||||
WARN
|
||||
ERROR
|
||||
CRITICAL
|
||||
FATAL
|
||||
)
|
||||
|
||||
// LoggerInterface represents behaviors of a logger provider.
|
||||
type LoggerInterface interface {
|
||||
Init(config string) error
|
||||
WriteMsg(msg string, skip, level int) error
|
||||
Destroy()
|
||||
Flush()
|
||||
}
|
||||
|
||||
type loggerType func() LoggerInterface
|
||||
|
||||
var adapters = make(map[string]loggerType)
|
||||
|
||||
// Register registers given logger provider to adapters.
|
||||
func Register(name string, log loggerType) {
|
||||
if log == nil {
|
||||
panic("log: register provider is nil")
|
||||
}
|
||||
if _, dup := adapters[name]; dup {
|
||||
panic("log: register called twice for provider \"" + name + "\"")
|
||||
}
|
||||
adapters[name] = log
|
||||
}
|
||||
|
||||
type logMsg struct {
|
||||
skip, level int
|
||||
msg string
|
||||
}
|
||||
|
||||
// Logger is default logger in beego application.
|
||||
// it can contain several providers and log message into all providers.
|
||||
type Logger struct {
|
||||
adapter string
|
||||
lock sync.Mutex
|
||||
level int
|
||||
msg chan *logMsg
|
||||
outputs map[string]LoggerInterface
|
||||
quit chan bool
|
||||
}
|
||||
|
||||
// newLogger initializes and returns a new logger.
|
||||
func newLogger(buffer int64) *Logger {
|
||||
l := &Logger{
|
||||
msg: make(chan *logMsg, buffer),
|
||||
outputs: make(map[string]LoggerInterface),
|
||||
quit: make(chan bool),
|
||||
}
|
||||
go l.StartLogger()
|
||||
return l
|
||||
}
|
||||
|
||||
// SetLogger sets new logger instanse with given logger adapter and config.
|
||||
func (l *Logger) SetLogger(adapter string, config string) error {
|
||||
l.lock.Lock()
|
||||
defer l.lock.Unlock()
|
||||
if log, ok := adapters[adapter]; ok {
|
||||
lg := log()
|
||||
if err := lg.Init(config); err != nil {
|
||||
return err
|
||||
}
|
||||
l.outputs[adapter] = lg
|
||||
l.adapter = adapter
|
||||
} else {
|
||||
panic("log: unknown adapter \"" + adapter + "\" (forgotten Register?)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DelLogger removes a logger adapter instance.
|
||||
func (l *Logger) DelLogger(adapter string) error {
|
||||
l.lock.Lock()
|
||||
defer l.lock.Unlock()
|
||||
if lg, ok := l.outputs[adapter]; ok {
|
||||
lg.Destroy()
|
||||
delete(l.outputs, adapter)
|
||||
} else {
|
||||
panic("log: unknown adapter \"" + adapter + "\" (forgotten Register?)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Logger) writerMsg(skip, level int, msg string) error {
|
||||
if l.level > level {
|
||||
return nil
|
||||
}
|
||||
lm := &logMsg{
|
||||
skip: skip,
|
||||
level: level,
|
||||
}
|
||||
|
||||
// Only error information needs locate position for debugging.
|
||||
if lm.level >= ERROR {
|
||||
pc, file, line, ok := runtime.Caller(skip)
|
||||
if ok {
|
||||
// Get caller function name.
|
||||
fn := runtime.FuncForPC(pc)
|
||||
var fnName string
|
||||
if fn == nil {
|
||||
fnName = "?()"
|
||||
} else {
|
||||
fnName = strings.TrimLeft(filepath.Ext(fn.Name()), ".") + "()"
|
||||
}
|
||||
|
||||
lm.msg = fmt.Sprintf("[%s:%d %s] %s", filepath.Base(file), line, fnName, msg)
|
||||
} else {
|
||||
lm.msg = msg
|
||||
}
|
||||
} else {
|
||||
lm.msg = msg
|
||||
}
|
||||
l.msg <- lm
|
||||
return nil
|
||||
}
|
||||
|
||||
// StartLogger starts logger chan reading.
|
||||
func (l *Logger) StartLogger() {
|
||||
for {
|
||||
select {
|
||||
case bm := <-l.msg:
|
||||
for _, l := range l.outputs {
|
||||
l.WriteMsg(bm.msg, bm.skip, bm.level)
|
||||
}
|
||||
case <-l.quit:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Flush flushs all chan data.
|
||||
func (l *Logger) Flush() {
|
||||
for _, l := range l.outputs {
|
||||
l.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
// Close closes logger, flush all chan data and destroy all adapter instances.
|
||||
func (l *Logger) Close() {
|
||||
l.quit <- true
|
||||
for {
|
||||
if len(l.msg) > 0 {
|
||||
bm := <-l.msg
|
||||
for _, l := range l.outputs {
|
||||
l.WriteMsg(bm.msg, bm.skip, bm.level)
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
for _, l := range l.outputs {
|
||||
l.Flush()
|
||||
l.Destroy()
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) Trace(format string, v ...interface{}) {
|
||||
msg := fmt.Sprintf("[T] "+format, v...)
|
||||
l.writerMsg(0, TRACE, msg)
|
||||
}
|
||||
|
||||
func (l *Logger) Debug(format string, v ...interface{}) {
|
||||
msg := fmt.Sprintf("[D] "+format, v...)
|
||||
l.writerMsg(0, DEBUG, msg)
|
||||
}
|
||||
|
||||
func (l *Logger) Info(format string, v ...interface{}) {
|
||||
msg := fmt.Sprintf("[I] "+format, v...)
|
||||
l.writerMsg(0, INFO, msg)
|
||||
}
|
||||
|
||||
func (l *Logger) Warn(format string, v ...interface{}) {
|
||||
msg := fmt.Sprintf("[W] "+format, v...)
|
||||
l.writerMsg(0, WARN, msg)
|
||||
}
|
||||
|
||||
func (l *Logger) Error(skip int, format string, v ...interface{}) {
|
||||
msg := fmt.Sprintf("[E] "+format, v...)
|
||||
l.writerMsg(skip, ERROR, msg)
|
||||
}
|
||||
|
||||
func (l *Logger) Critical(skip int, format string, v ...interface{}) {
|
||||
msg := fmt.Sprintf("[C] "+format, v...)
|
||||
l.writerMsg(skip, CRITICAL, msg)
|
||||
}
|
||||
|
||||
func (l *Logger) Fatal(skip int, format string, v ...interface{}) {
|
||||
msg := fmt.Sprintf("[F] "+format, v...)
|
||||
l.writerMsg(skip, FATAL, msg)
|
||||
l.Close()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
|
|
@ -10,10 +10,12 @@ import (
|
|||
"fmt"
|
||||
"path"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/Unknwon/macaron"
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/middleware"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
|
@ -55,7 +57,7 @@ func GetMailTmplData(u *models.User) map[interface{}]interface{} {
|
|||
// create a time limit code for user active
|
||||
func CreateUserActiveCode(u *models.User, startInf interface{}) string {
|
||||
minutes := setting.Service.ActiveCodeLives
|
||||
data := base.ToStr(u.Id) + u.Email + u.LowerName + u.Passwd + u.Rands
|
||||
data := com.ToStr(u.Id) + u.Email + u.LowerName + u.Passwd + u.Rands
|
||||
code := base.CreateTimeLimitCode(data, minutes, startInf)
|
||||
|
||||
// add tail hex username
|
||||
|
@ -64,7 +66,7 @@ func CreateUserActiveCode(u *models.User, startInf interface{}) string {
|
|||
}
|
||||
|
||||
// Send user register mail with active code
|
||||
func SendRegisterMail(r *middleware.Render, u *models.User) {
|
||||
func SendRegisterMail(r macaron.Render, u *models.User) {
|
||||
code := CreateUserActiveCode(u, nil)
|
||||
subject := "Register success, Welcome"
|
||||
|
||||
|
@ -72,7 +74,7 @@ func SendRegisterMail(r *middleware.Render, u *models.User) {
|
|||
data["Code"] = code
|
||||
body, err := r.HTMLString(string(AUTH_REGISTER_SUCCESS), data)
|
||||
if err != nil {
|
||||
log.Error("mail.SendRegisterMail(fail to render): %v", err)
|
||||
log.Error(4, "mail.SendRegisterMail(fail to render): %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -83,7 +85,7 @@ func SendRegisterMail(r *middleware.Render, u *models.User) {
|
|||
}
|
||||
|
||||
// Send email verify active email.
|
||||
func SendActiveMail(r *middleware.Render, u *models.User) {
|
||||
func SendActiveMail(r macaron.Render, u *models.User) {
|
||||
code := CreateUserActiveCode(u, nil)
|
||||
|
||||
subject := "Verify your e-mail address"
|
||||
|
@ -92,7 +94,7 @@ func SendActiveMail(r *middleware.Render, u *models.User) {
|
|||
data["Code"] = code
|
||||
body, err := r.HTMLString(string(AUTH_ACTIVE), data)
|
||||
if err != nil {
|
||||
log.Error("mail.SendActiveMail(fail to render): %v", err)
|
||||
log.Error(4, "mail.SendActiveMail(fail to render): %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -103,7 +105,7 @@ func SendActiveMail(r *middleware.Render, u *models.User) {
|
|||
}
|
||||
|
||||
// Send reset password email.
|
||||
func SendResetPasswdMail(r *middleware.Render, u *models.User) {
|
||||
func SendResetPasswdMail(r macaron.Render, u *models.User) {
|
||||
code := CreateUserActiveCode(u, nil)
|
||||
|
||||
subject := "Reset your password"
|
||||
|
@ -112,7 +114,7 @@ func SendResetPasswdMail(r *middleware.Render, u *models.User) {
|
|||
data["Code"] = code
|
||||
body, err := r.HTMLString(string(AUTH_RESET_PASSWORD), data)
|
||||
if err != nil {
|
||||
log.Error("mail.SendResetPasswdMail(fail to render): %v", err)
|
||||
log.Error(4, "mail.SendResetPasswdMail(fail to render): %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -157,7 +159,7 @@ func SendIssueNotifyMail(u, owner *models.User, repo *models.Repository, issue *
|
|||
}
|
||||
|
||||
// SendIssueMentionMail sends mail notification for who are mentioned in issue.
|
||||
func SendIssueMentionMail(r *middleware.Render, u, owner *models.User,
|
||||
func SendIssueMentionMail(r macaron.Render, u, owner *models.User,
|
||||
repo *models.Repository, issue *models.Issue, tos []string) error {
|
||||
|
||||
if len(tos) == 0 {
|
||||
|
@ -182,7 +184,7 @@ func SendIssueMentionMail(r *middleware.Render, u, owner *models.User,
|
|||
}
|
||||
|
||||
// SendCollaboratorMail sends mail notification to new collaborator.
|
||||
func SendCollaboratorMail(r *middleware.Render, u, owner *models.User,
|
||||
func SendCollaboratorMail(r macaron.Render, u, owner *models.User,
|
||||
repo *models.Repository) error {
|
||||
|
||||
subject := fmt.Sprintf("%s added you to %s", owner.Name, repo.Name)
|
||||
|
|
|
@ -56,7 +56,7 @@ func processMailQueue() {
|
|||
if len(msg.Info) > 0 {
|
||||
info = ", info: " + msg.Info
|
||||
}
|
||||
log.Error(fmt.Sprintf("Async sent email %d succeed, not send emails: %s%s err: %s", num, tos, info, err))
|
||||
log.Error(4, fmt.Sprintf("Async sent email %d succeed, not send emails: %s%s err: %s", num, tos, info, err))
|
||||
} else {
|
||||
log.Trace(fmt.Sprintf("Async sent email %d succeed, sent emails: %s%s", num, tos, info))
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/go-martini/martini"
|
||||
"github.com/Unknwon/macaron"
|
||||
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
@ -20,7 +20,7 @@ type ToggleOptions struct {
|
|||
DisableCsrf bool
|
||||
}
|
||||
|
||||
func Toggle(options *ToggleOptions) martini.Handler {
|
||||
func Toggle(options *ToggleOptions) macaron.Handler {
|
||||
return func(ctx *Context) {
|
||||
// Cannot view any page before installation.
|
||||
if !setting.InstallLock {
|
||||
|
@ -49,7 +49,7 @@ func Toggle(options *ToggleOptions) martini.Handler {
|
|||
ctx.Redirect("/user/login")
|
||||
return
|
||||
} else if !ctx.User.IsActive && setting.Service.RegisterEmailConfirm {
|
||||
ctx.Data["Title"] = "Activate Your Account"
|
||||
// ctx.Data["Title"] = "Activate Your Account"
|
||||
ctx.HTML(200, "user/activate")
|
||||
return
|
||||
}
|
||||
|
|
|
@ -16,7 +16,8 @@ import (
|
|||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/go-martini/martini"
|
||||
"github.com/Unknwon/macaron"
|
||||
"github.com/macaron-contrib/i18n"
|
||||
)
|
||||
|
||||
/*
|
||||
|
@ -37,44 +38,44 @@ import (
|
|||
// your own error handling, use Form or Json middleware directly.
|
||||
// An interface pointer can be added as a second argument in order
|
||||
// to map the struct to a specific interface.
|
||||
func Bind(obj interface{}, ifacePtr ...interface{}) martini.Handler {
|
||||
return func(context martini.Context, req *http.Request) {
|
||||
contentType := req.Header.Get("Content-Type")
|
||||
func Bind(obj interface{}, ifacePtr ...interface{}) macaron.Handler {
|
||||
return func(ctx *macaron.Context) {
|
||||
contentType := ctx.Req.Header.Get("Content-Type")
|
||||
|
||||
if strings.Contains(contentType, "form-urlencoded") {
|
||||
context.Invoke(Form(obj, ifacePtr...))
|
||||
ctx.Invoke(Form(obj, ifacePtr...))
|
||||
} else if strings.Contains(contentType, "multipart/form-data") {
|
||||
context.Invoke(MultipartForm(obj, ifacePtr...))
|
||||
ctx.Invoke(MultipartForm(obj, ifacePtr...))
|
||||
} else if strings.Contains(contentType, "json") {
|
||||
context.Invoke(Json(obj, ifacePtr...))
|
||||
ctx.Invoke(Json(obj, ifacePtr...))
|
||||
} else {
|
||||
context.Invoke(Json(obj, ifacePtr...))
|
||||
if getErrors(context).Count() > 0 {
|
||||
context.Invoke(Form(obj, ifacePtr...))
|
||||
ctx.Invoke(Json(obj, ifacePtr...))
|
||||
if getErrors(ctx).Count() > 0 {
|
||||
ctx.Invoke(Form(obj, ifacePtr...))
|
||||
}
|
||||
}
|
||||
|
||||
context.Invoke(ErrorHandler)
|
||||
ctx.Invoke(ErrorHandler)
|
||||
}
|
||||
}
|
||||
|
||||
// BindIgnErr will do the exactly same thing as Bind but without any
|
||||
// error handling, which user has freedom to deal with them.
|
||||
// This allows user take advantages of validation.
|
||||
func BindIgnErr(obj interface{}, ifacePtr ...interface{}) martini.Handler {
|
||||
return func(context martini.Context, req *http.Request) {
|
||||
func BindIgnErr(obj interface{}, ifacePtr ...interface{}) macaron.Handler {
|
||||
return func(ctx *macaron.Context, req *http.Request) {
|
||||
contentType := req.Header.Get("Content-Type")
|
||||
|
||||
if strings.Contains(contentType, "form-urlencoded") {
|
||||
context.Invoke(Form(obj, ifacePtr...))
|
||||
ctx.Invoke(Form(obj, ifacePtr...))
|
||||
} else if strings.Contains(contentType, "multipart/form-data") {
|
||||
context.Invoke(MultipartForm(obj, ifacePtr...))
|
||||
ctx.Invoke(MultipartForm(obj, ifacePtr...))
|
||||
} else if strings.Contains(contentType, "json") {
|
||||
context.Invoke(Json(obj, ifacePtr...))
|
||||
ctx.Invoke(Json(obj, ifacePtr...))
|
||||
} else {
|
||||
context.Invoke(Json(obj, ifacePtr...))
|
||||
if getErrors(context).Count() > 0 {
|
||||
context.Invoke(Form(obj, ifacePtr...))
|
||||
ctx.Invoke(Json(obj, ifacePtr...))
|
||||
if getErrors(ctx).Count() > 0 {
|
||||
ctx.Invoke(Form(obj, ifacePtr...))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -89,12 +90,12 @@ func BindIgnErr(obj interface{}, ifacePtr ...interface{}) martini.Handler {
|
|||
// keys, for example: key=val1&key=val2&key=val3
|
||||
// An interface pointer can be added as a second argument in order
|
||||
// to map the struct to a specific interface.
|
||||
func Form(formStruct interface{}, ifacePtr ...interface{}) martini.Handler {
|
||||
return func(context martini.Context, req *http.Request) {
|
||||
func Form(formStruct interface{}, ifacePtr ...interface{}) macaron.Handler {
|
||||
return func(ctx *macaron.Context) {
|
||||
ensureNotPointer(formStruct)
|
||||
formStruct := reflect.New(reflect.TypeOf(formStruct))
|
||||
errors := newErrors()
|
||||
parseErr := req.ParseForm()
|
||||
parseErr := ctx.Req.ParseForm()
|
||||
|
||||
// Format validation of the request body or the URL would add considerable overhead,
|
||||
// and ParseForm does not complain when URL encoding is off.
|
||||
|
@ -104,14 +105,14 @@ func Form(formStruct interface{}, ifacePtr ...interface{}) martini.Handler {
|
|||
errors.Overall[BindingDeserializationError] = parseErr.Error()
|
||||
}
|
||||
|
||||
mapForm(formStruct, req.Form, errors)
|
||||
mapForm(formStruct, ctx.Req.Form, errors)
|
||||
|
||||
validateAndMap(formStruct, context, errors, ifacePtr...)
|
||||
validateAndMap(formStruct, ctx, errors, ifacePtr...)
|
||||
}
|
||||
}
|
||||
|
||||
func MultipartForm(formStruct interface{}, ifacePtr ...interface{}) martini.Handler {
|
||||
return func(context martini.Context, req *http.Request) {
|
||||
func MultipartForm(formStruct interface{}, ifacePtr ...interface{}) macaron.Handler {
|
||||
return func(ctx *macaron.Context) {
|
||||
ensureNotPointer(formStruct)
|
||||
formStruct := reflect.New(reflect.TypeOf(formStruct))
|
||||
errors := newErrors()
|
||||
|
@ -119,7 +120,7 @@ func MultipartForm(formStruct interface{}, ifacePtr ...interface{}) martini.Hand
|
|||
// Workaround for multipart forms returning nil instead of an error
|
||||
// when content is not multipart
|
||||
// https://code.google.com/p/go/issues/detail?id=6334
|
||||
multipartReader, err := req.MultipartReader()
|
||||
multipartReader, err := ctx.Req.MultipartReader()
|
||||
if err != nil {
|
||||
errors.Overall[BindingDeserializationError] = err.Error()
|
||||
} else {
|
||||
|
@ -129,12 +130,12 @@ func MultipartForm(formStruct interface{}, ifacePtr ...interface{}) martini.Hand
|
|||
errors.Overall[BindingDeserializationError] = parseErr.Error()
|
||||
}
|
||||
|
||||
req.MultipartForm = form
|
||||
ctx.Req.MultipartForm = form
|
||||
}
|
||||
|
||||
mapForm(formStruct, req.MultipartForm.Value, errors)
|
||||
mapForm(formStruct, ctx.Req.MultipartForm.Value, errors)
|
||||
|
||||
validateAndMap(formStruct, context, errors, ifacePtr...)
|
||||
validateAndMap(formStruct, ctx, errors, ifacePtr...)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -143,21 +144,21 @@ func MultipartForm(formStruct interface{}, ifacePtr ...interface{}) martini.Hand
|
|||
// validated, but no error handling is actually performed here.
|
||||
// An interface pointer can be added as a second argument in order
|
||||
// to map the struct to a specific interface.
|
||||
func Json(jsonStruct interface{}, ifacePtr ...interface{}) martini.Handler {
|
||||
return func(context martini.Context, req *http.Request) {
|
||||
func Json(jsonStruct interface{}, ifacePtr ...interface{}) macaron.Handler {
|
||||
return func(ctx *macaron.Context) {
|
||||
ensureNotPointer(jsonStruct)
|
||||
jsonStruct := reflect.New(reflect.TypeOf(jsonStruct))
|
||||
errors := newErrors()
|
||||
|
||||
if req.Body != nil {
|
||||
defer req.Body.Close()
|
||||
if ctx.Req.Body != nil {
|
||||
defer ctx.Req.Body.Close()
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(req.Body).Decode(jsonStruct.Interface()); err != nil && err != io.EOF {
|
||||
if err := json.NewDecoder(ctx.Req.Body).Decode(jsonStruct.Interface()); err != nil && err != io.EOF {
|
||||
errors.Overall[BindingDeserializationError] = err.Error()
|
||||
}
|
||||
|
||||
validateAndMap(jsonStruct, context, errors, ifacePtr...)
|
||||
validateAndMap(jsonStruct, ctx, errors, ifacePtr...)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,15 +166,15 @@ func Json(jsonStruct interface{}, ifacePtr ...interface{}) martini.Handler {
|
|||
// passed in is a Validator, then the user-defined Validate method
|
||||
// is executed, and its errors are mapped to the context. This middleware
|
||||
// performs no error handling: it merely detects them and maps them.
|
||||
func Validate(obj interface{}) martini.Handler {
|
||||
return func(context martini.Context, req *http.Request) {
|
||||
func Validate(obj interface{}) macaron.Handler {
|
||||
return func(ctx *macaron.Context, l i18n.Locale) {
|
||||
errors := newErrors()
|
||||
validateStruct(errors, obj)
|
||||
|
||||
if validator, ok := obj.(Validator); ok {
|
||||
validator.Validate(errors, req, context)
|
||||
validator.Validate(ctx, errors, l)
|
||||
}
|
||||
context.Map(*errors)
|
||||
ctx.Map(*errors)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -387,9 +388,7 @@ func setWithProperType(valueKind reflect.Kind, val string, structField reflect.V
|
|||
}
|
||||
}
|
||||
|
||||
// Don't pass in pointers to bind to. Can lead to bugs. See:
|
||||
// https://github.com/codegangsta/martini-contrib/issues/40
|
||||
// https://github.com/codegangsta/martini-contrib/pull/34#issuecomment-29683659
|
||||
// Don't pass in pointers to bind to. Can lead to bugs.
|
||||
func ensureNotPointer(obj interface{}) {
|
||||
if reflect.TypeOf(obj).Kind() == reflect.Ptr {
|
||||
panic("Pointers are not accepted as binding models")
|
||||
|
@ -399,13 +398,13 @@ func ensureNotPointer(obj interface{}) {
|
|||
// Performs validation and combines errors from validation
|
||||
// with errors from deserialization, then maps both the
|
||||
// resulting struct and the errors to the context.
|
||||
func validateAndMap(obj reflect.Value, context martini.Context, errors *Errors, ifacePtr ...interface{}) {
|
||||
context.Invoke(Validate(obj.Interface()))
|
||||
errors.Combine(getErrors(context))
|
||||
context.Map(*errors)
|
||||
context.Map(obj.Elem().Interface())
|
||||
func validateAndMap(obj reflect.Value, ctx *macaron.Context, errors *Errors, ifacePtr ...interface{}) {
|
||||
ctx.Invoke(Validate(obj.Interface()))
|
||||
errors.Combine(getErrors(ctx))
|
||||
ctx.Map(*errors)
|
||||
ctx.Map(obj.Elem().Interface())
|
||||
if len(ifacePtr) > 0 {
|
||||
context.MapTo(obj.Elem().Interface(), ifacePtr[0])
|
||||
ctx.MapTo(obj.Elem().Interface(), ifacePtr[0])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -413,8 +412,8 @@ func newErrors() *Errors {
|
|||
return &Errors{make(map[string]string), make(map[string]string)}
|
||||
}
|
||||
|
||||
func getErrors(context martini.Context) Errors {
|
||||
return context.Get(reflect.TypeOf(Errors{})).Interface().(Errors)
|
||||
func getErrors(ctx *macaron.Context) Errors {
|
||||
return ctx.GetVal(reflect.TypeOf(Errors{})).Interface().(Errors)
|
||||
}
|
||||
|
||||
type (
|
||||
|
@ -422,7 +421,7 @@ type (
|
|||
// validation before the request even gets to your application.
|
||||
// The Validate method will be executed during the validation phase.
|
||||
Validator interface {
|
||||
Validate(*Errors, *http.Request, martini.Context)
|
||||
Validate(*macaron.Context, *Errors, i18n.Locale)
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -5,42 +5,33 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-martini/martini"
|
||||
|
||||
"github.com/gogits/cache"
|
||||
"github.com/gogits/git"
|
||||
"github.com/gogits/session"
|
||||
"github.com/Unknwon/macaron"
|
||||
"github.com/macaron-contrib/i18n"
|
||||
"github.com/macaron-contrib/session"
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/gogits/gogs/modules/auth"
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/git"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
// Context represents context of a request.
|
||||
type Context struct {
|
||||
*Render
|
||||
c martini.Context
|
||||
p martini.Params
|
||||
Req *http.Request
|
||||
Res http.ResponseWriter
|
||||
Flash *Flash
|
||||
Session session.SessionStore
|
||||
Cache cache.Cache
|
||||
*macaron.Context
|
||||
i18n.Locale
|
||||
Flash *session.Flash
|
||||
Session session.Store
|
||||
|
||||
User *models.User
|
||||
IsSigned bool
|
||||
|
||||
|
@ -68,7 +59,8 @@ type Context struct {
|
|||
HTTPS string
|
||||
Git string
|
||||
}
|
||||
Mirror *models.Mirror
|
||||
CommitsCount int
|
||||
Mirror *models.Mirror
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,12 +99,12 @@ func (ctx *Context) HasError() bool {
|
|||
}
|
||||
|
||||
// HTML calls render.HTML underlying but reduce one argument.
|
||||
func (ctx *Context) HTML(status int, name base.TplName, htmlOpt ...HTMLOptions) {
|
||||
ctx.Render.HTML(status, string(name), ctx.Data, htmlOpt...)
|
||||
func (ctx *Context) HTML(status int, name base.TplName) {
|
||||
ctx.Render.HTML(status, string(name), ctx.Data)
|
||||
}
|
||||
|
||||
// RenderWithErr used for page has form validation but need to prompt error to users.
|
||||
func (ctx *Context) RenderWithErr(msg string, tpl base.TplName, form auth.Form) {
|
||||
func (ctx *Context) RenderWithErr(msg string, tpl base.TplName, form interface{}) {
|
||||
if form != nil {
|
||||
auth.AssignForm(form, ctx.Data)
|
||||
}
|
||||
|
@ -124,8 +116,8 @@ func (ctx *Context) RenderWithErr(msg string, tpl base.TplName, form auth.Form)
|
|||
// Handle handles and logs error by given status.
|
||||
func (ctx *Context) Handle(status int, title string, err error) {
|
||||
if err != nil {
|
||||
log.Error("%s: %v", title, err)
|
||||
if martini.Dev != martini.Prod {
|
||||
log.Error(4, "%s: %v", title, err)
|
||||
if macaron.Env != macaron.PROD {
|
||||
ctx.Data["ErrorMsg"] = err
|
||||
}
|
||||
}
|
||||
|
@ -139,106 +131,6 @@ func (ctx *Context) Handle(status int, title string, err error) {
|
|||
ctx.HTML(status, base.TplName(fmt.Sprintf("status/%d", status)))
|
||||
}
|
||||
|
||||
func (ctx *Context) GetCookie(name string) string {
|
||||
cookie, err := ctx.Req.Cookie(name)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return cookie.Value
|
||||
}
|
||||
|
||||
func (ctx *Context) SetCookie(name string, value string, others ...interface{}) {
|
||||
cookie := http.Cookie{}
|
||||
cookie.Name = name
|
||||
cookie.Value = value
|
||||
|
||||
if len(others) > 0 {
|
||||
switch v := others[0].(type) {
|
||||
case int:
|
||||
cookie.MaxAge = v
|
||||
case int64:
|
||||
cookie.MaxAge = int(v)
|
||||
case int32:
|
||||
cookie.MaxAge = int(v)
|
||||
}
|
||||
}
|
||||
|
||||
// default "/"
|
||||
if len(others) > 1 {
|
||||
if v, ok := others[1].(string); ok && len(v) > 0 {
|
||||
cookie.Path = v
|
||||
}
|
||||
} else {
|
||||
cookie.Path = "/"
|
||||
}
|
||||
|
||||
// default empty
|
||||
if len(others) > 2 {
|
||||
if v, ok := others[2].(string); ok && len(v) > 0 {
|
||||
cookie.Domain = v
|
||||
}
|
||||
}
|
||||
|
||||
// default empty
|
||||
if len(others) > 3 {
|
||||
switch v := others[3].(type) {
|
||||
case bool:
|
||||
cookie.Secure = v
|
||||
default:
|
||||
if others[3] != nil {
|
||||
cookie.Secure = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// default false. for session cookie default true
|
||||
if len(others) > 4 {
|
||||
if v, ok := others[4].(bool); ok && v {
|
||||
cookie.HttpOnly = true
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Res.Header().Add("Set-Cookie", cookie.String())
|
||||
}
|
||||
|
||||
// Get secure cookie from request by a given key.
|
||||
func (ctx *Context) GetSecureCookie(Secret, key string) (string, bool) {
|
||||
val := ctx.GetCookie(key)
|
||||
if val == "" {
|
||||
return "", false
|
||||
}
|
||||
|
||||
parts := strings.SplitN(val, "|", 3)
|
||||
|
||||
if len(parts) != 3 {
|
||||
return "", false
|
||||
}
|
||||
|
||||
vs := parts[0]
|
||||
timestamp := parts[1]
|
||||
sig := parts[2]
|
||||
|
||||
h := hmac.New(sha1.New, []byte(Secret))
|
||||
fmt.Fprintf(h, "%s%s", vs, timestamp)
|
||||
|
||||
if fmt.Sprintf("%02x", h.Sum(nil)) != sig {
|
||||
return "", false
|
||||
}
|
||||
res, _ := base64.URLEncoding.DecodeString(vs)
|
||||
return string(res), true
|
||||
}
|
||||
|
||||
// Set Secure cookie for response.
|
||||
func (ctx *Context) SetSecureCookie(Secret, name, value string, others ...interface{}) {
|
||||
vs := base64.URLEncoding.EncodeToString([]byte(value))
|
||||
timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
|
||||
h := hmac.New(sha1.New, []byte(Secret))
|
||||
fmt.Fprintf(h, "%s%s", vs, timestamp)
|
||||
sig := fmt.Sprintf("%02x", h.Sum(nil))
|
||||
cookie := strings.Join([]string{vs, timestamp, sig}, "|")
|
||||
ctx.SetCookie(name, cookie, others...)
|
||||
}
|
||||
|
||||
func (ctx *Context) CsrfToken() string {
|
||||
if len(ctx.csrfToken) > 0 {
|
||||
return ctx.csrfToken
|
||||
|
@ -271,16 +163,16 @@ func (ctx *Context) ServeFile(file string, names ...string) {
|
|||
if len(names) > 0 {
|
||||
name = names[0]
|
||||
} else {
|
||||
name = filepath.Base(file)
|
||||
name = path.Base(file)
|
||||
}
|
||||
ctx.Res.Header().Set("Content-Description", "File Transfer")
|
||||
ctx.Res.Header().Set("Content-Type", "application/octet-stream")
|
||||
ctx.Res.Header().Set("Content-Disposition", "attachment; filename="+name)
|
||||
ctx.Res.Header().Set("Content-Transfer-Encoding", "binary")
|
||||
ctx.Res.Header().Set("Expires", "0")
|
||||
ctx.Res.Header().Set("Cache-Control", "must-revalidate")
|
||||
ctx.Res.Header().Set("Pragma", "public")
|
||||
http.ServeFile(ctx.Res, ctx.Req, file)
|
||||
ctx.Resp.Header().Set("Content-Description", "File Transfer")
|
||||
ctx.Resp.Header().Set("Content-Type", "application/octet-stream")
|
||||
ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+name)
|
||||
ctx.Resp.Header().Set("Content-Transfer-Encoding", "binary")
|
||||
ctx.Resp.Header().Set("Expires", "0")
|
||||
ctx.Resp.Header().Set("Cache-Control", "must-revalidate")
|
||||
ctx.Resp.Header().Set("Pragma", "public")
|
||||
http.ServeFile(ctx.Resp, ctx.Req, file)
|
||||
}
|
||||
|
||||
func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interface{}) {
|
||||
|
@ -291,87 +183,52 @@ func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interfa
|
|||
modtime = v
|
||||
}
|
||||
}
|
||||
ctx.Res.Header().Set("Content-Description", "File Transfer")
|
||||
ctx.Res.Header().Set("Content-Type", "application/octet-stream")
|
||||
ctx.Res.Header().Set("Content-Disposition", "attachment; filename="+name)
|
||||
ctx.Res.Header().Set("Content-Transfer-Encoding", "binary")
|
||||
ctx.Res.Header().Set("Expires", "0")
|
||||
ctx.Res.Header().Set("Cache-Control", "must-revalidate")
|
||||
ctx.Res.Header().Set("Pragma", "public")
|
||||
http.ServeContent(ctx.Res, ctx.Req, name, modtime, r)
|
||||
ctx.Resp.Header().Set("Content-Description", "File Transfer")
|
||||
ctx.Resp.Header().Set("Content-Type", "application/octet-stream")
|
||||
ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+name)
|
||||
ctx.Resp.Header().Set("Content-Transfer-Encoding", "binary")
|
||||
ctx.Resp.Header().Set("Expires", "0")
|
||||
ctx.Resp.Header().Set("Cache-Control", "must-revalidate")
|
||||
ctx.Resp.Header().Set("Pragma", "public")
|
||||
http.ServeContent(ctx.Resp, ctx.Req, name, modtime, r)
|
||||
}
|
||||
|
||||
type Flash struct {
|
||||
url.Values
|
||||
ErrorMsg, SuccessMsg string
|
||||
}
|
||||
|
||||
func (f *Flash) Error(msg string) {
|
||||
f.Set("error", msg)
|
||||
f.ErrorMsg = msg
|
||||
}
|
||||
|
||||
func (f *Flash) Success(msg string) {
|
||||
f.Set("success", msg)
|
||||
f.SuccessMsg = msg
|
||||
}
|
||||
|
||||
// InitContext initializes a classic context for a request.
|
||||
func InitContext() martini.Handler {
|
||||
return func(res http.ResponseWriter, r *http.Request, c martini.Context, rd *Render) {
|
||||
// Contexter initializes a classic context for a request.
|
||||
func Contexter() macaron.Handler {
|
||||
return func(c *macaron.Context, l i18n.Locale, sess session.Store, f *session.Flash) {
|
||||
ctx := &Context{
|
||||
c: c,
|
||||
// p: p,
|
||||
Req: r,
|
||||
Res: res,
|
||||
Cache: setting.Cache,
|
||||
Render: rd,
|
||||
Context: c,
|
||||
Locale: l,
|
||||
Flash: f,
|
||||
Session: sess,
|
||||
}
|
||||
// Cache: setting.Cache,
|
||||
|
||||
// Compute current URL for real-time change language.
|
||||
link := ctx.Req.RequestURI
|
||||
i := strings.Index(link, "?")
|
||||
if i > -1 {
|
||||
link = link[:i]
|
||||
}
|
||||
ctx.Data["Link"] = link
|
||||
|
||||
ctx.Data["PageStartTime"] = time.Now()
|
||||
|
||||
// start session
|
||||
ctx.Session = setting.SessionManager.SessionStart(res, r)
|
||||
|
||||
// Get flash.
|
||||
values, err := url.ParseQuery(ctx.GetCookie("gogs_flash"))
|
||||
if err != nil {
|
||||
log.Error("InitContext.ParseQuery(flash): %v", err)
|
||||
} else if len(values) > 0 {
|
||||
ctx.Flash = &Flash{Values: values}
|
||||
ctx.Flash.ErrorMsg = ctx.Flash.Get("error")
|
||||
ctx.Flash.SuccessMsg = ctx.Flash.Get("success")
|
||||
ctx.Data["Flash"] = ctx.Flash
|
||||
ctx.SetCookie("gogs_flash", "", -1)
|
||||
}
|
||||
ctx.Flash = &Flash{Values: url.Values{}}
|
||||
|
||||
rw := res.(martini.ResponseWriter)
|
||||
rw.Before(func(martini.ResponseWriter) {
|
||||
ctx.Session.SessionRelease(res)
|
||||
|
||||
if flash := ctx.Flash.Encode(); len(flash) > 0 {
|
||||
ctx.SetCookie("gogs_flash", flash, 0)
|
||||
}
|
||||
})
|
||||
|
||||
// Get user from session if logined.
|
||||
user := auth.SignedInUser(ctx.req.Header, ctx.Session)
|
||||
ctx.User = user
|
||||
ctx.IsSigned = user != nil
|
||||
|
||||
ctx.Data["IsSigned"] = ctx.IsSigned
|
||||
|
||||
if user != nil {
|
||||
ctx.Data["SignedUser"] = user
|
||||
ctx.Data["SignedUserId"] = user.Id
|
||||
ctx.Data["SignedUserName"] = user.Name
|
||||
ctx.User = auth.SignedInUser(ctx.Req.Header, ctx.Session)
|
||||
if ctx.User != nil {
|
||||
ctx.IsSigned = true
|
||||
ctx.Data["IsSigned"] = ctx.IsSigned
|
||||
ctx.Data["SignedUser"] = ctx.User
|
||||
ctx.Data["SignedUserId"] = ctx.User.Id
|
||||
ctx.Data["SignedUserName"] = ctx.User.Name
|
||||
ctx.Data["IsAdmin"] = ctx.User.IsAdmin
|
||||
}
|
||||
|
||||
// If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid.
|
||||
if r.Method == "POST" && strings.Contains(r.Header.Get("Content-Type"), "multipart/form-data") {
|
||||
if err = ctx.Req.ParseMultipartForm(setting.AttachmentMaxSize << 20); err != nil && !strings.Contains(err.Error(), "EOF") { // 32MB max size
|
||||
ctx.Handle(500, "issue.Comment(ctx.Req.ParseMultipartForm)", err)
|
||||
if ctx.Req.Method == "POST" && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") {
|
||||
if err := ctx.Req.ParseMultipartForm(setting.AttachmentMaxSize << 20); err != nil && !strings.Contains(err.Error(), "EOF") { // 32MB max size
|
||||
ctx.Handle(500, "ParseMultipartForm", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -381,7 +238,5 @@ func InitContext() martini.Handler {
|
|||
ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + ctx.csrfToken + `">`)
|
||||
|
||||
c.Map(ctx)
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
// Copyright 2014 The Gogs 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 middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/go-martini/martini"
|
||||
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
var isWindows bool
|
||||
|
||||
func init() {
|
||||
isWindows = runtime.GOOS == "windows"
|
||||
}
|
||||
|
||||
func Logger() martini.Handler {
|
||||
return func(res http.ResponseWriter, req *http.Request, ctx martini.Context, log *log.Logger) {
|
||||
if setting.DisableRouterLog {
|
||||
return
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
log.Printf("Started %s %s", req.Method, req.URL.Path)
|
||||
|
||||
rw := res.(martini.ResponseWriter)
|
||||
ctx.Next()
|
||||
|
||||
content := fmt.Sprintf("Completed %v %s in %v", rw.Status(), http.StatusText(rw.Status()), time.Since(start))
|
||||
if !isWindows {
|
||||
switch rw.Status() {
|
||||
case 200:
|
||||
content = fmt.Sprintf("\033[1;32m%s\033[0m", content)
|
||||
case 304:
|
||||
content = fmt.Sprintf("\033[1;33m%s\033[0m", content)
|
||||
case 404:
|
||||
content = fmt.Sprintf("\033[1;31m%s\033[0m", content)
|
||||
case 500:
|
||||
content = fmt.Sprintf("\033[1;36m%s\033[0m", content)
|
||||
}
|
||||
}
|
||||
log.Println(content)
|
||||
}
|
||||
}
|
|
@ -1,281 +0,0 @@
|
|||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// foked from https://github.com/martini-contrib/render/blob/master/render.go
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/go-martini/martini"
|
||||
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
)
|
||||
|
||||
const (
|
||||
ContentType = "Content-Type"
|
||||
ContentLength = "Content-Length"
|
||||
ContentJSON = "application/json"
|
||||
ContentHTML = "text/html"
|
||||
ContentXHTML = "application/xhtml+xml"
|
||||
defaultCharset = "UTF-8"
|
||||
)
|
||||
|
||||
var helperFuncs = template.FuncMap{
|
||||
"yield": func() (string, error) {
|
||||
return "", fmt.Errorf("yield called with no layout defined")
|
||||
},
|
||||
}
|
||||
|
||||
type Delims struct {
|
||||
Left string
|
||||
Right string
|
||||
}
|
||||
|
||||
type RenderOptions struct {
|
||||
Directory string
|
||||
Layout string
|
||||
Extensions []string
|
||||
Funcs []template.FuncMap
|
||||
Delims Delims
|
||||
Charset string
|
||||
IndentJSON bool
|
||||
HTMLContentType string
|
||||
}
|
||||
|
||||
type HTMLOptions struct {
|
||||
Layout string
|
||||
}
|
||||
|
||||
func Renderer(options ...RenderOptions) martini.Handler {
|
||||
opt := prepareOptions(options)
|
||||
cs := prepareCharset(opt.Charset)
|
||||
t := compile(opt)
|
||||
return func(res http.ResponseWriter, req *http.Request, c martini.Context) {
|
||||
var tc *template.Template
|
||||
if martini.Env == martini.Dev {
|
||||
|
||||
tc = compile(opt)
|
||||
} else {
|
||||
|
||||
tc, _ = t.Clone()
|
||||
}
|
||||
|
||||
rd := &Render{res, req, tc, opt, cs, base.TmplData{}, time.Time{}}
|
||||
|
||||
rd.Data["TmplLoadTimes"] = func() string {
|
||||
if rd.startTime.IsZero() {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprint(time.Since(rd.startTime).Nanoseconds()/1e6) + "ms"
|
||||
}
|
||||
|
||||
c.Map(rd.Data)
|
||||
c.Map(rd)
|
||||
}
|
||||
}
|
||||
|
||||
func prepareCharset(charset string) string {
|
||||
if len(charset) != 0 {
|
||||
return "; charset=" + charset
|
||||
}
|
||||
|
||||
return "; charset=" + defaultCharset
|
||||
}
|
||||
|
||||
func prepareOptions(options []RenderOptions) RenderOptions {
|
||||
var opt RenderOptions
|
||||
if len(options) > 0 {
|
||||
opt = options[0]
|
||||
}
|
||||
|
||||
if len(opt.Directory) == 0 {
|
||||
opt.Directory = "templates"
|
||||
}
|
||||
if len(opt.Extensions) == 0 {
|
||||
opt.Extensions = []string{".tmpl"}
|
||||
}
|
||||
if len(opt.HTMLContentType) == 0 {
|
||||
opt.HTMLContentType = ContentHTML
|
||||
}
|
||||
|
||||
return opt
|
||||
}
|
||||
|
||||
func compile(options RenderOptions) *template.Template {
|
||||
dir := options.Directory
|
||||
t := template.New(dir)
|
||||
t.Delims(options.Delims.Left, options.Delims.Right)
|
||||
|
||||
template.Must(t.Parse("Martini"))
|
||||
|
||||
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
r, err := filepath.Rel(dir, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ext := filepath.Ext(r)
|
||||
for _, extension := range options.Extensions {
|
||||
if ext == extension {
|
||||
|
||||
buf, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
name := (r[0 : len(r)-len(ext)])
|
||||
tmpl := t.New(filepath.ToSlash(name))
|
||||
|
||||
for _, funcs := range options.Funcs {
|
||||
tmpl = tmpl.Funcs(funcs)
|
||||
}
|
||||
|
||||
template.Must(tmpl.Funcs(helperFuncs).Parse(string(buf)))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
type Render struct {
|
||||
http.ResponseWriter
|
||||
req *http.Request
|
||||
t *template.Template
|
||||
opt RenderOptions
|
||||
compiledCharset string
|
||||
|
||||
Data base.TmplData
|
||||
|
||||
startTime time.Time
|
||||
}
|
||||
|
||||
func (r *Render) JSON(status int, v interface{}) {
|
||||
var result []byte
|
||||
var err error
|
||||
if r.opt.IndentJSON {
|
||||
result, err = json.MarshalIndent(v, "", " ")
|
||||
} else {
|
||||
result, err = json.Marshal(v)
|
||||
}
|
||||
if err != nil {
|
||||
http.Error(r, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
|
||||
r.Header().Set(ContentType, ContentJSON+r.compiledCharset)
|
||||
r.WriteHeader(status)
|
||||
r.Write(result)
|
||||
}
|
||||
|
||||
func (r *Render) JSONString(v interface{}) (string, error) {
|
||||
var result []byte
|
||||
var err error
|
||||
if r.opt.IndentJSON {
|
||||
result, err = json.MarshalIndent(v, "", " ")
|
||||
} else {
|
||||
result, err = json.Marshal(v)
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(result), nil
|
||||
}
|
||||
|
||||
func (r *Render) renderBytes(name string, binding interface{}, htmlOpt ...HTMLOptions) (*bytes.Buffer, error) {
|
||||
opt := r.prepareHTMLOptions(htmlOpt)
|
||||
|
||||
if len(opt.Layout) > 0 {
|
||||
r.addYield(name, binding)
|
||||
name = opt.Layout
|
||||
}
|
||||
|
||||
out, err := r.execute(name, binding)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (r *Render) HTML(status int, name string, binding interface{}, htmlOpt ...HTMLOptions) {
|
||||
r.startTime = time.Now()
|
||||
|
||||
out, err := r.renderBytes(name, binding, htmlOpt...)
|
||||
if err != nil {
|
||||
http.Error(r, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
r.Header().Set(ContentType, r.opt.HTMLContentType+r.compiledCharset)
|
||||
r.WriteHeader(status)
|
||||
io.Copy(r, out)
|
||||
}
|
||||
|
||||
func (r *Render) HTMLString(name string, binding interface{}, htmlOpt ...HTMLOptions) (string, error) {
|
||||
if out, err := r.renderBytes(name, binding, htmlOpt...); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
return out.String(), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Render) Error(status int, message ...string) {
|
||||
r.WriteHeader(status)
|
||||
if len(message) > 0 {
|
||||
r.Write([]byte(message[0]))
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Render) Redirect(location string, status ...int) {
|
||||
code := http.StatusFound
|
||||
if len(status) == 1 {
|
||||
code = status[0]
|
||||
}
|
||||
|
||||
http.Redirect(r, r.req, location, code)
|
||||
}
|
||||
|
||||
func (r *Render) Template() *template.Template {
|
||||
return r.t
|
||||
}
|
||||
|
||||
func (r *Render) execute(name string, binding interface{}) (*bytes.Buffer, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
return buf, r.t.ExecuteTemplate(buf, name, binding)
|
||||
}
|
||||
|
||||
func (r *Render) addYield(name string, binding interface{}) {
|
||||
funcs := template.FuncMap{
|
||||
"yield": func() (template.HTML, error) {
|
||||
buf, err := r.execute(name, binding)
|
||||
|
||||
return template.HTML(buf.String()), err
|
||||
},
|
||||
}
|
||||
r.t.Funcs(funcs)
|
||||
}
|
||||
|
||||
func (r *Render) prepareHTMLOptions(htmlOpt []HTMLOptions) HTMLOptions {
|
||||
if len(htmlOpt) > 0 {
|
||||
return htmlOpt[0]
|
||||
}
|
||||
|
||||
return HTMLOptions{
|
||||
Layout: r.opt.Layout,
|
||||
}
|
||||
}
|
|
@ -10,20 +10,19 @@ import (
|
|||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/go-martini/martini"
|
||||
|
||||
"github.com/gogits/git"
|
||||
"github.com/Unknwon/macaron"
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/gogits/gogs/modules/git"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
func RepoAssignment(redirect bool, args ...bool) martini.Handler {
|
||||
return func(ctx *Context, params martini.Params) {
|
||||
// valid brachname
|
||||
func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
|
||||
return func(ctx *Context) {
|
||||
// To valid brach name.
|
||||
var validBranch bool
|
||||
// display bare quick start if it is a bare repo
|
||||
// To display bare quick start if it is a bare repo.
|
||||
var displayBare bool
|
||||
|
||||
if len(args) >= 1 {
|
||||
|
@ -35,51 +34,53 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler {
|
|||
}
|
||||
|
||||
var (
|
||||
user *models.User
|
||||
err error
|
||||
u *models.User
|
||||
err error
|
||||
)
|
||||
|
||||
userName := params["username"]
|
||||
repoName := params["reponame"]
|
||||
refName := params["branchname"]
|
||||
userName := ctx.Params(":username")
|
||||
repoName := ctx.Params(":reponame")
|
||||
refName := ctx.Params(":branchname")
|
||||
if len(refName) == 0 {
|
||||
refName = ctx.Params(":path")
|
||||
}
|
||||
|
||||
// TODO: need more advanced onwership and access level check.
|
||||
// Collaborators who have write access can be seen as owners.
|
||||
if ctx.IsSigned {
|
||||
ctx.Repo.IsOwner, err = models.HasAccess(ctx.User.Name, userName+"/"+repoName, models.WRITABLE)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "RepoAssignment(HasAccess)", err)
|
||||
ctx.Handle(500, "HasAccess", err)
|
||||
return
|
||||
}
|
||||
ctx.Repo.IsTrueOwner = ctx.User.LowerName == strings.ToLower(userName)
|
||||
}
|
||||
|
||||
if !ctx.Repo.IsTrueOwner {
|
||||
user, err = models.GetUserByName(userName)
|
||||
u, err = models.GetUserByName(userName)
|
||||
if err != nil {
|
||||
if err == models.ErrUserNotExist {
|
||||
ctx.Handle(404, "RepoAssignment(GetUserByName)", err)
|
||||
ctx.Handle(404, "GetUserByName", err)
|
||||
return
|
||||
} else if redirect {
|
||||
ctx.Redirect("/")
|
||||
return
|
||||
}
|
||||
ctx.Handle(500, "RepoAssignment(GetUserByName)", err)
|
||||
ctx.Handle(500, "GetUserByName", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
user = ctx.User
|
||||
u = ctx.User
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
if u == nil {
|
||||
if redirect {
|
||||
ctx.Redirect("/")
|
||||
return
|
||||
}
|
||||
ctx.Handle(403, "RepoAssignment", errors.New("invliad user account for single repository"))
|
||||
ctx.Handle(404, "RepoAssignment", errors.New("invliad user account for single repository"))
|
||||
return
|
||||
}
|
||||
ctx.Repo.Owner = user
|
||||
ctx.Repo.Owner = u
|
||||
|
||||
// Organization owner team members are true owners as well.
|
||||
if ctx.IsSigned && ctx.Repo.Owner.IsOrganization() && ctx.Repo.Owner.IsOrgOwner(ctx.User.Id) {
|
||||
|
@ -87,16 +88,19 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler {
|
|||
}
|
||||
|
||||
// get repository
|
||||
repo, err := models.GetRepositoryByName(user.Id, repoName)
|
||||
repo, err := models.GetRepositoryByName(u.Id, repoName)
|
||||
if err != nil {
|
||||
if err == models.ErrRepoNotExist {
|
||||
ctx.Handle(404, "RepoAssignment", err)
|
||||
ctx.Handle(404, "GetRepositoryByName", err)
|
||||
return
|
||||
} else if redirect {
|
||||
ctx.Redirect("/")
|
||||
return
|
||||
}
|
||||
ctx.Handle(500, "RepoAssignment", err)
|
||||
ctx.Handle(500, "GetRepositoryByName", err)
|
||||
return
|
||||
} else if err = repo.GetOwner(); err != nil {
|
||||
ctx.Handle(500, "GetOwner", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -108,16 +112,16 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler {
|
|||
// Check access.
|
||||
if repo.IsPrivate && !ctx.Repo.IsOwner {
|
||||
if ctx.User == nil {
|
||||
ctx.Handle(404, "RepoAssignment(HasAccess)", nil)
|
||||
ctx.Handle(404, "HasAccess", nil)
|
||||
return
|
||||
}
|
||||
|
||||
hasAccess, err := models.HasAccess(ctx.User.Name, ctx.Repo.Owner.Name+"/"+repo.Name, models.READABLE)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "RepoAssignment(HasAccess)", err)
|
||||
ctx.Handle(500, "HasAccess", err)
|
||||
return
|
||||
} else if !hasAccess {
|
||||
ctx.Handle(404, "RepoAssignment(HasAccess)", nil)
|
||||
ctx.Handle(404, "HasAccess", nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -127,7 +131,7 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler {
|
|||
if repo.IsMirror {
|
||||
ctx.Repo.Mirror, err = models.GetMirror(repo.Id)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "RepoAssignment(GetMirror)", err)
|
||||
ctx.Handle(500, "GetMirror", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["MirrorInterval"] = ctx.Repo.Mirror.Interval
|
||||
|
@ -144,34 +148,33 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler {
|
|||
return
|
||||
}
|
||||
ctx.Repo.GitRepo = gitRepo
|
||||
ctx.Repo.RepoLink = "/" + user.Name + "/" + repo.Name
|
||||
ctx.Repo.RepoLink = "/" + u.Name + "/" + repo.Name
|
||||
|
||||
tags, err := ctx.Repo.GitRepo.GetTags()
|
||||
if err != nil {
|
||||
ctx.Handle(500, "RepoAssignment(GetTags))", err)
|
||||
ctx.Handle(500, "GetTags", err)
|
||||
return
|
||||
}
|
||||
ctx.Repo.Repository.NumTags = len(tags)
|
||||
|
||||
ctx.Data["Title"] = user.Name + "/" + repo.Name
|
||||
ctx.Data["Title"] = u.Name + "/" + repo.Name
|
||||
ctx.Data["Repository"] = repo
|
||||
ctx.Data["Owner"] = user
|
||||
ctx.Data["Owner"] = ctx.Repo.Repository.Owner
|
||||
ctx.Data["RepoLink"] = ctx.Repo.RepoLink
|
||||
ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner
|
||||
ctx.Data["IsRepositoryTrueOwner"] = ctx.Repo.IsTrueOwner
|
||||
ctx.Data["BranchName"] = ""
|
||||
|
||||
if setting.SshPort != 22 {
|
||||
ctx.Repo.CloneLink.SSH = fmt.Sprintf("ssh://%s@%s/%s/%s.git", setting.RunUser, setting.Domain, user.LowerName, repo.LowerName)
|
||||
ctx.Repo.CloneLink.SSH = fmt.Sprintf("ssh://%s@%s/%s/%s.git", setting.RunUser, setting.Domain, u.LowerName, repo.LowerName)
|
||||
} else {
|
||||
ctx.Repo.CloneLink.SSH = fmt.Sprintf("%s@%s:%s/%s.git", setting.RunUser, setting.Domain, user.LowerName, repo.LowerName)
|
||||
ctx.Repo.CloneLink.SSH = fmt.Sprintf("%s@%s:%s/%s.git", setting.RunUser, setting.Domain, u.LowerName, repo.LowerName)
|
||||
}
|
||||
ctx.Repo.CloneLink.HTTPS = fmt.Sprintf("%s%s/%s.git", setting.AppUrl, user.LowerName, repo.LowerName)
|
||||
ctx.Repo.CloneLink.HTTPS = fmt.Sprintf("%s%s/%s.git", setting.AppUrl, u.LowerName, repo.LowerName)
|
||||
ctx.Data["CloneLink"] = ctx.Repo.CloneLink
|
||||
|
||||
if ctx.Repo.Repository.IsGoget {
|
||||
ctx.Data["GoGetLink"] = fmt.Sprintf("%s%s/%s", setting.AppUrl, user.LowerName, repo.LowerName)
|
||||
ctx.Data["GoGetImport"] = fmt.Sprintf("%s/%s/%s", setting.Domain, user.LowerName, repo.LowerName)
|
||||
ctx.Data["GoGetLink"] = fmt.Sprintf("%s%s/%s", setting.AppUrl, u.LowerName, repo.LowerName)
|
||||
ctx.Data["GoGetImport"] = fmt.Sprintf("%s/%s/%s", setting.Domain, u.LowerName, repo.LowerName)
|
||||
}
|
||||
|
||||
// when repo is bare, not valid branch
|
||||
|
@ -211,7 +214,7 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler {
|
|||
return
|
||||
}
|
||||
} else {
|
||||
ctx.Handle(404, "RepoAssignment invalid repo", nil)
|
||||
ctx.Handle(404, "RepoAssignment invalid repo", errors.New("branch or tag not exist"))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -222,7 +225,7 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler {
|
|||
} else {
|
||||
brs, err := gitRepo.GetBranches()
|
||||
if err != nil {
|
||||
ctx.Handle(500, "RepoAssignment(GetBranches))", err)
|
||||
ctx.Handle(500, "GetBranches", err)
|
||||
return
|
||||
}
|
||||
refName = brs[0]
|
||||
|
@ -233,6 +236,13 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler {
|
|||
|
||||
ctx.Data["IsBranch"] = ctx.Repo.IsBranch
|
||||
ctx.Data["IsCommit"] = ctx.Repo.IsCommit
|
||||
|
||||
ctx.Repo.CommitsCount, err = ctx.Repo.Commit.CommitsCount()
|
||||
if err != nil {
|
||||
ctx.Handle(500, "CommitsCount", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
|
||||
}
|
||||
|
||||
log.Debug("displayBare: %v; IsBare: %v", displayBare, ctx.Repo.Repository.IsBare)
|
||||
|
@ -240,7 +250,7 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler {
|
|||
// repo is bare and display enable
|
||||
if displayBare && ctx.Repo.Repository.IsBare {
|
||||
log.Debug("Bare repository: %s", ctx.Repo.RepoLink)
|
||||
ctx.HTML(200, "repo/single_bare")
|
||||
ctx.HTML(200, "repo/bare")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -251,9 +261,10 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler {
|
|||
ctx.Data["TagName"] = ctx.Repo.TagName
|
||||
brs, err := ctx.Repo.GitRepo.GetBranches()
|
||||
if err != nil {
|
||||
log.Error("RepoAssignment(GetBranches): %v", err)
|
||||
log.Error(4, "GetBranches: %v", err)
|
||||
}
|
||||
ctx.Data["Branches"] = brs
|
||||
ctx.Data["BrancheCount"] = len(brs)
|
||||
|
||||
// If not branch selected, try default one.
|
||||
// If default branch doesn't exists, fall back to some other branch.
|
||||
|
@ -267,11 +278,11 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler {
|
|||
|
||||
ctx.Data["BranchName"] = ctx.Repo.BranchName
|
||||
ctx.Data["CommitId"] = ctx.Repo.CommitId
|
||||
ctx.Data["IsRepositoryWatching"] = ctx.Repo.IsWatching
|
||||
ctx.Data["IsWatchingRepo"] = ctx.Repo.IsWatching
|
||||
}
|
||||
}
|
||||
|
||||
func RequireTrueOwner() martini.Handler {
|
||||
func RequireTrueOwner() macaron.Handler {
|
||||
return func(ctx *Context) {
|
||||
if !ctx.Repo.IsTrueOwner {
|
||||
if !ctx.IsSigned {
|
||||
|
|
|
@ -1,127 +0,0 @@
|
|||
// Copyright 2013 The Martini Authors. All rights reserved.
|
||||
// Copyright 2014 The Gogs 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 middleware
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/go-martini/martini"
|
||||
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
// StaticOptions is a struct for specifying configuration options for the martini.Static middleware.
|
||||
type StaticOptions struct {
|
||||
// Prefix is the optional prefix used to serve the static directory content
|
||||
Prefix string
|
||||
// SkipLogging will disable [Static] log messages when a static file is served.
|
||||
SkipLogging bool
|
||||
// IndexFile defines which file to serve as index if it exists.
|
||||
IndexFile string
|
||||
// Expires defines which user-defined function to use for producing a HTTP Expires Header
|
||||
// https://developers.google.com/speed/docs/insights/LeverageBrowserCaching
|
||||
Expires func() string
|
||||
}
|
||||
|
||||
func prepareStaticOptions(options []StaticOptions) StaticOptions {
|
||||
var opt StaticOptions
|
||||
if len(options) > 0 {
|
||||
opt = options[0]
|
||||
}
|
||||
|
||||
// Defaults
|
||||
if len(opt.IndexFile) == 0 {
|
||||
opt.IndexFile = "index.html"
|
||||
}
|
||||
// Normalize the prefix if provided
|
||||
if opt.Prefix != "" {
|
||||
// Ensure we have a leading '/'
|
||||
if opt.Prefix[0] != '/' {
|
||||
opt.Prefix = "/" + opt.Prefix
|
||||
}
|
||||
// Remove any trailing '/'
|
||||
opt.Prefix = strings.TrimRight(opt.Prefix, "/")
|
||||
}
|
||||
return opt
|
||||
}
|
||||
|
||||
// Static returns a middleware handler that serves static files in the given directory.
|
||||
func Static(directory string, staticOpt ...StaticOptions) martini.Handler {
|
||||
if runtime.GOOS == "windows" {
|
||||
if len(directory) < 2 || directory[1] != ':' {
|
||||
directory = path.Join(setting.StaticRootPath, directory)
|
||||
}
|
||||
} else if !path.IsAbs(directory) {
|
||||
directory = path.Join(setting.StaticRootPath, directory)
|
||||
}
|
||||
|
||||
dir := http.Dir(directory)
|
||||
opt := prepareStaticOptions(staticOpt)
|
||||
|
||||
return func(res http.ResponseWriter, req *http.Request, log *log.Logger) {
|
||||
if req.Method != "GET" && req.Method != "HEAD" {
|
||||
return
|
||||
}
|
||||
file := req.URL.Path
|
||||
// if we have a prefix, filter requests by stripping the prefix
|
||||
if opt.Prefix != "" {
|
||||
if !strings.HasPrefix(file, opt.Prefix) {
|
||||
return
|
||||
}
|
||||
file = file[len(opt.Prefix):]
|
||||
if file != "" && file[0] != '/' {
|
||||
return
|
||||
}
|
||||
}
|
||||
f, err := dir.Open(file)
|
||||
if err != nil {
|
||||
// discard the error?
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// try to serve index file
|
||||
if fi.IsDir() {
|
||||
// redirect if missing trailing slash
|
||||
if !strings.HasSuffix(req.URL.Path, "/") {
|
||||
http.Redirect(res, req, req.URL.Path+"/", http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
file = path.Join(file, opt.IndexFile)
|
||||
f, err = dir.Open(file)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
fi, err = f.Stat()
|
||||
if err != nil || fi.IsDir() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if !opt.SkipLogging {
|
||||
log.Println("[Static] Serving " + file)
|
||||
}
|
||||
|
||||
// Add an Expires header to the static content
|
||||
if opt.Expires != nil {
|
||||
res.Header().Set("Expires", opt.Expires())
|
||||
}
|
||||
|
||||
http.ServeContent(res, req, file, fi.ModTime(), f)
|
||||
}
|
||||
}
|
|
@ -78,7 +78,7 @@ func ExecDir(timeout time.Duration, dir, desc, cmdName string, args ...string) (
|
|||
select {
|
||||
case <-time.After(timeout):
|
||||
if errKill := Kill(pid); errKill != nil {
|
||||
log.Error("Exec(%d:%s): %v", pid, desc, errKill)
|
||||
log.Error(4, "Exec(%d:%s): %v", pid, desc, errKill)
|
||||
}
|
||||
<-done
|
||||
return "", ErrExecTimeout.Error(), ErrExecTimeout
|
||||
|
|
|
@ -14,12 +14,12 @@ import (
|
|||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/Unknwon/goconfig"
|
||||
"github.com/macaron-contrib/session"
|
||||
|
||||
"github.com/gogits/cache"
|
||||
"github.com/gogits/session"
|
||||
|
||||
"github.com/gogits/gogs/modules/bin"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
// "github.com/gogits/gogs-ng/modules/ssh"
|
||||
)
|
||||
|
||||
type Scheme string
|
||||
|
@ -45,6 +45,7 @@ var (
|
|||
DisableRouterLog bool
|
||||
CertFile, KeyFile string
|
||||
StaticRootPath string
|
||||
EnableGzip bool
|
||||
|
||||
// Security settings.
|
||||
InstallLock bool
|
||||
|
@ -89,15 +90,22 @@ var (
|
|||
// Session settings.
|
||||
SessionProvider string
|
||||
SessionConfig *session.Config
|
||||
SessionManager *session.Manager
|
||||
|
||||
// Global setting objects.
|
||||
Cfg *goconfig.ConfigFile
|
||||
CustomPath string // Custom directory path.
|
||||
ProdMode bool
|
||||
RunUser string
|
||||
Cfg *goconfig.ConfigFile
|
||||
ConfRootPath string
|
||||
CustomPath string // Custom directory path.
|
||||
ProdMode bool
|
||||
RunUser string
|
||||
|
||||
// I18n settings.
|
||||
Langs, Names []string
|
||||
)
|
||||
|
||||
func init() {
|
||||
log.NewLogger(0, "console", `{"level": 0}`)
|
||||
}
|
||||
|
||||
func ExecPath() (string, error) {
|
||||
file, err := exec.LookPath(os.Args[0])
|
||||
if err != nil {
|
||||
|
@ -121,16 +129,13 @@ func WorkDir() (string, error) {
|
|||
func NewConfigContext() {
|
||||
workDir, err := WorkDir()
|
||||
if err != nil {
|
||||
log.Fatal("Fail to get work directory: %v", err)
|
||||
log.Fatal(4, "Fail to get work directory: %v", err)
|
||||
}
|
||||
ConfRootPath = path.Join(workDir, "conf")
|
||||
|
||||
data, err := bin.Asset("conf/app.ini")
|
||||
Cfg, err = goconfig.LoadConfigFile(path.Join(workDir, "conf/app.ini"))
|
||||
if err != nil {
|
||||
log.Fatal("Fail to read 'conf/app.ini': %v", err)
|
||||
}
|
||||
Cfg, err = goconfig.LoadFromData(data)
|
||||
if err != nil {
|
||||
log.Fatal("Fail to parse 'conf/app.ini': %v", err)
|
||||
log.Fatal(4, "Fail to parse 'conf/app.ini': %v", err)
|
||||
}
|
||||
|
||||
CustomPath = os.Getenv("GOGS_CUSTOM")
|
||||
|
@ -141,10 +146,10 @@ func NewConfigContext() {
|
|||
cfgPath := path.Join(CustomPath, "conf/app.ini")
|
||||
if com.IsFile(cfgPath) {
|
||||
if err = Cfg.AppendFiles(cfgPath); err != nil {
|
||||
log.Fatal("Fail to load custom 'conf/app.ini': %v", err)
|
||||
log.Fatal(4, "Fail to load custom 'conf/app.ini': %v", err)
|
||||
}
|
||||
} else {
|
||||
log.Warn("No custom 'conf/app.ini' found")
|
||||
log.Warn("No custom 'conf/app.ini' found, please go to '/install'")
|
||||
}
|
||||
|
||||
AppName = Cfg.MustValue("", "APP_NAME", "Gogs: Go Git Service")
|
||||
|
@ -165,6 +170,7 @@ func NewConfigContext() {
|
|||
DisableRouterLog = Cfg.MustBool("server", "DISABLE_ROUTER_LOG")
|
||||
StaticRootPath = Cfg.MustValue("server", "STATIC_ROOT_PATH", workDir)
|
||||
LogRootPath = Cfg.MustValue("log", "ROOT_PATH", path.Join(workDir, "log"))
|
||||
EnableGzip = Cfg.MustBool("server", "ENABLE_GZIP")
|
||||
|
||||
InstallLock = Cfg.MustBool("security", "INSTALL_LOCK")
|
||||
SecretKey = Cfg.MustValue("security", "SECRET_KEY")
|
||||
|
@ -173,14 +179,14 @@ func NewConfigContext() {
|
|||
CookieRememberName = Cfg.MustValue("security", "COOKIE_REMEMBER_NAME")
|
||||
ReverseProxyAuthUser = Cfg.MustValue("security", "REVERSE_PROXY_AUTHENTICATION_USER", "X-WEBAUTH-USER")
|
||||
|
||||
AttachmentPath = Cfg.MustValue("attachment", "PATH", "files/attachments")
|
||||
AttachmentAllowedTypes = Cfg.MustValue("attachment", "ALLOWED_TYPES", "*/*")
|
||||
AttachmentPath = Cfg.MustValue("attachment", "PATH", "data/attachments")
|
||||
AttachmentAllowedTypes = Cfg.MustValue("attachment", "ALLOWED_TYPES", "image/jpeg|image/png")
|
||||
AttachmentMaxSize = Cfg.MustInt64("attachment", "MAX_SIZE", 32)
|
||||
AttachmentMaxFiles = Cfg.MustInt("attachment", "MAX_FILES", 10)
|
||||
AttachmentEnabled = Cfg.MustBool("attachment", "ENABLE", true)
|
||||
|
||||
if err = os.MkdirAll(AttachmentPath, os.ModePerm); err != nil {
|
||||
log.Fatal("Could not create directory %s: %s", AttachmentPath, err)
|
||||
log.Fatal(4, "Could not create directory %s: %s", AttachmentPath, err)
|
||||
}
|
||||
|
||||
RunUser = Cfg.MustValue("", "RUN_USER")
|
||||
|
@ -190,13 +196,13 @@ func NewConfigContext() {
|
|||
}
|
||||
// Does not check run user when the install lock is off.
|
||||
if InstallLock && RunUser != curUser {
|
||||
log.Fatal("Expect user(%s) but current user is: %s", RunUser, curUser)
|
||||
log.Fatal(4, "Expect user(%s) but current user is: %s", RunUser, curUser)
|
||||
}
|
||||
|
||||
// Determine and create root git reposiroty path.
|
||||
homeDir, err := com.HomeDir()
|
||||
if err != nil {
|
||||
log.Fatal("Fail to get home directory: %v", err)
|
||||
log.Fatal(4, "Fail to get home directory: %v", err)
|
||||
}
|
||||
RepoRootPath = Cfg.MustValue("repository", "ROOT", filepath.Join(homeDir, "gogs-repositories"))
|
||||
if !filepath.IsAbs(RepoRootPath) {
|
||||
|
@ -206,13 +212,16 @@ func NewConfigContext() {
|
|||
}
|
||||
|
||||
if err = os.MkdirAll(RepoRootPath, os.ModePerm); err != nil {
|
||||
log.Fatal("Fail to create repository root path(%s): %v", RepoRootPath, err)
|
||||
log.Fatal(4, "Fail to create repository root path(%s): %v", RepoRootPath, err)
|
||||
}
|
||||
ScriptType = Cfg.MustValue("repository", "SCRIPT_TYPE", "bash")
|
||||
|
||||
PictureService = Cfg.MustValueRange("picture", "SERVICE", "server",
|
||||
[]string{"server"})
|
||||
DisableGravatar = Cfg.MustBool("picture", "DISABLE_GRAVATAR")
|
||||
|
||||
Langs = Cfg.MustValueArray("i18n", "LANGS", ",")
|
||||
Names = Cfg.MustValueArray("i18n", "NAMES", ",")
|
||||
}
|
||||
|
||||
var Service struct {
|
||||
|
@ -255,7 +264,7 @@ func newLogService() {
|
|||
mode = strings.TrimSpace(mode)
|
||||
modeSec := "log." + mode
|
||||
if _, err := Cfg.GetSection(modeSec); err != nil {
|
||||
log.Fatal("Unknown log mode: %s", mode)
|
||||
log.Fatal(4, "Unknown log mode: %s", mode)
|
||||
}
|
||||
|
||||
// Log level.
|
||||
|
@ -263,7 +272,7 @@ func newLogService() {
|
|||
[]string{"Trace", "Debug", "Info", "Warn", "Error", "Critical"})
|
||||
level, ok := logLevels[levelName]
|
||||
if !ok {
|
||||
log.Fatal("Unknown log level: %s", levelName)
|
||||
log.Fatal(4, "Unknown log level: %s", levelName)
|
||||
}
|
||||
|
||||
// Generate log configuration.
|
||||
|
@ -318,15 +327,15 @@ func newCacheService() {
|
|||
case "memory":
|
||||
CacheConfig = fmt.Sprintf(`{"interval":%d}`, Cfg.MustInt("cache", "INTERVAL", 60))
|
||||
case "redis", "memcache":
|
||||
CacheConfig = fmt.Sprintf(`{"conn":"%s"}`, Cfg.MustValue("cache", "HOST"))
|
||||
CacheConfig = fmt.Sprintf(`{"conn":"%s"}`, strings.Trim(Cfg.MustValue("cache", "HOST"), "\" "))
|
||||
default:
|
||||
log.Fatal("Unknown cache adapter: %s", CacheAdapter)
|
||||
log.Fatal(4, "Unknown cache adapter: %s", CacheAdapter)
|
||||
}
|
||||
|
||||
var err error
|
||||
Cache, err = cache.NewCache(CacheAdapter, CacheConfig)
|
||||
if err != nil {
|
||||
log.Fatal("Init cache system failed, adapter: %s, config: %s, %v\n",
|
||||
log.Fatal(4, "Init cache system failed, adapter: %s, config: %s, %v\n",
|
||||
CacheAdapter, CacheConfig, err)
|
||||
}
|
||||
|
||||
|
@ -338,12 +347,12 @@ func newSessionService() {
|
|||
[]string{"memory", "file", "redis", "mysql"})
|
||||
|
||||
SessionConfig = new(session.Config)
|
||||
SessionConfig.ProviderConfig = Cfg.MustValue("session", "PROVIDER_CONFIG")
|
||||
SessionConfig.ProviderConfig = strings.Trim(Cfg.MustValue("session", "PROVIDER_CONFIG"), "\" ")
|
||||
SessionConfig.CookieName = Cfg.MustValue("session", "COOKIE_NAME", "i_like_gogits")
|
||||
SessionConfig.CookieSecure = Cfg.MustBool("session", "COOKIE_SECURE")
|
||||
SessionConfig.Secure = Cfg.MustBool("session", "COOKIE_SECURE")
|
||||
SessionConfig.EnableSetCookie = Cfg.MustBool("session", "ENABLE_SET_COOKIE", true)
|
||||
SessionConfig.GcIntervalTime = Cfg.MustInt64("session", "GC_INTERVAL_TIME", 86400)
|
||||
SessionConfig.SessionLifeTime = Cfg.MustInt64("session", "SESSION_LIFE_TIME", 86400)
|
||||
SessionConfig.Gclifetime = Cfg.MustInt64("session", "GC_INTERVAL_TIME", 86400)
|
||||
SessionConfig.Maxlifetime = Cfg.MustInt64("session", "SESSION_LIFE_TIME", 86400)
|
||||
SessionConfig.SessionIDHashFunc = Cfg.MustValueRange("session", "SESSION_ID_HASHFUNC",
|
||||
"sha1", []string{"sha1", "sha256", "md5"})
|
||||
SessionConfig.SessionIDHashKey = Cfg.MustValue("session", "SESSION_ID_HASHKEY")
|
||||
|
@ -352,14 +361,6 @@ func newSessionService() {
|
|||
os.MkdirAll(path.Dir(SessionConfig.ProviderConfig), os.ModePerm)
|
||||
}
|
||||
|
||||
var err error
|
||||
SessionManager, err = session.NewManager(SessionProvider, *SessionConfig)
|
||||
if err != nil {
|
||||
log.Fatal("Init session system failed, provider: %s, %v",
|
||||
SessionProvider, err)
|
||||
}
|
||||
go SessionManager.GC()
|
||||
|
||||
log.Info("Session Service Enabled")
|
||||
}
|
||||
|
||||
|
@ -441,4 +442,5 @@ func NewServices() {
|
|||
newRegisterMailService()
|
||||
newNotifyMailService()
|
||||
newWebhookService()
|
||||
// ssh.Listen("2022")
|
||||
}
|
||||
|
|
119
modules/ssh/ssh.go
Normal file
119
modules/ssh/ssh.go
Normal file
|
@ -0,0 +1,119 @@
|
|||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Prototype, git client looks like do not recognize req.Reply.
|
||||
package ssh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"code.google.com/p/go.crypto/ssh"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
|
||||
"github.com/gogits/gogs-ng/modules/log"
|
||||
)
|
||||
|
||||
func handleServerConn(keyId string, chans <-chan ssh.NewChannel) {
|
||||
for newChan := range chans {
|
||||
if newChan.ChannelType() != "session" {
|
||||
newChan.Reject(ssh.UnknownChannelType, "unknown channel type")
|
||||
continue
|
||||
}
|
||||
channel, requests, err := newChan.Accept()
|
||||
if err != nil {
|
||||
log.Error(3, "Could not accept channel: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
go func(in <-chan *ssh.Request) {
|
||||
defer channel.Close()
|
||||
for req := range in {
|
||||
ok, payload := false, strings.TrimLeft(string(req.Payload), "\x00")
|
||||
fmt.Println("Request:", req.Type, req.WantReply, payload)
|
||||
switch req.Type {
|
||||
case "env":
|
||||
args := strings.Split(strings.Replace(payload, "\x00", "", -1), "\v")
|
||||
if len(args) != 2 {
|
||||
break
|
||||
}
|
||||
args[0] = strings.TrimLeft(args[0], "\x04")
|
||||
_, _, err := com.ExecCmdBytes("env", args[0]+"="+args[1])
|
||||
if err != nil {
|
||||
log.Error(3, "env: %v", err)
|
||||
channel.Stderr().Write([]byte(err.Error()))
|
||||
break
|
||||
}
|
||||
ok = true
|
||||
case "exec":
|
||||
os.Setenv("SSH_ORIGINAL_COMMAND", strings.TrimLeft(payload, "'("))
|
||||
log.Info("Payload: %v", strings.TrimLeft(payload, "'("))
|
||||
cmd := exec.Command("/Users/jiahuachen/Applications/Go/src/github.com/gogits/gogs-ng/gogs-ng", "serv", "key-"+keyId)
|
||||
cmd.Stdout = channel
|
||||
cmd.Stdin = channel
|
||||
cmd.Stderr = channel.Stderr()
|
||||
if err := cmd.Run(); err != nil {
|
||||
log.Error(3, "exec: %v", err)
|
||||
} else {
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
fmt.Println("Done:", ok)
|
||||
req.Reply(ok, nil) // BUG: Git on Mac seems not know this reply and hang?
|
||||
}
|
||||
fmt.Println("Done!!!")
|
||||
}(requests)
|
||||
}
|
||||
}
|
||||
|
||||
func listen(config *ssh.ServerConfig, port string) {
|
||||
listener, err := net.Listen("tcp", "0.0.0.0:"+port)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for {
|
||||
// Once a ServerConfig has been configured, connections can be accepted.
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
log.Error(3, "Fail to accept incoming connection: %v", err)
|
||||
continue
|
||||
}
|
||||
// Before use, a handshake must be performed on the incoming net.Conn.
|
||||
sConn, chans, reqs, err := ssh.NewServerConn(conn, config)
|
||||
if err != nil {
|
||||
log.Error(3, "Fail to handshake: %v", err)
|
||||
continue
|
||||
}
|
||||
// The incoming Request channel must be serviced.
|
||||
go ssh.DiscardRequests(reqs)
|
||||
go handleServerConn(sConn.Permissions.Extensions["key-id"], chans)
|
||||
}
|
||||
}
|
||||
|
||||
// Listen starts a SSH server listens on given port.
|
||||
func Listen(port string) {
|
||||
config := &ssh.ServerConfig{
|
||||
PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
|
||||
// keyCache[string(ssh.MarshalAuthorizedKey(key))] = 2
|
||||
return &ssh.Permissions{Extensions: map[string]string{"key-id": "2"}}, nil
|
||||
},
|
||||
}
|
||||
|
||||
privateBytes, err := ioutil.ReadFile("/Users/jiahuachen/.ssh/id_rsa")
|
||||
if err != nil {
|
||||
panic("failed to load private key")
|
||||
}
|
||||
private, err := ssh.ParsePrivateKey(privateBytes)
|
||||
if err != nil {
|
||||
panic("failed to parse private key")
|
||||
}
|
||||
config.AddHostKey(private)
|
||||
|
||||
go listen(config, port)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue