123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185 |
- package hermodr
- import (
- "io"
- "net/mail"
- "strings"
- "apiote.xyz/p/asgard/idavollr"
- "apiote.xyz/p/asgard/jotunheim"
- "apiote.xyz/p/gott/v2"
- "github.com/ProtonMail/gopenpgp/v2/helper"
- "github.com/emersion/go-imap"
- "github.com/emersion/go-smtp"
- )
- type EmptyMessageError struct{}
- func (EmptyMessageError) Error() string {
- return "Server didn't return message body"
- }
- type HermodrMailbox struct {
- idavollr.Mailbox
- }
- type HermodrImapMessage struct {
- idavollr.ImapMessage
- config jotunheim.Config
- literal imap.Literal
- mailMsg *mail.Message
- body string
- plain string
- armour string
- }
- func redirectMessages(am idavollr.AbstractMailbox) error {
- m := am.(*HermodrMailbox)
- for msg := range m.Messages() {
- imapMessage := idavollr.ImapMessage{
- Msg: msg,
- Sect: m.Section(),
- }
- imapMessage.SetClient(am.Client())
- r := gott.R[idavollr.AbstractImapMessage]{
- S: &HermodrImapMessage{
- ImapMessage: imapMessage,
- config: m.Conf,
- },
- }.
- Bind(getBodySection).
- Bind(readLiteralMessage).
- Bind(readLiteralBody).
- Map(composePlaintextBody).
- Bind(encrypt).
- Tee(send).
- Tee(markRead).
- Tee(moveMessage)
- if r.E != nil {
- return r.E
- }
- }
- return nil
- }
- func getBodySection(m idavollr.AbstractImapMessage) (idavollr.AbstractImapMessage, error) {
- hm := m.(*HermodrImapMessage)
- r := hm.Message().GetBody(m.Section())
- if r == nil {
- return hm, EmptyMessageError{}
- }
- hm.literal = r
- return hm, nil
- }
- func readLiteralMessage(m idavollr.AbstractImapMessage) (idavollr.AbstractImapMessage, error) {
- hm := m.(*HermodrImapMessage)
- msg, err := mail.ReadMessage(hm.literal)
- hm.mailMsg = msg
- return hm, err
- }
- func readLiteralBody(m idavollr.AbstractImapMessage) (idavollr.AbstractImapMessage, error) {
- hm := m.(*HermodrImapMessage)
- body, err := io.ReadAll(hm.mailMsg.Body)
- hm.body = string(body)
- return hm, err
- }
- func composePlaintextBody(m idavollr.AbstractImapMessage) idavollr.AbstractImapMessage {
- hm := m.(*HermodrImapMessage)
- header := hm.mailMsg.Header
- plainText := "Content-Type: " + header.Get("Content-Type") + "; protected-headers=\"v1\"\r\n"
- plainText += "From: " + header.Get("From") + "\r\n"
- plainText += "Message-ID: " + header.Get("Message-ID") + "\r\n"
- plainText += "Subject: " + header.Get("Subject") + "\r\n"
- plainText += "\r\n"
- plainText += string(hm.body)
- hm.plain = plainText
- return hm
- }
- func encrypt(m idavollr.AbstractImapMessage) (idavollr.AbstractImapMessage, error) {
- hm := m.(*HermodrImapMessage)
- armour, err := helper.EncryptMessageArmored(hm.config.Hermodr.PublicKey, hm.plain)
- hm.armour = armour
- return hm, err
- }
- func send(m idavollr.AbstractImapMessage) error {
- hm := m.(*HermodrImapMessage)
- from := hm.mailMsg.Header.Get("From")
- date := hm.mailMsg.Header.Get("Date")
- messageID := hm.mailMsg.Header.Get("Message-ID")
- to := []string{hm.config.Hermodr.Recipient}
- msg := strings.NewReader("To: " + hm.config.Hermodr.Recipient + "\r\n" +
- "From: " + from + "\r\n" +
- "Date: " + date + "\r\n" +
- "Message-ID: " + messageID + "\r\n" +
- "MIME-Version: 1.0\r\n" +
- "Subject: ...\r\n" +
- "Content-Type: multipart/encrypted; protocol=\"application/pgp-encrypted\"; boundary=\"---------------------997d365ae018229dc62ea2ff6b617cac\"; charset=utf-8\r\n" +
- "\r\n" +
- "This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)\r\n" +
- "-----------------------997d365ae018229dc62ea2ff6b617cac\r\n" +
- "Content-Type: application/pgp-encrypted\r\n" +
- "Content-Description: PGP/MIME version identification\r\n" +
- "\r\n" +
- "Version: 1\r\n" +
- "\r\n" +
- "-----------------------997d365ae018229dc62ea2ff6b617cac\r\n" +
- "Content-Type: application/octet-stream; name=\"encrypted.asc\"\r\n" +
- "Content-Description: OpenPGP encrypted message\r\n" +
- "Content-Disposition: inline; filename=\"encrypted.asc\"\r\n" +
- "\r\n" +
- hm.armour +
- "\r\n" +
- "\r\n" +
- "-----------------------997d365ae018229dc62ea2ff6b617cac--\r\n")
- err := smtp.SendMail(hm.config.Hermodr.SmtpServer, nil, hm.config.Hermodr.SmtpUsername, to, msg) // TODO send with login
- return err
- }
- func markRead(m idavollr.AbstractImapMessage) error {
- seqset := new(imap.SeqSet)
- seqset.AddNum(1) // TODO collect seqset
- item := imap.FormatFlagsOp(imap.AddFlags, true)
- flags := []interface{}{imap.SeenFlag}
- err := m.Client().Store(seqset, item, flags, nil) // TODO store outside of loop
- return err
- }
- func moveMessage(m idavollr.AbstractImapMessage) error { // TODO collect messages out of loop and move all
- hm := m.(*HermodrImapMessage)
- return idavollr.MoveMsg(m.Client(), hm.Msg, hm.config.Hermodr.ImapFolderRedirected)
- }
- func Hermodr(config jotunheim.Config) error {
- mailbox := &HermodrMailbox{
- Mailbox: idavollr.Mailbox{
- MboxName: config.Hermodr.ImapFolderInbox,
- ImapAdr: config.Hermodr.ImapAddress,
- ImapUser: config.Hermodr.ImapUsername,
- ImapPass: config.Hermodr.ImapPassword,
- Conf: config,
- },
- }
- mailbox.SetupChannels()
- r := gott.R[idavollr.AbstractMailbox]{
- S: mailbox,
- }.
- Bind(idavollr.ConnectInsecure).
- Tee(idavollr.Login).
- Bind(idavollr.SelectInbox).
- Tee(idavollr.CheckEmptyBox).
- Map(idavollr.FetchMessages).
- Tee(redirectMessages).
- Recover(idavollr.IgnoreEmptyBox).
- Recover(idavollr.Disconnect)
- return r.E
- }
|