backend.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. package backends
  2. import (
  3. "fmt"
  4. "github.com/flashmob/go-guerrilla/envelope"
  5. "github.com/flashmob/go-guerrilla/log"
  6. "reflect"
  7. "strconv"
  8. "strings"
  9. )
  10. var (
  11. mainlog log.Logger
  12. Service *BackendService
  13. // deprecated backends system
  14. backends = map[string]Backend{}
  15. // new backends system
  16. Processors map[string]ProcessorConstructor
  17. )
  18. func init() {
  19. Service = &BackendService{}
  20. Processors = make(map[string]ProcessorConstructor)
  21. }
  22. // Backends process received mail. Depending on the implementation, they can store mail in the database,
  23. // write to a file, check for spam, re-transmit to another server, etc.
  24. // Must return an SMTP message (i.e. "250 OK") and a boolean indicating
  25. // whether the message was processed successfully.
  26. type Backend interface {
  27. // Public methods
  28. Process(*envelope.Envelope) BackendResult
  29. Initialize(BackendConfig) error
  30. Shutdown() error
  31. }
  32. /*
  33. type Worker interface {
  34. // start save mail worker(s)
  35. saveMailWorker(chan *savePayload)
  36. // get the number of workers that will be stared
  37. getNumberOfWorkers() int
  38. // test database settings, permissions, correct paths, etc, before starting workers
  39. // parse the configuration files
  40. loadConfig(BackendConfig) error
  41. Shutdown() error
  42. Process(*envelope.Envelope) BackendResult
  43. Initialize(BackendConfig) error
  44. SetProcessors(p ...Decorator)
  45. }
  46. */
  47. type BackendConfig map[string]interface{}
  48. type ProcessorConstructor func() Decorator
  49. type baseConfig interface{}
  50. type saveStatus struct {
  51. err error
  52. hash string
  53. }
  54. type savePayload struct {
  55. mail *envelope.Envelope
  56. //from *envelope.EmailAddress
  57. //recipient *envelope.EmailAddress
  58. savedNotify chan *saveStatus
  59. }
  60. // BackendResult represents a response to an SMTP client after receiving DATA.
  61. // The String method should return an SMTP message ready to send back to the
  62. // client, for example `250 OK: Message received`.
  63. type BackendResult interface {
  64. fmt.Stringer
  65. // Code should return the SMTP code associated with this response, ie. `250`
  66. Code() int
  67. }
  68. // Internal implementation of BackendResult for use by backend implementations.
  69. type backendResult string
  70. func (br backendResult) String() string {
  71. return string(br)
  72. }
  73. // Parses the SMTP code from the first 3 characters of the SMTP message.
  74. // Returns 554 if code cannot be parsed.
  75. func (br backendResult) Code() int {
  76. trimmed := strings.TrimSpace(string(br))
  77. if len(trimmed) < 3 {
  78. return 554
  79. }
  80. code, err := strconv.Atoi(trimmed[:3])
  81. if err != nil {
  82. return 554
  83. }
  84. return code
  85. }
  86. func NewBackendResult(message string) BackendResult {
  87. return backendResult(message)
  88. }
  89. type ProcessorInitializer interface {
  90. Initialize(backendConfig BackendConfig) error
  91. }
  92. type ProcessorShutdowner interface {
  93. Shutdown() error
  94. }
  95. type Initialize func(backendConfig BackendConfig) error
  96. type Shutdown func() error
  97. // Satisfy ProcessorInitializer interface
  98. // So we can now pass an anonymous function that implements ProcessorInitializer
  99. func (i Initialize) Initialize(backendConfig BackendConfig) error {
  100. // delegate to the anonymous function
  101. return i(backendConfig)
  102. }
  103. // satisfy ProcessorShutdowner interface, same concept as Initialize type
  104. func (s Shutdown) Shutdown() error {
  105. // delegate
  106. return s()
  107. }
  108. type BackendService struct {
  109. ProcessorHandlers
  110. }
  111. type ProcessorHandlers struct {
  112. Initializers []ProcessorInitializer
  113. Shutdowners []ProcessorShutdowner
  114. }
  115. func (b *BackendService) AddInitializer(i ProcessorInitializer) {
  116. b.Initializers = append(b.Initializers, i)
  117. }
  118. func (b *BackendService) AddShutdowner(i ProcessorShutdowner) {
  119. b.Shutdowners = append(b.Shutdowners, i)
  120. }
  121. func (b *BackendService) Initialize(backend BackendConfig) {
  122. for i := range b.Initializers {
  123. b.Initializers[i].Initialize(backend)
  124. }
  125. }
  126. func (b *BackendService) Shutdown() {
  127. for i := range b.Shutdowners {
  128. b.Shutdowners[i].Shutdown()
  129. }
  130. }
  131. // extractConfig loads the backend config. It has already been unmarshalled
  132. // configData contains data from the main config file's "backend_config" value
  133. // configType is a Processor's specific config value.
  134. // The reason why using reflection is because we'll get a nice error message if the field is missing
  135. // the alternative solution would be to json.Marshal() and json.Unmarshal() however that will not give us any
  136. // error messages
  137. func (b *BackendService) extractConfig(configData BackendConfig, configType baseConfig) (interface{}, error) {
  138. // Use reflection so that we can provide a nice error message
  139. s := reflect.ValueOf(configType).Elem() // so that we can set the values
  140. m := reflect.ValueOf(configType).Elem()
  141. t := reflect.TypeOf(configType).Elem()
  142. typeOfT := s.Type()
  143. for i := 0; i < m.NumField(); i++ {
  144. f := s.Field(i)
  145. // read the tags of the config struct
  146. field_name := t.Field(i).Tag.Get("json")
  147. if len(field_name) > 0 {
  148. // parse the tag to
  149. // get the field name from struct tag
  150. split := strings.Split(field_name, ",")
  151. field_name = split[0]
  152. } else {
  153. // could have no tag
  154. // so use the reflected field name
  155. field_name = typeOfT.Field(i).Name
  156. }
  157. if f.Type().Name() == "int" {
  158. if intVal, converted := configData[field_name].(float64); converted {
  159. s.Field(i).SetInt(int64(intVal))
  160. } else {
  161. return configType, convertError("property missing/invalid: '" + field_name + "' of expected type: " + f.Type().Name())
  162. }
  163. }
  164. if f.Type().Name() == "string" {
  165. if stringVal, converted := configData[field_name].(string); converted {
  166. s.Field(i).SetString(stringVal)
  167. } else {
  168. return configType, convertError("missing/invalid: '" + field_name + "' of type: " + f.Type().Name())
  169. }
  170. }
  171. if f.Type().Name() == "bool" {
  172. if boolVal, converted := configData[field_name].(bool); converted {
  173. s.Field(i).SetBool(boolVal)
  174. } else {
  175. return configType, convertError("missing/invalid: '" + field_name + "' of type: " + f.Type().Name())
  176. }
  177. }
  178. }
  179. return configType, nil
  180. }