2
0

backend.go 6.4 KB

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