Compare commits
8 commits
a5a3cbe2b5
...
14f30beb10
Author | SHA1 | Date | |
---|---|---|---|
14f30beb10 | |||
b435c1bf96 | |||
c3831ed8dd | |||
89df3cca20 | |||
c7bf57dab3 | |||
acef6e76d7 | |||
1df0b80c1f | |||
47dc3d5bc8 |
15 changed files with 599 additions and 19 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
config
|
config
|
||||||
|
src/config
|
||||||
|
|
|
@ -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 /
|
COPY ./entrypoint.sh /
|
||||||
RUN chmod +x /entrypoint.sh
|
RUN chmod +x /entrypoint.sh
|
||||||
|
|
||||||
# Use the default production configuration
|
EXPOSE 8010
|
||||||
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
|
|
||||||
|
|
||||||
RUN a2enmod rewrite
|
|
||||||
|
|
||||||
EXPOSE 80
|
|
||||||
|
|
||||||
ENTRYPOINT ["/entrypoint.sh"]
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
|
|
|
@ -20,7 +20,7 @@ podman run -dt \
|
||||||
--name mailautoconf \
|
--name mailautoconf \
|
||||||
-v ./config:/var/www/html/config \
|
-v ./config:/var/www/html/config \
|
||||||
-p 8010:80 \
|
-p 8010:80 \
|
||||||
pswilde/automailconf
|
pswilde/mailautoconf
|
||||||
```
|
```
|
||||||
You will need a reverse proxy server to publish to the outside world and handle SSL encryption.
|
You will need a reverse proxy server to publish to the outside world and handle SSL encryption.
|
||||||
For example, in nginx:
|
For example, in nginx:
|
||||||
|
|
|
@ -1,29 +1,29 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
echo Removing old sample files…
|
echo Removing old sample files…
|
||||||
rm /var/www/html/config/*.sample.ini
|
rm /mailautoconf/config/*.sample.yaml
|
||||||
|
|
||||||
function write_file() {
|
function write_file() {
|
||||||
while read line;
|
while read line;
|
||||||
do
|
do
|
||||||
first_char=${line:0:1}
|
first_char=${line:0:1}
|
||||||
|
|
||||||
if [[ $first_char != ";" ]]; then
|
if [[ $first_char != "#" ]]; then
|
||||||
line="; "$line
|
line="#"$line
|
||||||
fi
|
fi
|
||||||
echo $line >> $2
|
echo $line >> $2
|
||||||
done < $1
|
done < $1
|
||||||
}
|
}
|
||||||
|
|
||||||
echo Setting up new sample config files…
|
echo Setting up new sample config files…
|
||||||
def_conf="/var/www/html/default-config/config.default.ini"
|
def_conf="/mailautoconf/default-config/config.default.yaml"
|
||||||
new_conf="/var/www/html/config/config.sample.ini"
|
new_conf="/mailautoconf/config/config.sample.yaml"
|
||||||
write_file $def_conf $new_conf
|
write_file $def_conf $new_conf
|
||||||
|
|
||||||
def_serv="/var/www/html/default-config/services.default.ini"
|
def_serv="/mailautoconf/default-config/services.default.yaml"
|
||||||
new_serv="/var/www/html/config/services.sample.ini"
|
new_serv="/mailautoconf/config/services.sample.yaml"
|
||||||
write_file $def_serv $new_serv
|
write_file $def_serv $new_serv
|
||||||
|
|
||||||
|
echo New sample files copied
|
||||||
|
|
||||||
|
cd /mailautoconf
|
||||||
echo Running HTTPD…
|
exec /mailautoconf/mailautoconf
|
||||||
exec apache2-foreground
|
|
||||||
|
|
17
src/default-config/config.default.yaml
Normal file
17
src/default-config/config.default.yaml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
---
|
||||||
|
Version : "0.1.1"
|
||||||
|
# Sample config.yaml file.
|
||||||
|
# Copy this file to "config/config.yaml" and adjust the
|
||||||
|
# settings to your requirements
|
||||||
|
|
||||||
|
# The URL of this application
|
||||||
|
BaseURL : "https://autoconfig.example.com"
|
||||||
|
|
||||||
|
# Set the email domains for use with this service. The first one is primary.
|
||||||
|
# each will need their own DNS A record for autoconfig.domain.name
|
||||||
|
Domains :
|
||||||
|
- "example.com"
|
||||||
|
- "example2,com"
|
||||||
|
|
||||||
|
# If you use a different domain to authenticate with, enter it here
|
||||||
|
LocalDomain : "example.local"
|
133
src/default-config/services.default.yaml
Normal file
133
src/default-config/services.default.yaml
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
# Sample services.yaml file.
|
||||||
|
# Copy this file to "config/services.yaml" and adjust the
|
||||||
|
# settings to your requirements
|
||||||
|
---
|
||||||
|
|
||||||
|
# The Incoming mail Server Config
|
||||||
|
InMail:
|
||||||
|
Name: "InMail"
|
||||||
|
|
||||||
|
# Enable this service
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
# Mail Type, i.e. IMAP, POP3
|
||||||
|
Type: "IMAP"
|
||||||
|
|
||||||
|
# Your IMAP server
|
||||||
|
Server: "imap.example.com"
|
||||||
|
|
||||||
|
# Your IMAP port
|
||||||
|
Port: 993
|
||||||
|
|
||||||
|
# The socket type : SSL, STARTTLS, or NONE
|
||||||
|
SocketType: "SSL"
|
||||||
|
|
||||||
|
# Use Secure Password Authentication
|
||||||
|
SPA: false
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# Authentication type,
|
||||||
|
#"password-cleartext" : Send password in the clear
|
||||||
|
# (dangerous, if SSL isn't used either).
|
||||||
|
# AUTH PLAIN, LOGIN or protocol-native login.
|
||||||
|
#"password-encrypted" : A secure encrypted password mechanism.
|
||||||
|
# Can be CRAM-MD5 or DIGEST-MD5. Not NTLM.
|
||||||
|
#"NTLM": Use NTLM (or NTLMv2 or successors),
|
||||||
|
# the Windows login mechanism.
|
||||||
|
Authentication: "password-cleartext"
|
||||||
|
|
||||||
|
# The Outgoing mail server config
|
||||||
|
OutMail:
|
||||||
|
# Enable this service
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
# Mail type, likely to only be SMTP
|
||||||
|
Type: "SMTP"
|
||||||
|
|
||||||
|
# Your SMTP server
|
||||||
|
Server: "smtp.example.com"
|
||||||
|
|
||||||
|
# Your SMTP port
|
||||||
|
Port: 465
|
||||||
|
|
||||||
|
# The socket type : SSL, STARTTLS or NONE
|
||||||
|
SocketType: "SSL"
|
||||||
|
|
||||||
|
# See InMail > Authentication
|
||||||
|
Authentication: "password-cleartext"
|
||||||
|
|
||||||
|
# Use Secure Password Authentication
|
||||||
|
SPA: false
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# Use POP Authentication? You probably shouldn't be.
|
||||||
|
POPAuth: false
|
||||||
|
|
||||||
|
# This setting is here to limit errors, I'm not sure what it does yet.
|
||||||
|
SMTPLast: false
|
||||||
|
|
||||||
|
|
||||||
|
# Currently not implemented, see https://wiki.mozilla.org/Thunderbird:Autoconfiguration:ConfigFileFormat
|
||||||
|
Calendar:
|
||||||
|
Name: "Calendar"
|
||||||
|
# Disable this service
|
||||||
|
Enabled: false
|
||||||
|
Server: "https://example.com/remote.php/dav/"
|
||||||
|
Port: 443
|
||||||
|
Type: "CalDAV"
|
||||||
|
Authentication: "http-basic"
|
||||||
|
UsernameIsFQDN: false
|
||||||
|
RequireLocalDomain : false
|
||||||
|
|
||||||
|
# Currently not implemented, see https://wiki.mozilla.org/Thunderbird:Autoconfiguration:ConfigFileFormat
|
||||||
|
AddressBook:
|
||||||
|
Name: "AddressBook"
|
||||||
|
# Disable this service
|
||||||
|
Enabled: false
|
||||||
|
Server: "https://example.com/remote.php/dav/"
|
||||||
|
Port: 443
|
||||||
|
Type: "CardDAV"
|
||||||
|
Authentication: "http-basic"
|
||||||
|
UsernameIsFQDN: false
|
||||||
|
RequireLocalDomain : false
|
||||||
|
|
||||||
|
# Currently not implemented, see https://wiki.mozilla.org/Thunderbird:Autoconfiguration:ConfigFileFormat
|
||||||
|
WebMail:
|
||||||
|
Name: "WebMail"
|
||||||
|
# Disable this service
|
||||||
|
Enabled: false
|
||||||
|
Server: "https://mail.example.com"
|
||||||
|
UsernameDivID: "username"
|
||||||
|
UsernameDivName: "username"
|
||||||
|
PasswordDivName: "password"
|
||||||
|
SubmitButtonID: "submit"
|
||||||
|
SubmitButtonName: "submit"
|
||||||
|
UsernameIsFQDN: false
|
||||||
|
RequireLocalDomain : false
|
||||||
|
|
||||||
|
|
||||||
|
# In theory, additional custom services can be configured and will be displayed with
|
||||||
|
# their options on the /get/all URL of this service. The third-party clients would need to
|
||||||
|
# check this service as part of their development for this to work
|
||||||
|
# Will not be shown in autodiscover.xml/json or config-v1.1.xml/autoconfig.xml
|
||||||
|
# i.e Nextcloud - ideally a nextcloud client could check autoconfig for this URL for ease of set up
|
||||||
|
#OtherServices:
|
||||||
|
# -
|
||||||
|
# Name : "NextCloud"
|
||||||
|
# Server : "https://nextcloud.example.com"
|
70
src/global/global.go
Normal file
70
src/global/global.go
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
package global
|
||||||
|
import (
|
||||||
|
. "mailautoconf/structs"
|
||||||
|
"fmt"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
// Global variables
|
||||||
|
var ThisSession Session
|
||||||
|
var MainConfig Config
|
||||||
|
const defaultConfigDir string = "default-config/"
|
||||||
|
const configDir string = "config/"
|
||||||
|
|
||||||
|
func NewConfig() Config {
|
||||||
|
MainConfig = loadConfig()
|
||||||
|
return MainConfig
|
||||||
|
}
|
||||||
|
func loadConfig() Config {
|
||||||
|
cfg := Config{}
|
||||||
|
fmt.Println("Loading Default Config…")
|
||||||
|
cfgfile := defaultConfigDir + "config.default.yaml"
|
||||||
|
unmarshalConfig(cfgfile, &cfg)
|
||||||
|
fmt.Println("Loading Custom Config…")
|
||||||
|
customcfgfile := configDir + "config.yaml"
|
||||||
|
unmarshalConfig(customcfgfile, &cfg)
|
||||||
|
fmt.Println("Loading Default Services…")
|
||||||
|
svcfile := defaultConfigDir + "services.default.yaml"
|
||||||
|
unmarshalConfig(svcfile, &cfg)
|
||||||
|
fmt.Println("Loading Custom Services…")
|
||||||
|
customsvcfile := configDir + "services.yaml"
|
||||||
|
unmarshalConfig(customsvcfile, &cfg)
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalConfig(file string, cfg *Config) {
|
||||||
|
if FileExists(file) {
|
||||||
|
content, err := ioutil.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error reading config :", file, " : ", err)
|
||||||
|
}
|
||||||
|
err2 := yaml.Unmarshal(content, &cfg)
|
||||||
|
if err2 != nil {
|
||||||
|
fmt.Println("Error unmarshalling config :", file, " : ", err2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func FileExists(file string) bool {
|
||||||
|
exists := false
|
||||||
|
if _, err := os.Stat(file); err == nil {
|
||||||
|
exists = true
|
||||||
|
} else {
|
||||||
|
fmt.Println(err)
|
||||||
|
fmt.Printf("File %s does not exist\n", file);
|
||||||
|
}
|
||||||
|
return exists
|
||||||
|
}
|
||||||
|
|
||||||
|
func JSONify(content interface{}) string {
|
||||||
|
data, err := json.Marshal(content)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
return string(data)
|
||||||
|
}
|
15
src/go.mod
Normal file
15
src/go.mod
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
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
|
||||||
|
)
|
28
src/go.sum
Normal file
28
src/go.sum
Normal file
|
@ -0,0 +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=
|
16
src/mailautoconf.go
Normal file
16
src/mailautoconf.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"log"
|
||||||
|
"mailautoconf/web/handler"
|
||||||
|
"mailautoconf/global"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
global.NewConfig()
|
||||||
|
http.HandleFunc("/", handler.WebHandler)
|
||||||
|
fmt.Println("Starting up Web Listener on port 8010")
|
||||||
|
log.Fatal(http.ListenAndServe(":8010", nil))
|
||||||
|
}
|
64
src/structs/structs.go
Normal file
64
src/structs/structs.go
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
package structs
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
|
||||||
|
type Session struct {
|
||||||
|
ResponseWriter http.ResponseWriter
|
||||||
|
Request *http.Request
|
||||||
|
Path string
|
||||||
|
WebContent string
|
||||||
|
ContentType string
|
||||||
|
}
|
||||||
|
type Config struct {
|
||||||
|
Version string `yaml:"Version"`
|
||||||
|
BaseURL string `yaml:"BaseURL"`
|
||||||
|
Domains []string `yaml:"Domains"`
|
||||||
|
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"`
|
||||||
|
|
||||||
|
}
|
||||||
|
type Service struct {
|
||||||
|
Name string `yaml:"Name"`
|
||||||
|
Enabled bool `yaml:"Enabled"`
|
||||||
|
Type string `yaml:"Type"`
|
||||||
|
Server string `yaml:"Server"`
|
||||||
|
Port int `yaml:"Port"`
|
||||||
|
SocketType string `yaml:"SocketType"`
|
||||||
|
SPA bool `yaml:"SPA"`
|
||||||
|
UsernameIsFQDN bool `yaml:"UsernameIsFQDN"`
|
||||||
|
RequireLocalDomain bool `yaml:"RequireLocalDomain"`
|
||||||
|
NoAuthRequired bool `yaml:"NoAuthRequired"`
|
||||||
|
Authentication string `yaml:"Authentication"`
|
||||||
|
// For Outgoing Mail
|
||||||
|
POPAuth bool `yaml:"POPAuth"`
|
||||||
|
SMTPLast bool `yaml:"SMTPLast"`
|
||||||
|
// For WebMail (Unused)
|
||||||
|
UsernameDivID string `yaml:"UsernameDivID"`
|
||||||
|
UsernameDivName string `yaml:"UsernameDivName"`
|
||||||
|
PasswordDivName string `yaml:"PasswordDivName"`
|
||||||
|
SubmitButtonID string `yaml:"SubmitButtonID"`
|
||||||
|
SubmitButtonName string `yaml:"SubmitButtonName"`
|
||||||
|
}
|
||||||
|
type Response struct {
|
||||||
|
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
|
||||||
|
Protocol string
|
||||||
|
Url string
|
||||||
|
}
|
||||||
|
type MSAutodiscoverJSONError struct{
|
||||||
|
ErrorCode string
|
||||||
|
ErrorMessage string
|
||||||
|
}
|
50
src/templates/autoconfig.xml
Normal file
50
src/templates/autoconfig.xml
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
<clientConfig version="1.1">
|
||||||
|
<emailProvider id="{{ index .Config.Domains 0 }}">
|
||||||
|
{{ range .Config.Domains }}<domain>{{ . }}</domain>
|
||||||
|
{{ end }}
|
||||||
|
<displayName>{{ .Email }}</displayName>
|
||||||
|
{{ with .Config.InMail }}
|
||||||
|
<incomingServer type="{{ .Type | lower }}">
|
||||||
|
<hostname>{{ .Server }}</hostname>
|
||||||
|
<port>{{ .Port }}</port>
|
||||||
|
<socketType>{{ .SocketType }}</socketType>
|
||||||
|
<username>{{ . | parseUsername }}</username>
|
||||||
|
<authentication>{{ .Authentication }}</authentication>
|
||||||
|
</incomingServer>
|
||||||
|
{{ end }}
|
||||||
|
{{ with .Config.OutMail }}
|
||||||
|
<outgoingServer type="{{ .Type | lower }}">
|
||||||
|
<hostname>{{ .Server }}</hostname>
|
||||||
|
<port>{{ .Port }}></port>
|
||||||
|
<socketType>{{ .SocketType }}</socketType>
|
||||||
|
<username>{{ . | parseUsername }}</username>
|
||||||
|
<authentication>{{ .Authentication }}</authentication>
|
||||||
|
</outgoingServer>
|
||||||
|
{{ end }}
|
||||||
|
{{ with .Config.AddressBook }}
|
||||||
|
<addressBook type="{{ .Type | lower }}">
|
||||||
|
<username>{{ . | parseUsername }}</username>
|
||||||
|
<authentication>{{ .Authentication }}</authentication>
|
||||||
|
<serverURL>{{ .Server }}</serverURL>
|
||||||
|
</addressBook>
|
||||||
|
{{ end }}
|
||||||
|
{{ with .Config.Calendar }}
|
||||||
|
<calendar type="{{ .Type | lower }}">
|
||||||
|
<username>{{ . | parseUsername }}</username>
|
||||||
|
<authentication>{{ .Authentication }}</authentication>
|
||||||
|
<serverURL>{{ .Server }}</serverURL>
|
||||||
|
</calendar>
|
||||||
|
{{ end }}
|
||||||
|
{{ with .Config.WebMail }}
|
||||||
|
<webMail>
|
||||||
|
<loginPage url="{{ .Server }}" />
|
||||||
|
<loginPageInfo url="{{ .Server }}">
|
||||||
|
<username>{{ . | parseUsername }}</username>
|
||||||
|
<usernameField id="{{ .UsernameDivID }}" name="{{ .UsernameDivID }}" />
|
||||||
|
<passwordField name="{{ .PasswordDivName }}" />
|
||||||
|
<loginButton id="{{ .SubmitButtonID }}" name="{{ .SubmitButtonName }}"/>
|
||||||
|
</loginPageInfo>
|
||||||
|
</webMail>
|
||||||
|
{{ end }}
|
||||||
|
</emailProvider>
|
||||||
|
</clientConfig>
|
35
src/templates/autodiscover.xml
Normal file
35
src/templates/autodiscover.xml
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006">
|
||||||
|
<Response xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a">
|
||||||
|
<Account>
|
||||||
|
<AccountType>email</AccountType>
|
||||||
|
<Action>settings</Action>
|
||||||
|
{{ with .Config.InMail }}
|
||||||
|
<Protocol>
|
||||||
|
<Type>{{ .Type }}</Type>
|
||||||
|
<Server>{{ .Server }}</Server>
|
||||||
|
<Port>{{ .Port }}</Port>
|
||||||
|
<DomainRequired>{{ .UsernameIsFQDN | onoff }}</DomainRequired>
|
||||||
|
<LoginName>{{ . | parseUsername }}</LoginName>
|
||||||
|
<SPA>{{ .SPA | onoff }}</SPA>
|
||||||
|
<SSL>{{ if eq .SocketType "SSL" }}on{{ else }}off{{ end }}</SSL>
|
||||||
|
<AuthRequired>{{ not .NoAuthRequired | onoff }}</AuthRequired>
|
||||||
|
</Protocol>
|
||||||
|
{{ end }}
|
||||||
|
{{ with .Config.OutMail }}
|
||||||
|
<Protocol>
|
||||||
|
<Type>{{ .Type }}</Type>
|
||||||
|
<Server>{{ .Server }}</Server>
|
||||||
|
<Port>{{ .Port }}</Port>
|
||||||
|
<DomainRequired>{{ .UsernameIsFQDN | onoff }}</DomainRequired>
|
||||||
|
<LoginName>{{ . | parseUsername }}</LoginName>
|
||||||
|
<SPA>{{ .SPA | onoff }}</SPA>
|
||||||
|
<Encryption>{{ .SocketType }}</Encryption>
|
||||||
|
<AuthRequired>{{ not .NoAuthRequired | onoff }}</AuthRequired>
|
||||||
|
<UsePOPAuth>{{ .POPAuth | onoff }}</UsePOPAuth>
|
||||||
|
<SMTPLast>{{ .SMTPLast | onoff }}</SMTPLast>
|
||||||
|
</Protocol>
|
||||||
|
{{ end }}
|
||||||
|
</Account>
|
||||||
|
</Response>
|
||||||
|
</Autodiscover>
|
39
src/web/handler/handler.go
Normal file
39
src/web/handler/handler.go
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
package handler
|
||||||
|
import (
|
||||||
|
. "mailautoconf/structs"
|
||||||
|
. "mailautoconf/global"
|
||||||
|
"mailautoconf/web/responses"
|
||||||
|
"strings"
|
||||||
|
"net/http"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
func WebHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Println("Request For :",r.URL)
|
||||||
|
ThisSession = Session{}
|
||||||
|
ThisSession.ResponseWriter = w
|
||||||
|
ThisSession.Request = r
|
||||||
|
ThisSession.Path = strings.ToLower(r.URL.Path[1:])
|
||||||
|
if ThisSession.Path == "" {
|
||||||
|
ThisSession.Path = "none"
|
||||||
|
}
|
||||||
|
switch ThisSession.Path {
|
||||||
|
case "mail/config-v1.1.xml",
|
||||||
|
"mail/autoconfig.xml":
|
||||||
|
ThisSession.WebContent = responses.MozAutoconfig()
|
||||||
|
case "autodiscover/autodiscover.xml":
|
||||||
|
ThisSession.WebContent = responses.MsAutoDiscoverXML()
|
||||||
|
case "autodiscover/autodiscover.json":
|
||||||
|
ThisSession.WebContent = responses.MsAutoDiscoverJSON()
|
||||||
|
case "get/config":
|
||||||
|
ThisSession.WebContent = responses.OurConfig()
|
||||||
|
default:
|
||||||
|
ThisSession.WebContent = responses.DefaultResponse()
|
||||||
|
}
|
||||||
|
|
||||||
|
writeWebOutput()
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeWebOutput () {
|
||||||
|
ThisSession.ResponseWriter.Header().Set("Content-Type", ThisSession.ContentType)
|
||||||
|
fmt.Fprintf(ThisSession.ResponseWriter, ThisSession.WebContent)
|
||||||
|
}
|
105
src/web/responses/responses.go
Normal file
105
src/web/responses/responses.go
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
package responses
|
||||||
|
import (
|
||||||
|
"mailautoconf/global"
|
||||||
|
. "mailautoconf/structs"
|
||||||
|
"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.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 {
|
||||||
|
// Example POST Request (sent from client) :
|
||||||
|
// <?xml version="1.0" \?\>
|
||||||
|
// <Autodiscover xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006">
|
||||||
|
// <Request>
|
||||||
|
// <EMailAddress>your@email.address</EMailAddress>
|
||||||
|
// <AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema>
|
||||||
|
// </Request>
|
||||||
|
// </Autodiscover>
|
||||||
|
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 ""
|
||||||
|
}
|
||||||
|
func DefaultResponse() string {
|
||||||
|
response := Response{}
|
||||||
|
response.Url = global.ThisSession.Path
|
||||||
|
global.ThisSession.ContentType = "application/json"
|
||||||
|
response.ContentType = global.ThisSession.ContentType
|
||||||
|
response.Message = "Success! Things are working! Please request a valid URL i.e. /mail/config-v1.1.xml";
|
||||||
|
return global.JSONify(response)
|
||||||
|
}
|
||||||
|
func OurConfig() string {
|
||||||
|
global.ThisSession.ContentType = "application/json"
|
||||||
|
content := global.JSONify(global.MainConfig)
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
func parseUsername(svc Service) string {
|
||||||
|
if email == "" {
|
||||||
|
return "<!-- not provided -->"
|
||||||
|
}
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue