Skip to content

Commit

Permalink
Merge pull request #3 from da-coda/release/v0.2
Browse files Browse the repository at this point in the history
Releasing v0.2
  • Loading branch information
da-coda committed Feb 9, 2021
2 parents 9080457 + c628c20 commit 8926f0f
Show file tree
Hide file tree
Showing 36 changed files with 1,470 additions and 279 deletions.
13 changes: 13 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM golang:1.16rc1-alpine3.13 AS build
WORKDIR /mailpie
COPY ./ .
RUN go build github.com/da-coda/mailpie .

FROM alpine:3.13
EXPOSE 1025
EXPOSE 1143
EXPOSE 8000
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=build /mailpie/mailpie .
CMD ["/root/mailpie", "-config", "/root/mailpie.yml"]
31 changes: 8 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
![Develop](https://github.com/da-coda/mailpie/workflows/Develop/badge.svg?branch=develop)
### Build with
This project was made possible thanks to the amazing work of other people
* [parsemail](https://github.com/DusanKasan/parsemail)
* [go-imap](https://github.com/emersion/go-imap)
* [mux](https://github.com/gorilla/mux)
* [smtpd](https://github.com/mhale/smtpd)
Expand All @@ -34,35 +33,21 @@ without any actual SMTP servers.
#### Implemented
Currently, MailPie only supports simple SMTP and IMAP. You are able to add MailPie as an SMTP server for your project
and as an IMAP server for you mail client. Any mail sent via SMTP to MailPie will be visible in your mail client.
You can configure MailPie by providing a config file or with CLI arguments.

#### Planned
- Dockerize MailPie and put it on Dockerhub for simple usage
- Webinterface with Vue 3 and Vuetify communicating over Server-Send-Events with the backend
- Webinterface with Vue 3 communicating over Server-Send-Events and REST Api with the backend
- REST-API that can be used in test suites for mail testing
- Switch from hardcoded stuff like ports to a way to use configs and CLI flags; Add debug mode
- Codeception(PHP) Module for testing with the REST-API
- Advanced SMTP and IMAP handling
- Maybe supporting usage as a proxy mail server for mail logging?
- Implement [spamassassin](https://github.com/Teamwork/spamc) support

## How to use MailPie?
### SMTP - settings in your project
1. Execute the MailPie binary or build the binary yourself
2. Add MailPie as you SMTP Server to your project
- *Port*: 1025
- *Username*: any
- *Password*: any
[Documentation](https://github.com/da-coda/mailpie/wiki)

Due to the fact that MailPie only acts as a mail catcher in Dev and Test environments there
is no credentials check needed.

### IMAP & SMTP - settings in your mail client
Add MailPie as a new account to your mail client
- Email address, Username and Password can be anything

![Mail Settings in Thunderbird 68][mail-settings]

Currently, you will need to trigger the mail receiving manually in your mail client or set the auto update to a short
duration.

[mail-settings]: readme/mail_settings.png
## Contact
Daniel Müller
- Twitter: [@da_coda_](https://twitter.com/da_coda_)
- LinkedIn: [daniel96mueller](https://www.linkedin.com/in/daniel96mueller/)
- E-Mail: [contact@daniel-mueller.de](mailto:contact@daniel-mueller.de)
9 changes: 5 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
module mailpie
module github.com/da-coda/mailpie

go 1.16

require (
github.com/DusanKasan/parsemail v1.2.0
github.com/emersion/go-imap v1.0.6
github.com/gorilla/mux v1.8.0
github.com/mhale/smtpd v0.0.0-20200509114310-d7a07f752336
github.com/r3labs/sse v0.0.0-20200828202401-10175c338a0a
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.7.0
github.com/stretchr/testify v1.7.0 // indirect
github.com/stretchr/testify v1.7.0
golang.org/x/net v0.0.0-20210119194325-5f4716e94777
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/mail.v2 v2.3.1
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
)
30 changes: 12 additions & 18 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
github.com/DusanKasan/parsemail v1.2.0 h1:CrzTL1nuPLxB41aO4zE/Tzc9GVD8jjifUftlbTKQQl4=
github.com/DusanKasan/parsemail v1.2.0/go.mod h1:B9lfMbpVe4DMqPImAOCGti7KEwasnRTrKKn66iQefVs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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=
Expand All @@ -17,38 +15,34 @@ github.com/martinlindhe/base36 v1.0.0 h1:eYsumTah144C0A8P1T/AVSUk5ZoLnhfYFM3OGQx
github.com/martinlindhe/base36 v1.0.0/go.mod h1:+AtEs8xrBpCeYgSLoY/aJ6Wf37jtBuR0s35750M27+8=
github.com/mhale/smtpd v0.0.0-20200509114310-d7a07f752336 h1:Rp+Y5NAgnPvY7FeVNPMRZdiEQzrHTw0cL+cK9AU1Gow=
github.com/mhale/smtpd v0.0.0-20200509114310-d7a07f752336/go.mod h1:qqKwvL5sfYgFxcMy96Kjx3TCorMfDaQBvmEL2nvdidc=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/r3labs/sse v0.0.0-20200828202401-10175c338a0a h1:JrklrdPohH7XHqYVv0a9wvznhkA6raBIB0tblZasMYw=
github.com/r3labs/sse v0.0.0-20200828202401-10175c338a0a/go.mod h1:S8xSOnV3CgpNrWd0GQ/OoQfMtlg2uPRSuTzcSGrzwK8=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20191116160921-f9c825593386 h1:ktbWvQrW08Txdxno1PiDpSxPXG6ndGsfnJjRRtkM0LQ=
golang.org/x/net v0.0.0-20191116160921-f9c825593386/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
gopkg.in/cenkalti/backoff.v1 v1.1.0 h1:Arh75ttbsvlpVA7WtVpH4u9h6Zl46xuptxqLxPiSo4Y=
gopkg.in/cenkalti/backoff.v1 v1.1.0/go.mod h1:J6Vskwqd+OMVJl8C33mmtxTBs2gyzfv7UDAkHu8BrjI=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/mail.v2 v2.3.1 h1:WYFn/oANrAGP2C0dcV6/pbkPzv8yGzqTjPmTeO7qoXk=
gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
104 changes: 47 additions & 57 deletions mailpie.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,22 @@ package main

import (
"embed"
"flag"
"fmt"
"github.com/da-coda/mailpie/pkg/config"
"github.com/da-coda/mailpie/pkg/event"
"github.com/da-coda/mailpie/pkg/handler"
"github.com/da-coda/mailpie/pkg/handler/imap"
"github.com/da-coda/mailpie/pkg/store"
"github.com/emersion/go-imap/server"
"github.com/gorilla/mux"
"github.com/mhale/smtpd"
"github.com/r3labs/sse"
"github.com/sirupsen/logrus"
"mailpie/pkg/handler"
"mailpie/pkg/handler/api"
"mailpie/pkg/handler/imap"
"net"
"net/http"
"os"
"os/signal"
"strconv"
"syscall"
"time"
)
Expand All @@ -24,26 +27,42 @@ type errorOrigin string
const (
SMTP errorOrigin = "smtp"
SPA errorOrigin = "spa"
API errorOrigin = "api"
SSE errorOrigin = "sse"
IMAP errorOrigin = "imap"
)

const listenOnAddress = "0.0.0.0"

type errorState struct {
err error
origin errorOrigin
}

func main() {
logrus.SetLevel(logrus.DebugLevel)
Run(flag.CommandLine, os.Args[1:])
}

//Run main entry point for mailpie. Loads the config, setup of globalMailStore and globalMessageQueue, starts all services
func Run(flags *flag.FlagSet, arguments []string) {
err := config.Load(flags, arguments)
if err != nil {
logrus.WithError(err).Fatal("Error during configuration setup")
}
logrus.SetLevel(config.GetConfig().LogrusLevel)
conf := config.GetConfig()
globalMessageQueue := event.CreateOrGet()
globalMailStore := store.CreateMailStore(*globalMessageQueue)

errorChannel := make(chan errorState)
go serveSPA(errorChannel)
go serveSSE(errorChannel)
go serveSMTP(errorChannel)
go serveAPI(errorChannel)
go serveIMAP(errorChannel)
if !conf.DisableHTTP {
go serveSPA(errorChannel)
}

if !conf.DisableSMTP {
smtpHandler := handler.CreateSmtpHandler(*globalMailStore)
go serveSMTP(errorChannel, smtpHandler)
}

if !conf.DisableIMAP {
go serveIMAP(errorChannel)
}

signals := make(chan os.Signal, 1)
signal.Notify(signals, os.Interrupt, syscall.SIGTERM)
Expand All @@ -62,14 +81,17 @@ func main() {
}
}

func serveSMTP(errorChannel chan errorState) {
addr := listenOnAddress + ":1025"
//serveSMTP Setup SMTP-Server and run ListenAndServe. If some error occurs during service runtime, the error gets send to Run
//via the errorChannel. Needs an SMTP handler which handles incoming mails
func serveSMTP(errorChannel chan errorState, smtpHandler handler.SmtpHandler) {
addr := config.GetConfig().NetworkConfigs.SMTP.Host + ":" + strconv.Itoa(config.GetConfig().NetworkConfigs.SMTP.Port)
srv := &smtpd.Server{
Addr: addr,
Handler: handler.SmtpHandler,
Handler: smtpHandler.Handle,
Appname: "Mailpie",
Hostname: "localhost",
AuthRequired: false,
//currently no auth is needed and implemented, so always return true on login
AuthHandler: func(remoteAddr net.Addr, mechanism string, username []byte, password []byte, shared []byte) (bool, error) {
return true, nil
},
Expand All @@ -79,30 +101,29 @@ func serveSMTP(errorChannel chan errorState) {
AuthMechs: map[string]bool{"PLAIN": true, "LOGIN": true, "CRAM-MD5": false},
}
logrus.WithField("Address", addr).Info("Starting SMTP server")
//run the server. In best case, this will never stop. If there is some error, send it to Run via errorChannel
err := srv.ListenAndServe()
if err != nil {
errorChannel <- errorState{err: err, origin: SMTP}
}
}

//embed the index html and the dist directory(introduced in go 1.16)
//go:embed "dist/index.html"
var indexHtml string

//go:embed "dist"
var dist embed.FS

//serveSPA serve the MailPie Single-Page-Application
func serveSPA(errorChannel chan errorState) {

router := mux.NewRouter()
spa := handler.SpaHandler{
Dist: dist,
Index: indexHtml,
}
spa := handler.NewSpaHandler(dist, indexHtml)
router.PathPrefix("/").Handler(spa).Methods("GET")

srv := &http.Server{
Handler: router,
Addr: listenOnAddress + ":8000",
Addr: config.GetConfig().NetworkConfigs.HTTP.Host + ":" + strconv.Itoa(config.GetConfig().NetworkConfigs.HTTP.Port),
WriteTimeout: 15 * time.Second,
ReadTimeout: 15 * time.Second,
}
Expand All @@ -115,44 +136,13 @@ func serveSPA(errorChannel chan errorState) {
}
}

func serveAPI(errorChannel chan errorState) {
router := mux.NewRouter()
subrouter := router.PathPrefix("/api/").Subrouter()
api.RegisterApiRoutes(subrouter)
srv := &http.Server{
Handler: router,
Addr: listenOnAddress + ":8001",
WriteTimeout: 15 * time.Second,
ReadTimeout: 15 * time.Second,
}
logrus.WithField("Address", srv.Addr).Info("Starting API server")
err := srv.ListenAndServe()
if err != nil {
errorChannel <- errorState{err: err, origin: API}
}
}

func serveSSE(errorChannel chan errorState) {
address := listenOnAddress + ":8002"
sseServer := sse.New()
sseServer.CreateStream("messages")
sseHandler := handler.NewOrGetSSEHandler(sseServer)
router := http.NewServeMux()
router.Handle("/events", sseHandler)

logrus.WithField("Address", address).Info("Starting SSE server")
err := http.ListenAndServe(address, router)
if err != nil {
errorChannel <- errorState{err: err, origin: SSE}
}
}

//serveIMAP runs the IMAP server
func serveIMAP(errorChannel chan errorState) {
be := imap.NewBackend()
s := server.New(be)
imapLogger := logrus.New()
imapLogger := logrus.StandardLogger()
s.Debug = imapLogger.Writer()
s.Addr = listenOnAddress + ":1143"
s.Addr = config.GetConfig().NetworkConfigs.IMAP.Host + ":" + strconv.Itoa(config.GetConfig().NetworkConfigs.IMAP.Port)
s.AllowInsecureAuth = true
logrus.WithField("Address", s.Addr).Info("Starting IMAP server")
err := s.ListenAndServe()
Expand Down

0 comments on commit 8926f0f

Please sign in to comment.