| @@ -5,6 +5,7 @@ | |||
| package models | |||
| import ( | |||
| "encoding/hex" | |||
| "errors" | |||
| "fmt" | |||
| "os" | |||
| @@ -17,6 +18,7 @@ import ( | |||
| "github.com/gogits/git" | |||
| "github.com/gogits/gogs/modules/base" | |||
| "github.com/gogits/gogs/modules/log" | |||
| ) | |||
| // User types. | |||
| @@ -139,9 +141,43 @@ func RegisterUser(user *User) (*User, error) { | |||
| return user, nil | |||
| } | |||
| // get user by erify code | |||
| func getVerifyUser(code string) (user *User) { | |||
| if len(code) <= base.TimeLimitCodeLength { | |||
| return nil | |||
| } | |||
| // use tail hex username query user | |||
| hexStr := code[base.TimeLimitCodeLength:] | |||
| if b, err := hex.DecodeString(hexStr); err == nil { | |||
| if user, err = GetUserByName(string(b)); user != nil { | |||
| return user | |||
| } | |||
| log.Error("user.getVerifyUser: %v", err) | |||
| } | |||
| return nil | |||
| } | |||
| // verify active code when active account | |||
| func VerifyUserActiveCode(code string) (user *User) { | |||
| minutes := base.Service.ActiveCodeLives | |||
| if user = getVerifyUser(code); user != nil { | |||
| // time limit code | |||
| prefix := code[:base.TimeLimitCodeLength] | |||
| data := base.ToStr(user.Id) + user.Email + user.LowerName + user.Passwd + user.Rands | |||
| if base.VerifyTimeLimitCode(data, minutes, prefix) { | |||
| return user | |||
| } | |||
| } | |||
| return nil | |||
| } | |||
| // UpdateUser updates user's information. | |||
| func UpdateUser(user *User) (err error) { | |||
| _, err = orm.Id(user.Id).Update(user) | |||
| _, err = orm.Id(user.Id).UseBool().Update(user) | |||
| return err | |||
| } | |||
| @@ -131,15 +131,15 @@ func newMailService() { | |||
| } | |||
| } | |||
| func newRegisterService() { | |||
| func newRegisterMailService() { | |||
| if !Cfg.MustBool("service", "REGISTER_EMAIL_CONFIRM") { | |||
| return | |||
| } else if MailService == nil { | |||
| log.Warn("Register Service: Mail Service is not enabled") | |||
| log.Warn("Register Mail Service: Mail Service is not enabled") | |||
| return | |||
| } | |||
| Service.RegisterEmailConfirm = true | |||
| log.Info("Register Service Enabled") | |||
| log.Info("Register Mail Service Enabled") | |||
| } | |||
| func init() { | |||
| @@ -177,5 +177,5 @@ func init() { | |||
| newService() | |||
| newLogService() | |||
| newMailService() | |||
| newRegisterService() | |||
| newRegisterMailService() | |||
| } | |||
| @@ -8,6 +8,7 @@ import ( | |||
| "container/list" | |||
| "fmt" | |||
| "html/template" | |||
| "strings" | |||
| "time" | |||
| ) | |||
| @@ -54,4 +55,7 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{ | |||
| "ActionDesc": ActionDesc, | |||
| "DateFormat": DateFormat, | |||
| "List": List, | |||
| "Mail2Domain": func(mail string) string { | |||
| return "mail." + strings.Split(mail, "@")[1] | |||
| }, | |||
| } | |||
| @@ -36,6 +36,35 @@ func GetRandomString(n int) string { | |||
| return string(bytes) | |||
| } | |||
| // verify time limit code | |||
| func VerifyTimeLimitCode(data string, minutes int, code string) bool { | |||
| if len(code) <= 18 { | |||
| return false | |||
| } | |||
| // split code | |||
| start := code[:12] | |||
| lives := code[12:18] | |||
| if d, err := StrTo(lives).Int(); err == nil { | |||
| minutes = d | |||
| } | |||
| // right active code | |||
| retCode := CreateTimeLimitCode(data, minutes, start) | |||
| if retCode == code && minutes > 0 { | |||
| // check time is expired or not | |||
| before, _ := DateParse(start, "YmdHi") | |||
| now := time.Now() | |||
| if before.Add(time.Minute*time.Duration(minutes)).Unix() > now.Unix() { | |||
| return true | |||
| } | |||
| } | |||
| return false | |||
| } | |||
| const TimeLimitCodeLength = 12 + 6 + 40 | |||
| // create a time limit code | |||
| // code format: 12 length date time string + 6 minutes string + 40 sha1 encoded string | |||
| func CreateTimeLimitCode(data string, minutes int, startInf interface{}) string { | |||
| @@ -283,16 +312,24 @@ func DateFormat(t time.Time, format string) string { | |||
| return t.Format(format) | |||
| } | |||
| type argInt []int | |||
| // convert string to specify type | |||
| func (a argInt) Get(i int, args ...int) (r int) { | |||
| if i >= 0 && i < len(a) { | |||
| r = a[i] | |||
| } | |||
| if len(args) > 0 { | |||
| r = args[0] | |||
| 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) String() string { | |||
| if f.Exist() { | |||
| return string(f) | |||
| } | |||
| return | |||
| return "" | |||
| } | |||
| // convert any type to string | |||
| @@ -334,6 +371,18 @@ func ToStr(value interface{}, args ...int) (s string) { | |||
| return s | |||
| } | |||
| type argInt []int | |||
| func (a argInt) Get(i int, args ...int) (r int) { | |||
| if i >= 0 && i < len(a) { | |||
| r = a[i] | |||
| } | |||
| if len(args) > 0 { | |||
| r = args[0] | |||
| } | |||
| return | |||
| } | |||
| type Actioner interface { | |||
| GetOpType() int | |||
| GetActUserName() string | |||
| @@ -37,9 +37,9 @@ func GetMailTmplData(user *models.User) map[interface{}]interface{} { | |||
| // create a time limit code for user active | |||
| func CreateUserActiveCode(user *models.User, startInf interface{}) string { | |||
| hours := base.Service.ActiveCodeLives / 60 | |||
| minutes := base.Service.ActiveCodeLives | |||
| data := base.ToStr(user.Id) + user.Email + user.LowerName + user.Passwd + user.Rands | |||
| code := base.CreateTimeLimitCode(data, hours, startInf) | |||
| code := base.CreateTimeLimitCode(data, minutes, startInf) | |||
| // add tail hex username | |||
| code += hex.EncodeToString([]byte(user.LowerName)) | |||
| @@ -70,11 +70,11 @@ func SendRegisterMail(r *middleware.Render, user *models.User) { | |||
| func SendActiveMail(r *middleware.Render, user *models.User) { | |||
| code := CreateUserActiveCode(user, nil) | |||
| subject := "Verify your email address" | |||
| subject := "Verify your e-mail address" | |||
| data := GetMailTmplData(user) | |||
| data["Code"] = code | |||
| body, err := r.HTMLString("mail/auth/active_email.html", data) | |||
| body, err := r.HTMLString("mail/auth/active_email", data) | |||
| if err != nil { | |||
| log.Error("mail.SendActiveMail(fail to render): %v", err) | |||
| return | |||
| @@ -6,6 +6,8 @@ package middleware | |||
| import ( | |||
| "github.com/codegangsta/martini" | |||
| "github.com/gogits/gogs/modules/base" | |||
| ) | |||
| // SignInRequire requires user to sign in. | |||
| @@ -16,10 +18,10 @@ func SignInRequire(redirect bool) martini.Handler { | |||
| ctx.Redirect("/") | |||
| } | |||
| return | |||
| } else if !ctx.User.IsActive { | |||
| // ctx.Data["Title"] = "Activate Your Account" | |||
| // ctx.Render.HTML(200, "user/active", ctx.Data) | |||
| // return | |||
| } else if !ctx.User.IsActive && base.Service.RegisterEmailConfirm { | |||
| ctx.Data["Title"] = "Activate Your Account" | |||
| ctx.Render.HTML(200, "user/active", ctx.Data) | |||
| return | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,25 @@ | |||
| // 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 dev | |||
| import ( | |||
| "github.com/codegangsta/martini" | |||
| "github.com/gogits/gogs/models" | |||
| "github.com/gogits/gogs/modules/base" | |||
| "github.com/gogits/gogs/modules/middleware" | |||
| ) | |||
| func TemplatePreview(ctx *middleware.Context, params martini.Params) { | |||
| ctx.Data["User"] = models.User{Name: "Unknown"} | |||
| ctx.Data["AppName"] = base.AppName | |||
| ctx.Data["AppVer"] = base.AppVer | |||
| ctx.Data["AppUrl"] = base.AppUrl | |||
| ctx.Data["AppLogo"] = base.AppLogo | |||
| ctx.Data["Code"] = "2014031910370000009fff6782aadb2162b4a997acb69d4400888e0b9274657374" | |||
| ctx.Data["ActiveCodeLives"] = base.Service.ActiveCodeLives / 60 | |||
| ctx.Data["ResetPwdCodeLives"] = base.Service.ResetPwdCodeLives / 60 | |||
| ctx.HTML(200, params["_1"], ctx.Data) | |||
| } | |||
| @@ -243,4 +243,18 @@ func Activate(ctx *middleware.Context) { | |||
| ctx.Render.HTML(200, "user/active", ctx.Data) | |||
| return | |||
| } | |||
| // Verify code. | |||
| if user := models.VerifyUserActiveCode(code); user != nil { | |||
| user.IsActive = true | |||
| user.Rands = models.GetUserSalt() | |||
| models.UpdateUser(user) | |||
| ctx.Session.Set("userId", user.Id) | |||
| ctx.Session.Set("userName", user.Name) | |||
| ctx.Redirect("/", 302) | |||
| return | |||
| } | |||
| ctx.Data["IsActivateFailed"] = true | |||
| ctx.Render.HTML(200, "user/active", ctx.Data) | |||
| } | |||
| @@ -1,25 +0,0 @@ | |||
| {{template "mail/base.html" .}} | |||
| {{define "title"}} | |||
| {{if eq .Lang "zh-CN"}} | |||
| {{.User.NickName}},激活你的账户 | |||
| {{end}} | |||
| {{if eq .Lang "en-US"}} | |||
| {{.User.NickName}}, please active your account | |||
| {{end}} | |||
| {{end}} | |||
| {{define "body"}} | |||
| {{if eq .Lang "zh-CN"}} | |||
| <p style="margin:0;padding:0 0 9px 0;">点击链接验证email,{{.ActiveCodeLives}} 分钟内有效</p> | |||
| <p style="margin:0;padding:0 0 9px 0;"> | |||
| <a href="{{.AppUrl}}active/{{.Code}}">{{.AppUrl}}active/{{.Code}}</a> | |||
| </p> | |||
| <p style="margin:0;padding:0 0 9px 0;">如果链接点击无反应,请复制到浏览器打开。</p> | |||
| {{end}} | |||
| {{if eq .Lang "en-US"}} | |||
| <p style="margin:0;padding:0 0 9px 0;">Please click following link to verify your e-mail in {{.ActiveCodeLives}} hours</p> | |||
| <p style="margin:0;padding:0 0 9px 0;"> | |||
| <a href="{{.AppUrl}}active/{{.Code}}">{{.AppUrl}}active/{{.Code}}</a> | |||
| </p> | |||
| <p style="margin:0;padding:0 0 9px 0;">Copy and paste it to your browser if it's not working.</p> | |||
| {{end}} | |||
| {{end}} | |||
| @@ -2,26 +2,30 @@ | |||
| <html> | |||
| <head> | |||
| <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | |||
| <title>{{.Title}} - {{AppName}}</title> | |||
| <title>{{.User.Name}}, please activate your account</title> | |||
| </head> | |||
| <body style="background:#eee;"> | |||
| <div style="color:#555; font:12px/1.5 Tahoma,Arial,sans-serif;; text-shadow:1px 1px #fff; padding:0; margin:0;"> | |||
| <div style="color:#333; font:12px/1.5 Tahoma,Arial,sans-serif;; text-shadow:1px 1px #fff; padding:0; margin:0;"> | |||
| <div style="width:600px;margin:0 auto; padding:40px 0 20px;"> | |||
| <div style="border:1px solid #d9d9d9;border-radius:3px; background:#fff; box-shadow: 0px 2px 5px rgba(0, 0, 0,.05); -webkit-box-shadow: 0px 2px 5px rgba(0, 0, 0,.05);"> | |||
| <div style="padding: 20px 15px;"> | |||
| <h1 style="font-size:20px; padding:10px 0 20px; margin:0; border-bottom:1px solid #ddd;"><a style="color:#333;text-decoration:none;" target="_blank" href="{{.AppUrl}}">{{AppName}}</a></h1> | |||
| <h1 style="font-size:20px; padding:10px 0 20px; margin:0; border-bottom:1px solid #ddd;"><img src="{{.AppUrl}}/{{.AppLogo}}" style="height: 32px; margin-bottom: -10px;"> <a style="color:#333;text-decoration:none;" target="_blank" href="{{.AppUrl}}">{{.AppName}}</a></h1> | |||
| <div style="padding:40px 15px;"> | |||
| <div style="font-size:16px; padding-bottom:30px; font-weight:bold;"> | |||
| {{.Title}} | |||
| Hi <span style="color: #00BFFF;">{{.User.Name}}</span>, | |||
| </div> | |||
| <div style="font-size:14px; padding:0 15px;"> | |||
| {{template "body" .}} | |||
| <p style="margin:0;padding:0 0 9px 0;">Please click following link to verify your e-mail address within <b>{{.ActiveCodeLives}} hours</b>.</p> | |||
| <p style="margin:0;padding:0 0 9px 0;"> | |||
| <a href="{{.AppUrl}}user/activate?code={{.Code}}">{{.AppUrl}}user/activate?code={{.Code}}</a> | |||
| </p> | |||
| <p style="margin:0;padding:0 0 9px 0;">Copy and paste it to your browser if the link is not working.</p> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div style="color:#aaa;padding:10px;text-align:center;"> | |||
| © 2014 <a style="color:#888;text-decoration:none;" target="_blank" href="{{.AppUrl}}">{{AppName}} | |||
| © 2014 <a style="color:#888;text-decoration:none;" target="_blank" href="http://gogits.org">Gogs: Go Git Service</a> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @@ -5,27 +5,27 @@ | |||
| <title>{{.User.Name}}, welcome to {{.AppName}}</title> | |||
| </head> | |||
| <body style="background:#eee;"> | |||
| <div style="color:#555; font:12px/1.5 Tahoma,Arial,sans-serif;; text-shadow:1px 1px #fff; padding:0; margin:0;"> | |||
| <div style="color:#333; font:12px/1.5 Tahoma,Arial,sans-serif;; text-shadow:1px 1px #fff; padding:0; margin:0;"> | |||
| <div style="width:600px;margin:0 auto; padding:40px 0 20px;"> | |||
| <div style="border:1px solid #d9d9d9;border-radius:3px; background:#fff; box-shadow: 0px 2px 5px rgba(0, 0, 0,.05); -webkit-box-shadow: 0px 2px 5px rgba(0, 0, 0,.05);"> | |||
| <div style="padding: 20px 15px;"> | |||
| <h1 style="font-size:20px; padding:10px 0 20px; margin:0; border-bottom:1px solid #ddd;"><a style="color:#333;text-decoration:none;" target="_blank" href="{{.AppUrl}}">{{.AppName}}</a></h1> | |||
| <h1 style="font-size:20px; padding:10px 0 20px; margin:0; border-bottom:1px solid #ddd;"><img src="{{.AppUrl}}/{{.AppLogo}}" style="height: 32px; margin-bottom: -10px;"> <a style="color:#333;text-decoration:none;" target="_blank" href="{{.AppUrl}}">{{.AppName}}</a></h1> | |||
| <div style="padding:40px 15px;"> | |||
| <div style="font-size:16px; padding-bottom:30px; font-weight:bold;"> | |||
| {{.User.Name}}, welcome to {{.AppName}} | |||
| Hi <span style="color: #00BFFF;">{{.User.Name}}</span>, welcome to register {{.AppName}}! | |||
| </div> | |||
| <div style="font-size:14px; padding:0 15px;"> | |||
| <p style="margin:0;padding:0 0 9px 0;">Please click following link to verify your e-mail in {{.ActiveCodeLives}} hours</p> | |||
| <p style="margin:0;padding:0 0 9px 0;">Please click following link to verify your e-mail address within <b>{{.ActiveCodeLives}} hours</b>.</p> | |||
| <p style="margin:0;padding:0 0 9px 0;"> | |||
| <a href="{{.AppUrl}}activate/code={{.Code}}">{{.AppUrl}}active/{{.Code}}</a> | |||
| <a href="{{.AppUrl}}user/activate?code={{.Code}}">{{.AppUrl}}user/activate?code={{.Code}}</a> | |||
| </p> | |||
| <p style="margin:0;padding:0 0 9px 0;">Copy and paste it to your browser if it's not working.</p> | |||
| <p style="margin:0;padding:0 0 9px 0;">Copy and paste it to your browser if the link is not working.</p> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div style="color:#aaa;padding:10px;text-align:center;"> | |||
| © 2014 <a style="color:#888;text-decoration:none;" target="_blank" href="{{.AppUrl}}">{{AppName}}</a> | |||
| © 2014 <a style="color:#888;text-decoration:none;" target="_blank" href="http://gogits.org">Gogs: Go Git Service</a> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @@ -1,17 +1,23 @@ | |||
| {{template "base/head" .}} | |||
| {{template "base/navbar" .}} | |||
| <div id="gogs-body" class="container"> | |||
| <form action="/user/activate" method="post" class="form-horizontal gogs-card" id="gogs-login-card"> | |||
| <form action="/user/activate" method="get" class="form-horizontal gogs-card" id="gogs-login-card"> | |||
| <h3>Activate Your Account</h3> | |||
| {{if .IsActivatePage}} | |||
| {{if .ServiceNotEnabled}} | |||
| <p>Sorry, Register Mail Confirmation has been disabled.</p> | |||
| {{else}} | |||
| <p>New confirmation e-mail has been sent to <b>{{.SignedUser.Email}}</b>, please check your inbox within {{.Hours}} hours.</p> | |||
| <p>New confirmation e-mail has been sent to <b>{{.SignedUser.Email}}</b>, please check your inbox within {{.Hours}} hours to complete your registeration.</p> | |||
| <hr/> | |||
| <a href="http://{{Mail2Domain .SignedUser.Email}}" class="btn btn-lg btn-success">Sign in to your e-mail</a> | |||
| {{end}} | |||
| {{else}} | |||
| {{if .IsSendRegisterMail}} | |||
| <p>A confirmation e-mail has been sent to <b>{{.Email}}</b>, please check your inbox within {{.Hours}} hours.</p> | |||
| <p>A confirmation e-mail has been sent to <b>{{.Email}}</b>, please check your inbox within {{.Hours}} hours to complete your registeration.</p> | |||
| <hr/> | |||
| <a href="http://{{Mail2Domain .Email}}" class="btn btn-lg btn-success">Sign in to your e-mail</a> | |||
| {{else if .IsActivateFailed}} | |||
| <p>Sorry, your confirmation code has been exipired or not valid.</p> | |||
| {{else}} | |||
| <p>Hi, {{.SignedUser.Name}}, you have an unconfirmed email address(<b>{{.SignedUser.Email}}</b>). If you haven't received a confirmation e-mail or need to resend a new one, please click botton below.</p> | |||
| <hr/> | |||
| @@ -21,6 +21,7 @@ import ( | |||
| "github.com/gogits/gogs/modules/log" | |||
| "github.com/gogits/gogs/modules/middleware" | |||
| "github.com/gogits/gogs/routers" | |||
| "github.com/gogits/gogs/routers/dev" | |||
| "github.com/gogits/gogs/routers/repo" | |||
| "github.com/gogits/gogs/routers/user" | |||
| ) | |||
| @@ -113,6 +114,10 @@ func runWeb(*cli.Context) { | |||
| m.Get("/:username/:reponame", ignSignIn, middleware.RepoAssignment(true), repo.Single) | |||
| if martini.Env == martini.Dev { | |||
| m.Get("/template/**", dev.TemplatePreview) | |||
| } | |||
| listenAddr := fmt.Sprintf("%s:%s", | |||
| base.Cfg.MustValue("server", "HTTP_ADDR"), | |||
| base.Cfg.MustValue("server", "HTTP_PORT", "3000")) | |||