github.com/glennzw/gophish@v0.8.1-0.20190824020715-24fe998a3aa0/worker/worker.go (about) 1 package worker 2 3 import ( 4 "context" 5 "time" 6 7 log "github.com/gophish/gophish/logger" 8 "github.com/gophish/gophish/mailer" 9 "github.com/gophish/gophish/models" 10 "github.com/sirupsen/logrus" 11 ) 12 13 // Worker is an interface that defines the operations needed for a background worker 14 type Worker interface { 15 Start() 16 LaunchCampaign(c models.Campaign) 17 SendTestEmail(s *models.EmailRequest) error 18 } 19 20 // DefaultWorker is the background worker that handles watching for new campaigns and sending emails appropriately. 21 type DefaultWorker struct { 22 mailer mailer.Mailer 23 } 24 25 // New creates a new worker object to handle the creation of campaigns 26 func New(options ...func(Worker) error) (Worker, error) { 27 defaultMailer := mailer.NewMailWorker() 28 w := &DefaultWorker{ 29 mailer: defaultMailer, 30 } 31 for _, opt := range options { 32 if err := opt(w); err != nil { 33 return nil, err 34 } 35 } 36 return w, nil 37 } 38 39 // WithMailer sets the mailer for a given worker. 40 // By default, workers use a standard, default mailworker. 41 func WithMailer(m mailer.Mailer) func(*DefaultWorker) error { 42 return func(w *DefaultWorker) error { 43 w.mailer = m 44 return nil 45 } 46 } 47 48 // Start launches the worker to poll the database every minute for any pending maillogs 49 // that need to be processed. 50 func (w *DefaultWorker) Start() { 51 log.Info("Background Worker Started Successfully - Waiting for Campaigns") 52 go w.mailer.Start(context.Background()) 53 for t := range time.Tick(1 * time.Minute) { 54 ms, err := models.GetQueuedMailLogs(t.UTC()) 55 if err != nil { 56 log.Error(err) 57 continue 58 } 59 // Lock the MailLogs (they will be unlocked after processing) 60 err = models.LockMailLogs(ms, true) 61 if err != nil { 62 log.Error(err) 63 continue 64 } 65 // We'll group the maillogs by campaign ID to (sort of) group 66 // them by sending profile. This lets the mailer re-use the Sender 67 // instead of having to re-connect to the SMTP server for every 68 // email. 69 msg := make(map[int64][]mailer.Mail) 70 for _, m := range ms { 71 msg[m.CampaignId] = append(msg[m.CampaignId], m) 72 } 73 74 // Next, we process each group of maillogs in parallel 75 for cid, msc := range msg { 76 go func(cid int64, msc []mailer.Mail) { 77 uid := msc[0].(*models.MailLog).UserId 78 c, err := models.GetCampaign(cid, uid) 79 if err != nil { 80 log.Error(err) 81 errorMail(err, msc) 82 return 83 } 84 if c.Status == models.CampaignQueued { 85 err := c.UpdateStatus(models.CampaignInProgress) 86 if err != nil { 87 log.Error(err) 88 return 89 } 90 } 91 log.WithFields(logrus.Fields{ 92 "num_emails": len(msc), 93 }).Info("Sending emails to mailer for processing") 94 w.mailer.Queue(msc) 95 }(cid, msc) 96 } 97 } 98 } 99 100 // LaunchCampaign starts a campaign 101 func (w *DefaultWorker) LaunchCampaign(c models.Campaign) { 102 ms, err := models.GetMailLogsByCampaign(c.Id) 103 if err != nil { 104 log.Error(err) 105 return 106 } 107 models.LockMailLogs(ms, true) 108 // This is required since you cannot pass a slice of values 109 // that implements an interface as a slice of that interface. 110 mailEntries := []mailer.Mail{} 111 currentTime := time.Now().UTC() 112 for _, m := range ms { 113 // Only send the emails scheduled to be sent for the past minute to 114 // respect the campaign scheduling options 115 if m.SendDate.After(currentTime) { 116 m.Unlock() 117 continue 118 } 119 mailEntries = append(mailEntries, m) 120 } 121 w.mailer.Queue(mailEntries) 122 } 123 124 // SendTestEmail sends a test email 125 func (w *DefaultWorker) SendTestEmail(s *models.EmailRequest) error { 126 go func() { 127 ms := []mailer.Mail{s} 128 w.mailer.Queue(ms) 129 }() 130 return <-s.ErrorChan 131 } 132 133 // errorMail is a helper to handle erroring out a slice of Mail instances 134 // in the case that an unrecoverable error occurs. 135 func errorMail(err error, ms []mailer.Mail) { 136 for _, m := range ms { 137 m.Error(err) 138 } 139 }