package main import ( "bytes" "fmt" "io" "log" "mime" "strconv" "strings" "github.com/emersion/go-imap/v2" "github.com/emersion/go-imap/v2/imapclient" "github.com/emersion/go-message/charset" "github.com/emersion/go-message/mail" ) type MailBox struct { Server string Port uint16 User string Password string SSL string InBox string Client *imapclient.Client } func (mb *MailBox) Configure(conf *MailBoxConfiguration) error { mb.Server = conf.Server port, err := strconv.ParseUint(conf.Port, 10, 16) if err != nil { return err } mb.Port = uint16(port) mb.User = conf.User mb.Password = conf.Password mb.SSL = conf.SSL mb.InBox = conf.InBox return nil } func (mb *MailBox) ListMessages() (string, error) { return "", nil } func (mb *MailBox) Connect() error { options := &imapclient.Options{ WordDecoder: &mime.WordDecoder{CharsetReader: charset.Reader}, } var err error imapServer := fmt.Sprintf("%s:%d", mb.Server, mb.Port) switch mb.SSL { case "SSL/TLS": mb.Client, err = imapclient.DialTLS(imapServer, options) case "StartTLS": mb.Client, err = imapclient.DialStartTLS(imapServer, options) case "NoTLS": mb.Client, err = imapclient.DialInsecure(imapServer, options) default: return fmt.Errorf("bad tls configuration") } if err != nil { log.Println("Error connnecting to", imapServer, ":", err) return err } log.Println("Connected") err = mb.Client.Login(mb.User, mb.Password).Wait() if err != nil { log.Fatal("failed to login:", err) } log.Println("Logged in") return nil } func (mb *MailBox) ListFolders() ([]string, error) { folders := make([]string, 0) mailBoxes, err := mb.Client.List("", "%", nil).Collect() if err != nil { return nil, err } for _, mbox := range mailBoxes { folders = append(folders, mbox.Mailbox) } return folders, nil } func (mb *MailBox) GetMessages() ([]BlogEntry, error) { inbox, err := mb.Client.Select(mb.InBox, nil).Wait() if err != nil { log.Fatal("Error selecting mailbox:", mb.InBox, err) } log.Println("Inbox has", inbox.NumMessages, "messages") if inbox.NumMessages <= 0 { return nil, fmt.Errorf("nomessages") } entries := make([]BlogEntry, 0) for i := uint32(1); i <= inbox.NumMessages; i++ { var entry BlogEntry seqSet := imap.SeqSetNum(i) bodySection := &imap.FetchItemBodySection{} fetchOptions := &imap.FetchOptions{ Envelope: true, BodySection: []*imap.FetchItemBodySection{bodySection}, } messages, err := mb.Client.Fetch(seqSet, fetchOptions).Collect() if err != nil { log.Fatal("Error fetching mails:", err) } entry.Title = strings.Trim(messages[0].Envelope.Subject, "\t ") if !strings.HasPrefix(strings.ToLower(entry.Title), "blog ") { continue } entry.Id = messages[0].Envelope.MessageID entry.Title = entry.Title[5:] entry.Author = messages[0].Envelope.From[0].Name section := messages[0].FindBodySection(bodySection) ioReader := bytes.NewReader(section) mailReader, err := mail.CreateReader(ioReader) if err != nil { return nil, err } for { part, err := mailReader.NextPart() if err == io.EOF { break } else if err != nil { return nil, err } switch part.Header.(type) { case *mail.AttachmentHeader: /* filename, _ := header.Filename() log.Println("------------FILE-----------") log.Println("Attachment:", filename) */ case *mail.InlineHeader: body, _ := io.ReadAll(part.Body) contentTypeFull := strings.TrimPrefix( strings.ToLower(part.Header.Get("Content-Type")), "content-type") mediaType, _, err := mime.ParseMediaType(strings.TrimLeft(contentTypeFull, ": ")) if err != nil { return nil, err } switch mediaType { case "text/plain": entry.Text = string(body) case "text/html": entry.HTML = string(body) default: log.Println("Content-Type:", part.Header.Get("Content-Type")) } default: log.Printf("Unkown part type %+v", part) } } entries = append(entries, entry) } return entries, nil } func (mb *MailBox) Close() { mb.Client.Close() }