diff --git a/README.md b/README.md index ec645cb..750d077 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,9 @@ Support input formats: * **.zip** file: zipped dmarc report in xml format +* **.eml** file: an electronic mail format or email saved in plain text - dovecot-report-converter + tries to extract .xml, .gz or .zip attachments from found eml files to `input.dir` + Support output formats: * **html_static** output file is a HTML, generated from builtin template htmlStaticTmpl (consts.go). diff --git a/cmd/dmarc-report-converter/files.go b/cmd/dmarc-report-converter/files.go index f8a5b89..3d72c11 100644 --- a/cmd/dmarc-report-converter/files.go +++ b/cmd/dmarc-report-converter/files.go @@ -54,6 +54,37 @@ func (c *filesConverter) ConvertWrite() error { } func (c *filesConverter) find() error { + emlFiles, err := filepath.Glob(filepath.Join(c.cfg.Input.Dir, "*.eml")) + if err != nil { + return err + } + if len(emlFiles) > 0 { + log.Printf("[INFO] files: found %d eml file(s), extract attachments to %v", len(emlFiles), c.cfg.Input.Dir) + for _, eml := range emlFiles { + br, err := os.Open(eml) + if err != nil { + log.Printf("[ERROR] files: unable to extract attachments from %v", eml) + continue + } + + isSuccess, err := extractAttachment(br, c.cfg.Input.Dir) + if err != nil { + log.Printf("[ERROR] files: %v, skip", err) + continue + } + + if isSuccess && c.cfg.Input.Delete { + log.Printf("[DEBUG] files: delete %v", eml) + err := os.Remove(eml) + if err != nil { + log.Printf("[ERROR] files: %v", err) + continue + } + } + br.Close() + } + } + files, err := filepath.Glob(filepath.Join(c.cfg.Input.Dir, "*.*")) if err != nil { return err diff --git a/cmd/dmarc-report-converter/imap.go b/cmd/dmarc-report-converter/imap.go index 059538d..f753396 100644 --- a/cmd/dmarc-report-converter/imap.go +++ b/cmd/dmarc-report-converter/imap.go @@ -86,53 +86,10 @@ func fetchIMAPAttachments(cfg *config) error { return fmt.Errorf("server didn't return message body") } - // create a new mail reader - mr, err := mail.CreateReader(br) + isSuccess, err := extractAttachment(br, cfg.Input.Dir) if err != nil { - return err - } - - // process each message's part - isSuccess := false - for { - p, err := mr.NextPart() - if err == io.EOF { - break - } else if err != nil { - log.Printf("[ERROR] imap: can't read next part: %v, skip", err) - break - } - - switch h := p.Header.(type) { - case *mail.AttachmentHeader: - // this is an attachment - filename, err := h.Filename() - if err != nil { - log.Printf("[ERROR] imap: %v, skip", err) - continue - } - log.Printf("[INFO] imap: found attachment: %v", filename) - - outFile := filepath.Join(cfg.Input.Dir, filename) - log.Printf("[INFO] imap: save attachment to: %v", outFile) - f, err := os.Create(outFile) - if err != nil { - log.Printf("[ERROR] imap: %v, skip", err) - continue - } - - _, err = io.Copy(f, p.Body) - if err != nil { - log.Printf("[ERROR] imap: %v, skip", err) - continue - } - err = f.Close() - if err != nil { - log.Printf("[ERROR] imap: %v, skip", err) - continue - } - isSuccess = true - } + log.Printf("[ERROR] imap: %v, skip", err) + continue } if isSuccess && cfg.Input.IMAP.Delete { @@ -168,3 +125,61 @@ func fetchIMAPAttachments(cfg *config) error { return nil } + +func extractAttachment(r io.Reader, inputDir string) (bool, error) { + // Create a new mail reader + mr, err := mail.CreateReader(r) + if err != nil { + return false, err + } + + // process each message's part + isSuccess := false + for { + p, err := mr.NextPart() + if err == io.EOF { + break + } else if err != nil { + log.Printf("[ERROR] imap: can't read next part: %v, skip", err) + break + } + + switch h := p.Header.(type) { + case *mail.AttachmentHeader: + // this is an attachment + filename, err := h.Filename() + if err != nil { + log.Printf("[ERROR] extractAttachment: %v, skip", err) + continue + } + if filename == "" { + log.Printf("[WARN] extractAttachment: found attachment with empty filename, skip") + continue + } + + log.Printf("[INFO] extractAttachment: found attachment: %v", filename) + + outFile := filepath.Join(inputDir, filename) + log.Printf("[INFO] extractAttachment: save attachment to: %v", outFile) + f, err := os.Create(outFile) + if err != nil { + log.Printf("[ERROR] extractAttachment: %v, skip", err) + continue + } + + _, err = io.Copy(f, p.Body) + if err != nil { + log.Printf("[ERROR] extractAttachment: %v, skip", err) + continue + } + err = f.Close() + if err != nil { + log.Printf("[ERROR] extractAttachment: %v, skip", err) + continue + } + isSuccess = true + } + } + + return isSuccess, err +}