Skip to content

Commit

Permalink
Refactoring json interface
Browse files Browse the repository at this point in the history
  • Loading branch information
Your Name committed Feb 19, 2021
1 parent d5c4f08 commit 1556510
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 132 deletions.
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

FROM golang AS restgomailbuildstage
RUN mkdir /restgomail
COPY restgomail.go jsondive.go /restgomail/
RUN cd /restgomail && CGO_ENABLED=0 GOOS=linux go build -a -o restgomail restgomail.go jsondive.go
COPY restgomail.go smartjson.go /restgomail/
RUN cd /restgomail && CGO_ENABLED=0 GOOS=linux go build -a -o restgomail restgomail.go smartjson.go

FROM debian AS restgomail
LABEL maintainer="hyper80@gmail.com" Description="RestGoMail - HTTPS REST-Go-MAIL (SMTP) gateway"
Expand Down
112 changes: 0 additions & 112 deletions jsondive.go

This file was deleted.

31 changes: 13 additions & 18 deletions restgomail.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"crypto/tls"
"crypto/x509"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
Expand Down Expand Up @@ -123,17 +122,15 @@ func readConfig(jsonfile string) bool {
return true
}

var confJSONParsed map[string]interface{}
confJSONError := json.Unmarshal(confJSONData, &confJSONParsed)
configJSON, confJSONError := parseSmartJSON(confJSONData)
if confJSONError != nil {
log.Printf("Error, configuration file has not valid JSON: %s\n", confJSONError.Error())
return true
}

for confName, configItemVal := range config {
switch configItemVal.vtype {
case "string":
sv, svt := getStringByPath(confJSONParsed, "restgomail/"+confName)
sv, svt := configJSON.getStringByPath("restgomail/" + confName)
if configItemVal.required && svt == "none" {
log.Printf("Error, incomplete config JSON, \"%s\" missing", confName)
return true
Expand All @@ -144,7 +141,7 @@ func readConfig(jsonfile string) bool {
config[confName].sval = sv
}
case "float":
fv, fvt := getFloat64ByPath(confJSONParsed, "restgomail/"+confName)
fv, fvt := configJSON.getFloat64ByPath("restgomail/" + confName)
if configItemVal.required && fvt == "none" {
log.Printf("Error, incomplete config JSON, \"%s\" missing", confName)
return true
Expand All @@ -155,7 +152,7 @@ func readConfig(jsonfile string) bool {
config[confName].fval = fv
}
case "bool":
bv, bvt := getBoolByPath(confJSONParsed, "restgomail/"+confName)
bv, bvt := configJSON.getBoolByPath("restgomail/" + confName)
if configItemVal.required && bvt == "none" {
log.Printf("Error, incomplete config JSON, \"%s\" missing", confName)
return true
Expand All @@ -173,7 +170,7 @@ func readConfig(jsonfile string) bool {
}

loadedCertsCounts := 0
cm, cmt := getMapByPath(confJSONParsed, "restgomail/knownCertificates")
cm, cmt := configJSON.getMapByPath("restgomail/knownCertificates")
if cmt == "map" {
for name, value := range cm {
if certstr, isStr := value.(string); isStr {
Expand Down Expand Up @@ -255,30 +252,28 @@ func main() {
}

func processRequest(req *[]byte, remote string) bool {
var parsed map[string]interface{}

if getConfigBool("debugMode") {
log.Println("******* BEGIN Request data ************")
log.Println(string(*req))
log.Println("******* END Request data **************")
}
error := json.Unmarshal(*req, &parsed)
if error != nil {
log.Printf("Error (%s) Not valid JSON: %s\n", remote, error.Error())
jsonmsg, jSerror := parseSmartJSON(*req)
if jSerror != nil {
log.Printf("Error (%s) Not valid JSON: %s\n", remote, jSerror.Error())
return true
}

if getConfigBool("debugMode") {
log.Println("JSON from client: ", remote)
log.Println("------- BEGIN Parsed JSON -------------")
log.Println(printParsedJSON(parsed))
log.Println(jsonmsg.toFormattedString())
log.Println("------- END Parsed JSON ---------------")
}

from, fromType := getStringByPath(parsed, "sendmail/from")
to, toType := getStringByPath(parsed, "sendmail/to")
subject, subjectType := getStringByPath(parsed, "sendmail/subject")
bodyhtml, bodyhtmlType := getStringByPath(parsed, "sendmail/bodyhtml")
from, fromType := jsonmsg.getStringByPath("sendmail/from")
to, toType := jsonmsg.getStringByPath("sendmail/to")
subject, subjectType := jsonmsg.getStringByPath("sendmail/subject")
bodyhtml, bodyhtmlType := jsonmsg.getStringByPath("sendmail/bodyhtml")

if fromType == "none" || toType == "none" ||
subjectType == "none" || bodyhtmlType == "none" {
Expand Down
147 changes: 147 additions & 0 deletions smartjson.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package main

/* Smart JSON functions - Helper functions to handle JSON
(C) 2021 Péter Deák (hyper80@gmail.com)
License: GPLv2
*/

import (
"encoding/json"
"fmt"
"strings"
)

type SmartJSON struct {
parsed map[string]interface{}
}

func parseSmartJSON(rawdata []byte) (SmartJSON, error) {
s := SmartJSON{}
s.parsed = make(map[string]interface{})
err := json.Unmarshal(rawdata, &s.parsed)
return s, err
}

func (sJSON SmartJSON) toFormattedString() (out string) {
return "JSON => {\n" + jsonNodeToString(sJSON.parsed, " ") + "}\n"
}

func jsonNodeToString(n map[string]interface{}, indent string) (out string) {
out = ""
for n, v := range n {
out += indent + n + " => "
if str, isStr := v.(string); isStr {
out += str + "\n"
continue
}
if flt, isFlt := v.(float64); isFlt {
out += fmt.Sprintf("%f\n", flt)
continue
}
if b, isB := v.(bool); isB {
if b {
out += "true\n"
} else {
out += "false\n"
}
continue
}

m, okm := v.(map[string]interface{})
if okm {
out += "{\n"
out += jsonNodeToString(m, indent+" ")
out += indent + "}\n"
continue
}
out += "?\n"
}
return
}

func (sJSON SmartJSON) getValueByPath(path string) (interface{}, string) {
parts := strings.Split(path, "/")
n := sJSON.parsed
for i := 0; i < len(parts); i++ {
v, ok := n[parts[i]]
if !ok {
return nil, "none"
}
if i == len(parts)-1 {
if s, isStr := v.(string); isStr {
return s, "string"
}
if f, isFlt := v.(float64); isFlt {
return f, "float64"
}
if b, isBool := v.(bool); isBool {
return b, "bool"
}
if m, isMap := v.(map[string]interface{}); isMap {
return m, "map"
}
}
if m, isMap := v.(map[string]interface{}); isMap {
n = m
continue
}
return nil, "none"
}
return nil, "none"
}

func (sJSON SmartJSON) getStringByPath(path string) (string, string) {
val, typ := sJSON.getValueByPath(path)
if str, isStr := val.(string); typ == "string" && isStr {
return str, typ
}
return "", "none"
}

func (sJSON SmartJSON) getFloat64ByPath(path string) (float64, string) {
val, typ := sJSON.getValueByPath(path)
if f, isFlt := val.(float64); typ == "float64" && isFlt {
return f, typ
}
return 0, "none"
}

func (sJSON SmartJSON) getBoolByPath(path string) (bool, string) {
val, typ := sJSON.getValueByPath(path)
if b, isBool := val.(bool); typ == "bool" && isBool {
return b, typ
}
return false, "none"
}

func (sJSON SmartJSON) getMapByPath(path string) (map[string]interface{}, string) {
val, typ := sJSON.getValueByPath(path)
if m, isMap := val.(map[string]interface{}); typ == "map" && isMap {
return m, typ
}
return nil, "none"
}

func (sJSON SmartJSON) getStringByPathWithDefault(path string, def string) string {
val, typ := sJSON.getValueByPath(path)
if str, isStr := val.(string); typ == "string" && isStr {
return str
}
return def
}

func (sJSON SmartJSON) getFloat64ByPathWithDefault(path string, def float64) float64 {
val, typ := sJSON.getValueByPath(path)
if f, isFlt := val.(float64); typ == "float64" && isFlt {
return f
}
return def
}

func (sJSON SmartJSON) getBoolByPathWithDefault(path string, def bool) bool {
val, typ := sJSON.getValueByPath(path)
if b, isBool := val.(bool); typ == "bool" && isBool {
return b
}
return def
}

0 comments on commit 1556510

Please sign in to comment.