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
|
package adc
// Works for both command_type+command_name and encoded_cid.
// For command names without type, the first byte of the FourCC = '*'.
type FourCC int32
type Arg string
// Protocol states
type State int
// TODO: feature broadcasts
type Message struct {
Header FourCC
Src FourCC // 0 if not present
Dest FourCC // 0 if not present
CID string // if Header.Type() == 'U', unmodified base32 string otherwise empty
PosArgs []Arg
NamedArgs map[[2]byte][]Arg // may be nil
}
const (
CTM FourCC = 0x2a43544d
GET FourCC = 0x2a474554
GFI FourCC = 0x2a474749
GPA FourCC = 0x2a475041
INF FourCC = 0x2a494e46
MSG FourCC = 0x2a4d5347
PAS FourCC = 0x2a504153
QUI FourCC = 0x2a515549
RCM FourCC = 0x2a52434d
RES FourCC = 0x2a524553
SCH FourCC = 0x2a534348
SID FourCC = 0x2a534944
SND FourCC = 0x2a534e44
STA FourCC = 0x2a535441
SUP FourCC = 0x2A535550
)
const (
InvalidState State = (1 << iota)
PROTOCOL
IDENTIFY
VERIFY
NORMAL
DATA
)
type cmdInfo struct {
args int // Number of fixed positional arguments
states State // OR'ed states in which this command is allowed
htypes string // allowed message types when received from a hub
}
// List of all known commands
var cmdList = map[FourCC]cmdInfo{
CTM: {3, NORMAL, "DEF"},
GET: {4, NORMAL, ""}, // htype = "I" for the BLOM extension
GFI: {2, NORMAL, ""},
GPA: {1, VERIFY, "I"},
INF: {0, IDENTIFY | NORMAL, "IBDEF"}, // (doesn't make much sense with htype=D/E/F, but it's allowed)
MSG: {1, NORMAL, "IBDEF"},
PAS: {1, VERIFY, ""},
QUI: {1, IDENTIFY | VERIFY | NORMAL, "I"},
RCM: {2, NORMAL, "DEF"},
RES: {0, NORMAL, "D"},
SCH: {0, NORMAL, "BDEF"},
SID: {1, PROTOCOL, "I"},
SND: {4, NORMAL, ""}, // htype = "I" for the BLOM extension
STA: {2, PROTOCOL | IDENTIFY | VERIFY | NORMAL | DATA, "IBDEF"},
SUP: {0, NORMAL | PROTOCOL, "I"},
}
// len(s) must be >= 4
func NewFourCC(s []byte) FourCC {
return FourCC(int32(s[0])<<24 + int32(s[1])<<16 + int32(s[2])<<8 + int32(s[3]))
}
func (f FourCC) Type() byte {
return byte(f >> 24)
}
func (f FourCC) SetType(t byte) FourCC {
return (f & 0xFFFFFF) | (FourCC(t) << 24)
}
func (f FourCC) Command() FourCC {
return f.SetType('*')
}
func (f FourCC) Format(b []byte) []byte {
return append(b, []byte{byte(f >> 24), byte(f >> 16), byte(f >> 8), byte(f)}...)
}
func (f FourCC) String() string {
return string(f.Format(nil))
}
func NewMessage(t byte, m FourCC) *Message {
return &Message{Header: m.SetType(t)}
}
func (m *Message) AddPos(s string) *Message {
m.PosArgs = append(m.PosArgs, Arg(s))
return m
}
func (m *Message) AddNamed(n, s string) *Message {
if m.NamedArgs == nil {
m.NamedArgs = make(map[[2]byte][]Arg)
}
b := [2]byte{n[0], n[1]}
m.NamedArgs[b] = append(m.NamedArgs[b], Arg(s))
return m
}
// Returns the first found value of the named argument, or an empty string if not found.
func (m *Message) Arg(a [2]byte) Arg {
if l := m.NamedArgs[a]; l != nil && len(l) > 0 {
return l[0]
}
return ""
}
// Some handy validation functions. Used by parse.go, but some would also be
// used in applications to perform further validation on incoming messages.
func isAlpha(b byte) bool {
return b >= 'A' && b <= 'Z'
}
func isAlphaNum(b byte) bool {
return isAlpha(b) || (b >= '0' && b <= '9')
}
func isBase32(b byte) bool {
return isAlpha(b) || (b >= '2' && b <= '7')
}
func isSID(b []byte) bool {
return len(b) >= 4 && isBase32(b[0]) && isBase32(b[1]) && isBase32(b[2]) && isBase32(b[3])
}
// Returns 0 if arg is not a valid FourCC
func (a Arg) FourCC() FourCC {
if len(a) == 4 && isAlphaNum(a[0]) && isAlphaNum(a[1]) && isAlphaNum(a[2]) && isAlphaNum(a[3]) {
return NewFourCC([]byte(a))
}
return FourCC(0)
}
func (a Arg) IsSID() bool {
return len(a) == 4 && isSID([]byte(a))
}
func (a Arg) IsBase32() bool {
for i := 0; i < len(a); i++ {
if !isBase32(a[i]) {
return false
}
}
return len(a) > 0
}
|