goguerrilla.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729
  1. /**
  2. Go-Guerrilla SMTPd
  3. Version: 1.2
  4. Author: Flashmob, GuerrillaMail.com
  5. Contact: [email protected]
  6. License: MIT
  7. Repository: https://github.com/flashmob/Go-Guerrilla-SMTPd
  8. Site: http://www.guerrillamail.com/
  9. See README for more details
  10. */
  11. package main
  12. import (
  13. "bufio"
  14. "bytes"
  15. "compress/zlib"
  16. "crypto/md5"
  17. "crypto/rand"
  18. "crypto/tls"
  19. "encoding/base64"
  20. "encoding/hex"
  21. "encoding/json"
  22. "errors"
  23. "flag"
  24. "fmt"
  25. "github.com/garyburd/redigo/redis"
  26. "github.com/sloonz/go-iconv"
  27. "github.com/sloonz/go-qprintable"
  28. "github.com/ziutek/mymysql/autorc"
  29. _ "github.com/ziutek/mymysql/godrv"
  30. "io"
  31. "io/ioutil"
  32. "log"
  33. "net"
  34. "net/http"
  35. "os"
  36. "os/signal"
  37. "regexp"
  38. "runtime"
  39. "strconv"
  40. "strings"
  41. "syscall"
  42. "time"
  43. )
  44. type Client struct {
  45. state int
  46. helo string
  47. mail_from string
  48. rcpt_to string
  49. read_buffer string
  50. response string
  51. address string
  52. data string
  53. subject string
  54. hash string
  55. time int64
  56. tls_on bool
  57. conn net.Conn
  58. bufin *bufio.Reader
  59. bufout *bufio.Writer
  60. kill_time int64
  61. errors int
  62. clientId int64
  63. savedNotify chan int
  64. }
  65. var TLSconfig *tls.Config
  66. var max_size int // max email DATA size
  67. var timeout time.Duration
  68. var allowedHosts = make(map[string]bool, 15)
  69. var sem chan int // currently active clients
  70. var signalChannel = make(chan os.Signal, 1)
  71. var SaveMailChan chan *Client // workers for saving mail
  72. var flagVerbouse, flagIface, flagConfigFile string;
  73. // defaults. Overwrite any of these in the configure() function which loads them from a json file
  74. var gConfig = map[string]string{
  75. "GSMTP_MAX_SIZE": "131072",
  76. "GSMTP_HOST_NAME": "server.example.com", // This should also be set to reflect your RDNS
  77. "GSMTP_VERBOSE": "Y",
  78. "GSMTP_LOG_FILE": "", // Eg. /var/log/goguerrilla.log or leave blank if no logging
  79. "GSMTP_TIMEOUT": "100", // how many seconds before timeout.
  80. "MYSQL_HOST": "127.0.0.1:3306",
  81. "MYSQL_USER": "gmail_mail",
  82. "MYSQL_PASS": "ok",
  83. "MYSQL_DB": "gmail_mail",
  84. "GM_MAIL_TABLE": "new_mail",
  85. "GSTMP_LISTEN_INTERFACE": "0.0.0.0:25",
  86. "GSMTP_PUB_KEY": "/etc/ssl/certs/ssl-cert-snakeoil.pem",
  87. "GSMTP_PRV_KEY": "/etc/ssl/private/ssl-cert-snakeoil.key",
  88. "GM_ALLOWED_HOSTS": "guerrillamail.de,guerrillamailblock.com",
  89. "GM_PRIMARY_MAIL_HOST": "guerrillamail.com",
  90. "GM_MAX_CLIENTS": "500",
  91. "NGINX_AUTH_ENABLED": "N", // Y or N
  92. "NGINX_AUTH": "127.0.0.1:8025", // If using Nginx proxy, ip and port to serve Auth requsts
  93. "PID_FILE": "/var/run/go-guerrilla.pid",
  94. }
  95. type redisClient struct {
  96. count int
  97. conn redis.Conn
  98. time int
  99. }
  100. func logln(level int, s string) {
  101. if gConfig["GSMTP_VERBOSE"] == "Y" {
  102. fmt.Println(s)
  103. }
  104. if level == 2 {
  105. log.Fatalf(s)
  106. }
  107. if len(gConfig["GSMTP_LOG_FILE"]) > 0 {
  108. log.Println(s)
  109. }
  110. }
  111. func readConfig() {
  112. log.SetOutput(os.Stdout)
  113. // parse command line arguments
  114. if !flag.Parsed() {
  115. flag.StringVar(&flagConfigFile, "config", "goguerrilla.conf", "Path to the configuration file")
  116. flag.StringVar(&flagVerbouse, "v", "n", "Verbose, [y | n] ")
  117. flag.StringVar(&flagIface, "if", "127.0.0.1:2525", "Interface and port to listen on, eg. 127.0.0.1:2525 ")
  118. flag.Parse()
  119. }
  120. // load in the config.
  121. b, err := ioutil.ReadFile(flagConfigFile)
  122. if err != nil {
  123. log.Fatalln("Could not read config file", err)
  124. }
  125. var myConfig map[string]string
  126. err = json.Unmarshal(b, &myConfig)
  127. if err != nil {
  128. log.Fatalln("Could not parse config file:", err)
  129. }
  130. for k, v := range myConfig {
  131. gConfig[k] = v
  132. }
  133. // copy command line flag over so it takes precedence
  134. if len(flagVerbouse) > 0 {
  135. gConfig["GSMTP_VERBOSE"] = strings.ToUpper(flagVerbouse)
  136. }
  137. if len(flagIface) > 0 {
  138. gConfig["GSTMP_LISTEN_INTERFACE"] = flagIface
  139. }
  140. // map the allow hosts for easy lookup
  141. if arr := strings.Split(gConfig["GM_ALLOWED_HOSTS"], ","); len(arr) > 0 {
  142. for i := 0; i < len(arr); i++ {
  143. allowedHosts[arr[i]] = true
  144. }
  145. }
  146. var n int
  147. var n_err error
  148. // timeout for reads
  149. if n, n_err = strconv.Atoi(gConfig["GSMTP_TIMEOUT"]); n_err != nil {
  150. timeout = time.Duration(10)
  151. } else {
  152. timeout = time.Duration(n)
  153. }
  154. // max email size
  155. if max_size, n_err = strconv.Atoi(gConfig["GSMTP_MAX_SIZE"]); n_err != nil {
  156. max_size = 131072
  157. }
  158. return
  159. }
  160. func sigHandler() {
  161. for range signalChannel {
  162. readConfig()
  163. fmt.Printf("Reloading Configuration!\n")
  164. }
  165. }
  166. func initialise() {
  167. var n int
  168. var n_err error
  169. // sem is an active clients channel used for counting clients
  170. if n, n_err = strconv.Atoi(gConfig["GM_MAX_CLIENTS"]); n_err != nil {
  171. n = 50
  172. }
  173. // currently active client list
  174. sem = make(chan int, n)
  175. // database writing workers
  176. SaveMailChan = make(chan *Client, 5)
  177. // custom log file
  178. if len(gConfig["GSMTP_LOG_FILE"]) > 0 {
  179. logfile, err := os.OpenFile(gConfig["GSMTP_LOG_FILE"], os.O_WRONLY|os.O_APPEND|os.O_CREATE|os.O_SYNC, 0600)
  180. if err != nil {
  181. log.Fatal("Unable to open log file ["+gConfig["GSMTP_LOG_FILE"]+"]: ", err)
  182. }
  183. log.SetOutput(logfile)
  184. }
  185. if f, err := os.Create(gConfig["PID_FILE"]); err == nil {
  186. defer f.Close()
  187. if _, err := f.WriteString(strconv.Itoa(os.Getpid())); err == nil {
  188. f.Sync()
  189. }
  190. }
  191. // handle SIGHUP for reloading the configuration while running
  192. signal.Notify(signalChannel, syscall.SIGHUP)
  193. go sigHandler()
  194. return
  195. }
  196. func main() {
  197. readConfig()
  198. initialise()
  199. cert, err := tls.LoadX509KeyPair(gConfig["GSMTP_PUB_KEY"], gConfig["GSMTP_PRV_KEY"])
  200. if err != nil {
  201. logln(2, fmt.Sprintf("There was a problem with loading the certificate: %s", err))
  202. }
  203. TLSconfig = &tls.Config{Certificates: []tls.Certificate{cert}, ClientAuth: tls.VerifyClientCertIfGiven, ServerName: gConfig["GSMTP_HOST_NAME"]}
  204. TLSconfig.Rand = rand.Reader
  205. // start some savemail workers
  206. for i := 0; i < 3; i++ {
  207. go saveMail()
  208. }
  209. if gConfig["NGINX_AUTH_ENABLED"] == "Y" {
  210. go nginxHTTPAuth()
  211. }
  212. // Start listening for SMTP connections
  213. listener, err := net.Listen("tcp", gConfig["GSTMP_LISTEN_INTERFACE"])
  214. if err != nil {
  215. logln(2, fmt.Sprintf("Cannot listen on port, %v", err))
  216. } else {
  217. logln(1, fmt.Sprintf("Listening on tcp %s", gConfig["GSTMP_LISTEN_INTERFACE"]))
  218. }
  219. var clientId int64
  220. clientId = 1
  221. for {
  222. conn, err := listener.Accept()
  223. if err != nil {
  224. logln(1, fmt.Sprintf("Accept error: %s", err))
  225. continue
  226. }
  227. logln(1, fmt.Sprintf(" There are now "+strconv.Itoa(runtime.NumGoroutine())+" serving goroutines"))
  228. sem <- 1 // Wait for active queue to drain.
  229. go handleClient(&Client{
  230. conn: conn,
  231. address: conn.RemoteAddr().String(),
  232. time: time.Now().Unix(),
  233. bufin: bufio.NewReader(conn),
  234. bufout: bufio.NewWriter(conn),
  235. clientId: clientId,
  236. savedNotify: make(chan int),
  237. })
  238. clientId++
  239. }
  240. }
  241. func handleClient(client *Client) {
  242. defer closeClient(client)
  243. // defer closeClient(client)
  244. greeting := "220 " + gConfig["GSMTP_HOST_NAME"] +
  245. " SMTP Guerrilla-SMTPd #" + strconv.FormatInt(client.clientId, 10) + " (" + strconv.Itoa(len(sem)) + ") " + time.Now().Format(time.RFC1123Z)
  246. advertiseTls := "250-STARTTLS\r\n"
  247. for i := 0; i < 100; i++ {
  248. switch client.state {
  249. case 0:
  250. responseAdd(client, greeting)
  251. client.state = 1
  252. case 1:
  253. input, err := readSmtp(client)
  254. if err != nil {
  255. logln(1, fmt.Sprintf("Read error: %v", err))
  256. if err == io.EOF {
  257. // client closed the connection already
  258. return
  259. }
  260. if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
  261. // too slow, timeout
  262. return
  263. }
  264. break
  265. }
  266. input = strings.Trim(input, " \n\r")
  267. cmd := strings.ToUpper(input)
  268. switch {
  269. case strings.Index(cmd, "HELO") == 0:
  270. if len(input) > 5 {
  271. client.helo = input[5:]
  272. }
  273. responseAdd(client, "250 "+gConfig["GSMTP_HOST_NAME"]+" Hello ")
  274. case strings.Index(cmd, "EHLO") == 0:
  275. if len(input) > 5 {
  276. client.helo = input[5:]
  277. }
  278. responseAdd(client, "250-"+gConfig["GSMTP_HOST_NAME"]+" Hello "+client.helo+"["+client.address+"]"+"\r\n"+"250-SIZE "+gConfig["GSMTP_MAX_SIZE"]+"\r\n"+advertiseTls+"250 HELP")
  279. case strings.Index(cmd, "MAIL FROM:") == 0:
  280. if len(input) > 10 {
  281. client.mail_from = input[10:]
  282. }
  283. responseAdd(client, "250 Ok")
  284. case strings.Index(cmd, "XCLIENT") == 0:
  285. // Nginx sends this
  286. // XCLIENT ADDR=212.96.64.216 NAME=[UNAVAILABLE]
  287. client.address = input[13:]
  288. client.address = client.address[0:strings.Index(client.address, " ")]
  289. fmt.Println("client address:[" + client.address + "]")
  290. responseAdd(client, "250 OK")
  291. case strings.Index(cmd, "RCPT TO:") == 0:
  292. if len(input) > 8 {
  293. client.rcpt_to = input[8:]
  294. }
  295. responseAdd(client, "250 Accepted")
  296. case strings.Index(cmd, "NOOP") == 0:
  297. responseAdd(client, "250 OK")
  298. case strings.Index(cmd, "RSET") == 0:
  299. client.mail_from = ""
  300. client.rcpt_to = ""
  301. responseAdd(client, "250 OK")
  302. case strings.Index(cmd, "DATA") == 0:
  303. responseAdd(client, "354 Enter message, ending with \".\" on a line by itself")
  304. client.state = 2
  305. case (strings.Index(cmd, "STARTTLS") == 0) && !client.tls_on:
  306. responseAdd(client, "220 Ready to start TLS")
  307. // go to start TLS state
  308. client.state = 3
  309. case strings.Index(cmd, "QUIT") == 0:
  310. responseAdd(client, "221 Bye")
  311. killClient(client)
  312. default:
  313. responseAdd(client, fmt.Sprintf("500 unrecognized command"))
  314. client.errors++
  315. if client.errors > 3 {
  316. responseAdd(client, fmt.Sprintf("500 Too many unrecognized commands"))
  317. killClient(client)
  318. }
  319. }
  320. case 2:
  321. var err error
  322. client.data, err = readSmtp(client)
  323. if err == nil {
  324. // to do: timeout when adding to SaveMailChan
  325. // place on the channel so that one of the save mail workers can pick it up
  326. SaveMailChan <- client
  327. // wait for the save to complete
  328. status := <-client.savedNotify
  329. if status == 1 {
  330. responseAdd(client, "250 OK : queued as "+client.hash)
  331. } else {
  332. responseAdd(client, "554 Error: transaction failed, blame it on the weather")
  333. }
  334. } else {
  335. logln(1, fmt.Sprintf("DATA read error: %v", err))
  336. }
  337. client.state = 1
  338. case 3:
  339. // upgrade to TLS
  340. var tlsConn *tls.Conn
  341. tlsConn = tls.Server(client.conn, TLSconfig)
  342. err := tlsConn.Handshake() // not necessary to call here, but might as well
  343. if err == nil {
  344. client.conn = net.Conn(tlsConn)
  345. client.bufin = bufio.NewReader(client.conn)
  346. client.bufout = bufio.NewWriter(client.conn)
  347. client.tls_on = true
  348. } else {
  349. logln(1, fmt.Sprintf("Could not TLS handshake:%v", err))
  350. }
  351. advertiseTls = ""
  352. client.state = 1
  353. }
  354. // Send a response back to the client
  355. err := responseWrite(client)
  356. if err != nil {
  357. if err == io.EOF {
  358. // client closed the connection already
  359. return
  360. }
  361. if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
  362. // too slow, timeout
  363. return
  364. }
  365. }
  366. if client.kill_time > 1 {
  367. return
  368. }
  369. }
  370. }
  371. func responseAdd(client *Client, line string) {
  372. client.response = line + "\r\n"
  373. }
  374. func closeClient(client *Client) {
  375. client.conn.Close()
  376. <-sem // Done; enable next client to run.
  377. }
  378. func killClient(client *Client) {
  379. client.kill_time = time.Now().Unix()
  380. }
  381. func readSmtp(client *Client) (input string, err error) {
  382. var reply string
  383. // Command state terminator by default
  384. suffix := "\r\n"
  385. if client.state == 2 {
  386. // DATA state
  387. suffix = "\r\n.\r\n"
  388. }
  389. for err == nil {
  390. client.conn.SetDeadline(time.Now().Add(timeout * time.Second))
  391. reply, err = client.bufin.ReadString('\n')
  392. if reply != "" {
  393. input = input + reply
  394. if len(input) > max_size {
  395. err = errors.New("Maximum DATA size exceeded (" + strconv.Itoa(max_size) + ")")
  396. return input, err
  397. }
  398. if client.state == 2 {
  399. // Extract the subject while we are at it.
  400. scanSubject(client, reply)
  401. }
  402. }
  403. if err != nil {
  404. break
  405. }
  406. if strings.HasSuffix(input, suffix) {
  407. break
  408. }
  409. }
  410. return input, err
  411. }
  412. // Scan the data part for a Subject line. Can be a multi-line
  413. func scanSubject(client *Client, reply string) {
  414. if client.subject == "" && (len(reply) > 8) {
  415. test := strings.ToUpper(reply[0:9])
  416. if i := strings.Index(test, "SUBJECT: "); i == 0 {
  417. // first line with \r\n
  418. client.subject = reply[9:]
  419. }
  420. } else if strings.HasSuffix(client.subject, "\r\n") {
  421. // chop off the \r\n
  422. client.subject = client.subject[0 : len(client.subject)-2]
  423. if (strings.HasPrefix(reply, " ")) || (strings.HasPrefix(reply, "\t")) {
  424. // subject is multi-line
  425. client.subject = client.subject + reply[1:]
  426. }
  427. }
  428. }
  429. func responseWrite(client *Client) (err error) {
  430. var size int
  431. client.conn.SetDeadline(time.Now().Add(timeout * time.Second))
  432. size, err = client.bufout.WriteString(client.response)
  433. client.bufout.Flush()
  434. client.response = client.response[size:]
  435. return err
  436. }
  437. func saveMail() {
  438. var to string
  439. var err error
  440. var body string
  441. var redis_err error
  442. var length int
  443. redis := &redisClient{}
  444. db := autorc.New("tcp", "", gConfig["MYSQL_HOST"], gConfig["MYSQL_USER"], gConfig["MYSQL_PASS"], gConfig["MYSQL_DB"])
  445. db.Register("set names utf8")
  446. sql := "INSERT INTO " + gConfig["GM_MAIL_TABLE"] + " "
  447. sql += "(`date`, `to`, `from`, `subject`, `body`, `charset`, `mail`, `spam_score`, `hash`, `content_type`, `recipient`, `has_attach`, `ip_addr`, `return_path`)"
  448. sql += " values (NOW(), ?, ?, ?, ? , 'UTF-8' , ?, 0, ?, '', ?, 0, ?, ?)"
  449. ins, sql_err := db.Prepare(sql)
  450. if sql_err != nil {
  451. logln(2, fmt.Sprintf("Sql statement incorrect: %s", sql_err))
  452. }
  453. sql = "UPDATE gm2_setting SET `setting_value` = `setting_value`+1 WHERE `setting_name`='received_emails' LIMIT 1"
  454. incr, sql_err := db.Prepare(sql)
  455. if sql_err != nil {
  456. logln(2, fmt.Sprintf("Sql statement incorrect: %s", sql_err))
  457. }
  458. // receives values from the channel repeatedly until it is closed.
  459. for {
  460. client := <-SaveMailChan
  461. if user, _, addr_err := validateEmailData(client); addr_err != nil { // user, host, addr_err
  462. logln(1, fmt.Sprintln("mail_from didnt validate: %v", addr_err)+" client.mail_from:"+client.mail_from)
  463. // notify client that a save completed, -1 = error
  464. client.savedNotify <- -1
  465. continue
  466. } else {
  467. to = user + "@" + gConfig["GM_PRIMARY_MAIL_HOST"]
  468. }
  469. length = len(client.data)
  470. client.subject = mimeHeaderDecode(client.subject)
  471. client.hash = md5hex(to + client.mail_from + client.subject + strconv.FormatInt(time.Now().UnixNano(), 10))
  472. // Add extra headers
  473. add_head := ""
  474. add_head += "Delivered-To: " + to + "\r\n"
  475. add_head += "Received: from " + client.helo + " (" + client.helo + " [" + client.address + "])\r\n"
  476. add_head += " by " + gConfig["GSMTP_HOST_NAME"] + " with SMTP id " + client.hash + "@" +
  477. gConfig["GSMTP_HOST_NAME"] + ";\r\n"
  478. add_head += " " + time.Now().Format(time.RFC1123Z) + "\r\n"
  479. // compress to save space
  480. client.data = compress(add_head + client.data)
  481. body = "gzencode"
  482. redis_err = redis.redisConnection()
  483. if redis_err == nil {
  484. _, do_err := redis.conn.Do("SETEX", client.hash, 3600, client.data)
  485. if do_err == nil {
  486. client.data = ""
  487. body = "redis"
  488. }
  489. } else {
  490. logln(1, fmt.Sprintf("redis: %v", redis_err))
  491. }
  492. // bind data to cursor
  493. ins.Bind(
  494. to,
  495. client.mail_from,
  496. client.subject,
  497. body,
  498. client.data,
  499. client.hash,
  500. to,
  501. client.address,
  502. client.mail_from)
  503. // save, discard result
  504. _, _, err = ins.Exec()
  505. if err != nil {
  506. logln(1, fmt.Sprintf("Database error, %v %v", err))
  507. client.savedNotify <- -1
  508. } else {
  509. logln(1, "Email saved "+client.hash+" len:"+strconv.Itoa(length))
  510. _, _, err = incr.Exec()
  511. if err != nil {
  512. logln(1, fmt.Sprintf("Failed to incr count:", err))
  513. }
  514. client.savedNotify <- 1
  515. }
  516. }
  517. }
  518. func (c *redisClient) redisConnection() (err error) {
  519. if c.count > 100 {
  520. c.conn.Close()
  521. c.count = 0
  522. }
  523. if c.count == 0 {
  524. c.conn, err = redis.Dial("tcp", ":6379")
  525. if err != nil {
  526. // handle error
  527. return err
  528. }
  529. }
  530. return nil
  531. }
  532. func validateEmailData(client *Client) (user string, host string, addr_err error) {
  533. if user, host, addr_err = extractEmail(client.mail_from); addr_err != nil {
  534. return user, host, addr_err
  535. }
  536. client.mail_from = user + "@" + host
  537. if user, host, addr_err = extractEmail(client.rcpt_to); addr_err != nil {
  538. return user, host, addr_err
  539. }
  540. client.rcpt_to = user + "@" + host
  541. // check if on allowed hosts
  542. if allowed := allowedHosts[host]; !allowed {
  543. return user, host, errors.New("invalid host:" + host)
  544. }
  545. return user, host, addr_err
  546. }
  547. func extractEmail(str string) (name string, host string, err error) {
  548. re, _ := regexp.Compile(`<(.+?)@(.+?)>`) // go home regex, you're drunk!
  549. if matched := re.FindStringSubmatch(str); len(matched) > 2 {
  550. host = validHost(matched[2])
  551. name = matched[1]
  552. } else {
  553. if res := strings.Split(str, "@"); len(res) > 1 {
  554. name = res[0]
  555. host = validHost(res[1])
  556. }
  557. }
  558. if host == "" || name == "" {
  559. err = errors.New("Invalid address, [" + name + "@" + host + "] address:" + str)
  560. }
  561. return name, host, err
  562. }
  563. // Decode strings in Mime header format
  564. // eg. =?ISO-2022-JP?B?GyRCIVo9dztSOWJAOCVBJWMbKEI=?=
  565. func mimeHeaderDecode(str string) string {
  566. reg, _ := regexp.Compile(`=\?(.+?)\?([QBqp])\?(.+?)\?=`)
  567. matched := reg.FindAllStringSubmatch(str, -1)
  568. var charset, encoding, payload string
  569. if matched != nil {
  570. for i := 0; i < len(matched); i++ {
  571. if len(matched[i]) > 2 {
  572. charset = matched[i][1]
  573. encoding = strings.ToUpper(matched[i][2])
  574. payload = matched[i][3]
  575. switch encoding {
  576. case "B":
  577. str = strings.Replace(str, matched[i][0], mailTransportDecode(payload, "base64", charset), 1)
  578. case "Q":
  579. str = strings.Replace(str, matched[i][0], mailTransportDecode(payload, "quoted-printable", charset), 1)
  580. }
  581. }
  582. }
  583. }
  584. return str
  585. }
  586. func validHost(host string) string {
  587. host = strings.Trim(host, " ")
  588. re, _ := regexp.Compile(`^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$`)
  589. if re.MatchString(host) {
  590. return host
  591. }
  592. return ""
  593. }
  594. // decode from 7bit to 8bit UTF-8
  595. // encoding_type can be "base64" or "quoted-printable"
  596. func mailTransportDecode(str string, encoding_type string, charset string) string {
  597. if charset == "" {
  598. charset = "UTF-8"
  599. } else {
  600. charset = strings.ToUpper(charset)
  601. }
  602. if encoding_type == "base64" {
  603. str = fromBase64(str)
  604. } else if encoding_type == "quoted-printable" {
  605. str = fromQuotedP(str)
  606. }
  607. if charset != "UTF-8" {
  608. charset = fixCharset(charset)
  609. // eg. charset can be "ISO-2022-JP"
  610. convstr, err := iconv.Conv(str, "UTF-8", charset)
  611. if err == nil {
  612. return convstr
  613. }
  614. }
  615. return str
  616. }
  617. func fromBase64(data string) string {
  618. buf := bytes.NewBufferString(data)
  619. decoder := base64.NewDecoder(base64.StdEncoding, buf)
  620. res, _ := ioutil.ReadAll(decoder)
  621. return string(res)
  622. }
  623. func fromQuotedP(data string) string {
  624. buf := bytes.NewBufferString(data)
  625. decoder := qprintable.NewDecoder(qprintable.BinaryEncoding, buf)
  626. res, _ := ioutil.ReadAll(decoder)
  627. return string(res)
  628. }
  629. func compress(s string) string {
  630. var b bytes.Buffer
  631. w, _ := zlib.NewWriterLevel(&b, zlib.BestSpeed) // flate.BestCompression
  632. w.Write([]byte(s))
  633. w.Close()
  634. return b.String()
  635. }
  636. func fixCharset(charset string) string {
  637. reg, _ := regexp.Compile(`[_:.\/\\]`)
  638. fixed_charset := reg.ReplaceAllString(charset, "-")
  639. // Fix charset
  640. // borrowed from http://squirrelmail.svn.sourceforge.net/viewvc/squirrelmail/trunk/squirrelmail/include/languages.php?revision=13765&view=markup
  641. // OE ks_c_5601_1987 > cp949
  642. fixed_charset = strings.Replace(fixed_charset, "ks-c-5601-1987", "cp949", -1)
  643. // Moz x-euc-tw > euc-tw
  644. fixed_charset = strings.Replace(fixed_charset, "x-euc", "euc", -1)
  645. // Moz x-windows-949 > cp949
  646. fixed_charset = strings.Replace(fixed_charset, "x-windows_", "cp", -1)
  647. // windows-125x and cp125x charsets
  648. fixed_charset = strings.Replace(fixed_charset, "windows-", "cp", -1)
  649. // ibm > cp
  650. fixed_charset = strings.Replace(fixed_charset, "ibm", "cp", -1)
  651. // iso-8859-8-i -> iso-8859-8
  652. fixed_charset = strings.Replace(fixed_charset, "iso-8859-8-i", "iso-8859-8", -1)
  653. if charset != fixed_charset {
  654. return fixed_charset
  655. }
  656. return charset
  657. }
  658. func md5hex(str string) string {
  659. h := md5.New()
  660. h.Write([]byte(str))
  661. sum := h.Sum([]byte{})
  662. return hex.EncodeToString(sum)
  663. }
  664. // If running Nginx as a proxy, give Nginx the IP address and port for the SMTP server
  665. // Primary use of Nginx is to terminate TLS so that Go doesn't need to deal with it.
  666. // This could perform auth and load balancing too
  667. // See http://wiki.nginx.org/MailCoreModule
  668. func nginxHTTPAuth() {
  669. parts := strings.Split(gConfig["GSTMP_LISTEN_INTERFACE"], ":")
  670. gConfig["HTTP_AUTH_HOST"] = parts[0]
  671. gConfig["HTTP_AUTH_PORT"] = parts[1]
  672. fmt.Println(parts)
  673. http.HandleFunc("/", nginxHTTPAuthHandler)
  674. err := http.ListenAndServe(gConfig["NGINX_AUTH"], nil)
  675. if err != nil {
  676. log.Fatal("ListenAndServe: ", err)
  677. }
  678. }
  679. func nginxHTTPAuthHandler(w http.ResponseWriter, r *http.Request) {
  680. w.Header().Add("Auth-Status", "OK")
  681. w.Header().Add("Auth-Server", gConfig["HTTP_AUTH_HOST"])
  682. w.Header().Add("Auth-Port", gConfig["HTTP_AUTH_PORT"])
  683. fmt.Fprint(w, "")
  684. }