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