api_test.go 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222
  1. package guerrilla
  2. import (
  3. "bufio"
  4. "errors"
  5. "fmt"
  6. "io/ioutil"
  7. "net"
  8. "os"
  9. "strings"
  10. "testing"
  11. "time"
  12. "github.com/flashmob/go-guerrilla/backends"
  13. _ "github.com/flashmob/go-guerrilla/chunk"
  14. "github.com/flashmob/go-guerrilla/log"
  15. "github.com/flashmob/go-guerrilla/mail"
  16. "github.com/flashmob/go-guerrilla/response"
  17. )
  18. // Test Starting smtp without setting up logger / backend
  19. func TestSMTP(t *testing.T) {
  20. done := make(chan bool)
  21. go func() {
  22. select {
  23. case <-time.After(time.Second * 40):
  24. t.Error("timeout")
  25. return
  26. case <-done:
  27. return
  28. }
  29. }()
  30. d := Daemon{}
  31. err := d.Start()
  32. if err != nil {
  33. t.Error(err)
  34. }
  35. // it should set to stderr automatically
  36. if d.Config.LogFile != log.OutputStderr.String() {
  37. t.Error("smtp.config.LogFile is not", log.OutputStderr.String())
  38. }
  39. if len(d.Config.AllowedHosts) == 0 {
  40. t.Error("smtp.config.AllowedHosts len should be 1, not 0", d.Config.AllowedHosts)
  41. }
  42. if d.Config.LogLevel != "debug" {
  43. t.Error("smtp.config.LogLevel expected'debug', it is", d.Config.LogLevel)
  44. }
  45. if len(d.Config.Servers) != 1 {
  46. t.Error("len(smtp.config.Servers) should be 1, got", len(d.Config.Servers))
  47. }
  48. time.Sleep(time.Second * 2)
  49. d.Shutdown()
  50. done <- true
  51. }
  52. // Suppressing log output
  53. func TestSMTPNoLog(t *testing.T) {
  54. // configure a default server with no log output
  55. cfg := &AppConfig{LogFile: log.OutputOff.String()}
  56. d := Daemon{Config: cfg}
  57. err := d.Start()
  58. if err != nil {
  59. t.Error(err)
  60. }
  61. time.Sleep(time.Second * 2)
  62. d.Shutdown()
  63. }
  64. // our custom server
  65. func TestSMTPCustomServer(t *testing.T) {
  66. cfg := &AppConfig{LogFile: log.OutputOff.String()}
  67. sc := ServerConfig{
  68. ListenInterface: "127.0.0.1:2526",
  69. IsEnabled: true,
  70. }
  71. cfg.Servers = append(cfg.Servers, sc)
  72. d := Daemon{Config: cfg}
  73. err := d.Start()
  74. if err != nil {
  75. t.Error("start error", err)
  76. } else {
  77. time.Sleep(time.Second * 2)
  78. d.Shutdown()
  79. }
  80. }
  81. // with a backend config
  82. func TestSMTPCustomBackend(t *testing.T) {
  83. cfg := &AppConfig{LogFile: log.OutputOff.String()}
  84. sc := ServerConfig{
  85. ListenInterface: "127.0.0.1:2526",
  86. IsEnabled: true,
  87. }
  88. cfg.Servers = append(cfg.Servers, sc)
  89. bcfg := backends.BackendConfig{
  90. "save_workers_size": 3,
  91. "save_process": "HeadersParser|Header|Hasher|Debugger",
  92. "log_received_mails": true,
  93. "primary_mail_host": "example.com",
  94. }
  95. cfg.BackendConfig = bcfg
  96. d := Daemon{Config: cfg}
  97. err := d.Start()
  98. if err != nil {
  99. t.Error("start error", err)
  100. } else {
  101. time.Sleep(time.Second * 2)
  102. d.Shutdown()
  103. }
  104. }
  105. // with a config from a json file
  106. func TestSMTPLoadFile(t *testing.T) {
  107. json := `{
  108. "log_file" : "./tests/testlog",
  109. "log_level" : "debug",
  110. "pid_file" : "tests/go-guerrilla.pid",
  111. "allowed_hosts": ["spam4.me","grr.la"],
  112. "backend_config" :
  113. {
  114. "log_received_mails" : true,
  115. "save_process": "HeadersParser|Header|Hasher|Debugger",
  116. "save_workers_size": 3
  117. },
  118. "servers" : [
  119. {
  120. "is_enabled" : true,
  121. "host_name":"mail.guerrillamail.com",
  122. "max_size": 100017,
  123. "timeout":160,
  124. "listen_interface":"127.0.0.1:2526",
  125. "max_clients": 2,
  126. "tls" : {
  127. "private_key_file":"config_test.go",
  128. "public_key_file":"config_test.go",
  129. "start_tls_on":false,
  130. "tls_always_on":false
  131. }
  132. }
  133. ]
  134. }
  135. `
  136. json2 := `{
  137. "log_file" : "./tests/testlog2",
  138. "log_level" : "debug",
  139. "pid_file" : "tests/go-guerrilla2.pid",
  140. "allowed_hosts": ["spam4.me","grr.la"],
  141. "backend_config" :
  142. {
  143. "log_received_mails" : true,
  144. "save_process": "HeadersParser|Header|Hasher|Debugger",
  145. "save_workers_size": 3
  146. },
  147. "servers" : [
  148. {
  149. "is_enabled" : true,
  150. "host_name":"mail.guerrillamail.com",
  151. "max_size": 100017,
  152. "timeout":160,
  153. "listen_interface":"127.0.0.1:2526",
  154. "max_clients": 2,
  155. "tls" : {
  156. "private_key_file":"config_test.go",
  157. "public_key_file":"config_test.go",
  158. "start_tls_on":false,
  159. "tls_always_on":false
  160. }
  161. }
  162. ]
  163. }
  164. `
  165. err := ioutil.WriteFile("goguerrilla.conf.api", []byte(json), 0644)
  166. if err != nil {
  167. t.Error("could not write guerrilla.conf.api", err)
  168. return
  169. }
  170. d := Daemon{}
  171. _, err = d.LoadConfig("goguerrilla.conf.api")
  172. if err != nil {
  173. t.Error("ReadConfig error", err)
  174. return
  175. }
  176. err = d.Start()
  177. if err != nil {
  178. t.Error("start error", err)
  179. return
  180. } else {
  181. time.Sleep(time.Second * 2)
  182. if d.Config.LogFile != "./tests/testlog" {
  183. t.Error("d.Config.LogFile != \"./tests/testlog\"")
  184. }
  185. if d.Config.PidFile != "tests/go-guerrilla.pid" {
  186. t.Error("d.Config.LogFile != tests/go-guerrilla.pid")
  187. }
  188. err := ioutil.WriteFile("goguerrilla.conf.api", []byte(json2), 0644)
  189. if err != nil {
  190. t.Error("could not write guerrilla.conf.api", err)
  191. return
  192. }
  193. if err = d.ReloadConfigFile("goguerrilla.conf.api"); err != nil {
  194. t.Error(err)
  195. }
  196. if d.Config.LogFile != "./tests/testlog2" {
  197. t.Error("d.Config.LogFile != \"./tests/testlog\"")
  198. }
  199. if d.Config.PidFile != "tests/go-guerrilla2.pid" {
  200. t.Error("d.Config.LogFile != \"go-guerrilla.pid\"")
  201. }
  202. d.Shutdown()
  203. }
  204. }
  205. func TestReopenLog(t *testing.T) {
  206. if err := os.Truncate("tests/testlog", 0); err != nil {
  207. t.Error(err)
  208. }
  209. cfg := &AppConfig{LogFile: "tests/testlog"}
  210. sc := ServerConfig{
  211. ListenInterface: "127.0.0.1:2526",
  212. IsEnabled: true,
  213. }
  214. cfg.Servers = append(cfg.Servers, sc)
  215. d := Daemon{Config: cfg}
  216. err := d.Start()
  217. if err != nil {
  218. t.Error("start error", err)
  219. } else {
  220. if err = d.ReopenLogs(); err != nil {
  221. t.Error(err)
  222. }
  223. time.Sleep(time.Second * 2)
  224. d.Shutdown()
  225. }
  226. b, err := ioutil.ReadFile("tests/testlog")
  227. if err != nil {
  228. t.Error("could not read logfile")
  229. return
  230. }
  231. if strings.Index(string(b), "re-opened log file") < 0 {
  232. t.Error("Server log did not re-opened, expecting \"re-opened log file\"")
  233. }
  234. if strings.Index(string(b), "re-opened main log file") < 0 {
  235. t.Error("Main log did not re-opened, expecting \"re-opened main log file\"")
  236. }
  237. }
  238. func TestSetConfig(t *testing.T) {
  239. if err := os.Truncate("tests/testlog", 0); err != nil {
  240. t.Error(err)
  241. }
  242. cfg := AppConfig{LogFile: "tests/testlog"}
  243. sc := ServerConfig{
  244. ListenInterface: "127.0.0.1:2526",
  245. IsEnabled: true,
  246. }
  247. cfg.Servers = append(cfg.Servers, sc)
  248. d := Daemon{Config: &cfg}
  249. // lets add a new server
  250. sc.ListenInterface = "127.0.0.1:2527"
  251. cfg.Servers = append(cfg.Servers, sc)
  252. err := d.SetConfig(cfg)
  253. if err != nil {
  254. t.Error("SetConfig returned an error:", err)
  255. return
  256. }
  257. err = d.Start()
  258. if err != nil {
  259. t.Error("start error", err)
  260. } else {
  261. time.Sleep(time.Second * 2)
  262. d.Shutdown()
  263. }
  264. b, err := ioutil.ReadFile("tests/testlog")
  265. if err != nil {
  266. t.Error("could not read logfile")
  267. return
  268. }
  269. //fmt.Println(string(b))
  270. // has 127.0.0.1:2527 started?
  271. if strings.Index(string(b), "127.0.0.1:2527") < 0 {
  272. t.Error("expecting 127.0.0.1:2527 to start")
  273. }
  274. }
  275. func TestSetConfigError(t *testing.T) {
  276. if err := os.Truncate("tests/testlog", 0); err != nil {
  277. t.Error(err)
  278. }
  279. cfg := AppConfig{LogFile: "tests/testlog"}
  280. sc := ServerConfig{
  281. ListenInterface: "127.0.0.1:2526",
  282. IsEnabled: true,
  283. }
  284. cfg.Servers = append(cfg.Servers, sc)
  285. d := Daemon{Config: &cfg}
  286. // lets add a new server with bad TLS
  287. sc.ListenInterface = "127.0.0.1:2527"
  288. sc.TLS.StartTLSOn = true
  289. sc.TLS.PublicKeyFile = "tests/testlog" // totally wrong :->
  290. sc.TLS.PrivateKeyFile = "tests/testlog" // totally wrong :->
  291. cfg.Servers = append(cfg.Servers, sc)
  292. err := d.SetConfig(cfg)
  293. if err == nil {
  294. t.Error("SetConfig should have returned an error compalning about bad tls settings")
  295. return
  296. }
  297. }
  298. var funkyLogger = func() backends.Decorator {
  299. backends.Svc.AddInitializer(
  300. backends.InitializeWith(
  301. func(backendConfig backends.BackendConfig) error {
  302. backends.Log().Info("Funky logger is up & down to funk!")
  303. return nil
  304. }),
  305. )
  306. backends.Svc.AddShutdowner(
  307. backends.ShutdownWith(
  308. func() error {
  309. backends.Log().Info("The funk has been stopped!")
  310. return nil
  311. }),
  312. )
  313. return func(p backends.Processor) backends.Processor {
  314. return backends.ProcessWith(
  315. func(e *mail.Envelope, task backends.SelectTask) (backends.Result, error) {
  316. if task == backends.TaskValidateRcpt {
  317. // log the last recipient appended to e.Rcpt
  318. backends.Log().Infof(
  319. "another funky recipient [%s]",
  320. e.RcptTo[len(e.RcptTo)-1])
  321. // if valid then forward call to the next processor in the chain
  322. return p.Process(e, task)
  323. // if invalid, return a backend result
  324. //return backends.NewResult(response.Canned.FailRcptCmd), nil
  325. } else if task == backends.TaskSaveMail {
  326. backends.Log().Info("Another funky email!")
  327. }
  328. return p.Process(e, task)
  329. })
  330. }
  331. }
  332. // How about a custom processor?
  333. func TestSetAddProcessor(t *testing.T) {
  334. if err := os.Truncate("tests/testlog", 0); err != nil {
  335. t.Error(err)
  336. }
  337. cfg := &AppConfig{
  338. LogFile: "tests/testlog",
  339. AllowedHosts: []string{"grr.la"},
  340. BackendConfig: backends.BackendConfig{
  341. "save_process": "HeadersParser|Debugger|FunkyLogger",
  342. "validate_process": "FunkyLogger",
  343. },
  344. }
  345. d := Daemon{Config: cfg}
  346. d.AddProcessor("FunkyLogger", funkyLogger)
  347. if err := d.Start(); err != nil {
  348. t.Error(err)
  349. }
  350. // lets have a talk with the server
  351. if err := talkToServer("127.0.0.1:2525", ""); err != nil {
  352. t.Error(err)
  353. }
  354. d.Shutdown()
  355. b, err := ioutil.ReadFile("tests/testlog")
  356. if err != nil {
  357. t.Error("could not read logfile")
  358. return
  359. }
  360. // lets check for fingerprints
  361. if strings.Index(string(b), "another funky recipient") < 0 {
  362. t.Error("did not log: another funky recipient")
  363. }
  364. if strings.Index(string(b), "Another funky email!") < 0 {
  365. t.Error("Did not log: Another funky email!")
  366. }
  367. if strings.Index(string(b), "Funky logger is up & down to funk") < 0 {
  368. t.Error("Did not log: Funky logger is up & down to funk")
  369. }
  370. if strings.Index(string(b), "The funk has been stopped!") < 0 {
  371. t.Error("Did not log:The funk has been stopped!")
  372. }
  373. }
  374. func talkToServer(address string, body string) (err error) {
  375. conn, err := net.Dial("tcp", address)
  376. if err != nil {
  377. return
  378. }
  379. in := bufio.NewReader(conn)
  380. str, err := in.ReadString('\n')
  381. if err != nil {
  382. return err
  383. }
  384. _, err = fmt.Fprint(conn, "HELO maildiranasaurustester\r\n")
  385. if err != nil {
  386. return err
  387. }
  388. str, err = in.ReadString('\n')
  389. if err != nil {
  390. return err
  391. }
  392. _, err = fmt.Fprint(conn, "MAIL FROM:<[email protected]>\r\n")
  393. if err != nil {
  394. return err
  395. }
  396. str, err = in.ReadString('\n')
  397. if err != nil {
  398. return err
  399. }
  400. if err != nil {
  401. return err
  402. }
  403. _, err = fmt.Fprint(conn, "RCPT TO:<[email protected]>\r\n")
  404. if err != nil {
  405. return err
  406. }
  407. str, err = in.ReadString('\n')
  408. if err != nil {
  409. return err
  410. }
  411. _, err = fmt.Fprint(conn, "DATA\r\n")
  412. if err != nil {
  413. return err
  414. }
  415. str, err = in.ReadString('\n')
  416. if err != nil {
  417. return err
  418. }
  419. if body == "" {
  420. _, err = fmt.Fprint(conn, "Subject: Test subject\r\n")
  421. if err != nil {
  422. return err
  423. }
  424. _, err = fmt.Fprint(conn, "\r\n")
  425. if err != nil {
  426. return err
  427. }
  428. _, err = fmt.Fprint(conn, "A an email body\r\n")
  429. if err != nil {
  430. return err
  431. }
  432. _, err = fmt.Fprint(conn, ".\r\n")
  433. if err != nil {
  434. return err
  435. }
  436. } else {
  437. _, err = fmt.Fprint(conn, body)
  438. if err != nil {
  439. return err
  440. }
  441. _, err = fmt.Fprint(conn, ".\r\n")
  442. if err != nil {
  443. return err
  444. }
  445. }
  446. str, err = in.ReadString('\n')
  447. if err != nil {
  448. return err
  449. }
  450. _, err = fmt.Fprint(conn, "QUIT\r\n")
  451. if err != nil {
  452. return err
  453. }
  454. _ = str
  455. return nil
  456. }
  457. // Test hot config reload
  458. // Here we forgot to add FunkyLogger so backend will fail to init
  459. func TestReloadConfig(t *testing.T) {
  460. if err := os.Truncate("tests/testlog", 0); err != nil {
  461. t.Error(err)
  462. }
  463. d := Daemon{}
  464. if err := d.Start(); err != nil {
  465. t.Error(err)
  466. }
  467. defer d.Shutdown()
  468. cfg := AppConfig{
  469. LogFile: "tests/testlog",
  470. AllowedHosts: []string{"grr.la"},
  471. BackendConfig: backends.BackendConfig{
  472. "save_process": "HeadersParser|Debugger|FunkyLogger",
  473. "validate_process": "FunkyLogger",
  474. },
  475. }
  476. // Look mom, reloading the config without shutting down!
  477. if err := d.ReloadConfig(cfg); err != nil {
  478. t.Error(err)
  479. }
  480. }
  481. func TestPubSubAPI(t *testing.T) {
  482. if err := os.Truncate("tests/testlog", 0); err != nil {
  483. t.Error(err)
  484. }
  485. d := Daemon{Config: &AppConfig{LogFile: "tests/testlog"}}
  486. if err := d.Start(); err != nil {
  487. t.Error(err)
  488. }
  489. defer d.Shutdown()
  490. // new config
  491. cfg := AppConfig{
  492. PidFile: "tests/pidfilex.pid",
  493. LogFile: "tests/testlog",
  494. AllowedHosts: []string{"grr.la"},
  495. BackendConfig: backends.BackendConfig{
  496. "save_process": "HeadersParser|Debugger|FunkyLogger",
  497. "validate_process": "FunkyLogger",
  498. },
  499. }
  500. var i = 0
  501. pidEvHandler := func(c *AppConfig) {
  502. i++
  503. if i > 1 {
  504. t.Error("number > 1, it means d.Unsubscribe didn't work")
  505. }
  506. d.Logger.Info("number", i)
  507. }
  508. if err := d.Subscribe(EventConfigPidFile, pidEvHandler); err != nil {
  509. t.Error(err)
  510. }
  511. if err := d.ReloadConfig(cfg); err != nil {
  512. t.Error(err)
  513. }
  514. if err := d.Unsubscribe(EventConfigPidFile, pidEvHandler); err != nil {
  515. t.Error(err)
  516. }
  517. cfg.PidFile = "tests/pidfile2.pid"
  518. d.Publish(EventConfigPidFile, &cfg)
  519. if err := d.ReloadConfig(cfg); err != nil {
  520. t.Error(err)
  521. }
  522. b, err := ioutil.ReadFile("tests/testlog")
  523. if err != nil {
  524. t.Error("could not read logfile")
  525. return
  526. }
  527. // lets interrogate the log
  528. if strings.Index(string(b), "number1") < 0 {
  529. t.Error("it lools like d.ReloadConfig(cfg) did not fire EventConfigPidFile, pidEvHandler not called")
  530. }
  531. }
  532. func TestAPILog(t *testing.T) {
  533. if err := os.Truncate("tests/testlog", 0); err != nil {
  534. t.Error(err)
  535. }
  536. d := Daemon{}
  537. l := d.Log()
  538. l.Info("logtest1") // to stderr
  539. if l.GetLevel() != log.InfoLevel.String() {
  540. t.Error("Log level does not eq info, it is ", l.GetLevel())
  541. }
  542. d.Logger = nil
  543. d.Config = &AppConfig{LogFile: "tests/testlog"}
  544. l = d.Log()
  545. l.Info("logtest1") // to tests/testlog
  546. //
  547. l = d.Log()
  548. if l.GetLogDest() != "tests/testlog" {
  549. t.Error("log dest is not tests/testlog, it was ", l.GetLogDest())
  550. }
  551. b, err := ioutil.ReadFile("tests/testlog")
  552. if err != nil {
  553. t.Error("could not read logfile")
  554. return
  555. }
  556. // lets interrogate the log
  557. if strings.Index(string(b), "logtest1") < 0 {
  558. t.Error("hai was not found in the log, it should have been in tests/testlog")
  559. }
  560. }
  561. // Test the allowed_hosts config option with a single entry of ".", which will allow all hosts.
  562. func TestSkipAllowsHost(t *testing.T) {
  563. d := Daemon{}
  564. defer d.Shutdown()
  565. // setting the allowed hosts to a single entry with a dot will let any host through
  566. d.Config = &AppConfig{AllowedHosts: []string{"."}, LogFile: "off"}
  567. if err := d.Start(); err != nil {
  568. t.Error(err)
  569. }
  570. conn, err := net.Dial("tcp", d.Config.Servers[0].ListenInterface)
  571. if err != nil {
  572. t.Error(t)
  573. return
  574. }
  575. in := bufio.NewReader(conn)
  576. if _, err := fmt.Fprint(conn, "HELO test\r\n"); err != nil {
  577. t.Error(err)
  578. }
  579. if _, err := fmt.Fprint(conn, "RCPT TO:<[email protected]>\r\n"); err != nil {
  580. t.Error(err)
  581. }
  582. if _, err := in.ReadString('\n'); err != nil {
  583. t.Error(err)
  584. }
  585. if _, err := in.ReadString('\n'); err != nil {
  586. t.Error(err)
  587. }
  588. str, _ := in.ReadString('\n')
  589. if strings.Index(str, "250") != 0 {
  590. t.Error("expected 250 reply, got:", str)
  591. }
  592. }
  593. var customBackend2 = func() backends.Decorator {
  594. return func(p backends.Processor) backends.Processor {
  595. return backends.ProcessWith(
  596. func(e *mail.Envelope, task backends.SelectTask) (backends.Result, error) {
  597. if task == backends.TaskValidateRcpt {
  598. return p.Process(e, task)
  599. } else if task == backends.TaskSaveMail {
  600. backends.Log().Info("Another funky email!")
  601. err := errors.New("system shock")
  602. return backends.NewResult(response.Canned.FailReadErrorDataCmd, response.SP, err), err
  603. }
  604. return p.Process(e, task)
  605. })
  606. }
  607. }
  608. // Test a custom backend response
  609. func TestCustomBackendResult(t *testing.T) {
  610. if err := os.Truncate("tests/testlog", 0); err != nil {
  611. t.Error(err)
  612. }
  613. cfg := &AppConfig{
  614. LogFile: "tests/testlog",
  615. AllowedHosts: []string{"grr.la"},
  616. BackendConfig: backends.BackendConfig{
  617. "save_process": "HeadersParser|Debugger|Custom",
  618. "validate_process": "Custom",
  619. },
  620. }
  621. d := Daemon{Config: cfg}
  622. d.AddProcessor("Custom", customBackend2)
  623. if err := d.Start(); err != nil {
  624. t.Error(err)
  625. }
  626. // lets have a talk with the server
  627. if err := talkToServer("127.0.0.1:2525", ""); err != nil {
  628. t.Error(err)
  629. }
  630. d.Shutdown()
  631. b, err := ioutil.ReadFile("tests/testlog")
  632. if err != nil {
  633. t.Error("could not read logfile")
  634. return
  635. }
  636. // lets check for fingerprints
  637. if strings.Index(string(b), "451 4.3.0 Error") < 0 {
  638. t.Error("did not log: 451 4.3.0 Error")
  639. }
  640. if strings.Index(string(b), "system shock") < 0 {
  641. t.Error("did not log: system shock")
  642. }
  643. }
  644. func TestStreamProcessor(t *testing.T) {
  645. if err := os.Truncate("tests/testlog", 0); err != nil {
  646. t.Error(err)
  647. }
  648. cfg := &AppConfig{
  649. LogFile: "tests/testlog",
  650. AllowedHosts: []string{"grr.la"},
  651. BackendConfig: backends.BackendConfig{
  652. "save_process": "HeadersParser|Debugger",
  653. "stream_save_process": "Header|headersparser|compress|Decompress|debug",
  654. },
  655. }
  656. d := Daemon{Config: cfg}
  657. if err := d.Start(); err != nil {
  658. t.Error(err)
  659. }
  660. body := "Subject: Test subject\r\n" +
  661. //"\r\n" +
  662. "A an email body.\r\n" +
  663. "Header|headersparser|compress|Decompress|debug Header|headersparser|compress|Decompress|debug.\r\n" +
  664. "Header|headersparser|compress|Decompress|debug Header|headersparser|compress|Decompress|debug.\r\n" +
  665. "Header|headersparser|compress|Decompress|debug Header|headersparser|compress|Decompress|debug.\r\n" +
  666. "Header|headersparser|compress|Decompress|debug Header|headersparser|compress|Decompress|debug.\r\n" +
  667. "Header|headersparser|compress|Decompress|debug Header|headersparser|compress|Decompress|debug.\r\n" +
  668. "Header|headersparser|compress|Decompress|debug Header|headersparser|compress|Decompress|debug.\r\n" +
  669. "Header|headersparser|compress|Decompress|debug Header|headersparser|compress|Decompress|debug.\r\n" +
  670. "Header|headersparser|compress|Decompress|debug Header|headersparser|compress|Decompress|debug.\r\n" +
  671. "Header|headersparser|compress|Decompress|debug Header|headersparser|compress|Decompress|debug.\r\n" +
  672. "Header|headersparser|compress|Decompress|debug Header|headersparser|compress|Decompress|debug.\r\n" +
  673. "Header|headersparser|compress|Decompress|debug Header|headersparser|compress|Decompress|debug.\r\n" +
  674. "Header|headersparser|compress|Decompress|debug Header|headersparser|compress|Decompress|debug.\r\n"
  675. // lets have a talk with the server
  676. if err := talkToServer("127.0.0.1:2525", body); err != nil {
  677. t.Error(err)
  678. }
  679. d.Shutdown()
  680. b, err := ioutil.ReadFile("tests/testlog")
  681. if err != nil {
  682. t.Error("could not read logfile")
  683. return
  684. }
  685. // lets check for fingerprints
  686. if strings.Index(string(b), "Debug stream") < 0 {
  687. t.Error("did not log: Debug stream")
  688. }
  689. if strings.Index(string(b), "Error") != -1 {
  690. t.Error("There was an error", string(b))
  691. }
  692. }
  693. var mime0 = `MIME-Version: 1.0
  694. X-Mailer: MailBee.NET 8.0.4.428
  695. Subject: test
  696. subject
  697. To: [email protected]
  698. Content-Type: multipart/mixed;
  699. boundary="XXXXboundary text"
  700. --XXXXboundary text
  701. Content-Type: multipart/alternative;
  702. boundary="XXXXboundary text"
  703. --XXXXboundary text
  704. Content-Type: text/plain;
  705. charset="utf-8"
  706. Content-Transfer-Encoding: quoted-printable
  707. This is the body text of a sample message.
  708. --XXXXboundary text
  709. Content-Type: text/html;
  710. charset="utf-8"
  711. Content-Transfer-Encoding: quoted-printable
  712. <pre>This is the body text of a sample message.</pre>
  713. --XXXXboundary text
  714. Content-Type: text/plain;
  715. name="log_attachment.txt"
  716. Content-Disposition: attachment;
  717. filename="log_attachment.txt"
  718. Content-Transfer-Encoding: base64
  719. TUlNRS1WZXJzaW9uOiAxLjANClgtTWFpbGVyOiBNYWlsQmVlLk5FVCA4LjAuNC40MjgNClN1Ympl
  720. Y3Q6IHRlc3Qgc3ViamVjdA0KVG86IGtldmlubUBkYXRhbW90aW9uLmNvbQ0KQ29udGVudC1UeXBl
  721. OiBtdWx0aXBhcnQvYWx0ZXJuYXRpdmU7DQoJYm91bmRhcnk9Ii0tLS09X05leHRQYXJ0XzAwMF9B
  722. RTZCXzcyNUUwOUFGLjg4QjdGOTM0Ig0KDQoNCi0tLS0tLT1fTmV4dFBhcnRfMDAwX0FFNkJfNzI1
  723. RTA5QUYuODhCN0Y5MzQNCkNvbnRlbnQtVHlwZTogdGV4dC9wbGFpbjsNCgljaGFyc2V0PSJ1dGYt
  724. OCINCkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IHF1b3RlZC1wcmludGFibGUNCg0KdGVzdCBi
  725. b2R5DQotLS0tLS09X05leHRQYXJ0XzAwMF9BRTZCXzcyNUUwOUFGLjg4QjdGOTM0DQpDb250ZW50
  726. LVR5cGU6IHRleHQvaHRtbDsNCgljaGFyc2V0PSJ1dGYtOCINCkNvbnRlbnQtVHJhbnNmZXItRW5j
  727. b2Rpbmc6IHF1b3RlZC1wcmludGFibGUNCg0KPHByZT50ZXN0IGJvZHk8L3ByZT4NCi0tLS0tLT1f
  728. TmV4dFBhcnRfMDAwX0FFNkJfNzI1RTA5QUYuODhCN0Y5MzQtLQ0K
  729. --XXXXboundary text--
  730. `
  731. var mime2 = `From: [email protected]
  732. Content-Type: multipart/mixed;
  733. boundary="----_=_NextPart_001_01CBE273.65A0E7AA"
  734. To: [email protected]
  735. This is a multi-part message in MIME format.
  736. ------_=_NextPart_001_01CBE273.65A0E7AA
  737. Content-Type: multipart/alternative;
  738. boundary="----_=_NextPart_002_01CBE273.65A0E7AA"
  739. ------_=_NextPart_002_01CBE273.65A0E7AA
  740. Content-Type: text/plain;
  741. charset="UTF-8"
  742. Content-Transfer-Encoding: base64
  743. [base64-content]
  744. ------_=_NextPart_002_01CBE273.65A0E7AA
  745. Content-Type: text/html;
  746. charset="UTF-8"
  747. Content-Transfer-Encoding: base64
  748. [base64-content]
  749. ------_=_NextPart_002_01CBE273.65A0E7AA--
  750. ------_=_NextPart_001_01CBE273.65A0E7AA
  751. Content-Type: message/rfc822
  752. Content-Transfer-Encoding: 7bit
  753. X-MimeOLE: Produced By Microsoft Exchange V6.5
  754. Content-class: urn:content-classes:message
  755. MIME-Version: 1.0
  756. Content-Type: multipart/mixed;
  757. boundary="----_=_NextPart_003_01CBE272.13692C80"
  758. From: [email protected]
  759. To: [email protected]
  760. This is a multi-part message in MIME format.
  761. ------_=_NextPart_003_01CBE272.13692C80
  762. Content-Type: multipart/alternative;
  763. boundary="----_=_NextPart_004_01CBE272.13692C80"
  764. ------_=_NextPart_004_01CBE272.13692C80
  765. Content-Type: text/plain;
  766. charset="iso-8859-1"
  767. Content-Transfer-Encoding: quoted-printable
  768. =20
  769. Viele Gr=FC=DFe
  770. ------_=_NextPart_004_01CBE272.13692C80
  771. Content-Type: text/html;
  772. charset="iso-8859-1"
  773. Content-Transfer-Encoding: quoted-printable
  774. <html>...</html>
  775. ------_=_NextPart_004_01CBE272.13692C80--
  776. ------_=_NextPart_003_01CBE272.13692C80
  777. Content-Type: application/x-zip-compressed;
  778. name="abc.zip"
  779. Content-Transfer-Encoding: base64
  780. Content-Disposition: attachment;
  781. filename="abc.zip"
  782. [base64-content]
  783. ------_=_NextPart_003_01CBE272.13692C80--
  784. ------_=_NextPart_001_01CBE273.65A0E7AA--
  785. `
  786. var mime3 = `From [email protected] Mon Feb 19 22:24:21 2001
  787. Received: from [137.154.210.66] by hotmail.com (3.2) with ESMTP id MHotMailBC5B58230039400431D5899AD24289FA0; Mon Feb 19 22:22:29 2001
  788. Received: from lancelot.cit.nepean.uws.edu.au (lancelot.cit.uws.edu.au [137.154.148.30])
  789. by day.uws.edu.au (8.11.1/8.11.1) with ESMTP id f1K6MN404936;
  790. Tue, 20 Feb 2001 17:22:24 +1100 (EST)
  791. Received: from hotmail.com (law2-f35.hotmail.com [216.32.181.35])
  792. by lancelot.cit.nepean.uws.edu.au (8.10.0.Beta10/8.10.0.Beta10) with ESMTP id f1K6MJb13619;
  793. Tue, 20 Feb 2001 17:22:19 +1100 (EST)
  794. Received: from mail pickup service by hotmail.com with Microsoft SMTPSVC;
  795. Mon, 19 Feb 2001 22:21:44 -0800
  796. Received: from 203.54.221.89 by lw2fd.hotmail.msn.com with HTTP; Tue, 20 Feb 2001 06:21:44 GMT
  797. X-Originating-IP: [203.54.221.89]
  798. From: "lara devine" <[email protected]>
  799. To: [email protected], [email protected],
  800. [email protected], [email protected],
  801. [email protected], [email protected],
  802. [email protected], [email protected],
  803. [email protected]
  804. Subject: Fwd: Goldfish
  805. Date: Tue, 20 Feb 2001 06:21:44
  806. Mime-Version: 1.0
  807. Content-Type: text/plain; format=flowed
  808. Message-ID: <[email protected]>
  809. X-OriginalArrivalTime: 20 Feb 2001 06:21:44.0718 (UTC) FILETIME=[658BDAE0:01C09B05]
  810. >> >Two builders (Chris and James) are seated either side of a table in a
  811. > > >rough
  812. > > >pub when a well-dressed man enters, orders beer and sits on a stool at
  813. > > >the bar.
  814. > > >The two builders start to speculate about the occupation of the suit.
  815. > > >
  816. > > >Chris: - I reckon he's an accountant.
  817. > > >
  818. > > >James: - No way - he's a stockbroker.
  819. > > >
  820. > > >Chris: - He ain't no stockbroker! A stockbroker wouldn't come in here!
  821. > > >
  822. > > >The argument repeats itself for some time until the volume of beer gets
  823. > > >the better of Chris and he makes for the toilet. On entering the toilet
  824. > > >he
  825. > > >sees that the suit is standing at a urinal. Curiosity and the several
  826. > > >beers
  827. > > >get the better of the builder...
  828. > > >
  829. > > >Chris: - 'scuse me.... no offence meant, but me and me mate were
  830. > > wondering
  831. > > >
  832. > > > what you do for a living?
  833. > > >
  834. > > >Suit: - No offence taken! I'm a Logical Scientist by profession!
  835. > > >
  836. > > >Chris: - Oh! What's that then?
  837. > > >
  838. > > >Suit:- I'll try to explain by example... Do you have a goldfish at
  839. >home?
  840. > > >
  841. > > >Chris:- Er...mmm... well yeah, I do as it happens!
  842. > > >
  843. > > >Suit: - Well, it's logical to follow that you keep it in a bowl or in a
  844. > > >pond. Which is it?
  845. > > >
  846. > > >Chris: - It's in a pond!
  847. > > >
  848. > > >Suit: - Well then it's reasonable to suppose that you have a large
  849. > > >garden
  850. > > >then?
  851. > > >
  852. > > >Chris: - As it happens, yes I have got a big garden!
  853. > > >
  854. > > >Suit: - Well then it's logical to assume that in this town that if you
  855. > > >have a large garden that you have a large house?
  856. > > >
  857. > > >Chris: - As it happens I've got a five bedroom house... built it
  858. >myself!
  859. > > >
  860. > > >Suit: - Well given that you've built a five-bedroom house it is logical
  861. > > >to asume that you haven't built it just for yourself and that you are
  862. > > >quite
  863. > > >probably married?
  864. > > >
  865. > > >Chris: - Yes I am married, I live with my wife and three children!
  866. > > >
  867. > > >Suit: - Well then it is logical to assume that you are sexually active
  868. > > >with your wife on a regular basis?
  869. > > >
  870. > > >Chris:- Yep! Four nights a week!
  871. > > >
  872. > > >Suit: - Well then it is logical to suggest that you do not masturbate
  873. > > >very often?
  874. > > >
  875. > > >Chris: - Me? Never.
  876. > > >
  877. > > >Suit: - Well there you are! That's logical science at work!
  878. > > >
  879. > > >Chris:- How's that then?
  880. > > >
  881. > > >Suit: - Well from finding out that you had a goldfish, I've told you
  882. > > >about the size of garden you have, size of house, your family and your
  883. > > >sex
  884. > > >life!
  885. > > >
  886. > > >Chris: - I see! That's pretty impressive... thanks mate!
  887. > > >
  888. > > >Both leave the toilet and Chris returns to his mate.
  889. > > >
  890. > > >James: - I see the suit was in there. Did you ask him what he does?
  891. > > >
  892. > > >Chris: - Yep! He's a logical scientist!
  893. > > >
  894. > > >James: What's a logical Scientist?
  895. > > >
  896. > > >Chris: - I'll try and explain. Do you have a goldfish?
  897. > > >
  898. > > >James: - Nope.
  899. > > >
  900. > > >Chris: - Well then, you're a wanker.
  901. >
  902. _________________________________________________________________________
  903. Get Your Private, Free E-mail from MSN Hotmail at http://www.hotmail.com.
  904. `
  905. /*
  906. 1 0 166 1514
  907. 1.1 186 260 259
  908. 1.2 280 374 416
  909. 1.3 437 530 584
  910. 1.4 605 769 1514
  911. */
  912. func TestStreamMimeProcessor(t *testing.T) {
  913. if err := os.Truncate("tests/testlog", 0); err != nil {
  914. t.Error(err)
  915. }
  916. cfg := &AppConfig{
  917. LogFile: "tests/testlog",
  918. AllowedHosts: []string{"grr.la"},
  919. BackendConfig: backends.BackendConfig{
  920. "save_process": "HeadersParser|Debugger",
  921. "stream_save_process": "mimeanalyzer|headersparser|compress|Decompress|debug",
  922. },
  923. }
  924. d := Daemon{Config: cfg}
  925. if err := d.Start(); err != nil {
  926. t.Error(err)
  927. }
  928. go func() {
  929. time.Sleep(time.Second * 15)
  930. // for debugging deadlocks
  931. //pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
  932. //os.Exit(1)
  933. }()
  934. // change \n to \r\n
  935. mime0 = strings.Replace(mime2, "\n", "\r\n", -1)
  936. // lets have a talk with the server
  937. if err := talkToServer("127.0.0.1:2525", mime0); err != nil {
  938. t.Error(err)
  939. }
  940. d.Shutdown()
  941. b, err := ioutil.ReadFile("tests/testlog")
  942. if err != nil {
  943. t.Error("could not read logfile")
  944. return
  945. }
  946. // lets check for fingerprints
  947. if strings.Index(string(b), "Debug stream") < 0 {
  948. t.Error("did not log: Debug stream")
  949. }
  950. if strings.Index(string(b), "Error") != -1 {
  951. t.Error("There was an error", string(b))
  952. }
  953. }
  954. var email = `From: Al Gore <[email protected]>
  955. To: White House Transportation Coordinator <[email protected]>
  956. Subject: [Fwd: Map of Argentina with Description]
  957. MIME-Version: 1.0
  958. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed; s=ncr424; d=reliancegeneral.co.in;
  959. h=List-Unsubscribe:MIME-Version:From:To:Reply-To:Date:Subject:Content-Type:Content-Transfer-Encoding:Message-ID; [email protected];
  960. bh=F4UQPGEkpmh54C7v3DL8mm2db1QhZU4gRHR1jDqffG8=;
  961. b=MVltcq6/I9b218a370fuNFLNinR9zQcdBSmzttFkZ7TvV2mOsGrzrwORT8PKYq4KNJNOLBahswXf
  962. GwaMjDKT/5TXzegdX/L3f/X4bMAEO1einn+nUkVGLK4zVQus+KGqm4oP7uVXjqp70PWXScyWWkbT
  963. 1PGUwRfPd/HTJG5IUqs=
  964. Content-Type: multipart/mixed;
  965. boundary="D7F------------D7FD5A0B8AB9C65CCDBFA872"
  966. This is a multi-part message in MIME format.
  967. --D7F------------D7FD5A0B8AB9C65CCDBFA872
  968. Content-Type: text/plain; charset=us-ascii
  969. Content-Transfer-Encoding: 7bit
  970. Fred,
  971. Fire up Air Force One! We're going South!
  972. Thanks,
  973. Al
  974. --D7F------------D7FD5A0B8AB9C65CCDBFA872
  975. Content-Type: message/rfc822
  976. Content-Transfer-Encoding: 7bit
  977. Content-Disposition: inline
  978. Return-Path: <[email protected]>
  979. Received: from mailhost.whitehouse.gov ([192.168.51.200])
  980. by heartbeat.whitehouse.gov (8.8.8/8.8.8) with ESMTP id SAA22453
  981. for <[email protected]>;
  982. Mon, 13 Aug 1998 l8:14:23 +1000
  983. Received: from the_big_box.whitehouse.gov ([192.168.51.50])
  984. by mailhost.whitehouse.gov (8.8.8/8.8.7) with ESMTP id RAA20366
  985. for [email protected]; Mon, 13 Aug 1998 17:42:41 +1000
  986. Date: Mon, 13 Aug 1998 17:42:41 +1000
  987. Message-Id: <[email protected]>
  988. From: Bill Clinton <[email protected]>
  989. To: A1 (The Enforcer) Gore <[email protected]>
  990. Subject: Map of Argentina with Description
  991. MIME-Version: 1.0
  992. Content-Type: multipart/mixed;
  993. boundary="DC8------------DC8638F443D87A7F0726DEF7"
  994. This is a multi-part message in MIME format.
  995. --DC8------------DC8638F443D87A7F0726DEF7
  996. Content-Type: text/plain; charset=us-ascii
  997. Content-Transfer-Encoding: 7bit
  998. Hi A1,
  999. I finally figured out this MIME thing. Pretty cool. I'll send you
  1000. some sax music in .au files next week!
  1001. Anyway, the attached image is really too small to get a good look at
  1002. Argentina. Try this for a much better map:
  1003. http://www.1one1yp1anet.com/dest/sam/graphics/map-arg.htm
  1004. Then again, shouldn't the CIA have something like that?
  1005. Bill
  1006. --DC8------------DC8638F443D87A7F0726DEF7
  1007. Content-Type: image/gif; name="map_of_Argentina.gif"
  1008. Content-Transfer-Encoding: base64
  1009. Content-Disposition: inline; filename="map_of_Argentina.gif"
  1010. R01GOD1hJQA1AKIAAP/////78P/omn19fQAAAAAAAAAAAAAAACwAAAAAJQA1AAAD7Qi63P5w
  1011. wEmjBCLrnQnhYCgM1wh+pkgqqeC9XrutmBm7hAK3tP31gFcAiFKVQrGFR6kscnonTe7FAAad
  1012. GugmRu3CmiBt57fsVq3Y0VFKnpYdxPC6M7Ze4crnnHum4oN6LFJ1bn5NXTN7OF5fQkN5WYow
  1013. BEN2dkGQGWJtSzqGTICJgnQuTJN/WJsojad9qXMuhIWdjXKjY4tenjo6tjVssk2gaWq3uGNX
  1014. U6ZGxseyk8SasGw3J9GRzdTQky1iHNvcPNNI4TLeKdfMvy0vMqLrItvuxfDW8ubjueDtJufz
  1015. 7itICBxISKDBgwgTKjyYAAA7
  1016. --DC8------------DC8638F443D87A7F0726DEF7--
  1017. --D7F------------D7FD5A0B8AB9C65CCDBFA872--
  1018. `
  1019. func TestStreamChunkSaver(t *testing.T) {
  1020. if err := os.Truncate("tests/testlog", 0); err != nil {
  1021. t.Error(err)
  1022. }
  1023. go func() {
  1024. time.Sleep(time.Second * 15)
  1025. // for debugging deadlocks
  1026. //pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
  1027. //os.Exit(1)
  1028. }()
  1029. cfg := &AppConfig{
  1030. LogFile: "tests/testlog",
  1031. AllowedHosts: []string{"grr.la"},
  1032. BackendConfig: backends.BackendConfig{
  1033. "stream_save_process": "mimeanalyzer|chunksaver",
  1034. "chunksaver_chunk_size": 1024 * 32,
  1035. "stream_buffer_size": 1024 * 16,
  1036. "chunksaver_storage_engine": "memory",
  1037. },
  1038. }
  1039. d := Daemon{Config: cfg}
  1040. if err := d.Start(); err != nil {
  1041. t.Error(err)
  1042. }
  1043. // change \n to \r\n
  1044. email = strings.Replace(email, "\n", "\r\n", -1)
  1045. // lets have a talk with the server
  1046. if err := talkToServer("127.0.0.1:2525", email); err != nil {
  1047. t.Error(err)
  1048. }
  1049. time.Sleep(time.Second * 1)
  1050. d.Shutdown()
  1051. b, err := ioutil.ReadFile("tests/testlog")
  1052. if err != nil {
  1053. t.Error("could not read logfile")
  1054. return
  1055. }
  1056. fmt.Println(string(b))
  1057. // lets check for fingerprints
  1058. if strings.Index(string(b), "Debug stream") < 0 {
  1059. // t.Error("did not log: Debug stream")
  1060. }
  1061. }