1package xmlrpc
2
3import (
4	"bytes"
5	"encoding/xml"
6	"fmt"
7	"reflect"
8	"strconv"
9	"strings"
10	"time"
11)
12
13type encodeFunc func(reflect.Value) ([]byte, error)
14
15func marshal(v interface{}) ([]byte, error) {
16	if v == nil {
17		return []byte{}, nil
18	}
19
20	val := reflect.ValueOf(v)
21	return encodeValue(val)
22}
23
24func encodeValue(val reflect.Value) ([]byte, error) {
25	var b []byte
26	var err error
27
28	if val.Kind() == reflect.Ptr || val.Kind() == reflect.Interface {
29		if val.IsNil() {
30			return []byte("<value/>"), nil
31		}
32
33		val = val.Elem()
34	}
35
36	switch val.Kind() {
37	case reflect.Struct:
38		switch val.Interface().(type) {
39		case time.Time:
40			t := val.Interface().(time.Time)
41			b = []byte(fmt.Sprintf("<dateTime.iso8601>%s</dateTime.iso8601>", t.Format(iso8601)))
42		default:
43			b, err = encodeStruct(val)
44		}
45	case reflect.Map:
46		b, err = encodeMap(val)
47	case reflect.Slice:
48		b, err = encodeSlice(val)
49	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
50		b = []byte(fmt.Sprintf("<int>%s</int>", strconv.FormatInt(val.Int(), 10)))
51	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
52		b = []byte(fmt.Sprintf("<i4>%s</i4>", strconv.FormatUint(val.Uint(), 10)))
53	case reflect.Float32, reflect.Float64:
54		b = []byte(fmt.Sprintf("<double>%s</double>",
55			strconv.FormatFloat(val.Float(), 'g', -1, val.Type().Bits())))
56	case reflect.Bool:
57		if val.Bool() {
58			b = []byte("<boolean>1</boolean>")
59		} else {
60			b = []byte("<boolean>0</boolean>")
61		}
62	case reflect.String:
63		var buf bytes.Buffer
64
65		xml.Escape(&buf, []byte(val.String()))
66
67		if _, ok := val.Interface().(Base64); ok {
68			b = []byte(fmt.Sprintf("<base64>%s</base64>", buf.String()))
69		} else {
70			b = []byte(fmt.Sprintf("<string>%s</string>", buf.String()))
71		}
72	default:
73		return nil, fmt.Errorf("xmlrpc encode error: unsupported type")
74	}
75
76	if err != nil {
77		return nil, err
78	}
79
80	return []byte(fmt.Sprintf("<value>%s</value>", string(b))), nil
81}
82
83func encodeStruct(value reflect.Value) ([]byte, error) {
84	var b bytes.Buffer
85
86	b.WriteString("<struct>")
87
88	vals := []reflect.Value{value}
89	for j := 0; j < len(vals); j++ {
90		val := vals[j]
91		t := val.Type()
92		for i := 0; i < t.NumField(); i++ {
93			f := t.Field(i)
94			tag := f.Tag.Get("xmlrpc")
95			name := f.Name
96			fieldVal := val.FieldByName(f.Name)
97			fieldValKind := fieldVal.Kind()
98
99			// Omit unexported fields
100			if !fieldVal.CanInterface() {
101				continue
102			}
103
104			// Omit fields who are structs that contain no fields themselves
105			if fieldValKind == reflect.Struct && fieldVal.NumField() == 0 {
106				continue
107			}
108
109			// Omit empty slices
110			if fieldValKind == reflect.Slice && fieldVal.Len() == 0 {
111				continue
112			}
113
114			// Omit empty fields (defined as nil pointers)
115			if tag != "" {
116				parts := strings.Split(tag, ",")
117				name = parts[0]
118				if len(parts) > 1 && parts[1] == "omitempty" {
119					if fieldValKind == reflect.Ptr && fieldVal.IsNil() {
120						continue
121					}
122				}
123			}
124
125			// Drill down into anonymous/embedded structs and do not expose the
126			// containing embedded struct in request.
127			// This will effectively pull up fields in embedded structs to look
128			// as part of the original struct in the request.
129			if f.Anonymous {
130				vals = append(vals, fieldVal)
131				continue
132			}
133
134			b.WriteString("<member>")
135			b.WriteString(fmt.Sprintf("<name>%s</name>", name))
136
137			p, err := encodeValue(fieldVal)
138			if err != nil {
139				return nil, err
140			}
141			b.Write(p)
142
143			b.WriteString("</member>")
144		}
145	}
146
147	b.WriteString("</struct>")
148
149	return b.Bytes(), nil
150}
151
152func encodeMap(val reflect.Value) ([]byte, error) {
153	var t = val.Type()
154
155	if t.Key().Kind() != reflect.String {
156		return nil, fmt.Errorf("xmlrpc encode error: only maps with string keys are supported")
157	}
158
159	var b bytes.Buffer
160
161	b.WriteString("<struct>")
162
163	keys := val.MapKeys()
164
165	for i := 0; i < val.Len(); i++ {
166		key := keys[i]
167		kval := val.MapIndex(key)
168
169		b.WriteString("<member>")
170		b.WriteString(fmt.Sprintf("<name>%s</name>", key.String()))
171
172		p, err := encodeValue(kval)
173
174		if err != nil {
175			return nil, err
176		}
177
178		b.Write(p)
179		b.WriteString("</member>")
180	}
181
182	b.WriteString("</struct>")
183
184	return b.Bytes(), nil
185}
186
187func encodeSlice(val reflect.Value) ([]byte, error) {
188	var b bytes.Buffer
189
190	b.WriteString("<array><data>")
191
192	for i := 0; i < val.Len(); i++ {
193		p, err := encodeValue(val.Index(i))
194		if err != nil {
195			return nil, err
196		}
197
198		b.Write(p)
199	}
200
201	b.WriteString("</data></array>")
202
203	return b.Bytes(), nil
204}
205