From c27359ab4a803ea6625ec9a5d9d3ab5ac5c6e946 Mon Sep 17 00:00:00 2001
From: Quentin DAUCHY <quentindauchy@evil.educ.wifi>
Date: Tue, 15 Dec 2015 16:48:09 +0100
Subject: [PATCH] Add mails library

---
 .gitlab-ci.yml     |   7 +-
 mails/README.md    |  45 ++++++++++
 mails/TODO.md      |   2 +
 mails/email.go     | 206 +++++++++++++++++++++++++++++++++++++++++++++
 mails/mail_test.go | 106 +++++++++++++++++++++++
 mails/testImg.gif  | Bin 0 -> 13974 bytes
 6 files changed, 364 insertions(+), 2 deletions(-)
 create mode 100644 mails/README.md
 create mode 100644 mails/TODO.md
 create mode 100644 mails/email.go
 create mode 100644 mails/mail_test.go
 create mode 100644 mails/testImg.gif

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index e4e719e..c3cc499 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -9,6 +9,7 @@ Unit tests:
     - strong # Disable this build on small runners
   services:
     - "lesterpig/mongo:latest" # Use this light version of mongo
+    - "lesterpig/postfix:latest"
   #artifacts: # Waiting GitLab 8.2.1...
   #  paths:
   #    - "coverage.html"
@@ -16,6 +17,7 @@ Unit tests:
     - "go get gopkg.in/mgo.v2"
     - "go test -coverprofile auth.part -v ./auth"
     - "go test -coverprofile mgdb.part -v ./mgdb"
+    - "go test -coverprofile mails.part -v ./mails"
     - "echo 'mode: set' *part > c.out"
     - "grep -h -v 'mode: set' *part >> c.out"
     - "go tool cover -html=c.out -o coverage.html"
@@ -26,7 +28,8 @@ ARM tests:
     - arm
   script:
     - "go get gopkg.in/mgo.v2"
-    - "go test -cover -short -run 'Test[^M][^o][^n][^g][^o]' -v ./..."
+    - "go test -cover -short -v ./auth"
+    - "go test -cover -short -v ./mgdb"
 
 Code lint:
   stage: test
@@ -38,4 +41,4 @@ Code lint:
     - "go get github.com/alecthomas/gometalinter"
     - "go get gopkg.in/mgo.v2"
     - "gometalinter --install"
-    - "gometalinter -t --deadline=60s ./..."
+    - "gometalinter -t --deadline=100s -j1 ./..."
diff --git a/mails/README.md b/mails/README.md
new file mode 100644
index 0000000..4226c1d
--- /dev/null
+++ b/mails/README.md
@@ -0,0 +1,45 @@
+### DFSS - Mails lib ###
+
+This library is designed to wrap the smtp library of go.
+
+## Initiating a connection to a server ##
+
+To start a connection to a server, create a CustomClient via NewCustomClient
+This takes :
+- A sender (ex : qdauchy@insa-rennes.fr)
+- A host (ex : mailhost.insa-rennes.fr)
+- A port (ex : 587)
+- A user (ex : qdauchy)
+- A password
+
+This requires the server to have TLS
+
+## Using the connection ##
+
+The connection that has been created can then be used to send one or several mails
+
+Using Send requires :
+- A slice of receivers
+- A subject
+- A message
+- A (possibly empty) slice of extensions
+- A (possibly empty) slice of filenames. This slice must be of the same length as the extensions one.
+
+## Closing the connection ##
+
+Finally, close the connection using Close.
+
+## Example ###
+
+Refer to the doc's to see the library in practice
+
+## Testing the library ##
+
+The testing file uses the following variables to set up the tests :
+DFSS_TEST_MAIL_SENDER
+DFSS_TEST_MAIL_HOST
+DFSS_TEST_MAIL_PORT
+DFSS_TEST_MAIL_USER
+DFSS_TEST_MAIL_PASSWORD
+DFSS_TEST_MAIL_RCPT1
+DFSS_TEST_MAIL_RCPT2
diff --git a/mails/TODO.md b/mails/TODO.md
new file mode 100644
index 0000000..d007783
--- /dev/null
+++ b/mails/TODO.md
@@ -0,0 +1,2 @@
+Modify the header so that the spam score is lower (9.986 is cutting it close)
+Document
diff --git a/mails/email.go b/mails/email.go
new file mode 100644
index 0000000..6dc595e
--- /dev/null
+++ b/mails/email.go
@@ -0,0 +1,206 @@
+// Package mails provides a simple interface with the smtp library
+package mails
+
+import (
+	"bytes"
+	"crypto/rand"
+	"crypto/tls"
+	"encoding/base64"
+	"errors"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"mime/multipart"
+	"net/smtp"
+	"net/textproto"
+	"strings"
+	"time"
+)
+
+// CustomClient :
+// Modelizes the constants of a connection : an actual client, and a sender
+type CustomClient struct {
+	sender string
+	client *smtp.Client
+}
+
+const rfc2822 = "Fri 18 Dec 2015 10:01:17 -0606" // used to format time to rfc2822. Not accurate but fmt can't see a ,
+
+// NewCustomClient starts up a custom client.
+func NewCustomClient(sender, host, port, username, password string) (*CustomClient, error) {
+
+	// Connect to the server. Type of connection is smtp.Client
+	connection, err := smtp.Dial(host + ":" + port)
+	if err != nil {
+		return nil, err
+	}
+
+	// Check that the server does implement TLS
+	if ok, _ := connection.Extension("StartTLS"); !ok {
+		return nil, errors.New("Connection failed : mail server doesn't support TLS")
+	}
+
+	// Start tls
+	if err := connection.StartTLS(&tls.Config{InsecureSkipVerify: true, ServerName: host}); err != nil {
+		return nil, err
+	}
+
+	// Authenticate to the server if it is supported
+	if ok, _ := connection.Extension("AUTH"); ok {
+		auth := smtp.PlainAuth(username, username, password, host)
+		if err := connection.Auth(auth); err != nil {
+			return nil, err
+		}
+	}
+
+	return &CustomClient{sender, connection}, nil
+}
+
+// Send a mail with the custom client. Returns nil on success.
+func (c *CustomClient) Send(receivers []string, subject, message string, extensions, filenames []string) error {
+	// Keep the connection in a local variable for ease of access
+	connection := c.client
+
+	boundary := randomBoundary()
+	header := createHeader(c.sender, subject, boundary)
+
+	// Encode the message in base64 ONCE
+	base64Message := base64.StdEncoding.EncodeToString([]byte(message))
+
+	for _, receiver := range receivers {
+
+		// Set the sender
+		if err := connection.Mail(c.sender); err != nil {
+			return err
+		}
+
+		// Set the receiver. This modifies the header in the current instance
+		if err := connection.Rcpt(receiver); err != nil {
+			return err
+		}
+
+		// Set the message : header, then message encoded in base64, then attachments
+		var localBuffer bytes.Buffer
+		if err := createFullMessage(&localBuffer, receiver, c.sender, header, base64Message, extensions, filenames, boundary); err != nil {
+			return err
+		}
+
+		// Send it. Data returns a writer to which one can write to write the message itself
+		emailWriter, err := connection.Data()
+		if err != nil {
+			return err
+		}
+
+		_, err = fmt.Fprintf(emailWriter, localBuffer.String())
+		if err != nil {
+			return err
+		}
+		err = emailWriter.Close()
+		if err != nil {
+			return err
+		}
+
+		// Reset the envellope
+		err = connection.Reset()
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// Close the connection of CustomClient
+func (c *CustomClient) Close() error {
+	return c.client.Close()
+}
+
+// Creates the header for all messages
+func createHeader(sender, subject, boundary string) string {
+	var buffer bytes.Buffer
+	fmt.Fprintf(&buffer, "From: %s\r\n", sender)
+	fmt.Fprintf(&buffer, "MIME-Version: 1.0\r\n")
+	fmt.Fprintf(&buffer, "Subject: %s\r\n", subject)
+	// Replace the first space with a comma and a space to conform to rfc2822
+	fmt.Fprintf(&buffer, "Date: %s%s", strings.Replace(time.Now().UTC().Format(rfc2822), " ", ", ", 1), "\r\n")
+	fmt.Fprintf(&buffer, "Content-Type: multipart/mixed; boundary=\"%s\"; charset=\"UTF-8\"\r\n", boundary)
+	fmt.Fprintf(&buffer, "To: ")
+	return buffer.String()
+}
+
+// Create the full message for a single receiver
+func createFullMessage(b *bytes.Buffer, receiver, sender, globalHeader, base64Message string, extensions, filenames []string, boundary string) error {
+	fmt.Fprintf(b, "%s%s\r\n", globalHeader, receiver)
+
+	writer := multipart.NewWriter(b)
+	if err := writer.SetBoundary(boundary); err != nil {
+		return err
+	}
+	// Set the message
+	if err := createText(writer, base64Message); err != nil {
+		return err
+	}
+
+	// Set attachments. Here for now because the boundaries are wanted unique
+	for index, value := range filenames {
+		if err := createAttachment(writer, extensions[index], value); err != nil {
+			return err
+		}
+	}
+	if err := writer.Close(); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Create an attachment with a certain extension
+func createAttachment(writer *multipart.Writer, extension, path string) error {
+	// Create a header
+	newHeader := make(textproto.MIMEHeader)
+	newHeader.Add("Content-Type", extension)
+	newHeader.Add("Content-Transfer-Encoding", "base64")
+	newHeader.Add("Content-Disposition", "attachment; filename="+path+";")
+
+	// Create a writer for the file
+	output, err := writer.CreatePart(newHeader)
+	if err != nil {
+		return err
+	}
+
+	// Write the file to the message
+	data, err := ioutil.ReadFile(path)
+	if err != nil {
+		return err
+	}
+	fmt.Fprintf(output, base64.StdEncoding.EncodeToString([]byte(data)))
+
+	return nil
+}
+
+// Creates the equivalent of the message wrapped in a boundary. The message is expected to have been encoded via base64
+func createText(writer *multipart.Writer, message string) error {
+	// Create the mime header for the message
+	mimeHeaderMessage := make(textproto.MIMEHeader)
+	mimeHeaderMessage.Add("Content-Transfer-Encoding", "base64")
+	mimeHeaderMessage.Add("Content-Type", "text/plain; charset=\"UTF-8\"")
+
+	// Set the message
+	output, err := writer.CreatePart(mimeHeaderMessage)
+	if err != nil {
+		return err
+	}
+
+	fmt.Fprintf(output, "%s", message)
+	return nil
+}
+
+// Totally copied from go stl
+func randomBoundary() string {
+	var buf [30]byte
+	_, err := io.ReadFull(rand.Reader, buf[:])
+	if err != nil {
+		panic(err)
+	}
+	return fmt.Sprintf("%x", buf[:])
+}
diff --git a/mails/mail_test.go b/mails/mail_test.go
new file mode 100644
index 0000000..b04f106
--- /dev/null
+++ b/mails/mail_test.go
@@ -0,0 +1,106 @@
+package mails
+
+import (
+	"fmt"
+	"os"
+	"testing"
+)
+
+var client *CustomClient
+var err error
+var rcpt1 string
+var rcpt2 string
+
+func TestMain(m *testing.M) {
+	// Setup is based on environment variables
+	sender := os.Getenv("DFSS_TEST_MAIL_SENDER")
+	host := os.Getenv("DFSS_TEST_MAIL_HOST")
+	port := os.Getenv("DFSS_TEST_MAIL_PORT")
+	username := os.Getenv("DFSS_TEST_MAIL_USER")
+	password := os.Getenv("DFSS_TEST_MAIL_PASSWORD")
+	rcpt1 = os.Getenv("DFSS_TEST_MAIL_RCPT1")
+	rcpt2 = os.Getenv("DFSS_TEST_MAIL_RCPT2")
+	client, err = NewCustomClient(sender, host, port, username, password)
+	if err != nil {
+		fmt.Println(err)
+	}
+
+	code := m.Run()
+
+	err = client.Close()
+	if err != nil {
+		fmt.Println(err)
+	}
+	os.Exit(code)
+}
+
+func TestSingleMail(t *testing.T) {
+	err = client.Send([]string{rcpt1}, "TestSingleMail", "Gros espoirs!", []string{}, []string{})
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestDoubleMail(t *testing.T) {
+	err = client.Send([]string{rcpt1, rcpt2}, "TestDoubleMail", "Gros espoirs!", []string{}, []string{})
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestRuneMail(t *testing.T) {
+	err = client.Send([]string{rcpt1}, "TestRuneMail", "测试", []string{}, []string{})
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestAttachmentMailText(t *testing.T) {
+	err = client.Send([]string{rcpt1}, "TestAttachmentMailText", "What would make a good attachment?", []string{"text/plain"}, []string{"mail_test.go"})
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestAttachmentMailImage(t *testing.T) {
+	err = client.Send([]string{rcpt1}, "TestAttachmentMailImage", "What would make a good attachment?", []string{"image/gif"}, []string{"testImg.gif"})
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
+func ExampleCustomClient() {
+
+	// Create a connection
+	client, err := NewCustomClient("qdauchy@insa-rennes.fr", "mailhost.insa-rennes.fr", "587", "qdauchy", "notreallymypass")
+	if err != nil {
+		fmt.Println(err)
+	}
+
+	// Some reused variables
+	receiver1 := "lbonniot@insa-rennes.fr"
+	receiver2 := "qdauchy@insa-rennes.fr"
+	receiver3 := "tclaverie@insa-rennes.fr"
+	subject := "Mail example"
+	message := `Hello, this is a mail example. It's not like the cactus is going
+  to be jealous or anything...`
+
+	// Send a first mail, without attachments
+	err = client.Send([]string{receiver1, receiver2}, subject, message, []string{}, []string{})
+	if err != nil {
+		fmt.Println(err)
+	}
+
+	// Send a second mail, with some attachments
+	err = client.Send([]string{receiver1, receiver3}, subject, message, []string{"text/plain", "image/gif"}, []string{"email.go", "testImg.gif"})
+	if err != nil {
+		fmt.Println(err)
+	}
+
+	// Close the connection
+	err = client.Close()
+	if err != nil {
+		fmt.Println(err)
+	}
+
+}
diff --git a/mails/testImg.gif b/mails/testImg.gif
new file mode 100644
index 0000000000000000000000000000000000000000..c161ddeb013501d84d9b7414973869333f373a61
GIT binary patch
literal 13974
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
YH^x{!HBH^P!E|uwK*yhdSS;ZFf4l69+yDRo

literal 0
HcmV?d00001

-- 
GitLab