## Acknowledgments
+- Mail service is based on [WeTalk](https://github.com/beego/wetalk).
- Logo inspired by [martini](https://github.com/martini-contrib).
## Contributors
ACTIVE_CODE_LIVE_MINUTES = 180
RESET_PASSWD_CODE_LIVE_MINUTES = 180
; User need to confirm e-mail for registration
-REGISTER_EMAIL_CONFIRM = true
+REGISTER_EMAIL_CONFIRM = false
[mailer]
ENABLED = false
; Name displayed in mail title
SUBJECT = %(APP_NAME)s
; Mail server
+; Gmail: smtp.gmail.com:587
HOST =
+; Mail from address
+FROM =
; Mailer user name and password
USER =
PASSWD =
}
// RegisterUser creates record of a new user.
-func RegisterUser(user *User) (err error) {
+func RegisterUser(user *User) (*User, error) {
isExist, err := IsUserExist(user.Name)
if err != nil {
- return err
+ return nil, err
} else if isExist {
- return ErrUserAlreadyExist
+ return nil, ErrUserAlreadyExist
}
isExist, err = IsEmailUsed(user.Email)
if err != nil {
- return err
+ return nil, err
} else if isExist {
- return ErrEmailAlreadyUsed
+ return nil, ErrEmailAlreadyUsed
}
user.LowerName = strings.ToLower(user.Name)
user.Expired = time.Now().Add(3 * 24 * time.Hour)
user.Rands = GetUserSalt()
if err = user.EncodePasswd(); err != nil {
- return err
+ return nil, err
} else if _, err = orm.Insert(user); err != nil {
- return err
+ return nil, err
} else if err = os.MkdirAll(UserPath(user.Name), os.ModePerm); err != nil {
if _, err := orm.Id(user.Id).Delete(&User{}); err != nil {
- return errors.New(fmt.Sprintf(
+ return nil, errors.New(fmt.Sprintf(
"both create userpath %s and delete table record faild: %v", user.Name, err))
}
- return err
- }
-
- // Send confirmation e-mail.
- if base.Service.RegisterEmailConfitm {
-
+ return nil, err
}
- return nil
+ return user, nil
}
// UpdateUser updates user's information.
// create a time limit code for user active
func CreateUserActiveCode(user *models.User, startInf interface{}) string {
hours := base.Service.ActiveCodeLives / 60
- data := fmt.Sprintf("%d", user.Id) + user.Email + user.LowerName + user.Passwd + user.Rands
+ data := base.ToStr(user.Id) + user.Email + user.LowerName + user.Passwd + user.Rands
code := base.CreateTimeLimitCode(data, hours, startInf)
// add tail hex username
data := mailer.GetMailTmplData(user)
data["Code"] = code
body := base.RenderTemplate("mail/auth/register_success.html", data)
- _, _, _ = code, subject, body
- // msg := mailer.NewMailMessage([]string{user.Email}, subject, body)
- // msg.Info = fmt.Sprintf("UID: %d, send register mail", user.Id)
+ msg := mailer.NewMailMessage([]string{user.Email}, subject, body)
+ msg.Info = fmt.Sprintf("UID: %d, send register mail", user.Id)
- // // async send mail
- // mailer.SendAsync(msg)
+ // async send mail
+ mailer.SendAsync(msg)
}
)
var Service struct {
- RegisterEmailConfitm bool
+ RegisterEmailConfirm bool
ActiveCodeLives int
ResetPwdCodeLives int
}
log.Warn("Register Service: Mail Service is not enabled")
return
}
- Service.RegisterEmailConfitm = true
+ Service.RegisterEmailConfirm = true
log.Info("Register Service Enabled")
}
"encoding/json"
"fmt"
"math"
+ "strconv"
"strings"
"time"
)
// create sha1 encode string
sh := sha1.New()
- sh.Write([]byte(data + SecretKey + startStr + endStr + fmt.Sprintf("%d", minutes)))
+ sh.Write([]byte(data + SecretKey + startStr + endStr + ToStr(minutes)))
encoded := hex.EncodeToString(sh.Sum(nil))
code := fmt.Sprintf("%s%06d%s", startStr, minutes, encoded)
return code
}
+// TODO:
func RenderTemplate(TplNames string, Data map[interface{}]interface{}) string {
// if beego.RunMode == "dev" {
// beego.BuildTemplate(beego.ViewsPath)
return t.Format(format)
}
+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
+}
+
+// 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 Actioner interface {
GetOpType() int
GetActUserName() string
"github.com/gogits/gogs/modules/base"
)
+// Create New mail message use MailFrom and MailUser
+func NewMailMessage(To []string, subject, body string) Message {
+ msg := NewHtmlMessage(To, base.MailService.User, subject, body)
+ msg.User = base.MailService.User
+ return msg
+}
+
func GetMailTmplData(user *models.User) map[interface{}]interface{} {
data := make(map[interface{}]interface{}, 10)
data["AppName"] = base.AppName
--- /dev/null
+// 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 mailer
+
+import (
+ "fmt"
+ "net/smtp"
+ "strings"
+
+ "github.com/gogits/gogs/modules/base"
+ "github.com/gogits/gogs/modules/log"
+)
+
+type Message struct {
+ To []string
+ From string
+ Subject string
+ Body string
+ User string
+ Type string
+ Massive bool
+ Info string
+}
+
+// create mail content
+func (m Message) Content() string {
+ // set mail type
+ contentType := "text/plain; charset=UTF-8"
+ if m.Type == "html" {
+ contentType = "text/html; charset=UTF-8"
+ }
+
+ // create mail content
+ content := "From: " + m.User + "<" + m.From +
+ ">\r\nSubject: " + m.Subject + "\r\nContent-Type: " + contentType + "\r\n\r\n" + m.Body
+ return content
+}
+
+// Direct Send mail message
+func Send(msg Message) (int, error) {
+ log.Trace("Sending mails to: %s", strings.Join(msg.To, "; "))
+ host := strings.Split(base.MailService.Host, ":")
+
+ // get message body
+ content := msg.Content()
+
+ auth := smtp.PlainAuth("", base.MailService.User, base.MailService.Passwd, host[0])
+
+ if len(msg.To) == 0 {
+ return 0, fmt.Errorf("empty receive emails")
+ }
+
+ if len(msg.Body) == 0 {
+ return 0, fmt.Errorf("empty email body")
+ }
+
+ if msg.Massive {
+ // send mail to multiple emails one by one
+ num := 0
+ for _, to := range msg.To {
+ body := []byte("To: " + to + "\r\n" + content)
+ err := smtp.SendMail(base.MailService.Host, auth, msg.From, []string{to}, body)
+ if err != nil {
+ return num, err
+ }
+ num++
+ }
+ return num, nil
+ } else {
+ body := []byte("To: " + strings.Join(msg.To, ";") + "\r\n" + content)
+
+ // send to multiple emails in one message
+ err := smtp.SendMail(base.MailService.Host, auth, msg.From, msg.To, body)
+ if err != nil {
+ return 0, err
+ } else {
+ return 1, nil
+ }
+ }
+}
+
+// Async Send mail message
+func SendAsync(msg Message) {
+ // TODO may be need pools limit concurrent nums
+ go func() {
+ num, err := Send(msg)
+ tos := strings.Join(msg.To, "; ")
+ info := ""
+ if err != nil {
+ if len(msg.Info) > 0 {
+ info = ", info: " + msg.Info
+ }
+ // log failed
+ log.Error(fmt.Sprintf("Async sent email %d succeed, not send emails: %s%s err: %s", num, tos, info, err))
+ return
+ }
+ log.Trace(fmt.Sprintf("Async sent email %d succeed, sent emails: %s%s", num, tos, info))
+ }()
+}
+
+// Create html mail message
+func NewHtmlMessage(To []string, From, Subject, Body string) Message {
+ return Message{
+ To: To,
+ From: From,
+ Subject: Subject,
+ Body: Body,
+ Type: "html",
+ }
+}
if err := models.DeleteRepository(ctx.User.Id, ctx.Repo.Repository.Id, ctx.User.LowerName); err != nil {
ctx.Handle(200, "repo.Delete", err)
- log.Trace("%s Repository deleted: %s/%s", ctx.Req.RequestURI, ctx.User.LowerName, ctx.Repo.Repository.LowerName)
return
}
}
+ log.Trace("%s Repository deleted: %s/%s", ctx.Req.RequestURI, ctx.User.LowerName, ctx.Repo.Repository.LowerName)
ctx.Render.Redirect("/", 302)
}
Name: form.UserName,
Email: form.Email,
Passwd: form.Password,
- IsActive: !base.Service.RegisterEmailConfitm,
+ IsActive: !base.Service.RegisterEmailConfirm,
}
- if err := models.RegisterUser(u); err != nil {
+ var err error
+ if u, err = models.RegisterUser(u); err != nil {
switch err.Error() {
case models.ErrUserAlreadyExist.Error():
ctx.RenderWithErr("Username has been already taken", "user/signup", &form)
}
log.Trace("%s User created: %s", ctx.Req.RequestURI, strings.ToLower(form.UserName))
+
+ // Send confirmation e-mail.
+ if base.Service.RegisterEmailConfirm {
+ auth.SendRegisterMail(u)
+ }
ctx.Render.Redirect("/user/login")
}