123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- package main
- import (
- "bytes"
- "database/sql"
- "errors"
- "fmt"
- "io"
- "log"
- "net/http"
- "regexp"
- "strings"
- "github.com/emersion/go-imap"
- "github.com/emersion/go-imap/client"
- "github.com/emersion/go-message"
- _ "github.com/emersion/go-message/charset"
- "github.com/emersion/go-msgauth/dkim"
- )
- func checkRecipientTemplate(recipients []*imap.Address, config Config) (string, error) {
- categories := config.Mímir.Categories
- if len(categories) == 0 {
- return "", nil
- }
- parts := strings.SplitN(config.Mímir.RecipientTemplate, "[:]", 2) // todo if [:] not found
- r, err := regexp.Compile(parts[0] + "(.*)" + parts[1])
- if err != nil {
- return "", err
- }
- for _, recipient := range recipients {
- matches := r.FindStringSubmatch(recipient.Address())
- if len(matches) != 2 {
- return "", errors.New("No category in recipient")
- }
- for _, category := range categories {
- if matches[1] == category {
- return category, nil
- }
- }
- }
- return "", errors.New("Unknown category")
- }
- func getPlainTextPart(r io.Reader) (io.Reader, error) {
- m, err := message.Read(r)
- if err != nil {
- log.Fatal(err)
- }
- if mr := m.MultipartReader(); mr != nil {
- for {
- p, err := mr.NextPart()
- if err == io.EOF {
- break
- } else if err != nil {
- log.Fatal(err)
- }
- t, _, _ := p.Header.ContentType()
- if t == "text/plain" {
- return p.Body, nil
- }
- }
- } else {
- t, _, _ := m.Header.ContentType()
- if t == "text/plain" {
- return m.Body, nil
- }
- }
- return nil, errors.New("text/plain no found")
- }
- func archiveInbox(db *sql.DB, mboxName string, c *client.Client, config Config) error {
- mbox, err := c.Select(mboxName, true)
- if err != nil {
- return err
- }
- fmt.Printf("reading %d messages from %s\n", mbox.Messages, mboxName)
- if mbox.Messages == 0 {
- return nil
- }
- from := uint32(1)
- to := mbox.Messages
- seqset := new(imap.SeqSet)
- seqset.AddRange(from, to)
- section := &imap.BodySectionName{}
- items := []imap.FetchItem{imap.FetchEnvelope, section.FetchItem()}
- messages := make(chan *imap.Message, 10)
- done := make(chan error, 1)
- go func() {
- done <- c.Fetch(seqset, items, messages)
- }()
- for msg := range messages {
- recipients := append(msg.Envelope.To, msg.Envelope.Cc...)
- category, err := checkRecipientTemplate(recipients, config)
- if err != nil {
- return err
- }
- sender := msg.Envelope.From[0]
- messageID := msg.Envelope.MessageId
- subject := msg.Envelope.Subject
- date := msg.Envelope.Date.UTC()
- inReplyTo := msg.Envelope.InReplyTo
- dkimStatus := false
- r := msg.GetBody(section)
- if r == nil {
- return errors.New("No body in message")
- }
- messageBytes, err := io.ReadAll(r)
- if err != nil {
- panic(err)
- }
- r = bytes.NewReader(messageBytes)
- verifications, err := dkim.Verify(r)
- if err != nil {
- fmt.Println("there")
- return err
- }
- for _, v := range verifications {
- if v.Err == nil && sender.HostName == v.Domain {
- dkimStatus = true
- }
- }
- r = bytes.NewReader(messageBytes)
- bodyReader, err := getPlainTextPart(r)
- if err != nil {
- return err
- }
- body, err := io.ReadAll(bodyReader)
- if err != nil {
- return err
- }
- addArchiveEntry(db, messageID, category, subject, body, date, inReplyTo, dkimStatus, sender, recipients)
- if sender.Address() != config.Mímir.PersonalAddress { // todo if forwarding is on
- forwardMessage(config, category, []string{sender.Address(), strings.Replace(config.Mímir.RecipientTemplate, "[:]", category, 1)}, messageID, inReplyTo, subject, body)
- }
- }
- if err := <-done; err != nil {
- return err
- }
- return nil
- }
- func mimir(db *sql.DB, config Config) {
- c, err := client.DialTLS(config.Mímir.ImapAddress, nil)
- if err != nil {
- log.Fatalln(err)
- }
- log.Println("Connected")
- defer c.Logout()
- if err := c.Login(config.Mímir.ImapUsername, config.Mímir.ImapPassword); err != nil {
- log.Fatalln(err)
- }
- log.Println("Logged in")
- err = archiveInbox(db, config.Mímir.ImapInbox, c, config)
- if err != nil {
- log.Fatalln(err)
- }
- }
- func mimir_serve(w http.ResponseWriter, r *http.Request) {
- // todo
- }
|