]> source.dussan.org Git - gitea.git/commitdiff
Add send register confirm mail
authorUnknown <joe2010xtmf@163.com>
Wed, 19 Mar 2014 12:27:27 +0000 (08:27 -0400)
committerUnknown <joe2010xtmf@163.com>
Wed, 19 Mar 2014 12:27:27 +0000 (08:27 -0400)
README.md
conf/app.ini
models/user.go
modules/auth/mail.go
modules/base/conf.go
modules/base/tool.go
modules/mailer/mail.go
modules/mailer/mailer.go [new file with mode: 0644]
routers/repo/repo.go
routers/user/user.go

index 08d97871897790b1e5375f280499a538eddacdb6..c0cd9cceb206209a779aadeddc57cb0e80a2e9eb 100644 (file)
--- a/README.md
+++ b/README.md
@@ -39,6 +39,7 @@ There are two ways to install Gogs:
 
 ## Acknowledgments
 
+- Mail service is based on [WeTalk](https://github.com/beego/wetalk).
 - Logo inspired by [martini](https://github.com/martini-contrib).
 
 ## Contributors
index 82d78d2334f982e93859a18d3a16f6840828c1f6..c2c299cf63f6fc6e2dac7c763db086256e9501b8 100644 (file)
@@ -35,14 +35,17 @@ SECRET_KEY = !#@FDEWREWR&*(
 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 = 
index 579f6a74861967de1c914eb7bb2e7e0af4216f7d..5f08f9e92d61bb5c77f1460f0fa180b401ded009 100644 (file)
@@ -105,19 +105,19 @@ func GetUserSalt() string {
 }
 
 // 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)
@@ -126,22 +126,17 @@ func RegisterUser(user *User) (err error) {
        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.
index 6f6bf20a069d870b305bd54e45dd9c48530f26ea..cdfcce4f99df6c034e11e33f0fb69d709c45e96f 100644 (file)
@@ -16,7 +16,7 @@ import (
 // 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
@@ -32,11 +32,10 @@ func SendRegisterMail(user *models.User) {
        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)
 }
index ee5638ed689c8df5cd9aee2f5a7fe547da9229ec..24ee1d7f069286ccddc6b031f7247325c36b8b80 100644 (file)
@@ -37,7 +37,7 @@ var (
 )
 
 var Service struct {
-       RegisterEmailConfitm bool
+       RegisterEmailConfirm bool
        ActiveCodeLives      int
        ResetPwdCodeLives    int
 }
@@ -138,7 +138,7 @@ func newRegisterService() {
                log.Warn("Register Service: Mail Service is not enabled")
                return
        }
-       Service.RegisterEmailConfitm = true
+       Service.RegisterEmailConfirm = true
        log.Info("Register Service Enabled")
 }
 
index 2a989b377d5349bfd87927ecb7ad05fd7bce8d10..fc3b4c45b8c51d8cdb47a68e3b74205fa969000a 100644 (file)
@@ -13,6 +13,7 @@ import (
        "encoding/json"
        "fmt"
        "math"
+       "strconv"
        "strings"
        "time"
 )
@@ -59,13 +60,14 @@ func CreateTimeLimitCode(data string, minutes int, startInf interface{}) string
 
        // 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)
@@ -300,6 +302,57 @@ func DateFormat(t time.Time, format string) string {
        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
index fe74af9eef4df72527e4ed66bbab974094333959..cc4fd6d0592d3e05bedda24ac9f2950a5d9a506f 100644 (file)
@@ -9,6 +9,13 @@ import (
        "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
diff --git a/modules/mailer/mailer.go b/modules/mailer/mailer.go
new file mode 100644 (file)
index 0000000..cc76acb
--- /dev/null
@@ -0,0 +1,112 @@
+// 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",
+       }
+}
index 08fe1ed15b5312086aba60649ea14867243d8ea6..61bf47c1f5bb835890ca86e34dda0ef12843934e 100644 (file)
@@ -50,10 +50,10 @@ func SettingPost(ctx *middleware.Context) {
 
                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)
 }
index 05aeac60ecfa5254afe231c4c0a9d9a9ed1386eb..2d6bcedce5e634d4bc376b95ac6ab20b7d2727ff 100644 (file)
@@ -134,10 +134,11 @@ func SignUp(ctx *middleware.Context, form auth.RegisterForm) {
                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)
@@ -150,6 +151,11 @@ func SignUp(ctx *middleware.Context, form auth.RegisterForm) {
        }
 
        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")
 }