diff --git a/src/default-config/config.default.yaml b/src/default-config/config.default.yaml index 32b6cdf..9696177 100644 --- a/src/default-config/config.default.yaml +++ b/src/default-config/config.default.yaml @@ -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 diff --git a/src/global/global.go b/src/global/global.go index c518d11..a2316b5 100644 --- a/src/global/global.go +++ b/src/global/global.go @@ -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 +} diff --git a/src/structs/structs.go b/src/structs/structs.go index 12197c3..61186d8 100644 --- a/src/structs/structs.go +++ b/src/structs/structs.go @@ -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:"_"` } diff --git a/src/templates/autoconfig.xml b/src/templates/autoconfig.xml index a721a4b..9fc4796 100644 --- a/src/templates/autoconfig.xml +++ b/src/templates/autoconfig.xml @@ -9,7 +9,7 @@ {{ .Server }} {{ .Port }} {{ .SocketType }} - {{ . | parseUsername }} + {{ $.Email | parseUsername . }} {{ .Authentication }} {{ end }} @@ -20,7 +20,7 @@ {{ .Server }} {{ .Port }}> {{ .SocketType }} - {{ . | parseUsername }} + {{ $.Email | parseUsername . }} {{ .Authentication }} {{ end }} @@ -28,7 +28,7 @@ {{ with .Config.AddressBook }} {{ if .Enabled }} - {{ . | parseUsername }} + {{ $.Email | parseUsername . }} {{ .Authentication }} {{ .Server }} @@ -37,7 +37,7 @@ {{ with .Config.Calendar }} {{ if .Enabled }} - {{ . | parseUsername }} + {{ $.Email | parseUsername . }} {{ .Authentication }} {{ .Server }} @@ -48,7 +48,7 @@ - {{ . | parseUsername }} + {{ $.Email | parseUsername . }} diff --git a/src/templates/autodiscover.xml b/src/templates/autodiscover.xml index 8669b2b..acbf376 100644 --- a/src/templates/autodiscover.xml +++ b/src/templates/autodiscover.xml @@ -11,7 +11,7 @@ {{ .Server }} {{ .Port }} {{ .UsernameIsFQDN | onoff }} - {{ . | parseUsername }} + {{ $.Email | parseUsername . }} {{ .SPA | onoff }} {{ if eq .SocketType "SSL" }}on{{ else }}off{{ end }} {{ not .NoAuthRequired | onoff }} @@ -25,7 +25,7 @@ {{ .Server }} {{ .Port }} {{ .UsernameIsFQDN | onoff }} - {{ . | parseUsername }} + {{ $.Email | parseUsername . }} {{ .SPA | onoff }} {{ .SocketType }} {{ not .NoAuthRequired | onoff }} diff --git a/src/web/handler/handler.go b/src/web/handler/handler.go index f9ceb1f..0ff1839 100644 --- a/src/web/handler/handler.go +++ b/src/web/handler/handler.go @@ -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" diff --git a/src/web/responses/responses.go b/src/web/responses/responses.go index be3aa3e..a6eafc9 100644 --- a/src/web/responses/responses.go +++ b/src/web/responses/responses.go @@ -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) : // // @@ -46,27 +51,64 @@ func MsAutoDiscoverXML() string { // 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) + + // 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\>`) + 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" - } -}