From cbc78ad89196ce6e6fb5695ccdf3ee393d758e20 Mon Sep 17 00:00:00 2001 From: Alex Arce <19386312+alex-arce@users.noreply.github.com> Date: Wed, 10 Mar 2021 09:01:02 +0100 Subject: [PATCH] Initial commit :-D --- LICENSE | 21 ++++++ README.md | 18 +++++ cmd/build.sh | 1 + cmd/main.go | 50 ++++++++++++ cmd/o365sender-example.conf | 4 + go.mod | 5 ++ go.sum | 2 + o365sender.go | 147 ++++++++++++++++++++++++++++++++++++ 8 files changed, 248 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 cmd/build.sh create mode 100644 cmd/main.go create mode 100644 cmd/o365sender-example.conf create mode 100644 go.mod create mode 100644 go.sum create mode 100644 o365sender.go diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e2f2b5f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Alex Arce + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..3dca5ab --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +# O365Sender +Office 365 Mail tester + +#### Compilation + +Clone the repository: +`git clone https://github.com/alex-arce/O365Sender` + +Build the binary: +``` +cd cmd +./build.sh +``` + +#### Usage +``` +./O365Sender -config o365sender.conf --from="user@domain.com" --to="user@domain.com" --subject="TEST SUBJECT" --text="TEST BODY" +``` diff --git a/cmd/build.sh b/cmd/build.sh new file mode 100644 index 0000000..2042a34 --- /dev/null +++ b/cmd/build.sh @@ -0,0 +1 @@ +go build -o O365Sender diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 0000000..a07e2ff --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,50 @@ +package main + +import ( + "flag" + "fmt" + + o365 "github.com/alex-arce/O365Sender" + "github.com/BurntSushi/toml" +) + +var configFilePath string + +var mailServerCfg *o365.MailServerConfig + +var from, to, subject, text string + +//var filenameToAttach string + +func main() { + fmt.Println("[O365 Mail sender]") + + flag.StringVar(&configFilePath, "config", "o365sender.conf", "config location") + + flag.StringVar(&from, "from", "", "FROM address") + flag.StringVar(&to, "to", "", "TO address") + flag.StringVar(&subject, "subject", "", "Subject") + flag.StringVar(&text, "text", "", "Mail text") + //flag.StringVar(&filenameToAttach, "filename", "", "filename to add") + + flag.Parse() + + if _, err := toml.DecodeFile(configFilePath, &mailServerCfg); err != nil { + fmt.Println(err) + return + } + + o365Client := o365.NewMailClient(mailServerCfg) + + o365Client.AddFrom(from) + o365Client.AddTo(to) + o365Client.AddSubject(subject) + o365Client.AddBody(text) + + sendError := o365Client.Send() + if sendError != nil { + fmt.Errorf("[-] ERROR sending mail :-(") + } + + fmt.Println("[+] Mail Sent!!") +} diff --git a/cmd/o365sender-example.conf b/cmd/o365sender-example.conf new file mode 100644 index 0000000..b31e5d3 --- /dev/null +++ b/cmd/o365sender-example.conf @@ -0,0 +1,4 @@ +Username = "" +Password = "" +Host = "outlook.office365.com" +Port = "587" diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..e745e30 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module github.com/alex-arce/O365Sender + +go 1.15 + +require github.com/BurntSushi/toml v0.3.1 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..9cb2df8 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= diff --git a/o365sender.go b/o365sender.go new file mode 100644 index 0000000..153e0b5 --- /dev/null +++ b/o365sender.go @@ -0,0 +1,147 @@ +package o365 + +import ( + "bytes" + "crypto/tls" + "errors" + "fmt" + "net" + "net/smtp" +) + +type MailServerConfig struct { + Username string + Password string + Host string + Port string +} + +func NewMailServerConfig(username, password, host, port string) *MailServerConfig { + return &MailServerConfig{ + Username: username, + Password: password, + Host: host, + Port: port, + } +} + +type Attachment struct { + filename string + data []byte +} + +type O365Client struct { + mailServerConfig *MailServerConfig + fromAddress string + toAddress []string + subject string + body []byte + attachments []Attachment +} + +func NewMailClient(cfgMail *MailServerConfig) *O365Client { + return &O365Client{ + mailServerConfig: cfgMail, + fromAddress: "", + toAddress: nil, + subject: "", + body: nil, + attachments: nil, + } +} + +//------------------------------------------------- + +type loginAuth struct { + username, password string +} + +func LoginAuth(username, password string) smtp.Auth { + return &loginAuth{username, password} +} + +func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) { + return "LOGIN", []byte(a.username), nil +} + +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 from server") + } + } + return nil, nil +} + +//------------------------------------------------- + +func (oc *O365Client) AddFrom(fromAddress string) { + oc.fromAddress = fromAddress +} + +func (oc *O365Client) AddTo(toAdd string) { + oc.toAddress = append(oc.toAddress, toAdd) +} + +func (oc *O365Client) AddSubject(subject string) { + oc.subject = subject +} + +func (oc *O365Client) AddBody(content string) { + var body bytes.Buffer + mimeHeaders := "MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n\n" + body.Write([]byte(fmt.Sprintf("Subject: %s\n%s\n\n", oc.subject, mimeHeaders))) + body.Write([]byte(content)) + oc.body = body.Bytes() +} + +func (oc *O365Client) AddAttachment(filename string) { + // TODO +} + +func (oc *O365Client) Send() error { + server := fmt.Sprintf("%s:%s", oc.mailServerConfig.Host, oc.mailServerConfig.Port) + + conn, err := net.Dial("tcp", server) + if err != nil { + println(err) + return err + } + + c, err := smtp.NewClient(conn, oc.mailServerConfig.Host) + if err != nil { + println(err) + return err + } + + tlsconfig := &tls.Config{ + ServerName: oc.mailServerConfig.Host, + } + + if err = c.StartTLS(tlsconfig); err != nil { + println(err) + return err + } + + auth := LoginAuth(oc.mailServerConfig.Username, oc.mailServerConfig.Password) + if err = c.Auth(auth); err != nil { + println(err) + return err + } + + var body bytes.Buffer + mimeHeaders := "MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n\n" + body.Write([]byte(fmt.Sprintf("Subject: %s\n%s\n\n", oc.subject, mimeHeaders))) + err = smtp.SendMail(oc.mailServerConfig.Host+":"+oc.mailServerConfig.Port, auth, oc.fromAddress, oc.toAddress, body.Bytes()) + if err != nil { + fmt.Println(err) + return err + } + + return nil +}