diff --git a/Containerfile b/Containerfile index f8c164e..f7f2701 100644 --- a/Containerfile +++ b/Containerfile @@ -1,15 +1,22 @@ -FROM php:7.4-apache +FROM golang:1-alpine3.14 AS builder -COPY src/ /var/www/html/ +COPY src/ /mailautoconf +WORKDIR /mailautoconf +RUN go build -o /mailautoconf/mailautoconf + +FROM alpine:3.14 + +ENV UID=1426 \ + GID=1426 + +RUN apk add --no-cache bash +COPY --from=builder /mailautoconf/mailautoconf /mailautoconf/mailautoconf +COPY --from=builder /mailautoconf/default-config /mailautoconf/default-config +COPY --from=builder /mailautoconf/templates /mailautoconf/templates COPY ./entrypoint.sh / RUN chmod +x /entrypoint.sh -# Use the default production configuration -RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" - -RUN a2enmod rewrite - -EXPOSE 80 +EXPOSE 8010 ENTRYPOINT ["/entrypoint.sh"] diff --git a/entrypoint.sh b/entrypoint.sh index 57b8053..b3613b1 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,29 +1,29 @@ #!/usr/bin/env bash echo Removing old sample files… -rm /var/www/html/config/*.sample.ini +rm /mailautoconf/config/*.sample.yaml function write_file() { while read line; do first_char=${line:0:1} - if [[ $first_char != ";" ]]; then - line="; "$line + if [[ $first_char != "#" ]]; then + line="#"$line fi echo $line >> $2 done < $1 } echo Setting up new sample config files… -def_conf="/var/www/html/default-config/config.default.ini" -new_conf="/var/www/html/config/config.sample.ini" +def_conf="/mailautoconf/default-config/config.default.yaml" +new_conf="/mailautoconf/config/config.sample.yaml" write_file $def_conf $new_conf -def_serv="/var/www/html/default-config/services.default.ini" -new_serv="/var/www/html/config/services.sample.ini" +def_serv="/mailautoconf/default-config/services.default.yaml" +new_serv="/mailautoconf/config/services.sample.yaml" write_file $def_serv $new_serv +echo New sample files copied - -echo Running HTTPD… -exec apache2-foreground +cd /mailautoconf +exec /mailautoconf/mailautoconf diff --git a/src/default-config/config.default.yaml b/src/default-config/config.default.yaml index 9d16d1a..870d458 100644 --- a/src/default-config/config.default.yaml +++ b/src/default-config/config.default.yaml @@ -14,4 +14,4 @@ Domains : - "example2,com" # If you use a different domain to authenticate with, enter it here -LogonDomain : "example.local" +LocalDomain : "example.local" diff --git a/src/default-config/services.default.yaml b/src/default-config/services.default.yaml index b65d573..52d636b 100644 --- a/src/default-config/services.default.yaml +++ b/src/default-config/services.default.yaml @@ -28,6 +28,9 @@ InMail: # Change to true if you need the domain/logondomain to form part of the username UsernameIsFQDN: false + # Use the LogonDomain instead of the Email Domain + RequireLocalDomain : false + # Do you need to authenticate to your mail server? You should! so this should be false! NoAuthRequired: false @@ -67,6 +70,9 @@ OutMail: # Change to true if you need the domain/logondomain to form part of the username UsernameIsFQDN: false + # Use the LogonDomain instead of the Email Domain + RequireLocalDomain : false + # Do you need to authenticate to your mail server? You should! so this should be false! NoAuthRequired: false @@ -87,6 +93,7 @@ Calendar: Type: "CalDAV" Authentication: "http-basic" UsernameIsFQDN: false + RequireLocalDomain : false # Currently not implemented, see https://wiki.mozilla.org/Thunderbird:Autoconfiguration:ConfigFileFormat AddressBook: @@ -98,6 +105,7 @@ AddressBook: Type: "CardDAV" Authentication: "http-basic" UsernameIsFQDN: false + RequireLocalDomain : false # Currently not implemented, see https://wiki.mozilla.org/Thunderbird:Autoconfiguration:ConfigFileFormat WebMail: @@ -111,6 +119,7 @@ WebMail: SubmitButtonID: "submit" SubmitButtonName: "submit" UsernameIsFQDN: false + RequireLocalDomain : false # In theory, additional custom services can be configured and will be displayed with diff --git a/src/go.mod b/src/go.mod index 884016d..db9995d 100644 --- a/src/go.mod +++ b/src/go.mod @@ -3,5 +3,13 @@ module mailautoconf go 1.16 require ( + github.com/Masterminds/goutils v1.1.1 // indirect + github.com/Masterminds/semver v1.5.0 // indirect + github.com/Masterminds/sprig v2.22.0+incompatible // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/huandu/xstrings v1.3.2 // indirect + github.com/imdario/mergo v0.3.12 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + golang.org/x/crypto v0.0.0-20210813211128-0a44fdfbc16e // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/src/go.sum b/src/go.sum index 7534661..bd2f174 100644 --- a/src/go.sum +++ b/src/go.sum @@ -1,3 +1,28 @@ +github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= +github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= +github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= +github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +golang.org/x/crypto v0.0.0-20210813211128-0a44fdfbc16e h1:VvfwVmMH40bpMeizC9/K7ipM5Qjucuu16RWfneFPyhQ= +golang.org/x/crypto v0.0.0-20210813211128-0a44fdfbc16e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +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/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/src/structs/structs.go b/src/structs/structs.go index 0a80933..358a0da 100644 --- a/src/structs/structs.go +++ b/src/structs/structs.go @@ -14,11 +14,12 @@ type Config struct { Version string `yaml:"Version"` BaseURL string `yaml:"BaseURL"` Domains []string `yaml:"Domains"` - LogonDomain string `yaml:"LogonDomain"` + LocalDomain string `yaml:"LocalDomain"` InMail Service `yaml:"InMail"` OutMail Service `yaml:"OutMail"` Calendar Service `yaml:"Calendar"` AddressBook Service `yaml:"AddressBook"` + WebMail Service `yaml:"WebMail"` OtherServices []Service `yaml:"OtherServices"` } @@ -29,8 +30,9 @@ type Service struct { Server string `yaml:"Server"` Port int `yaml:"Port"` SocketType string `yaml:"SocketType"` - SPA bool `yaml:"SPA"` + SPA bool `yaml:"SPA"` UsernameIsFQDN bool `yaml:"UsernameIsFQDN"` + RequireLocalDomain bool `yaml:"RequireLocalDomain"` NoAuthRequired bool `yaml:"NoAuthRequired"` Authentication string `yaml:"Authentication"` // For Outgoing Mail @@ -44,10 +46,12 @@ type Service struct { SubmitButtonName string `yaml:"SubmitButtonName"` } type Response struct { - Url string `json:"url"` - ContentType string `json:"content_type"` - Message string `json:"message"` - Content []interface{} `json:"content"` + Url string `json:"url"` + ContentType string `json:"content_type"` + Message string `json:"message"` + Content map[string]interface{} `json:"content"` + Config Config + Email string } type MSAutodiscoverJSONResponse struct { // More work to do - handling of MS Autodiscover.json requests diff --git a/src/templates/autoconfig.html b/src/templates/autoconfig.html deleted file mode 100644 index 169baf1..0000000 --- a/src/templates/autoconfig.html +++ /dev/null @@ -1,64 +0,0 @@ - -

Go templates

-

The user is {{ .Name }}

-

Skills:

-{{ range .Skills }} -

{{ . }}

-{{ end }} - - - ">{{ . }} - - - - %EMAILADDRESS% - - "> - - - - get_username($service,$email_address); ?> - - - - "> - - - - get_username($service,$email_address);?> - - - - "> - get_username($service,$email_address);?> - - - - - "> - get_username($service,$email_address);?> - - - - - - " /> - "> - get_username($service,$email_address);?> - " name="" /> - " /> - " name=""/> - - - - - diff --git a/src/templates/autoconfig.xml b/src/templates/autoconfig.xml new file mode 100644 index 0000000..9320e50 --- /dev/null +++ b/src/templates/autoconfig.xml @@ -0,0 +1,50 @@ + + + {{ range .Config.Domains }}{{ . }} + {{ end }} + {{ .Email }} + {{ with .Config.InMail }} + + {{ .Server }} + {{ .Port }} + {{ .SocketType }} + {{ . | parseUsername }} + {{ .Authentication }} + + {{ end }} + {{ with .Config.OutMail }} + + {{ .Server }} + {{ .Port }}> + {{ .SocketType }} + {{ . | parseUsername }} + {{ .Authentication }} + + {{ end }} + {{ with .Config.AddressBook }} + + {{ . | parseUsername }} + {{ .Authentication }} + {{ .Server }} + + {{ end }} + {{ with .Config.Calendar }} + + {{ . | parseUsername }} + {{ .Authentication }} + {{ .Server }} + + {{ end }} + {{ with .Config.WebMail }} + + + + {{ . | parseUsername }} + + + + + + {{ end }} + + diff --git a/src/templates/autodiscover.html b/src/templates/autodiscover.html deleted file mode 100644 index 3a9342b..0000000 --- a/src/templates/autodiscover.html +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - email - settings - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/templates/autodiscover.xml b/src/templates/autodiscover.xml new file mode 100644 index 0000000..b024b74 --- /dev/null +++ b/src/templates/autodiscover.xml @@ -0,0 +1,35 @@ + + + + + email + settings + {{ with .Config.InMail }} + + {{ .Type }} + {{ .Server }} + {{ .Port }} + {{ .UsernameIsFQDN | onoff }} + {{ . | parseUsername }} + {{ .SPA | onoff }} + {{ if eq .SocketType "SSL" }}on{{ else }}off{{ end }} + {{ not .NoAuthRequired | onoff }} + + {{ end }} + {{ with .Config.OutMail }} + + {{ .Type }} + {{ .Server }} + {{ .Port }} + {{ .UsernameIsFQDN | onoff }} + {{ . | parseUsername }} + {{ .SPA | onoff }} + {{ .SocketType }} + {{ not .NoAuthRequired | onoff }} + {{ .POPAuth | onoff }} + {{ .SMTPLast | onoff }} + + {{ end }} + + + diff --git a/src/web/handler/handler.go b/src/web/handler/handler.go index b2144b9..f9ceb1f 100644 --- a/src/web/handler/handler.go +++ b/src/web/handler/handler.go @@ -8,6 +8,7 @@ import ( "fmt" ) func WebHandler(w http.ResponseWriter, r *http.Request) { + fmt.Println("Request For :",r.URL) ThisSession = Session{} ThisSession.ResponseWriter = w ThisSession.Request = r @@ -15,9 +16,9 @@ func WebHandler(w http.ResponseWriter, r *http.Request) { if ThisSession.Path == "" { ThisSession.Path = "none" } - switch ThisSession.Path { - case "mail/config-v1.1.xml": + case "mail/config-v1.1.xml", + "mail/autoconfig.xml": ThisSession.WebContent = responses.MozAutoconfig() case "autodiscover/autodiscover.xml": ThisSession.WebContent = responses.MsAutoDiscoverXML() diff --git a/src/web/responses/responses.go b/src/web/responses/responses.go index 6a4a05e..d0cc973 100644 --- a/src/web/responses/responses.go +++ b/src/web/responses/responses.go @@ -2,30 +2,65 @@ package responses import ( "mailautoconf/global" . "mailautoconf/structs" - "html/template" - // "fmt" + "text/template" + "fmt" + "path" + "strings" + "bytes" + "regexp" ) +var email string +var fmap = template.FuncMap{ + "lower": strings.ToLower, + "parseUsername": parseUsername, + "onoff": chooseOnOff, + } func MozAutoconfig() string { // The below link has config-v1.1.xml information // https://wiki.mozilla.org/Thunderbird:Autoconfiguration:ConfigFileFormat - tmpl := "templates/autoconfig.html" - t := template.Must(template.ParseFiles(tmpl)) - data := struct { - Name string - Skills []string - }{ - Name: "John Doe", - Skills: []string{ - "C++", - "Java", - "Python", - }, - } - t.Execute(global.ThisSession.ResponseWriter, data) - return "" + tmpl := "templates/autoconfig.xml" + response := Response{} + response.Email = global.ThisSession.Request.FormValue("emailaddress") + response.Config = global.MainConfig + + name := path.Base(tmpl) + t, err1 := template.New(name).Funcs(fmap).ParseFiles(tmpl) + if err1 != nil { + panic (err1) + } + global.ThisSession.ContentType = "application/xml" + var result bytes.Buffer + err := t.Execute(&result, response) + if err != nil { + fmt.Println(err) + } + return result.String() } func MsAutoDiscoverXML() string { - return "" + // Example POST Request (sent from client) : + // + // + // + // your@email.address + // http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a + // + // + tmpl := "templates/autodiscover.xml" + email = global.ThisSession.Request.FormValue("EMailAddress") + response := Response{} + response.Config = global.MainConfig + name := path.Base(tmpl) + t, err1 := template.New(name).Funcs(fmap).ParseFiles(tmpl) + if err1 != nil { + panic (err1) + } + global.ThisSession.ContentType = "application/xml" + var result bytes.Buffer + err := t.Execute(&result, response) + if err != nil { + fmt.Println(err) + } + return result.String() } func MsAutoDiscoverJSON() string { return "" @@ -43,3 +78,28 @@ func OurConfig() string { content := global.JSONify(global.MainConfig) return content } +func parseUsername(svc Service) string { + if email == "" { + return "" + } + if svc.UsernameIsFQDN && !svc.RequireLocalDomain{ + return email + } else if svc.UsernameIsFQDN && svc.RequireLocalDomain { + re := regexp.MustCompile(`[^@]+$`) + domain := re.FindString(email) + localemail := strings.Replace(email, domain, + global.MainConfig.LocalDomain,1) + return localemail + } else { + re := regexp.MustCompile(`^[^@]+`) + username := re.FindString(email) + return username + } +} +func chooseOnOff(value bool) string { + if value { + return "on" + } else { + return "off" + } +}