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