summaryrefslogtreecommitdiff
path: root/src/globster/adc/types.go
blob: 83f94915c4a8bf8949e353472c4aa1e00c09b52c (plain)
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
}