123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- package main
- import (
- "fmt"
- "github.com/garyburd/redigo/redis"
- "github.com/ziutek/mymysql/autorc"
- _ "github.com/ziutek/mymysql/godrv"
- "log"
- "strconv"
- "time"
- "errors"
- )
- type savePayload struct {
- client *Client
- server *SmtpdServer
- }
- var SaveMailChan chan *savePayload // workers for saving mail
- type redisClient struct {
- count int
- conn redis.Conn
- time int
- }
- func saveMail() {
- var to, recipient, body string
- var err error
- var redis_err error
- var length int
- redisClient := &redisClient{}
- db := autorc.New(
- "tcp",
- "",
- mainConfig.Mysql_host,
- mainConfig.Mysql_user,
- mainConfig.Mysql_pass,
- mainConfig.Mysql_db)
- db.Register("set names utf8")
- sql := "INSERT INTO " + mainConfig.Mysql_table + " "
- sql += "(`date`, `to`, `from`, `subject`, `body`, `charset`, `mail`, `spam_score`, `hash`, `content_type`, `recipient`, `has_attach`, `ip_addr`, `return_path`, `is_tls`)"
- sql += " values (NOW(), ?, ?, ?, ? , 'UTF-8' , ?, 0, ?, '', ?, 0, ?, ?, ?)"
- ins, sql_err := db.Prepare(sql)
- if sql_err != nil {
- log.Fatalf(fmt.Sprintf("Sql statement incorrect: %s\n", sql_err))
- }
- sql = "UPDATE gm2_setting SET `setting_value` = `setting_value`+1 WHERE `setting_name`='received_emails' LIMIT 1"
- incr, sql_err := db.Prepare(sql)
- if sql_err != nil {
- log.Fatalf(fmt.Sprintf("Sql statement incorrect: %s\n", sql_err))
- }
- // receives values from the channel repeatedly until it is closed.
- for {
- payload := <-SaveMailChan
- if user, host, addr_err := validateEmailData(payload.client); addr_err != nil {
- payload.server.logln(1, fmt.Sprintf("mail_from didnt validate: %v", addr_err)+" client.mail_from:"+payload.client.mail_from)
- // notify client that a save completed, -1 = error
- payload.client.savedNotify <- -1
- continue
- } else {
- recipient = user + "@" + host
- to = user + "@" + mainConfig.Primary_host
- }
- length = len(payload.client.data)
- ts := strconv.FormatInt(time.Now().UnixNano(), 10);
- payload.client.subject = mimeHeaderDecode(payload.client.subject)
- payload.client.hash = md5hex(
- &to,
- &payload.client.mail_from,
- &payload.client.subject,
- &ts)
- // Add extra headers
- add_head := ""
- add_head += "Delivered-To: " + to + "\r\n"
- add_head += "Received: from " + payload.client.helo + " (" + payload.client.helo + " [" + payload.client.address + "])\r\n"
- add_head += " by " + payload.server.Config.Host_name + " with SMTP id " + payload.client.hash + "@" +
- payload.server.Config.Host_name + ";\r\n"
- add_head += " " + time.Now().Format(time.RFC1123Z) + "\r\n"
- // compress to save space
- payload.client.data = compress(&add_head, &payload.client.data)
- body = "gzencode"
- redis_err = redisClient.redisConnection()
- if redis_err == nil {
- _, do_err := redisClient.conn.Do("SETEX", payload.client.hash, mainConfig.Redis_expire_seconds, payload.client.data)
- if do_err == nil {
- payload.client.data = ""
- body = "redis"
- }
- } else {
- payload.server.logln(1, fmt.Sprintf("redis: %v", redis_err))
- }
- // bind data to cursor
- ins.Bind(
- to,
- payload.client.mail_from,
- payload.client.subject,
- body,
- payload.client.data,
- payload.client.hash,
- recipient,
- payload.client.address,
- payload.client.mail_from,
- payload.client.tls_on,
- )
- // save, discard result
- _, _, err = ins.Exec()
- if err != nil {
- payload.server.logln(1, fmt.Sprintf("Database error, %v ", err))
- payload.client.savedNotify <- -1
- } else {
- payload.server.logln(0, "Email saved "+payload.client.hash+" len:"+strconv.Itoa(length))
- _, _, err = incr.Exec()
- if err != nil {
- payload.server.logln(1, fmt.Sprintf("Failed to incr count: %v", err))
- }
- payload.client.savedNotify <- 1
- }
- }
- }
- func (c *redisClient) redisConnection() (err error) {
- if c.count == 0 {
- c.conn, err = redis.Dial("tcp", mainConfig.Redis_interface)
- if err != nil {
- // handle error
- return err
- }
- }
- return nil
- }
- // test database connection settings
- func testDbConnections() (err error) {
- db := autorc.New(
- "tcp",
- "",
- mainConfig.Mysql_host,
- mainConfig.Mysql_user,
- mainConfig.Mysql_pass,
- mainConfig.Mysql_db)
- if mysql_err := db.Raw.Connect(); mysql_err != nil {
- err = errors.New("MySql cannot connect, check your settings. " + mysql_err.Error() )
- } else {
- db.Raw.Close();
- }
- redisClient := &redisClient{}
- if redis_err := redisClient.redisConnection(); redis_err != nil {
- err = errors.New("Redis cannot connect, check your settings. " + redis_err.Error())
- }
- return
- }
|