reworked templates so they're not read from disk each time

This commit is contained in:
Paul Wilde 2021-08-18 12:45:30 +01:00
parent 4fa86d82f0
commit b093b46cea
7 changed files with 156 additions and 61 deletions

View file

@ -1,5 +1,5 @@
---
Version : "0.1.2"
Version : "0.1.3"
# Sample config.yaml file.
# Copy this file to "config/config.yaml" and adjust the
# settings to your requirements

View file

@ -6,18 +6,35 @@ import (
"io/ioutil"
"os"
"encoding/json"
"text/template"
"path"
"regexp"
"strings"
"time"
// "reflect"
)
// Global variables
var ThisSession Session
var MainConfig Config
// Template declaration
var templates []string = []string{"autodiscover.xml","autoconfig.xml"}
var Templates map[string]*template.Template = make(map[string]*template.Template)
const defaultConfigDir string = "default-config/"
const configDir string = "config/"
func NewSessionID() string{
timecode := time.Now()
id := timecode.Format("20060102150405.000")
id = strings.Replace(id,".","",1)
return id
}
func NewConfig() Config {
MainConfig = loadConfig()
loadXMLTemplates()
return MainConfig
}
func loadConfig() Config {
@ -37,7 +54,22 @@ func loadConfig() Config {
removeDisabledItems(&cfg)
return cfg
}
func loadXMLTemplates(){
for _, tmpl := range templates {
tmpl := fmt.Sprintf("templates/%s",tmpl)
name := path.Base(tmpl)
var fmap = template.FuncMap{
"lower": strings.ToLower,
"parseUsername": parseUsername,
"onoff": chooseOnOff,
}
t, err := template.New(name).Funcs(fmap).ParseFiles(tmpl)
if err != nil {
panic (err)
}
Templates[name] = t
}
}
func unmarshalConfig(file string, cfg *Config) {
if FileExists(file) {
content, err := ioutil.ReadFile(file)
@ -94,3 +126,40 @@ func JSONify(content interface{}) string {
}
return string(data)
}
func parseUsername(svc Service, email string) string {
if email == "" {
return "not-provided"
}
if svc.UsernameIsFQDN && !svc.RequireLocalDomain{
return email
} else if svc.UsernameIsFQDN && svc.RequireLocalDomain {
re := regexp.MustCompile(`[^@(%40)]+$`)
domain := re.FindString(email)
localemail := strings.Replace(email, domain,
MainConfig.LocalDomain,1)
return localemail
} else {
re := regexp.MustCompile(`^[^@(%40)]+`)
username := re.FindString(email)
return username
}
}
func chooseOnOff(value bool) string {
if value {
return "on"
} else {
return "off"
}
}
// GetIP gets a requests IP address by reading off the forwarded-for
// header (for proxies) and falls back to use the remote address.
func GetSessionIP() string {
r := ThisSession.Request
ip := r.RemoteAddr
forwarded := r.Header.Get("X-FORWARDED-FOR")
if forwarded != "" {
ip = forwarded
}
fmt.Printf("Session %s Connect From : %s\r\f",ThisSession.ID, ip)
return ip
}

View file

@ -4,12 +4,15 @@ import "net/http"
type Session struct {
ID string
IP string
ResponseWriter http.ResponseWriter
Request *http.Request
Path string
WebContent string
ContentType string
}
type Config struct {
Version string `yaml:"Version"`
BaseURL string `yaml:"BaseURL"`
@ -49,7 +52,7 @@ type Response struct {
Url string `json:"url"`
ContentType string `json:"content_type"`
Message string `json:"message"`
Content map[string]interface{} `json:"content"`
Content map[string]interface{} `json:"content"`
Config Config `json:"_"`
Email string `json:"_"`
}

View file

@ -9,7 +9,7 @@
<hostname>{{ .Server }}</hostname>
<port>{{ .Port }}</port>
<socketType>{{ .SocketType }}</socketType>
<username>{{ . | parseUsername }}</username>
<username>{{ $.Email | parseUsername . }}</username>
<authentication>{{ .Authentication }}</authentication>
</incomingServer>
{{ end }}
@ -20,7 +20,7 @@
<hostname>{{ .Server }}</hostname>
<port>{{ .Port }}></port>
<socketType>{{ .SocketType }}</socketType>
<username>{{ . | parseUsername }}</username>
<username>{{ $.Email | parseUsername . }}</username>
<authentication>{{ .Authentication }}</authentication>
</outgoingServer>
{{ end }}
@ -28,7 +28,7 @@
{{ with .Config.AddressBook }}
{{ if .Enabled }}
<addressBook type="{{ .Type | lower }}">
<username>{{ . | parseUsername }}</username>
<username>{{ $.Email | parseUsername . }}</username>
<authentication>{{ .Authentication }}</authentication>
<serverURL>{{ .Server }}</serverURL>
</addressBook>
@ -37,7 +37,7 @@
{{ with .Config.Calendar }}
{{ if .Enabled }}
<calendar type="{{ .Type | lower }}">
<username>{{ . | parseUsername }}</username>
<username>{{ $.Email | parseUsername . }}</username>
<authentication>{{ .Authentication }}</authentication>
<serverURL>{{ .Server }}</serverURL>
</calendar>
@ -48,7 +48,7 @@
<webMail>
<loginPage url="{{ .Server }}" />
<loginPageInfo url="{{ .Server }}">
<username>{{ . | parseUsername }}</username>
<username>{{ $.Email | parseUsername . }}</username>
<usernameField id="{{ .UsernameDivID }}" name="{{ .UsernameDivID }}" />
<passwordField name="{{ .PasswordDivName }}" />
<loginButton id="{{ .SubmitButtonID }}" name="{{ .SubmitButtonName }}"/>

View file

@ -11,7 +11,7 @@
<Server>{{ .Server }}</Server>
<Port>{{ .Port }}</Port>
<DomainRequired>{{ .UsernameIsFQDN | onoff }}</DomainRequired>
<LoginName>{{ . | parseUsername }}</LoginName>
<LoginName>{{ $.Email | parseUsername . }}</LoginName>
<SPA>{{ .SPA | onoff }}</SPA>
<SSL>{{ if eq .SocketType "SSL" }}on{{ else }}off{{ end }}</SSL>
<AuthRequired>{{ not .NoAuthRequired | onoff }}</AuthRequired>
@ -25,7 +25,7 @@
<Server>{{ .Server }}</Server>
<Port>{{ .Port }}</Port>
<DomainRequired>{{ .UsernameIsFQDN | onoff }}</DomainRequired>
<LoginName>{{ . | parseUsername }}</LoginName>
<LoginName>{{ $.Email | parseUsername . }}</LoginName>
<SPA>{{ .SPA | onoff }}</SPA>
<Encryption>{{ .SocketType }}</Encryption>
<AuthRequired>{{ not .NoAuthRequired | onoff }}</AuthRequired>

View file

@ -8,10 +8,16 @@ import (
"fmt"
)
func WebHandler(w http.ResponseWriter, r *http.Request) {
fmt.Println("Request For :",r.URL)
ThisSession = Session{}
ThisSession.ResponseWriter = w
ThisSession.Request = r
ThisSession.ID = NewSessionID()
fmt.Printf("Session %s Request For : %s\r\f",ThisSession.ID, r.URL)
ThisSession.IP = GetSessionIP()
ThisSession.Path = strings.ToLower(r.URL.Path[1:])
if ThisSession.Path == "" {
ThisSession.Path = "none"

View file

@ -2,42 +2,47 @@ package responses
import (
"mailautoconf/global"
. "mailautoconf/structs"
"text/template"
// "text/template"
"fmt"
"path"
// "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"
// parse the querystring
if err := global.ThisSession.Request.ParseForm(); err != nil {
fmt.Println(err)
}
// build the response
response := Response{}
response.Email = global.ThisSession.Request.FormValue("emailaddress")
email = response.Email
response.Config = global.MainConfig
name := path.Base(tmpl)
t, err1 := template.New(name).Funcs(fmap).ParseFiles(tmpl)
if err1 != nil {
panic (err1)
}
// set content type to XML
global.ThisSession.ContentType = "application/xml"
// execute the template
var result bytes.Buffer
err := t.Execute(&result, response)
template := global.Templates["autoconfig.xml"]
err := template.Execute(&result, response)
if err != nil {
fmt.Println(err)
}
// return our string of xml
return result.String()
}
func MsAutoDiscoverXML() string {
// MS Outlook Autodiscover.xml
//
// 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">
@ -46,27 +51,64 @@ func MsAutoDiscoverXML() string {
// <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)
// Parse the form to get the values
if err := global.ThisSession.Request.ParseForm(); err != nil {
fmt.Println(err)
}
// convert the input to a string so we can extract the email address
form := fmt.Sprintf("%s",global.ThisSession.Request.Form)
// fine the EMailAddress section
find := regexp.MustCompile(`\<EMailAddress\>(.*?)\<\/EMailAddress\>`)
email = find.FindString(form)
// replace the tags
replace := regexp.MustCompile(`\<[\/]?EMailAddress\>`)
email = replace.ReplaceAllString(email,``)
fmt.Printf("Session %s Request for email : %s\r\f",global.ThisSession.ID,email)
// build the reponse
response := Response{}
response.Email = email
response.Config = global.MainConfig
// execute the template
template := global.Templates["autodiscover.xml"]
global.ThisSession.ContentType = "application/xml"
var result bytes.Buffer
err := t.Execute(&result, response)
err := template.Execute(&result, response)
if err != nil {
fmt.Println(err)
}
// return our string of xml
return result.String()
}
func MsAutoDiscoverJSON() string {
// MS Outlook Autodiscover.json - undocumented
//
// Example Request
// /autodiscover/autodiscover.json?Email=you@your.domain&Protocol=Autodiscoverv1&RedirectCount=1
return ""
email = global.ThisSession.Request.FormValue("Email")
protocol := global.ThisSession.Request.FormValue("Protocol")
fmt.Println(protocol)
global.ThisSession.ContentType = "application/json"
switch strings.ToLower(protocol) {
case "autodiscoverv1":
response := MSAutodiscoverJSONResponse{}
response.Protocol = "AutodiscoverV1"
response.Url = fmt.Sprintf("%s/Autodiscover/Autodiscover.xml", global.MainConfig.BaseURL)
return global.JSONify(response)
default:
response := MSAutodiscoverJSONError{}
response.ErrorCode = "InvalidProtocol";
response.ErrorMessage = fmt.Sprintf("The given protocol value '%s' is invalid. Supported values are 'AutodiscoverV1'", protocol)
return global.JSONify(response)
}
}
func DefaultResponse() string {
response := Response{}
@ -81,28 +123,3 @@ func OurConfig() string {
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(`[^@(%40)]+$`)
domain := re.FindString(email)
localemail := strings.Replace(email, domain,
global.MainConfig.LocalDomain,1)
return localemail
} else {
re := regexp.MustCompile(`^[^@(%40)]+`)
username := re.FindString(email)
return username
}
}
func chooseOnOff(value bool) string {
if value {
return "on"
} else {
return "off"
}
}