diff --git a/README.md b/README.md index 6bbe042..103cb72 100644 --- a/README.md +++ b/README.md @@ -164,6 +164,8 @@ Gatherers: Gatherers: : Type: imap + Inbox: INBOX // is the default + InboxPrefix: gmail_label // every gmail label starting with Config: Server: server:993 Username: diff --git a/archivar/archiver/archivers/imap/client/imap.go b/archivar/archiver/archivers/imap/client/imap.go index 1a7c1e2..32af4a7 100644 --- a/archivar/archiver/archivers/imap/client/imap.go +++ b/archivar/archiver/archivers/imap/client/imap.go @@ -23,6 +23,8 @@ type Imap struct { username string password string inbox string + inboxPrefix string + processingInbox string allowInsecureSSL bool client *client.Client section *imap.BodySectionName @@ -30,12 +32,13 @@ type Imap struct { logger *logrus.Logger } -func New(server, username, password, inbox string, allowInsecureSSL bool, logger *logrus.Logger) *Imap { +func New(server, username, password, inbox, inboxPrefix string, allowInsecureSSL bool, logger *logrus.Logger) *Imap { i := &Imap{ server: server, username: username, password: password, inbox: inbox, + inboxPrefix: inboxPrefix, allowInsecureSSL: allowInsecureSSL, logger: logger, } @@ -96,7 +99,7 @@ func (i Imap) ProcessMessage(msg *imap.Message, upload archivers.UploadFunc) err mailData.subject = subject } - filePrefixPath := mailData.getFilePath() + filePrefixPath := mailData.getFilePath(i.processingInbox, true, true) if err != nil { log.Fatal(err) @@ -166,7 +169,7 @@ var subjectCleanup = regexp.MustCompile(`[^a-zA-Z0-9\-_ ]+`) const SUBJECT_LENGTH = 30 -func (m mailData) getFilePath() string { +func (m mailData) getFilePath(inbox string, addPlusStringToPath, addInboxToPath bool) string { // TODO add variant options timestamp := fmt.Sprintf( "%04d%02d%02d_%02d%02d%02d", @@ -178,15 +181,19 @@ func (m mailData) getFilePath() string { m.date.Second(), ) - rootDirectory := m.to.Address - foundPlusString := emailPlusPart.FindSubmatch([]byte(m.to.String())) - if len(foundPlusString) > 1 { - rootDirectory = string(foundPlusString[1]) + pathParts := []string{} + pathParts = append(pathParts, inbox) + pathParts = append(pathParts, m.to.Address) + if addPlusStringToPath { + foundPlusString := emailPlusPart.FindSubmatch([]byte(m.to.String())) + if len(foundPlusString) > 1 { + pathParts = append(pathParts, string(foundPlusString[1])) + } } - subjectCleanupPath := subjectCleanup.ReplaceAllString(m.subject, "") + pathParts = append(pathParts, subjectCleanup.ReplaceAllString(timestamp+"-"+m.subject, "")) - return path.Join(rootDirectory, timestamp+"_"+subjectCleanupPath) + return path.Join(pathParts...) } func (i Imap) FlagAndDeleteMessages(readMsgSeq *imap.SeqSet) (err error) { @@ -202,20 +209,76 @@ func (i Imap) FlagAndDeleteMessages(readMsgSeq *imap.SeqSet) (err error) { return } +func (i *Imap) getInboxesByPrefix(prefix string) []string { + i.Connect() + mailboxes := make(chan *imap.MailboxInfo, 10) + done := make(chan error, 1) + go func() { + done <- i.client.List("", prefix+"*", mailboxes) + }() + + inboxes := []string{} + for m := range mailboxes { + inboxes = append(inboxes, m.Name) + } + + return inboxes +} +func (i *Imap) ListInboxes() { + i.Connect() + mailboxes := make(chan *imap.MailboxInfo, 10) + done := make(chan error, 1) + go func() { + done <- i.client.List("", "*", mailboxes) + }() + + log.Println("Mailboxes:") + for m := range mailboxes { + log.Println("* " + m.Name) + } +} func (i *Imap) GetMessages(messageChan chan *imap.Message, deleteDownloaded bool) (err error) { i.Connect() - mbox, err := i.client.Select(i.inbox, false) + inboxes := []string{} + if i.inbox != "" { + inboxes = append(inboxes, i.inbox) + } + + if i.inboxPrefix != "" { + inboxes = append(inboxes, i.getInboxesByPrefix(i.inboxPrefix)...) + } + + for _, inbox := range inboxes { + i.processingInbox = inbox + + err := i.processInboxMessages(inbox, messageChan, deleteDownloaded) + if err != nil { + return nil + } + } + + return nil +} + +func (i *Imap) processInboxMessages(inbox string, messageChan chan *imap.Message, deleteDownloaded bool) (err error) { + mbox, err := i.client.Select(inbox, false) if err != nil { - return + i.ListInboxes() + return err } - i.logger.Debugf("selected '%s'", i.inbox) + i.logger.Debugf("selected '%s'", inbox) criteria := imap.NewSearchCriteria() criteria.WithoutFlags = []string{imap.DeletedFlag} + foundMsgs, err := i.client.Search(criteria) + if err != nil { + return err + } + if mbox.Messages == 0 { i.logger.Debug("no messages") return nil diff --git a/archivar/archiver/archivers/imap/gatherer.go b/archivar/archiver/archivers/imap/gatherer.go index ebb149b..f0c6b3b 100644 --- a/archivar/archiver/archivers/imap/gatherer.go +++ b/archivar/archiver/archivers/imap/gatherer.go @@ -14,6 +14,7 @@ type ImapGathererConfig struct { Username string Password string Inbox string + InboxPrefix string AllowInsecureSSL bool DeleteDownloaded bool } @@ -26,12 +27,14 @@ type ImapGatherer struct { } func NewGatherer(c interface{}, storage archivers.Archiver, logger *logrus.Logger) (i archivers.Gatherer) { - igc := ImapGathererConfig{ - Inbox: "Inbox", - } + igc := ImapGathererConfig{} config.ConfigFromStruct(c, &igc) + if igc.Inbox == "" && igc.InboxPrefix == "" { + igc.Inbox = "Inbox" + } + return &ImapGatherer{ deleteDownloaded: igc.DeleteDownloaded, storage: storage, @@ -41,6 +44,7 @@ func NewGatherer(c interface{}, storage archivers.Archiver, logger *logrus.Logge igc.Username, igc.Password, igc.Inbox, + igc.InboxPrefix, igc.AllowInsecureSSL, logger, ), diff --git a/etc/archivar.yaml.dist b/etc/archivar.yaml.dist index 65fcb8c..1e572f9 100644 --- a/etc/archivar.yaml.dist +++ b/etc/archivar.yaml.dist @@ -49,6 +49,15 @@ Gatherers: Password: Server: https://server/remote.php/dav/files/username/ UploadDirectory: /input_directory/ + imap_gmail_with_prefix_labels: + Type: imap + InboxPrefix: archivar + Config: + Server: server:993 + Username: + Password: + # DeleteDownloaded: False + # AllowInsecureSSL: False imap_mail_account: Type: imap Config: @@ -57,6 +66,7 @@ Gatherers: Password: # DeleteDownloaded: False # AllowInsecureSSL: False + InboxPrefix: archivar Processors: superSecretDataEncrypter: Type: encrypter