Ocean/ICCC/Message2Data.go

179 lines
5.0 KiB
Go
Raw Permalink Normal View History

package ICCC
import (
"bytes"
"encoding/base64"
"encoding/binary"
"fmt"
"github.com/SommerEngineering/Ocean/Log"
LM "github.com/SommerEngineering/Ocean/Log/Meta"
"io"
"net/url"
"reflect"
"strings"
)
2015-06-17 15:44:52 +00:00
// Function to convert an ICCC message to HTTP data.
func Message2Data(channel, command string, message interface{}) (data map[string][]string) {
defer func() {
if err := recover(); err != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNamePARSE, fmt.Sprintf("Was not able to convert the message to HTTP values. %s", err))
data = make(map[string][]string, 0)
return
}
}()
2015-06-17 15:44:52 +00:00
// Create the map:
data = make(map[string][]string)
2015-06-17 15:44:52 +00:00
// Add the meta information:
data[`command`] = []string{command}
data[`channel`] = []string{channel}
if message == nil {
return
}
2015-06-17 15:44:52 +00:00
// Use reflection to determine the types:
element := reflect.ValueOf(message)
elementType := element.Type()
2015-06-17 15:44:52 +00:00
// Iterate over all fields of the data type.
// Transform the data regarding the type.
for i := 0; i < element.NumField(); i++ {
field := element.Field(i)
keyName := elementType.Field(i).Name
// A buffer for the binary representation:
buffer := new(bytes.Buffer)
// For possible errors:
var errConverter error = nil
// The key for this element:
key := ``
// Look for the right data type:
switch field.Kind().String() {
case `int64`:
key = fmt.Sprintf(`int:%s`, keyName)
// Converts the value in a byte array:
errConverter = binary.Write(buffer, binary.LittleEndian, field.Int())
case `string`:
key = fmt.Sprintf(`str:%s`, keyName)
// URL encode the string and copy its bytes to the buffer:
io.Copy(buffer, strings.NewReader(url.QueryEscape(field.String())))
case `float64`:
key = fmt.Sprintf(`f64:%s`, keyName)
// Converts the value in a byte array:
errConverter = binary.Write(buffer, binary.LittleEndian, field.Float())
case `bool`:
key = fmt.Sprintf(`bool:%s`, keyName)
// Directly convert the bool in a byte:
if field.Bool() {
// Case: True
buffer.WriteByte(0x1) // Write 1
} else {
// Case: False
buffer.WriteByte(0x0) // Write 0
}
case `uint8`: // a byte
key = fmt.Sprintf(`ui8:%s`, keyName)
// uint8 is a byte, thus, write it directly in the buffer:
buffer.WriteByte(byte(field.Uint()))
2015-06-17 15:44:52 +00:00
// Case: Arrays...
case `slice`:
sliceLen := field.Len()
if sliceLen > 0 {
// Which kind of data is this?
sliceKind := field.Index(0).Kind()
// Select the right data type:
switch sliceKind.String() {
case `uint8`: // a byte array
key = fmt.Sprintf(`ui8[]:%s`, keyName)
values := field.Interface().([]uint8)
// Directly write the bytes in the buffer:
for _, val := range values {
buffer.WriteByte(byte(val))
}
case `int64`:
key = fmt.Sprintf(`int[]:%s`, keyName)
values := field.Interface().([]int64)
// Converts the array in a byte array:
errConverter = binary.Write(buffer, binary.LittleEndian, values)
case `bool`:
key = fmt.Sprintf(`bool[]:%s`, keyName)
values := field.Interface().([]bool)
// Cannot convert bool to bytes by using binary.Write(). Thus,
// convert it by ower own:
// Loop over all values:
for _, val := range values {
if val {
// If the value is true:
buffer.WriteByte(0x1) // Write 1
} else {
// If the value is false:
buffer.WriteByte(0x0) // Write 0
}
}
case `string`:
key = fmt.Sprintf(`str[]:%s`, keyName)
values := field.Interface().([]string)
// Mask every string by using a URL encoding.
// This masks e.g. every new-line i.e \n, etc.
// This allows us to combine later the strings by
// using \n:
masked := make([]string, len(values))
// Loop over each string and convert it:
for n, val := range values {
masked[n] = url.QueryEscape(val)
}
// Join all masked strings by using \n and copy the byte array
// representation in the buffer:
io.Copy(buffer, strings.NewReader(strings.Join(masked, "\n")))
case `float64`:
key = fmt.Sprintf(`f64[]:%s`, keyName)
values := field.Interface().([]float64)
// Converts the array in a byte array:
errConverter = binary.Write(buffer, binary.LittleEndian, values)
}
}
}
if errConverter != nil {
// An error occurs:
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNamePARSE, `It was not possible to convert an array for an ICCC message.`, fmt.Sprintf("channel='%s'", channel), fmt.Sprintf("command='%s'", command), errConverter.Error())
} else {
// Convert the byte array to a base64 string for the transportation on wire:
data[key] = []string{base64.StdEncoding.EncodeToString(buffer.Bytes())}
}
}
return
}