abstract.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. package backends
  2. import (
  3. log "github.com/Sirupsen/logrus"
  4. "errors"
  5. "fmt"
  6. "github.com/flashmob/go-guerrilla/envelope"
  7. "reflect"
  8. "strings"
  9. )
  10. type AbstractBackend struct {
  11. config abstractConfig
  12. extend Backend
  13. }
  14. type abstractConfig struct {
  15. LogReceivedMails bool `json:"log_received_mails"`
  16. }
  17. // Your backend should implement this method and set b.config field with a custom config struct
  18. // Therefore, your implementation would have your own custom config type instead of dummyConfig
  19. func (b *AbstractBackend) loadConfig(backendConfig BackendConfig) (err error) {
  20. // Load the backend config for the backend. It has already been unmarshalled
  21. // from the main config file 'backend' config "backend_config"
  22. // Now we need to convert each type and copy into the dummyConfig struct
  23. configType := baseConfig(&abstractConfig{})
  24. bcfg, err := b.extractConfig(backendConfig, configType)
  25. if err != nil {
  26. return err
  27. }
  28. m := bcfg.(*abstractConfig)
  29. b.config = *m
  30. return nil
  31. }
  32. func (b *AbstractBackend) Initialize(config BackendConfig) error {
  33. if b.extend != nil {
  34. return b.extend.loadConfig(config)
  35. }
  36. err := b.loadConfig(config)
  37. if err != nil {
  38. return err
  39. }
  40. return nil
  41. }
  42. func (b *AbstractBackend) Shutdown() error {
  43. if b.extend != nil {
  44. return b.extend.Shutdown()
  45. }
  46. return nil
  47. }
  48. func (b *AbstractBackend) Process(mail *envelope.Envelope) BackendResult {
  49. if b.extend != nil {
  50. return b.extend.Process(mail)
  51. }
  52. mail.ParseHeaders()
  53. if b.config.LogReceivedMails {
  54. log.Infof("Mail from: %s / to: %v", mail.MailFrom.String(), mail.RcptTo)
  55. log.Info("Headers are: %s", mail.Header)
  56. }
  57. return NewBackendResult("250 OK")
  58. }
  59. func (b *AbstractBackend) saveMailWorker(saveMailChan chan *savePayload) {
  60. if b.extend != nil {
  61. b.extend.saveMailWorker(saveMailChan)
  62. return
  63. }
  64. defer func() {
  65. if r := recover(); r != nil {
  66. // recover form closed channel
  67. fmt.Println("Recovered in f", r)
  68. }
  69. // close any connections / files
  70. // ...
  71. }()
  72. for {
  73. payload := <-saveMailChan
  74. if payload == nil {
  75. log.Debug("No more saveMailChan payload")
  76. return
  77. }
  78. // process the email here
  79. result := b.Process(payload.mail)
  80. // if all good
  81. if result.Code() < 300 {
  82. payload.savedNotify <- &saveStatus{nil, "s0m3l337Ha5hva1u3LOL"}
  83. } else {
  84. payload.savedNotify <- &saveStatus{errors.New(result.String()), "s0m3l337Ha5hva1u3LOL"}
  85. }
  86. }
  87. }
  88. func (b *AbstractBackend) getNumberOfWorkers() int {
  89. if b.extend != nil {
  90. return b.extend.getNumberOfWorkers()
  91. }
  92. return 1
  93. }
  94. func (b *AbstractBackend) testSettings() error {
  95. if b.extend != nil {
  96. return b.extend.testSettings()
  97. }
  98. return nil
  99. }
  100. // Load the backend config for the backend. It has already been unmarshalled
  101. // from the main config file 'backend' config "backend_config"
  102. // Now we need to convert each type and copy into the guerrillaDBAndRedisConfig struct
  103. // The reason why using reflection is because we'll get a nice error message if the field is missing
  104. // the alternative solution would be to json.Marshal() and json.Unmarshal() however that will not give us any
  105. // error messages
  106. func (h *AbstractBackend) extractConfig(configData BackendConfig, configType baseConfig) (interface{}, error) {
  107. // Use reflection so that we can provide a nice error message
  108. s := reflect.ValueOf(configType).Elem() // so that we can set the values
  109. m := reflect.ValueOf(configType).Elem()
  110. t := reflect.TypeOf(configType).Elem()
  111. typeOfT := s.Type()
  112. for i := 0; i < m.NumField(); i++ {
  113. f := s.Field(i)
  114. // read the tags of the config struct
  115. field_name := t.Field(i).Tag.Get("json")
  116. if len(field_name) > 0 {
  117. // parse the tag to
  118. // get the field name from struct tag
  119. split := strings.Split(field_name, ",")
  120. field_name = split[0]
  121. } else {
  122. // could have no tag
  123. // so use the reflected field name
  124. field_name = typeOfT.Field(i).Name
  125. }
  126. if f.Type().Name() == "int" {
  127. if intVal, converted := configData[field_name].(float64); converted {
  128. s.Field(i).SetInt(int64(intVal))
  129. } else {
  130. return configType, convertError("property missing/invalid: '" + field_name + "' of expected type: " + f.Type().Name())
  131. }
  132. }
  133. if f.Type().Name() == "string" {
  134. if stringVal, converted := configData[field_name].(string); converted {
  135. s.Field(i).SetString(stringVal)
  136. } else {
  137. return configType, convertError("missing/invalid: '" + field_name + "' of type: " + f.Type().Name())
  138. }
  139. }
  140. }
  141. return configType, nil
  142. }