mailscanner.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  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) Reconfigure() {
  77. ms.signalChannel <- SignalReconfigure
  78. }
  79. func (ms *MailScanner) checkEmailRegistred(email string) bool {
  80. emails, err := ms.storage.GetAllEmails()
  81. if err != nil {
  82. return false
  83. }
  84. for _, e := range emails {
  85. if email == e {
  86. return true
  87. }
  88. }
  89. return false
  90. }
  91. func (ms *MailScanner) readEmailMaps() {
  92. registredEmails, err := ms.storage.GetAllEmails()
  93. if err != nil {
  94. log.Fatal(err)
  95. return
  96. }
  97. mailPath := config.ConfigInstance().VMailboxBase
  98. emailMaps := make(map[string]string)
  99. mapsFile := config.ConfigInstance().VMailboxMaps
  100. if !utils.FileExists(mapsFile) {
  101. log.Fatal("Could not read virtual mailbox maps")
  102. return
  103. }
  104. file, err := os.Open(mapsFile)
  105. if err != nil {
  106. log.Fatalf("Unable to open virtual mailbox maps %s\n", mapsFile)
  107. }
  108. scanner := bufio.NewScanner(file)
  109. for scanner.Scan() {
  110. emailMapPair := strings.Split(scanner.Text(), " ")
  111. if len(emailMapPair) != 2 {
  112. log.Printf("Invalid record in virtual mailbox maps %s\n", scanner.Text())
  113. continue
  114. }
  115. found := false
  116. email := emailMapPair[0]
  117. for _, registredEmail := range registredEmails {
  118. if email == registredEmail {
  119. found = true
  120. }
  121. }
  122. if !found {
  123. log.Fatalf("Found non-registred mailbox <%s> in mail maps. Database has inconsistancy.\n", email)
  124. return
  125. }
  126. emailMaps[email] = mailPath + "/" + emailMapPair[1]
  127. }
  128. for _, registredEmail := range registredEmails {
  129. if _, exists := emailMaps[registredEmail]; !exists {
  130. log.Fatalf("Found existing mailbox <%s> in database. Mail maps has inconsistancy.\n", registredEmail)
  131. }
  132. }
  133. ms.emailMaps = emailMaps
  134. for mailbox, mailPath := range ms.emailMaps {
  135. if !utils.FileExists(mailPath) {
  136. file, err := os.Create(mailPath)
  137. if err != nil {
  138. fmt.Printf("Unable to create mailbox for watching %s\n", err)
  139. continue
  140. }
  141. file.Close()
  142. }
  143. mails := ms.readMailFile(mailPath)
  144. for _, mail := range mails {
  145. ms.storage.SaveMail(mailbox, common.Inbox, mail, false)
  146. }
  147. log.Printf("New email for %s, emails read %d", mailPath, len(mails))
  148. err := ms.watcher.Add(mailPath)
  149. if err != nil {
  150. fmt.Printf("Unable to add mailbox for watching\n")
  151. } else {
  152. fmt.Printf("Add mail file %s for watching\n", mailPath)
  153. }
  154. }
  155. }
  156. func (ms *MailScanner) Run() {
  157. go func() {
  158. ms.readEmailMaps()
  159. for {
  160. select {
  161. case signal := <-ms.signalChannel:
  162. switch signal {
  163. case SignalReconfigure:
  164. ms.readEmailMaps()
  165. }
  166. case event, ok := <-ms.watcher.Events:
  167. if !ok {
  168. return
  169. }
  170. if event.Op&fsnotify.Write == fsnotify.Write {
  171. log.Println("iNotify write")
  172. mailPath := event.Name
  173. mailbox := ""
  174. for k, v := range ms.emailMaps {
  175. if v == mailPath {
  176. mailbox = k
  177. }
  178. }
  179. if mailbox != "" {
  180. mails := ms.readMailFile(mailPath)
  181. if len(mails) > 0 {
  182. ms.notifyMailboxUpdate(mailbox)
  183. }
  184. for _, mail := range mails {
  185. ms.storage.SaveMail(mailbox, common.Inbox, mail, false)
  186. ms.notifyNewMail(mailbox, *mail)
  187. }
  188. log.Printf("New email for %s, emails read %d", mailPath, len(mails))
  189. } else {
  190. log.Printf("Invalid path update triggered: %s", mailPath)
  191. }
  192. }
  193. case err, ok := <-ms.watcher.Errors:
  194. if !ok {
  195. return
  196. }
  197. log.Println("error:", err)
  198. }
  199. }
  200. }()
  201. }
  202. func (ms *MailScanner) Stop() {
  203. defer ms.watcher.Close()
  204. }
  205. func (ms *MailScanner) readMailFile(mailPath string) (mails []*common.Mail) {
  206. log.Println("Read mail file")
  207. defer log.Println("Exit read mail file")
  208. if !utils.FileExists(mailPath) {
  209. return nil
  210. }
  211. file, err := utils.OpenAndLockWait(mailPath)
  212. if err != nil {
  213. return nil
  214. }
  215. defer file.CloseAndUnlock()
  216. mails = parseFile(file)
  217. if len(mails) > 0 {
  218. file.Truncate(0)
  219. }
  220. return mails
  221. }
  222. func (ms *MailScanner) RegisterNotifier(notifier common.Notifier) {
  223. if notifier != nil {
  224. ms.notifiersLock.Lock()
  225. defer ms.notifiersLock.Unlock()
  226. ms.notifiers = append(ms.notifiers, notifier)
  227. }
  228. }
  229. func (ms *MailScanner) notifyNewMail(email string, mail common.Mail) {
  230. ms.notifiersLock.Lock()
  231. defer ms.notifiersLock.Unlock()
  232. for _, notifier := range ms.notifiers {
  233. notifier.NotifyNewMail(email, mail)
  234. }
  235. }
  236. func (ms *MailScanner) notifyMailboxUpdate(email string) {
  237. ms.notifiersLock.Lock()
  238. defer ms.notifiersLock.Unlock()
  239. for _, notifier := range ms.notifiers {
  240. notifier.NotifyMaiboxUpdate(email)
  241. }
  242. }