Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
m.gavrilenko committed Jun 8, 2021
1 parent 0a368e3 commit 57c9632
Show file tree
Hide file tree
Showing 6 changed files with 231 additions and 17 deletions.
16 changes: 1 addition & 15 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,15 +1 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/
.idea
29 changes: 27 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,27 @@
# ngr-emailer
Tiny lib for email (with attachments) sending
# emailer
Tiny lib for email (with attachments) sending (smtp)

# import
```import "github.com/NGRsoftlab/ngr-emailer"```

# example
```
s := NewSender(
"[email protected]",
"password",
"[email protected]",
fmt.Sprintf("%v:%v", "test.com", "587"),
)
err := s.NewMessage("topic", []string{"[email protected]"}, "test", []AttachData{{
fileName: "test.txt",
fileData: []byte("test"),
}})
if err != nil {
log.Fatal(err)
}
err = s.Send()
if err != nil {
log.Fatal(err)
}
```
152 changes: 152 additions & 0 deletions emailer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// Copyright 2020 NGR Softlab
//
package emailer

import (
"bytes"
"encoding/base64"
"errors"
"fmt"
"github.com/NGRsoftlab/ngr-logging"
"mime/multipart"
"net/smtp"
"strings"
"time"
)

// Struct for auth.
type loginAuth struct {
username, password string
}

// Struct for attachments.
type AttachData struct {
FileName string
FileData []byte
}

// Auth with login and password.
func LoginAuth(username, password string) smtp.Auth {
return &loginAuth{username, password}
}

// Login auth start.
func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
return "LOGIN", []byte{}, nil
}

// Next loginAuth.
func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
if more {
switch string(fromServer) {
case "Username:":
return []byte(a.username), nil
case "Password:":
return []byte(a.password), nil
default:
return nil, errors.New("Unknown fromServer")
}
}
return nil, nil
}

/////////////////////////////////////////////

// Struct - sender for sending smtp packs.
type Sender struct {
Login string // user login
Email string // user email address
Password string // user password
ServerSMTP string // smtp server string
client *smtp.Client // smtp client pointer
message []byte // message text
to []string // receivers of email
}

// Send smtp pack (mail).
func (s *Sender) Send() error {
err := smtp.SendMail(s.ServerSMTP,
LoginAuth(s.Login, s.Password),
s.Login, s.to, s.message)

if err != nil {
logging.Logger.Error("SEND ERROR: ", err)
return err
}
return nil
}

/////////////////////////////////////////////

// Creating new *Sender obj.
func NewSender(login, password, email, server string) *Sender {
auth := Sender{
Login: login,
Email: email,
Password: password,
ServerSMTP: server}
return &auth
}

// Creating new email message.
func (s *Sender) NewMessage(subject string, to []string, body string, files []AttachData) error {
logging.Logger.Info("files: ", len(files))

attachments, err := attachFile(files)
if err != nil {
logging.Logger.Error(err)
return err
}

withAttachments := len(attachments) > 0
var headers = make(map[string]string)
headers["From"] = s.Email
headers["To"] = strings.Join(to, ";")
headers["Subject"] = subject
headers["MIME-Version"] = "1.0"
headers["Date"] = time.Now().Format(time.RFC1123Z)
var buf = bytes.NewBuffer(nil)
writer := multipart.NewWriter(buf)
boundary := writer.Boundary()

for k, v := range headers {
buf.WriteString(fmt.Sprintf("%s: %s\r\n", k, v))
}

if withAttachments {
buf.WriteString(fmt.Sprintf(`Content-Type: multipart/mixed; boundary="%s"`, boundary))
buf.WriteString("\r\n\r\n")
buf.WriteString(fmt.Sprintf("--%s\r\n", boundary))
}
buf.WriteString("Content-Type: text/plain; charset=utf-8\r\n")
buf.WriteString("MIME-Version: 1.0\r\n")
buf.WriteString("\r\n" + body)
if withAttachments {
for k, v := range attachments {
buf.WriteString(fmt.Sprintf("\r\n--%s\r\n", boundary))
buf.WriteString("Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\r\n")
buf.WriteString("Content-Transfer-Encoding: base64\r\n")
buf.WriteString("MIME-Version: 1.0\r\n")
buf.WriteString(fmt.Sprintf(`Content-Disposition: attachment; filename="%s"`, k))
buf.WriteString("\r\n\r\n")

var b = make([]byte, base64.StdEncoding.EncodedLen(len(v)))
base64.StdEncoding.Encode(b, v)
buf.Write(b)
}
buf.WriteString("--")
}
s.to = to
s.message = buf.Bytes()

return nil
}

// Attaching file to mail. Returns attachments map: "filename": filedata.
func attachFile(files []AttachData) (map[string][]byte, error) {
var attachments = make(map[string][]byte)
for _, f := range files {
attachments[f.FileName] = f.FileData
}
return attachments, nil
}
31 changes: 31 additions & 0 deletions emailer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2020 NGR Softlab
//
package emailer

import (
"fmt"
"testing"
)

/////////////////////////////////////////////////
// Put correct here before testing.
func TestSimpleMail(t *testing.T) {
s := NewSender(
"[email protected]",
"password",
"[email protected]",
fmt.Sprintf("%v:%v", "test.com", "587"),
)

err := s.NewMessage("topic", []string{"[email protected]"}, "test", []AttachData{{
FileName: "test.txt",
FileData: []byte("test"),
}})
if err != nil {
t.Fatal(err)
}
err = s.Send()
if err != nil {
t.Fatal(err)
}
}
8 changes: 8 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module github.com/NGRsoftlab/ngr-emailer

go 1.13

require (
github.com/NGRsoftlab/ngr-logging v1.0.0
github.com/sirupsen/logrus v1.8.1 // indirect
)
12 changes: 12 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
github.com/NGRsoftlab/ngr-logging v1.0.0 h1:Yp42kvw/bofZ6xXC5jPlPx1HNabZQY9cvzGnB0earJY=
github.com/NGRsoftlab/ngr-logging v1.0.0/go.mod h1:99kZ+XwSK7rKRitmhZvqOdYnPf9Qepywt3zjJJcJDME=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

0 comments on commit 57c9632

Please sign in to comment.