forked from S1mpleTheBest/hilink
-
Notifications
You must be signed in to change notification settings - Fork 0
/
util.go
255 lines (210 loc) · 5.96 KB
/
util.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
package hilink
import (
"bytes"
"errors"
"fmt"
"io"
"github.com/clbanning/mxj"
)
var (
// ErrBadStatusCode is the bad status code error.
ErrBadStatusCode = errors.New("bad status code")
// ErrInvalidResponse is the invalid response error.
ErrInvalidResponse = errors.New("invalid response")
// ErrInvalidError is the invalid error error.
ErrInvalidError = errors.New("invalid error")
// ErrInvalidValue is the invalid value error.
ErrInvalidValue = errors.New("invalid value")
// ErrInvalidXML is the invalid xml error.
ErrInvalidXML = errors.New("invalid xml")
// ErrMissingRootElement is the missing root element error.
ErrMissingRootElement = errors.New("missing root element")
// ErrMessageTooLong is the message too long error.
ErrMessageTooLong = errors.New("message too long")
)
// SmsBoxType represents the different inbox types available on a hilink device.
type SmsBoxType uint
// SmsBoxType values.
const (
SmsBoxTypeInbox SmsBoxType = iota + 1
SmsBoxTypeOutbox
SmsBoxTypeDraft
)
// PinType are the PIN types for a PIN command.
type PinType int
// PinType values.
const (
PinTypeEnter PinType = iota
PinTypeActivate
PinTypeDeactivate
PinTypeChange
PinTypeEnterPuk
)
// UssdState represents the different USSD states.
type UssdState int
// UssdState values.
const (
UssdStateNone UssdState = iota
UssdStateActive
UssdStateWaiting
)
// XMLData is a map of XML data to encode/decode.
type XMLData mxj.Map
// xmlPairs combines xml name/value pairs as a properly formatted XML buffer.
func xmlPairs(indent string, vals ...string) []byte {
// make sure we have pairs
if len(vals)%2 != 0 {
panic(fmt.Errorf("xmlPairs can only accept pairs of strings, length: %d", len(vals)))
}
var buf bytes.Buffer
// loop over pairs
for i := 0; i < len(vals); i += 2 {
buf.WriteString(fmt.Sprintf("%s<%s>%s</%s>\n", indent, vals[i], vals[i+1], vals[i]))
}
return buf.Bytes()
}
// xmlPairsString builds a string of XML pairs.
func xmlPairsString(indent string, vals ...string) string {
return string(xmlPairs(indent, vals...))
}
// XMLMapString builds a string of XML string map.
func XMLMapString(indent string, vals map[string]string) string {
pairs := make([]string, 0)
for k, v := range vals {
pairs = append(pairs, k)
pairs = append(pairs, v)
}
return string(xmlPairs(indent, pairs...))
}
// xmlNvp (ie, name value pair) builds a <Name>name</Name><Value>value</Value> XML pair.
func xmlNvp(name, value string) string {
return xmlPairsString("", "Name", name, "Value", value)
}
// SimpleRequestXML creates an XML string from value pairs.
//
// Unfortunately the XML parser (or whatever underyling code) included with the
// WebUI on Hilink devices expects parameters in a specific order. This makes
// packages like mxj or other map based solutions not feasible for use, as Go
// has random key ordering for maps.
//
// On another note, XML sucks.
func SimpleRequestXML(vals ...string) []byte {
var buf bytes.Buffer
// write header
buf.WriteString(`<?xml version="1.0" encoding="UTF-8"?>`)
buf.WriteString("\n<request>\n")
// add pairs
buf.Write(xmlPairs(" ", vals...))
// end string
buf.WriteString("</request>\n")
return buf.Bytes()
}
// boolToString converts a bool to a "0" or "1".
func boolToString(b bool) string {
if b {
return "1"
}
return "0"
}
// ErrorCodeMessageMap contains the known message strings for Hilink devices.
//
// see: http://www.bez-kabli.pl/viewtopic.php?t=42168
var ErrorCodeMessageMap = map[string]string{
"-1": "system not available",
"100002": "not supported by firmware or incorrect API path",
"100003": "unauthorized",
"100004": "system busy",
"100005": "unknown error",
"100006": "invalid parameter",
"100009": "write error",
"103002": "unknown error",
"103015": "unknown error",
"108001": "invalid username",
"108002": "invalid password",
"108003": "user already logged in",
"108006": "invalid username or password",
"108007": "invalid username, password, or session timeout",
"110024": "battery charge less than 50%",
"111019": "no network response",
"111020": "network timeout",
"111022": "network not supported",
"113018": "system busy",
"114001": "file already exists",
"114002": "file already exists",
"114003": "SD card currently in use",
"114004": "path does not exist",
"114005": "path too long",
"114006": "no permission for specified file or directory",
"115001": "unknown error",
"117001": "incorrect WiFi password",
"117004": "incorrect WISPr password",
"120001": "voice busy",
"125001": "invalid token",
}
// encodeXML encodes a map to standard XML values.
func encodeXML(v interface{}) (io.Reader, error) {
var err error
var buf []byte
switch x := v.(type) {
case []byte:
buf = x
case XMLData:
// wrap in request element
m := mxj.Map(map[string]interface{}{
"request": map[string]interface{}(x),
})
// encode xml
buf, err = m.XmlIndent("", " ")
if err != nil {
return nil, err
}
default:
return nil, errors.New("unsupported type in encodeXML")
}
return bytes.NewReader(buf), nil
}
// decodeXML decodes buf into its simple xml values.
func decodeXML(buf []byte, takeFirstEl bool) (interface{}, error) {
// decode xml
m, err := mxj.NewMapXml(buf)
if err != nil {
return nil, err
}
// check if error was returned
if e, ok := m["error"]; ok {
z, ok := e.(map[string]interface{})
if !ok {
return nil, ErrInvalidError
}
// grab message if not passed by the api
msg, _ := z["message"].(string)
if msg == "" {
c, _ := z["code"].(string)
msg = ErrorCodeMessageMap[c]
}
return nil, fmt.Errorf("hilink error %v: %s", z["code"], msg)
}
// check there is only one element
if len(m) != 1 {
return nil, ErrMissingRootElement
}
// bail if not grabbing the first XML element
if !takeFirstEl {
return m, nil
}
// grab root element
rootEl := ""
for k := range m {
rootEl = k
}
r, ok := m[rootEl]
if !ok {
return nil, ErrInvalidResponse
}
// convert
t, ok := r.(map[string]interface{})
if !ok {
return nil, ErrInvalidXML
}
return t, nil
}