From bf521a9b294bf710a591a35db216bf95b111f932 Mon Sep 17 00:00:00 2001 From: Christian Ezeani Date: Fri, 26 Jul 2024 23:06:23 +0100 Subject: [PATCH] WIP: smtp package --- go.mod | 5 +++ go.sum | 2 + imap/fn-init.go | 10 +++++ imap/imap.go | 5 +++ message.go | 6 +++ person.go | 36 ++++++++++++++++ pop3/fn-init.go | 10 +++++ pop3/pop3.go | 5 +++ smtp/attachment-file.go | 9 ++++ smtp/attachment-reader.go | 9 ++++ smtp/conn.go | 46 +++++++++++++++++++++ smtp/content.go | 55 +++++++++++++++++++++++++ smtp/envelope.go | 86 +++++++++++++++++++++++++++++++++++++++ smtp/mail.go | 24 +++++++++++ smtp/smtp.go | 76 ++++++++++++++++++++++++++++++++++ 15 files changed, 384 insertions(+) create mode 100644 go.mod create mode 100644 go.sum create mode 100644 imap/fn-init.go create mode 100644 imap/imap.go create mode 100644 message.go create mode 100644 person.go create mode 100644 pop3/fn-init.go create mode 100644 pop3/pop3.go create mode 100644 smtp/attachment-file.go create mode 100644 smtp/attachment-reader.go create mode 100644 smtp/conn.go create mode 100644 smtp/content.go create mode 100644 smtp/envelope.go create mode 100644 smtp/mail.go create mode 100644 smtp/smtp.go diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..c51be33 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module cyberpull.com/gomail + +go 1.22.5 + +require cyberpull.com/gokit v1.3.1 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..1408eff --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +cyberpull.com/gokit v1.3.1 h1:Q5xhL0BitMazjNpGFj7jfztsaVRAibod2WL5MGANCu8= +cyberpull.com/gokit v1.3.1/go.mod h1:VhMdwjZLITNSOT/Dj2FsMA/ZD6PUCLdKkBw1w3P6//k= diff --git a/imap/fn-init.go b/imap/fn-init.go new file mode 100644 index 0000000..8f2f61f --- /dev/null +++ b/imap/fn-init.go @@ -0,0 +1,10 @@ +package imap + +type initializable interface { + initialize() +} + +func d[T initializable](x T) T { + x.initialize() + return x +} diff --git a/imap/imap.go b/imap/imap.go new file mode 100644 index 0000000..22bab73 --- /dev/null +++ b/imap/imap.go @@ -0,0 +1,5 @@ +package imap + +type IMAP struct { + // +} diff --git a/message.go b/message.go new file mode 100644 index 0000000..0be0e09 --- /dev/null +++ b/message.go @@ -0,0 +1,6 @@ +package gomail + +type Message struct { + HTML string + Text string +} diff --git a/person.go b/person.go new file mode 100644 index 0000000..f4661d2 --- /dev/null +++ b/person.go @@ -0,0 +1,36 @@ +package gomail + +import ( + "fmt" + "strings" +) + +type Person struct { + Name string + Email string +} + +func (x *Person) toEmailWithName() string { + value := x.toEmail() + + x.Name = strings.TrimSpace(x.Name) + + if x.Name != "" { + value = fmt.Sprintf(`"%s" %s`, x.Name, value) + } + + return value +} + +func (x *Person) toEmail() string { + if x.Email == "" { + return x.Email + } + + return fmt.Sprintf("<%s>", x.Email) +} + +func (x *Person) clear() { + x.Name = "" + x.Email = "" +} diff --git a/pop3/fn-init.go b/pop3/fn-init.go new file mode 100644 index 0000000..02208ab --- /dev/null +++ b/pop3/fn-init.go @@ -0,0 +1,10 @@ +package pop3 + +type initializable interface { + initialize() +} + +func d[T initializable](x T) T { + x.initialize() + return x +} diff --git a/pop3/pop3.go b/pop3/pop3.go new file mode 100644 index 0000000..81d4085 --- /dev/null +++ b/pop3/pop3.go @@ -0,0 +1,5 @@ +package pop3 + +type POP3 struct { + // +} diff --git a/smtp/attachment-file.go b/smtp/attachment-file.go new file mode 100644 index 0000000..6789f15 --- /dev/null +++ b/smtp/attachment-file.go @@ -0,0 +1,9 @@ +package smtp + +type AttachmentFile struct { + // +} + +func (x *AttachmentFile) read() (b []byte, err error) { + return +} diff --git a/smtp/attachment-reader.go b/smtp/attachment-reader.go new file mode 100644 index 0000000..c35b110 --- /dev/null +++ b/smtp/attachment-reader.go @@ -0,0 +1,9 @@ +package smtp + +type AttachmentReader struct { + // +} + +func (x *AttachmentReader) read() (b []byte, err error) { + return +} diff --git a/smtp/conn.go b/smtp/conn.go new file mode 100644 index 0000000..762cabe --- /dev/null +++ b/smtp/conn.go @@ -0,0 +1,46 @@ +package smtp + +import ( + "bufio" + "net" + "sync" +) + +type Conn struct { + wm sync.Mutex + rm sync.Mutex + conn net.Conn + reader *bufio.Reader +} + +func (x *Conn) Read(b []byte) (n int, err error) { + x.rm.Lock() + + defer x.rm.Unlock() + + return x.conn.Read(b) +} + +func (x *Conn) Write(b []byte) (n int, err error) { + x.wm.Lock() + + defer x.wm.Unlock() + + return x.conn.Write(b) +} + +func (x *Conn) Close() (err error) { + if x.conn != nil { + err = x.conn.Close() + } + + return +} + +// ====================== + +func newConn(conn net.Conn) *Conn { + value := &Conn{conn: conn} + value.reader = bufio.NewReader(value) + return value +} diff --git a/smtp/content.go b/smtp/content.go new file mode 100644 index 0000000..1b276ba --- /dev/null +++ b/smtp/content.go @@ -0,0 +1,55 @@ +package smtp + +import "sync" + +type Attachment interface { + read() (b []byte, err error) +} + +type Content struct { + mutex sync.Mutex + attachment []Attachment + subject string + html string + text string +} + +func (x *Content) Attachment(attachments ...Attachment) { + x.mutex.Lock() + + defer x.mutex.Unlock() + + x.subject = subject +} + +func (x *Content) Subject(subject string) { + x.mutex.Lock() + + defer x.mutex.Unlock() + + x.subject = subject +} + +func (x *Content) HTML(html string) { + x.mutex.Lock() + + defer x.mutex.Unlock() + + x.html = html +} + +func (x *Content) Text(text string) { + x.mutex.Lock() + + defer x.mutex.Unlock() + + x.text = text +} + +func (x *Content) validate() (err error) { + x.mutex.Lock() + + defer x.mutex.Unlock() + + return +} diff --git a/smtp/envelope.go b/smtp/envelope.go new file mode 100644 index 0000000..c8e958b --- /dev/null +++ b/smtp/envelope.go @@ -0,0 +1,86 @@ +package smtp + +import ( + "sync" + + "cyberpull.com/gomail" +) + +type Envelope struct { + mutex sync.Mutex + From gomail.Person + ReplyTo gomail.Person + To []gomail.Person + Cc []gomail.Person + Bcc []gomail.Person +} + +func (x *Envelope) AddTo(persons ...gomail.Person) { + x.mutex.Lock() + + defer x.mutex.Unlock() + + x.initialize() + + for _, person := range persons { + if person.Email == "" { + continue + } + + x.To = append(x.To, person) + } +} + +func (x *Envelope) AddCc(persons ...gomail.Person) { + x.mutex.Lock() + + defer x.mutex.Unlock() + + x.initialize() + + for _, person := range persons { + if person.Email == "" { + continue + } + + x.Cc = append(x.Cc, person) + } +} + +func (x *Envelope) AddBcc(persons ...gomail.Person) { + x.mutex.Lock() + + defer x.mutex.Unlock() + + x.initialize() + + for _, person := range persons { + if person.Email == "" { + continue + } + + x.Bcc = append(x.Bcc, person) + } +} + +func (x *Envelope) validate() (err error) { + x.mutex.Lock() + + defer x.mutex.Unlock() + + return +} + +func (x *Envelope) initialize() { + if x.To == nil { + x.To = make([]gomail.Person, 0) + } + + if x.Cc == nil { + x.Cc = make([]gomail.Person, 0) + } + + if x.Bcc == nil { + x.Bcc = make([]gomail.Person, 0) + } +} diff --git a/smtp/mail.go b/smtp/mail.go new file mode 100644 index 0000000..d53e96d --- /dev/null +++ b/smtp/mail.go @@ -0,0 +1,24 @@ +package smtp + +type Mail struct { + Envelope Envelope + Content Content +} + +func (x *Mail) send(smtp *SMTP) (err error) { + if err = x.Envelope.validate(); err != nil { + return + } + + if err = x.Content.validate(); err != nil { + return + } + + defer func() { + if err != nil { + smtp.reset() + } + }() + + return +} diff --git a/smtp/smtp.go b/smtp/smtp.go new file mode 100644 index 0000000..ccfd5ef --- /dev/null +++ b/smtp/smtp.go @@ -0,0 +1,76 @@ +package smtp + +import ( + "sync" + + "cyberpull.com/gokit/errors" +) + +type SMTP struct { + mutex sync.Mutex + conn *Conn + address string +} + +func (x *SMTP) Open(address string) (err error) { + x.mutex.Lock() + + defer func() { + x.mutex.Unlock() + + if err != nil { + x.Close() + } + }() + + x.address = address + + return +} + +func (x *SMTP) Close() (err error) { + x.mutex.Lock() + + defer x.mutex.Unlock() + + if x.conn != nil { + x.conn.Close() + x.conn = nil + } + + x.address = "" + + return +} + +func (x *SMTP) Send(mails ...*Mail) (err error) { + x.mutex.Lock() + + defer x.mutex.Unlock() + + if x.conn == nil { + err = errors.New("Connection not open") + return + } + + for _, mail := range mails { + if mail == nil { + err = errors.New("Invalid mail object") + break + } + + err = mail.send(x) + + if err != nil { + break + } + } + + return +} + +func (x *SMTP) reset() { + x.mutex.Lock() + + defer x.mutex.Unlock() +}