func Run() { l, err := os.OpenFile(models.FromRootDir("log/utm.log"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) if err != nil { log.Println("error opening utm log file: %v", err) } defer l.Close() multi := io.MultiWriter(l, os.Stdout) utmlog = log.New(multi, "", log.Ldate|log.Ltime) utm := http.NewServeMux() utm.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { mem := new(runtime.MemStats) runtime.ReadMemStats(mem) w.Write([]byte("Welcome to San Tropez! (Conn: " + strconv.Itoa(models.Db.Stats().OpenConnections) + " Allocate: " + strconv.FormatUint(mem.Alloc, 10) + ")")) }) utm.HandleFunc("/robots.txt", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain; charset=utf-8") w.Write([]byte("# " + models.Config.Version + "\nUser-agent: *\nDisallow: /data/\nDisallow: /files/\nDisallow: /unsubscribe/\nDisallow: /unsubscribe\nDisallow: /redirect/\nDisallow: /web/\nDisallow: /open/\n")) }) utm.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "image/x-icon") ico, _ := base64.StdEncoding.DecodeString("AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAABILAAASCwAAAAAAAAAAAAByGSL/chki/3IZIv9yGSL/chki/3IZIv9yGSL/chki/3IZIv9yGSL/chki/3IZIv9yGSL/chki/3IZIv9yGSL/chki/8q2uP9yGSL/yra4/3IZIv/Ktrj/yra4/3IZIv9yGSL/yra4/8q2uP9yGSL/yra4/3IZIv/Ktrj/chki/3IZIv/Ktrj/chki/+je3/9yGSL/yra4/3IZIv/Ktrj/chki/8q2uP9yGSL/chki/8q2uP9yGSL/yra4/3IZIv9yGSL/yra4/+je3//Ktrj/chki/8q2uP9yGSL/yra4/3IZIv/Ktrj/yra4/3IZIv/Ktrj/yra4/3IZIv9yGSL/chki/+je3/9yGSL/yra4/3IZIv/Ktrj/chki/8q2uP9yGSL/yra4/3IZIv9yGSL/yra4/3IZIv/Ktrj/chki/3IZIv/Ktrj/chki/8q2uP9yGSL/yra4/8q2uP9yGSL/chki/8q2uP/Ktrj/chki/8q2uP/Ktrj/yra4/3IZIv9yGSL/chki/3IZIv9yGSL/chki/3IZIv9yGSL/chki/3IZIv9yGSL/chki/3IZIv9yGSL/chki/3IZIv9yGSL/chki/+je3//o3t//6N7f/+je3//o3t//6N7f/+je3/9yGSL/6N7f/+je3//o3t//6N7f/+je3//o3t//chki/3IZIv/o3t//yra4/8q2uP/Ktrj/yra4/8q2uP/Ktrj/chki/8q2uP/Ktrj/yra4/8q2uP/Ktrj/6N7f/3IZIv9yGSL/6N7f/8q2uP9yGSL/chki/3IZIv/Ktrj/6N7f/3IZIv/Ktrj/yra4/3IZIv9yGSL/yra4/+je3/9yGSL/chki/+je3//Ktrj/chki/8q2uP/o3t//6N7f/8q2uP9yGSL/6N7f/+je3/9yGSL/chki/8q2uP/o3t//chki/3IZIv/o3t//yra4/3IZIv/o3t//yra4/8q2uP/Ktrj/chki/8q2uP/Ktrj/chki/3IZIv/Ktrj/6N7f/3IZIv9yGSL/6N7f/+je3/9yGSL/chki/3IZIv9yGSL/chki/3IZIv/Ktrj/yra4/8q2uP/Ktrj/6N7f/+je3/9yGSL/chki/+je3//o3t//6N7f/+je3//o3t//6N7f/+je3/9yGSL/6N7f/+je3//o3t//6N7f/+je3//o3t//chki/3IZIv/Ktrj/yra4/8q2uP/Ktrj/yra4/8q2uP/Ktrj/chki/3IZIv9yGSL/chki/3IZIv9yGSL/chki/3IZIv9yGSL/chki/3IZIv9yGSL/chki/3IZIv9yGSL/chki/3IZIv9yGSL/chki/3IZIv9yGSL/chki/3IZIv9yGSL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==") w.Write(ico) }) utm.Handle("/files/", http.StripPrefix("/files/", http.FileServer(http.Dir(models.FromRootDir("files/"))))) utm.HandleFunc("/unsubscribe/", func(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { w.Header().Set("Content-Type", "text/html; charset=utf-8") message, data, err := models.DecodeData(strings.Split(r.URL.Path, "/")[2]) if err != nil { utmlog.Println(err) http.Error(w, "", http.StatusInternalServerError) return } if data == "mail" { if err := message.Unsubscribe(map[string]string{"Unsubscribed": "from header link"}); err != nil { utmlog.Println(err) http.Error(w, "", http.StatusInternalServerError) return } else { if t, err := template.ParseFiles(message.UnsubscribeTemplateDir() + "/success.html"); err != nil { utmlog.Println(err) http.Error(w, "", http.StatusInternalServerError) return } else { t.Execute(w, map[string]string{ "campaignId": message.CampaignId, "recipientId": message.RecipientId, }) } } } if data == "web" { if t, err := template.ParseFiles(message.UnsubscribeTemplateDir() + "/accept.html"); err != nil { utmlog.Println(err) http.Error(w, "", http.StatusInternalServerError) return } else { t.Execute(w, map[string]string{ "campaignId": message.CampaignId, "recipientId": message.RecipientId, }) } } } }) utm.HandleFunc("/unsubscribe", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html; charset=utf-8") if r.Method == "POST" { var message models.Message err := message.New(r.PostFormValue("recipientId")) if err != nil { utmlog.Println(err) http.Error(w, "", http.StatusInternalServerError) return } if message.CampaignId != r.PostFormValue("campaignId") { utmlog.Println(err) http.Error(w, "Not valid request", http.StatusInternalServerError) return } if t, err := template.ParseFiles(message.UnsubscribeTemplateDir() + "/success.html"); err != nil { utmlog.Println(err) http.Error(w, "", http.StatusInternalServerError) return } else { var extra = map[string]string{} for name, value := range r.PostForm { if name != "campaignId" && name != "recipientId" && name != "unsubscribe" { extra[name] = strings.Join(value, "|") } } if err := message.Unsubscribe(extra); err != nil { utmlog.Println(err) http.Error(w, "", http.StatusInternalServerError) return } t.Execute(w, map[string]string{ "campaignId": message.CampaignId, "recipientId": message.RecipientId, }) } } }) utm.HandleFunc("/redirect/", func(w http.ResponseWriter, r *http.Request) { message, data, err := models.DecodeData(strings.Split(r.URL.Path, "/")[2]) if err != nil { utmlog.Println(err) http.Error(w, "", http.StatusInternalServerError) return } models.Db.Exec("INSERT INTO jumping (campaign_id, recipient_id, url) VALUES (?, ?, ?)", message.CampaignId, message.RecipientId, data) models.Db.Exec("UPDATE `recipient` SET `web_agent`= ? WHERE `id`=? AND `web_agent` IS NULL", models.GetIP(r)+" "+r.UserAgent(), message.RecipientId) url := regexp.MustCompile(`\s*?(\[.*?\])\s*?`).Split(data, 2) http.Redirect(w, r, strings.TrimSpace(url[len(url)-1]), http.StatusFound) }) utm.HandleFunc("/web/", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html; charset=utf-8") message, _, err := models.DecodeData(strings.Split(r.URL.Path, "/")[2]) if err != nil { utmlog.Println(err) http.Error(w, "", http.StatusInternalServerError) return } models.Db.Exec("INSERT INTO jumping (campaign_id, recipient_id, url) VALUES (?, ?, 'web_version')", message.CampaignId, message.RecipientId) models.Db.Exec("UPDATE `recipient` SET `web_agent`= ? WHERE `id`=? AND `web_agent` IS NULL", models.GetIP(r)+" "+r.UserAgent(), message.RecipientId) data, err := message.RenderMessage() if err != nil { utmlog.Println(err) http.Error(w, "", http.StatusInternalServerError) return } w.Write([]byte(data)) }) utm.HandleFunc("/open/", func(w http.ResponseWriter, r *http.Request) { message, _, err := models.DecodeData(strings.Split(r.URL.Path, "/")[2]) if err != nil { utmlog.Println(err) http.Error(w, "", http.StatusInternalServerError) return } models.Db.Exec("INSERT INTO jumping (campaign_id, recipient_id, url) VALUES (?, ?, 'open_trace')", message.CampaignId, message.RecipientId) models.Db.Exec("UPDATE `recipient` SET `client_agent`= ? WHERE `id`=? AND `client_agent` IS NULL", models.GetIP(r)+" "+r.UserAgent(), message.RecipientId) //w.Header().Set("Content-Type", "image/png") //png, _ := base64.StdEncoding.DecodeString("iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAADUExURUxpcU3H2DoAAAABdFJOUwBA5thmAAAADUlEQVQY02NgGAXIAAABEAAB7JfjegAAAABJRU5ErkJggg==iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAADUExURUxpcU3H2DoAAAABdFJOUwBA5thmAAAAEklEQVQ4y2NgGAWjYBSMAuwAAAQgAAFWu83mAAAAAElFTkSuQmCC") //w.Write(png) w.Header().Set("Content-Type", "image/gif") gif, _ := base64.StdEncoding.DecodeString("R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7") w.Write(gif) }) // Old statistic todo delete later utm.HandleFunc("/data/", func(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { var param *oldJson data, err := base64.URLEncoding.DecodeString(strings.Split(strings.Split(r.URL.Path, "/")[2], ".")[0]) if err != nil { utmlog.Println(err) http.Error(w, "", http.StatusInternalServerError) return } err = json.Unmarshal([]byte(data), ¶m) if err != nil { utmlog.Println(err) http.Error(w, "", http.StatusInternalServerError) return } userAgent := models.GetIP(r) + " " + r.UserAgent() if param.Opened != "" { models.Db.Exec("INSERT INTO jumping (campaign_id, recipient_id, url) VALUES (?, ?, ?)", param.Campaign, param.Recipient, "open_trace") models.Db.Exec("UPDATE `recipient` SET `client_agent`= ? WHERE `id`=? AND `client_agent` IS NULL", userAgent, param.Recipient) w.Header().Set("Content-Type", "image/gif") gif, _ := base64.StdEncoding.DecodeString("R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7") w.Write(gif) } else if param.Url != "" { models.Db.Exec("INSERT INTO jumping (campaign_id, recipient_id, url) VALUES (?, ?, ?)", param.Campaign, param.Recipient, param.Url) models.Db.Exec("UPDATE `recipient` SET `web_agent`= ? WHERE `id`=? AND `web_agent` IS NULL", userAgent, param.Recipient) http.Redirect(w, r, param.Url, http.StatusFound) } else if param.Webver != "" { w.Header().Set("Content-Type", "text/html; charset=utf-8") models.Db.Exec("INSERT INTO jumping (campaign_id, recipient_id, url) VALUES (?, ?, ?)", param.Campaign, param.Recipient, "web_version") models.Db.Exec("UPDATE `recipient` SET `web_agent`= ? WHERE `id`=? AND `web_agent` IS NULL", userAgent, param.Recipient) var m models.Message m.New(param.Recipient) message, err := m.RenderMessage() if err != nil { utmlog.Println(err) http.Error(w, "", http.StatusInternalServerError) return } w.Write([]byte(message)) } else if param.Unsubscribe != "" { w.Header().Set("Content-Type", "text/html; charset=utf-8") var name string models.Db.QueryRow("SELECT `group`.`template` FROM `campaign` INNER JOIN `group` ON `campaign`.`group_id`=`group`.`id` WHERE `group`.`template` IS NOT NULL AND `campaign`.`id`=?", param.Campaign).Scan(&name) if name == "" { name = "default" } else { if _, err := os.Stat(models.FromRootDir("templates/" + name + "/accept.html")); err != nil { name = "default" } if _, err := os.Stat(models.FromRootDir("templates/" + name + "/success.html")); err != nil { name = "default" } } if t, err := template.ParseFiles(models.FromRootDir("templates/" + name + "/accept.html")); err != nil { utmlog.Println(err) http.Error(w, "", http.StatusInternalServerError) return } else { t.Execute(w, map[string]string{ "campaignId": param.Campaign, "recipientId": param.Recipient, }) } } } }) // /Old statistic todo delete later utmlog.Println("UTM listening on port " + models.Config.StatPort + "...") utmlog.Fatal(http.ListenAndServe(":"+models.Config.StatPort, muxLog(utm))) }
// Send mail for recipient this campaign data func (r recipient) send(c *campaign, iface, host string) string { start := time.Now() data := new(MailData) data.Iface, data.Host = iface, host data.From_email = c.from_email data.From_name = c.from_name data.Attachments = c.attachments data.To_email = r.to_email data.To_name = r.to_name message := new(models.Message) message.CampaignId = c.id message.RecipientId = r.id message.CampaignSubject = c.subject message.CampaignTemplate = c.body message.RecipientEmail = r.to_email message.RecipientName = r.to_name var rs string m, e := message.RenderMessage() if e == nil { data.Subject = message.CampaignSubject data.Html = m var extraHeader bytes.Buffer extraHeader.WriteString("List-Unsubscribe: " + message.UnsubscribeMailLink() + "\nPrecedence: bulk\n") extraHeader.WriteString("Message-ID: <" + strconv.FormatInt(time.Now().Unix(), 10) + c.id + "." + r.id + "@" + data.Host + ">" + "\n") extraHeader.WriteString("X-Postmaster-Msgtype: campaign" + c.id + "\n") data.Extra_header = extraHeader.String() var res error if models.Config.RealSend { // Send mail res = data.Send() } else { wait := time.Duration(rand.Int()/10000000000) * time.Nanosecond time.Sleep(wait) res = errors.New("Test send") } if res == nil { rs = "Ok" } else { rs = res.Error() } } else { rs = e.Error() } camplog.Printf("Campaign %s for recipient id %s email %s is %s send time %s", c.id, r.id, data.To_email, rs, time.Since(start)) return rs }