From e4fb6561549e95435db7b0758d1af58ffff167ab Mon Sep 17 00:00:00 2001 From: L11R Date: Fri, 30 Nov 2018 00:23:30 +0300 Subject: [PATCH] Initial commit --- .gitignore | 5 ++ README.md | 17 ++++++ config.yml | 11 ++++ config/config.go | 41 +++++++++++++ go.mod | 13 ++++ handlers/handlers.go | 119 ++++++++++++++++++++++++++++++++++++ main.go | 61 ++++++++++++++++++ templates/autoconfig.tmpl | 25 ++++++++ templates/autodiscover.tmpl | 52 ++++++++++++++++ templates/mobileconfig.tmpl | 77 +++++++++++++++++++++++ 10 files changed, 421 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 config.yml create mode 100644 config/config.go create mode 100644 go.mod create mode 100644 handlers/handlers.go create mode 100644 main.go create mode 100644 templates/autoconfig.tmpl create mode 100644 templates/autodiscover.tmpl create mode 100644 templates/mobileconfig.tmpl diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3b7cc24 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# jetbrains goland +.idea + +# go modules +go.sum \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..611aae4 --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# go-autoconfig +IMAP/SMTP autodiscover feature for Thunderbird, Apple Mail and Microsoft Outlook + +You need DNS SRV-record to get work Outlook and Thunderbird: +``` +_autodiscover._tcp IN SRV 0 0 443 autoconfig.example.com. +``` +Of course `autoconfig.example.com` domain should point to your server with this service. + +### Thunderbird +`GET https://autoconfig.example.com/mail/config-v1.1.xml` + +### Apple Mail +`GET https://autoconfig.example.com/email.mobileconfig?email=your@email.com` + +### Outlook +`POST https://autoconfig.example.com/autodiscover/autodiscover.xml` \ No newline at end of file diff --git a/config.yml b/config.yml new file mode 100644 index 0000000..6edaff9 --- /dev/null +++ b/config.yml @@ -0,0 +1,11 @@ +service_addr: ":1323" + +domain: lord.sh + +imap: + server: imap.mailbox.org + port: 993 + +smtp: + server: smtp.mailbox.org + port: 465 \ No newline at end of file diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..2de9261 --- /dev/null +++ b/config/config.go @@ -0,0 +1,41 @@ +package config + +import ( + "gopkg.in/go-playground/validator.v9" + "gopkg.in/yaml.v2" + "io/ioutil" +) + +type Config struct { + ServiceAddr string `yaml:"service_addr"` + + Domain string `yaml:"domain" validate:"required"` + + IMAP *Server `yaml:"imap"` + SMTP *Server `yaml:"smtp"` +} + +type Server struct { + Host string `yaml:"server" validate:"required"` + Port int `yaml:"port" validate:"required"` + STARTTLS bool `yaml:"starttls"` +} + +func NewConfig(p string) (*Config, error) { + b, err := ioutil.ReadFile(p) + if err != nil { + return nil, err + } + + var c Config + if err := yaml.Unmarshal(b, &c); err != nil { + return nil, err + } + + validate := validator.New() + if err := validate.Struct(c); err != nil { + return nil, err + } + + return &c, nil +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..72f479d --- /dev/null +++ b/go.mod @@ -0,0 +1,13 @@ +module go-autoconfig + +require ( + github.com/go-playground/locales v0.12.1 // indirect + github.com/go-playground/universal-translator v0.16.0 // indirect + github.com/labstack/echo v3.3.8+incompatible + github.com/labstack/gommon v0.2.8 // indirect + golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85 // indirect + golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35 // indirect + gopkg.in/go-playground/assert.v1 v1.2.1 // indirect + gopkg.in/go-playground/validator.v9 v9.23.0 + gopkg.in/yaml.v2 v2.2.2 +) diff --git a/handlers/handlers.go b/handlers/handlers.go new file mode 100644 index 0000000..3be4e70 --- /dev/null +++ b/handlers/handlers.go @@ -0,0 +1,119 @@ +package handlers + +import ( + "encoding/xml" + "github.com/labstack/echo" + "go-autoconfig/config" + "io/ioutil" + "net/http" +) + +type Handler struct { + Config *config.Config +} + +type server struct { + Host string + Port int + STARTTLS bool +} + +func (h *Handler) Outlook(ctx echo.Context) error { + var req struct { + XMLName xml.Name `xml:"Autodiscover"` + Text string `xml:",chardata"` + Xmlns string `xml:"xmlns,attr"` + Request struct { + Text string `xml:",chardata"` + EMailAddress string `xml:"EMailAddress"` + AcceptableResponseSchema string `xml:"AcceptableResponseSchema"` + } `xml:"Request"` + } + + b, err := ioutil.ReadAll(ctx.Request().Body) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, err.Error()) + } + + if err := xml.Unmarshal(b, &req); err != nil { + return echo.NewHTTPError(http.StatusBadRequest, err.Error()) + } + + data := struct { + Schema string + Email string + Domain string + IMAP *server + SMTP *server + }{ + Schema: req.Request.AcceptableResponseSchema, + Email: req.Request.EMailAddress, + Domain: h.Config.Domain, + IMAP: &server{ + Host: h.Config.IMAP.Host, + Port: h.Config.IMAP.Port, + STARTTLS: h.Config.IMAP.STARTTLS, + }, + SMTP: &server{ + Host: h.Config.SMTP.Host, + Port: h.Config.SMTP.Port, + STARTTLS: h.Config.SMTP.STARTTLS, + }, + } + + ctx.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationXMLCharsetUTF8) + return ctx.Render(http.StatusOK, "outlook", data) +} + +func (h *Handler) Thunderbird(ctx echo.Context) error { + data := struct { + Domain string + IMAP *server + SMTP *server + }{ + Domain: h.Config.Domain, + IMAP: &server{ + Host: h.Config.IMAP.Host, + Port: h.Config.IMAP.Port, + STARTTLS: h.Config.IMAP.STARTTLS, + }, + SMTP: &server{ + Host: h.Config.SMTP.Host, + Port: h.Config.SMTP.Port, + STARTTLS: h.Config.SMTP.STARTTLS, + }, + } + + ctx.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationXMLCharsetUTF8) + return ctx.Render(http.StatusOK, "thunderbird", data) +} + +func (h *Handler) AppleMail(ctx echo.Context) error { + var req struct { + Email string `query:"email"` + } + if err := ctx.Bind(&req); err != nil { + return echo.NewHTTPError(http.StatusBadRequest, err.Error()) + } + + data := struct { + Email string + Domain string + IMAP *server + SMTP *server + }{ + Email: req.Email, + Domain: h.Config.Domain, + IMAP: &server{ + Host: h.Config.IMAP.Host, + Port: h.Config.IMAP.Port, + }, + SMTP: &server{ + Host: h.Config.SMTP.Host, + Port: h.Config.SMTP.Port, + }, + } + + ctx.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationXMLCharsetUTF8) + return ctx.Render(http.StatusOK, "applemail", data) +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..51aa2b0 --- /dev/null +++ b/main.go @@ -0,0 +1,61 @@ +package main // import "go-autoconfig" + +import ( + "flag" + "fmt" + "github.com/labstack/echo" + "github.com/labstack/echo/middleware" + "go-autoconfig/config" + "go-autoconfig/handlers" + "io" + "os" + "path/filepath" + "text/template" +) + +var path = flag.String( + "config", + "", + "enter path to config file", +) + +func main() { + // Parse at first startup + flag.Parse() + + // Read config + conf, err := config.NewConfig(*path) + if err != nil { + fmt.Printf("Incorrect path or config itself! See help.\n%s\n", err.Error()) + os.Exit(2) + } + + tmpl := &Template{ + templates: template.Must(template.ParseGlob(filepath.Join("templates", "*.tmpl"))), + } + + // Init Echo + e := echo.New() + e.Renderer = tmpl + + // Middleware + e.Use(middleware.Logger()) + e.Use(middleware.Recover()) + + // Routes + h := handlers.Handler{conf} + e.POST("/autodiscover/autodiscover.xml", h.Outlook) + e.GET("/mail/config-v1.1.xml", h.Thunderbird) + e.GET("/email.mobileconfig", h.AppleMail) + + // Start server + e.Logger.Fatal(e.Start(conf.ServiceAddr)) +} + +type Template struct { + templates *template.Template +} + +func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error { + return t.templates.ExecuteTemplate(w, name, data) +} diff --git a/templates/autoconfig.tmpl b/templates/autoconfig.tmpl new file mode 100644 index 0000000..13802b5 --- /dev/null +++ b/templates/autoconfig.tmpl @@ -0,0 +1,25 @@ +{{define "thunderbird"}} + + + {{ .Domain }} + + %EMAILADDRESS% + %EMAILLOCALPART% + + + {{ .IMAP.Host }} + {{ .IMAP.Port }} + {{ if .IMAP.STARTTLS }}STARTTLS{{ else }}SSL{{ end }} + password-cleartext + %EMAILADDRESS% + + + + {{ .SMTP.Host }} + {{ .SMTP.Port }} + {{ if .SMTP.STARTTLS }}STARTTLS{{ else }}SSL{{ end }} + password-cleartext + %EMAILADDRESS% + + +{{end}} \ No newline at end of file diff --git a/templates/autodiscover.tmpl b/templates/autodiscover.tmpl new file mode 100644 index 0000000..a3cd535 --- /dev/null +++ b/templates/autodiscover.tmpl @@ -0,0 +1,52 @@ +{{ define "outlook" }} + + + + {{ .Email }} + + + + email + settings + + + IMAP + 1 + + {{ .IMAP.Host }} + {{ .IMAP.Port }} + + {{ .Email }} + + on + {{ .Domain }} + + off + {{ if .IMAP.STARTTLS }}on{{ else }}on{{ end }} + on + + + + + email + settings + + + SMTP + 1 + + {{ .SMTP.Host }} + {{ .SMTP.Port }} + + {{ .Email }} + + on + {{ .Domain }} + + off + {{ if .SMTP.STARTTLS }}on{{ else }}on{{ end }} + on + + + +{{ end }} \ No newline at end of file diff --git a/templates/mobileconfig.tmpl b/templates/mobileconfig.tmpl new file mode 100644 index 0000000..be89afe --- /dev/null +++ b/templates/mobileconfig.tmpl @@ -0,0 +1,77 @@ +{{ define "applemail" }} + + + + HasRemovalPasscode + + PayloadContent + + + EmailAccountDescription + {{ .Email }} + EmailAccountName + {{ .Email }} + EmailAccountType + EmailTypeIMAP + EmailAddress + {{ .Email }} + IncomingMailServerAuthentication + EmailAuthPassword + IncomingMailServerHostName + {{ .IMAP.Host }} + IncomingMailServerPortNumber + {{ .IMAP.Port }} + IncomingMailServerUseSSL + + IncomingMailServerUsername + {{ .Email }} + OutgoingMailServerAuthentication + EmailAuthPassword + OutgoingMailServerHostName + {{ .SMTP.Host }} + OutgoingMailServerPortNumber + {{ .SMTP.Port }} + OutgoingMailServerUseSSL + + OutgoingMailServerUsername + {{ .Email }} + OutgoingPasswordSameAsIncomingPassword + + PayloadDescription + Configure Email Settings + PayloadDisplayName + {{ .Email }} + PayloadIdentifier + com.l11r.goautoconfig.com.apple.mail.managed.7A981A9E-D5D0-4EF8-87FE-39FD6A506FAC + PayloadType + com.apple.mail.managed + PayloadUUID + 7A981A9E-D5D0-4EF8-87FE-39FD6A506FAC + PayloadVersion + 1 + SMIMEEnablePerMessageSwitch + + SMIMEEnabled + + disableMailRecentsSyncing + + + + PayloadDescription + Configure Email Settings + PayloadDisplayName + {{ .Email }} + PayloadIdentifier + com.l11r.goautoconfig + PayloadOrganization + {{ .Domain }} + PayloadRemovalDisallowed + + PayloadType + Configuration + PayloadUUID + 48C88203-4DB9-49E8-B593-4831903605A0 + PayloadVersion + 1 + +{{ end }} \ No newline at end of file