diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index e4e719e5862d8fde98b08f69f2c6513b138a754c..c3cc4996c31030c390bc57b25a7fff8ff6f7dc70 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 0000000000000000000000000000000000000000..4226c1df5a4153cfef18b4c296a909d6170cd93e
--- /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 0000000000000000000000000000000000000000..d0077834eca47622d3c24a64852fa2d6ae643bdc
--- /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 0000000000000000000000000000000000000000..6dc595e22e9551bc00df2c835a064b3e6592f74f
--- /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 0000000000000000000000000000000000000000..b04f1066e0afc62f8b83dda831a77e1ec0edaccf
--- /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
Binary files /dev/null and b/mails/testImg.gif differ