mailscanner.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. /*
  2. * MIT License
  3. *
  4. * Copyright (c) 2020 Alexey Edelev <semlanik@gmail.com>
  5. *
  6. * This file is part of gostfix project https://git.semlanik.org/semlanik/gostfix
  7. *
  8. * Permission is hereby granted, free of charge, to any person obtaining a copy of this
  9. * software and associated documentation files (the "Software"), to deal in the Software
  10. * without restriction, including without limitation the rights to use, copy, modify,
  11. * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
  12. * to permit persons to whom the Software is furnished to do so, subject to the following
  13. * conditions:
  14. *
  15. * The above copyright notice and this permission notice shall be included in all copies
  16. * or substantial portions of the Software.
  17. *
  18. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  19. * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  20. * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
  21. * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  22. * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  23. * DEALINGS IN THE SOFTWARE.
  24. */
  25. package scanner
  26. import (
  27. "bufio"
  28. "fmt"
  29. "log"
  30. "os"
  31. "strings"
  32. "sync"
  33. "git.semlanik.org/semlanik/gostfix/common"
  34. config "git.semlanik.org/semlanik/gostfix/config"
  35. db "git.semlanik.org/semlanik/gostfix/db"
  36. utils "git.semlanik.org/semlanik/gostfix/utils"
  37. fsnotify "github.com/fsnotify/fsnotify"
  38. )
  39. const (
  40. SignalReconfigure = iota
  41. )
  42. type MailScanner struct {
  43. watcher *fsnotify.Watcher
  44. emailMaps map[string]string
  45. storage *db.Storage
  46. signalChannel chan int
  47. notifiers []common.Notifier
  48. notifiersLock sync.Mutex
  49. }
  50. func NewMailScanner() (ms *MailScanner) {
  51. watcher, err := fsnotify.NewWatcher()
  52. if err != nil {
  53. log.Fatal(err)
  54. return
  55. }
  56. storage, err := db.NewStorage()
  57. if err != nil {
  58. log.Fatal(err)
  59. return
  60. }
  61. if !utils.DirectoryExists(config.ConfigInstance().AttachmentsPath) {
  62. err = os.Mkdir(config.ConfigInstance().AttachmentsPath, 0755)
  63. if err != nil {
  64. log.Fatal(err)
  65. return
  66. }
  67. }
  68. ms = &MailScanner{
  69. watcher: watcher,
  70. storage: storage,
  71. signalChannel: make(chan int),
  72. notifiers: []common.Notifier{},
  73. }
  74. return
  75. }
  76. func (ms *MailScanner) checkEmailRegistred(email string) bool {
  77. emails, err := ms.storage.GetAllEmails()
  78. if err != nil {
  79. return false
  80. }
  81. for _, e := range emails {
  82. if email == e {
  83. return true
  84. }
  85. }
  86. return false
  87. }
  88. func (ms *MailScanner) readEmailMaps() {
  89. registredEmails, err := ms.storage.GetAllEmails()
  90. if err != nil {
  91. log.Fatal(err)
  92. return
  93. }
  94. mailPath := config.ConfigInstance().VMailboxBase
  95. emailMaps := make(map[string]string)
  96. mapsFile := config.ConfigInstance().VMailboxMaps
  97. if !utils.FileExists(mapsFile) {
  98. log.Fatal("Could not read virtual mailbox maps")
  99. return
  100. }
  101. file, err := os.Open(mapsFile)
  102. if err != nil {
  103. log.Fatalf("Unable to open virtual mailbox maps %s\n", mapsFile)
  104. }
  105. scanner := bufio.NewScanner(file)
  106. for scanner.Scan() {
  107. emailMapPair := strings.Split(scanner.Text(), " ")
  108. if len(emailMapPair) != 2 {
  109. log.Printf("Invalid record in virtual mailbox maps %s\n", scanner.Text())
  110. continue
  111. }
  112. found := false
  113. email := emailMapPair[0]
  114. for _, registredEmail := range registredEmails {
  115. if email == registredEmail {
  116. found = true
  117. }
  118. }
  119. if !found {
  120. log.Fatalf("Found non-registred mailbox <%s> in mail maps. Database has inconsistancy.\n", email)
  121. return
  122. }
  123. emailMaps[email] = mailPath + "/" + emailMapPair[1]
  124. }
  125. for _, registredEmail := range registredEmails {
  126. if _, exists := emailMaps[registredEmail]; !exists {
  127. log.Fatalf("Found existing mailbox <%s> in database. Mail maps has inconsistancy.\n", registredEmail)
  128. }
  129. }
  130. ms.emailMaps = emailMaps
  131. for mailbox, mailPath := range ms.emailMaps {
  132. if !utils.FileExists(mailPath) {
  133. file, err := os.Create(mailPath)
  134. if err != nil {
  135. fmt.Printf("Unable to create mailbox for watching %s\n", err)
  136. continue
  137. }
  138. file.Close()
  139. }
  140. mails := ms.readMailFile(mailPath)
  141. for _, mail := range mails {
  142. ms.storage.SaveMail(mailbox, common.Inbox, mail, false)
  143. }
  144. log.Printf("New email for %s, emails read %d", mailPath, len(mails))
  145. err := ms.watcher.Add(mailPath)
  146. if err != nil {
  147. fmt.Printf("Unable to add mailbox for watching\n")
  148. } else {
  149. fmt.Printf("Add mail file %s for watching\n", mailPath)
  150. }
  151. }
  152. }
  153. func (ms *MailScanner) Run() {
  154. go func() {
  155. ms.readEmailMaps()
  156. for {
  157. select {
  158. case signal := <-ms.signalChannel:
  159. switch signal {
  160. case SignalReconfigure:
  161. ms.readEmailMaps()
  162. }
  163. case event, ok := <-ms.watcher.Events:
  164. if !ok {
  165. return
  166. }
  167. if event.Op&fsnotify.Write == fsnotify.Write {
  168. log.Println("iNotify write")
  169. mailPath := event.Name
  170. mailbox := ""
  171. for k, v := range ms.emailMaps {
  172. if v == mailPath {
  173. mailbox = k
  174. }
  175. }
  176. if mailbox != "" {
  177. mails := ms.readMailFile(mailPath)
  178. if len(mails) > 0 {
  179. ms.notifyMailboxUpdate(mailbox)
  180. }
  181. for _, mail := range mails {
  182. ms.storage.SaveMail(mailbox, common.Inbox, mail, false)
  183. ms.notifyNewMail(mailbox, *mail)
  184. }
  185. log.Printf("New email for %s, emails read %d", mailPath, len(mails))
  186. } else {
  187. log.Printf("Invalid path update triggered: %s", mailPath)
  188. }
  189. }
  190. case err, ok := <-ms.watcher.Errors:
  191. if !ok {
  192. return
  193. }
  194. log.Println("error:", err)
  195. }
  196. }
  197. }()
  198. }
  199. func (ms *MailScanner) Stop() {
  200. defer ms.watcher.Close()
  201. }
  202. func (ms *MailScanner) readMailFile(mailPath string) (mails []*common.Mail) {
  203. log.Println("Read mail file")
  204. defer log.Println("Exit read mail file")
  205. if !utils.FileExists(mailPath) {
  206. return nil
  207. }
  208. file, err := utils.OpenAndLockWait(mailPath)
  209. if err != nil {
  210. return nil
  211. }
  212. defer file.CloseAndUnlock()
  213. mails = parseFile(file)
  214. if len(mails) > 0 {
  215. file.Truncate(0)
  216. }
  217. return mails
  218. }
  219. func (ms *MailScanner) RegisterNotifier(notifier common.Notifier) {
  220. if notifier != nil {
  221. ms.notifiersLock.Lock()
  222. defer ms.notifiersLock.Unlock()
  223. ms.notifiers = append(ms.notifiers, notifier)
  224. }
  225. }
  226. func (ms *MailScanner) notifyNewMail(email string, mail common.Mail) {
  227. ms.notifiersLock.Lock()
  228. defer ms.notifiersLock.Unlock()
  229. for _, notifier := range ms.notifiers {
  230. notifier.NotifyNewMail(email, mail)
  231. }
  232. }
  233. func (ms *MailScanner) notifyMailboxUpdate(email string) {
  234. ms.notifiersLock.Lock()
  235. defer ms.notifiersLock.Unlock()
  236. for _, notifier := range ms.notifiers {
  237. notifier.NotifyMaiboxUpdate(email)
  238. }
  239. }