goagi.parseEndpos   A
last analyzed

Complexity

Conditions 5

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 8
nop 1
dl 0
loc 11
rs 9.3333
c 0
b 0
f 0
1
package goagi
2
3
import (
4
	"strconv"
5
	"strings"
6
)
7
8
// Response interface that all commands return.
9
// Helps to access different parts of AGI response
10
type Response interface {
11
	// Code of response: 200, 510 etc
12
	Code() int
13
	// RawResponse return full text of AGI response
14
	RawResponse() string
15
	// Result returns value of result= field
16
	Result() int
17
	// Value returns value field: (timeout)
18
	Value() string
19
	// Data returns text for error responses and dtmf values for command like GetData
20
	Data() string
21
	// EndPos returns value for endpos= field
22
	EndPos() int64
23
	// Digit return digit from digit= field
24
	Digit() string
25
	// SResults return value for results= field
26
	SResults() int
27
}
28
29
type response struct {
30
	code   int
31
	result int
32
	raw    string
33
	data   string
34
}
35
36
func (r *response) Code() int           { return r.code }
37
func (r *response) RawResponse() string { return r.raw }
38
func (r *response) Result() int         { return r.result }
39
func (r *response) Value() string       { return r.data }
40
func (r *response) Data() string        { return r.data }
41
func (r *response) EndPos() int64       { return 0 }
42
func (r *response) Digit() string       { return "" }
43
func (r *response) SResults() int       { return 0 }
44
45
type responseSuccess struct {
46
	response
47
	value    string
48
	endpos   int64
49
	digit    string
50
	sresults int
51
}
52
53
func (r *responseSuccess) Value() string { return r.value }
54
func (r *responseSuccess) EndPos() int64 { return r.endpos }
55
func (r *responseSuccess) Digit() string { return r.digit }
56
func (r *responseSuccess) SResults() int { return r.sresults }
57
58
func (agi *AGI) sessionSetup(data []string) {
59
	agi.dbg("[>] sessionSetup")
60
	agi.env = make(map[string]string)
61
	agi.arg = make([]string, 0)
62
63
	for _, line := range data {
64
		idx := strings.Index(line, ": ")
65
		if idx == -1 || line[:4] != "agi_" {
66
			agi.dbg(" [!] ignore invalid line: %q", line)
67
			continue
68
		}
69
		if line[:8] == "agi_arg_" {
70
			arg := line[idx+2:]
71
			agi.arg = append(agi.arg, arg)
72
			agi.dbg(" [v] add arg: %q", arg)
73
			continue
74
		}
75
		key, val := line[4:idx], line[idx+2:]
76
		agi.dbg(" [v] add env: %s => %s", key, val)
77
		agi.env[key] = val
78
	}
79
}
80
81
// read and parse response
82
func (agi *AGI) parseResponse(data string, code int) (Response, error) {
83
	agi.dbg("[>] parseResponse")
84
85
	if len(data) < 4 {
86
		agi.dbg(" [!] response is invalid: %q", data)
87
		return nil, ErrAGI.Msg("Received response is invalid: %q", data)
88
	}
89
90
	if code == codeEarly {
91
		return agi.parseEarlyResponse(data, code)
92
	}
93
94
	if code == codeSucc {
95
		return agi.parseSuccessResponse(data)
96
	}
97
98
	if code > 500 && code < 600 {
99
		return agi.parseErrorResponse(data, code)
100
	}
101
	return nil, ErrAGI.Msg("Can not recognize the response: %q", data)
102
}
103
104
func (agi *AGI) parseSuccessResponse(data string) (Response, error) {
105
	agi.dbg("[>] parseSuccessResponse: %q", data)
106
	resp := &responseSuccess{}
107
	resp.code = codeSucc
108
	resp.raw = data
109
110
	data = data[4:]
111
	data = trimLastNL(data)
112
	data, result := scanResult(data)
113
	resp.result = result
114
115
	if len(data) == 0 {
116
		return resp, nil
117
	}
118
119
	// parse value
120
	data, value := parseValue(data)
121
	resp.value = value
122
	tokens := strings.Fields(data)
123
	resp.endpos = parseEndpos(tokens)
124
	resp.digit = parseDigit(tokens)
125
	resp.sresults = parseSResults(tokens)
126
	return resp, nil
127
}
128
129
func parseSResults(tokens []string) int {
130
	for _, tok := range tokens {
131
		if len(tok) < 9 || tok[:8] != "results=" {
132
			continue
133
		}
134
		if num, err := strconv.Atoi(tok[8:]); err == nil {
135
			return num
136
		}
137
		return 0
138
	}
139
	return 0
140
}
141
142
func parseDigit(tokens []string) string {
143
	for _, tok := range tokens {
144
		if len(tok) < 7 || tok[:6] != "digit=" {
145
			continue
146
		}
147
		return tok[6:]
148
	}
149
	return ""
150
}
151
152
func parseEndpos(tokens []string) int64 {
153
	for _, tok := range tokens {
154
		if len(tok) < 8 || tok[:7] != "endpos=" {
155
			continue
156
		}
157
		if pos, err := strconv.ParseInt(tok[7:], 10, 64); err == nil {
158
			return pos
159
		}
160
		return 0
161
	}
162
	return 0
163
}
164
165
func parseValue(data string) (string, string) {
166
	if data[0] != '(' {
167
		return data, ""
168
	}
169
	if idx := strings.IndexByte(data, ')'); idx > 0 {
170
		return data[idx+1:], data[1:idx]
171
	}
172
	return data, ""
173
}
174
175
func (agi *AGI) parseEarlyResponse(data string, code int) (Response, error) {
176
	agi.dbg("[>] parseEarlyResponse %d: %q", code, data)
177
	resp := &response{}
178
	resp.code = code
179
	resp.raw = data
180
181
	data = data[4:]
182
	data = trimLastNL(data)
183
184
	data, result := scanResult(data)
185
	resp.result = result
186
	resp.data = data
187
188
	return resp, nil
189
}
190
191
func (agi *AGI) parseErrorResponse(data string, code int) (Response, error) {
192
	agi.dbg("[>] parseErrorResponse %d: %q", code, data)
193
	resp := &response{}
194
	resp.code = code
195
	resp.raw = data
196
197
	if code == codeE520 && data[:4] == "520-" {
198
		resp.data = scanE520Usage(data)
199
		return resp, nil
200
	}
201
202
	data = data[4:]
203
	data = trimLastNL(data)
204
205
	data, result := scanResult(data)
206
	resp.result = result
207
	resp.data = data
208
209
	return resp, nil
210
}
211
212
func trimLastNL(data string) string {
213
	l := len(data)
214
	if l > 0 && data[l-1:] == "\n" {
215
		return data[:l-1]
216
	}
217
	return data
218
}
219
220
func scanResult(data string) (string, int) {
221
	if len(data) < 7 || data[:7] != "result=" {
222
		return data, 0
223
	}
224
	token := strings.SplitN(data, " ", 2)
225
	resultTok := strings.SplitN(token[0], "=", 2)
226
227
	if len(resultTok) > 1 {
228
		if num, err := strconv.Atoi(resultTok[1]); err == nil {
229
			return strings.Join(token[1:], " "), num
230
		}
231
	}
232
	return strings.Join(token[1:], " "), 0
233
}
234
235
func scanResultStrFromRaw(data string) string {
236
	raw := data[4:]
237
	raw = trimLastNL(raw)
238
	tokens := strings.Fields(raw)
239
	if strings.Compare(tokens[0], "result=") <= 0 {
240
		return ""
241
	}
242
	return tokens[0][7:]
243
}
244
245
func scanE520Usage(data string) string {
246
	token := strings.Split(data, "\n")
247
248
	return strings.Join(token[1:len(token)-2], "\n")
249
}
250