-
Notifications
You must be signed in to change notification settings - Fork 3
/
response.go
222 lines (190 loc) · 4.76 KB
/
response.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
// This file is part of https://github.com/racingmars/go3270/
// Copyright 2020 by Matthew R. Wilson, licensed under the MIT license. See
// LICENSE in the project root for license information.
package go3270
import (
"bytes"
"fmt"
"net"
"os"
)
// Response encapsulates data received from a 3270 client in response to the
// previously sent screen.
type Response struct {
// Which Action ID key did the user press?
AID AID
// Row the cursor was on (0-based).
Row int
// Column the cursor was on (0-based).
Col int
// Field values.
Values map[string]string
}
// AID is an Action ID character.
type AID byte
const (
AIDNone AID = 0x60
AIDEnter AID = 0x7D
AIDPF1 AID = 0xF1
AIDPF2 AID = 0xF2
AIDPF3 AID = 0xF3
AIDPF4 AID = 0xF4
AIDPF5 AID = 0xF5
AIDPF6 AID = 0xF6
AIDPF7 AID = 0xF7
AIDPF8 AID = 0xF8
AIDPF9 AID = 0xF9
AIDPF10 AID = 0x7A
AIDPF11 AID = 0x7B
AIDPF12 AID = 0x7C
AIDPF13 AID = 0xC1
AIDPF14 AID = 0xC2
AIDPF15 AID = 0xC3
AIDPF16 AID = 0xC4
AIDPF17 AID = 0xC5
AIDPF18 AID = 0xC6
AIDPF19 AID = 0xC7
AIDPF20 AID = 0xC8
AIDPF21 AID = 0xC9
AIDPF22 AID = 0x4A
AIDPF23 AID = 0x4B
AIDPF24 AID = 0x4C
AIDPA1 AID = 0x6C
AIDPA2 AID = 0x6E
AIDPA3 AID = 0x6B
AIDClear AID = 0x6D
)
func readResponse(c net.Conn, fm fieldmap) (Response, error) {
var r Response
aid, err := readAID(c)
if err != nil {
return r, err
}
r.AID = aid
// If the use pressed clear, or a PA key we should return now
// TODO: actually, we should consume the 0xffef, but that will
// currently get taken care of in our next AID search.
if r.AID == AIDClear || r.AID == AIDPA1 || r.AID == AIDPA2 ||
r.AID == AIDPA3 {
return r, nil
}
row, col, _, err := readPosition(c)
if err != nil {
return r, err
}
r.Col = col
r.Row = row
var fieldValues map[string]string
if fieldValues, err = readFields(c, fm); err != nil {
return r, err
}
r.Values = fieldValues
return r, nil
}
func readAID(c net.Conn) (AID, error) {
for {
b, valid, _, err := telnetRead(c, false)
if !valid && err != nil {
return AIDNone, err
}
if (b == 0x60) || (b >= 0x6b && b <= 0x6e) ||
(b >= 0x7a && b <= 0x7d) || (b >= 0x4a && b <= 0x4c) ||
(b >= 0xf1 && b <= 0xf9) || (b >= 0xc1 && b <= 0xc9) {
// We found a valid AID
debugf("Got AID byte: %x\n", b)
return AID(b), nil
}
// Consume non-AID bytes continuing loop
debugf("Got non-AID byte: %x\n", b)
}
}
func readPosition(c net.Conn) (row, col, addr int, err error) {
raw := make([]byte, 2)
// Read two bytes
for i := 0; i < 2; i++ {
b, _, _, err := telnetRead(c, false)
if err != nil {
return 0, 0, 0, err
}
raw[i] = b
}
// Decode the raw position
addr = decodeBufAddr([2]byte{raw[0], raw[1]})
row = addr % 80
col = (addr - row) / 80
debugf("Got position bytes %02x %02x, decoded to %d\n", raw[0], raw[1],
addr)
return row, col, addr, nil
}
func readFields(c net.Conn, fm fieldmap) (map[string]string, error) {
var infield bool
var fieldpos int
var fieldval bytes.Buffer
var values = make(map[string]string)
// consume bytes until we get 0xffef
for {
// Read a byte
b, _, eor, err := telnetRead(c, true)
if err != nil {
return nil, err
}
// Check for end of data stream (0xffef)
if eor {
// Finish the current field
if infield {
debugf("Field %d: %s\n", fieldpos, e2a(fieldval.Bytes()))
handleField(fieldpos, fieldval.Bytes(), fm, values)
}
return values, nil
}
// No? Check for start-of-field
if b == 0x11 {
// Finish the previous field, if necessary
if infield {
debugf("Field %d: %s\n", fieldpos, e2a(fieldval.Bytes()))
handleField(fieldpos, fieldval.Bytes(), fm, values)
}
// Start a new field
infield = true
fieldval = bytes.Buffer{}
fieldpos = 0
if _, _, fieldpos, err = readPosition(c); err != nil {
return nil, err
}
continue
}
// Consume all other bytes as field contents if we're in a field
if !infield {
debugf("Got unexpected byte while processing fields: %02x\n", b)
continue
}
fieldval.WriteByte(b)
}
}
func handleField(addr int, value []byte, fm fieldmap, values map[string]string) bool {
name, ok := fm[addr]
// Field is not present in the fieldmap
if !ok {
return false
}
// Otherwise, populate the value
values[name] = string(e2a(value))
return true
}
// decodeBufAddr decodes a raw 2-byte encoded buffer address and returns the
// integer value of the address (i.e. 0-1919)
func decodeBufAddr(raw [2]byte) int {
if decodes[raw[0]] > 254 {
fmt.Fprintf(os.Stderr,
"UNEXPECTED VALUE: decodeBufAddr got raw value of %02x %02x\n",
raw[0], raw[1])
}
if decodes[raw[1]] > 254 {
fmt.Fprintf(os.Stderr,
"UNEXPECTED VALUE: decodeBufAddr got raw value of %02x %02x\n",
raw[0], raw[1])
}
hi := decodes[raw[0]] << 6
lo := decodes[raw[1]]
return hi | lo
}