diff --git a/AdcProtocol.go b/AdcProtocol.go index a99577e..5eb099a 100644 --- a/AdcProtocol.go +++ b/AdcProtocol.go @@ -1,12 +1,32 @@ package libnmdc +import ( + "strings" +) + +type adcState int + +const ( + adcStateProtocol adcState = 0 + adcStateIdentify adcState = 1 + adcStateVerify adcState = 2 + adcStateNormal adcState = 3 + adcStateData adcState = 4 +) + type AdcProtocol struct { - hc *HubConnection + hc *HubConnection + state adcState + sid string + supports map[string]struct{} } func NewAdcProtocol(hc *HubConnection) Protocol { - proto := AdcProtocol{} - proto.hc = hc + proto := AdcProtocol{ + hc: hc, + state: adcStateProtocol, + supports: make(map[string]struct{}), + } // Start logging in hc.SayRaw("HSUP ADBASE ADTIGR\n") @@ -16,6 +36,123 @@ func NewAdcProtocol(hc *HubConnection) Protocol { func (this *AdcProtocol) ProcessCommand(msg string) { this.hc.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: msg}) + + if len(msg) == 0 { + return + } + + parts := strings.Split(msg, " ") + switch parts[0] { + + case "ISUP": + if !(this.state == adcStateProtocol || this.state == adcStateNormal) { + this.malformed(parts) + return + } + for _, supportflag := range parts[1:] { + if len(supportflag) < 2 { + this.malformed(parts) + return + } + + if supportflag[0:2] == "AD" { + this.supports[supportflag[2:]] = struct{}{} + } else if supportflag[0:2] == "RM" { + delete(this.supports, supportflag[2:]) + } else { + this.malformed(parts) + return + } + } + if this.state == adcStateProtocol { + this.state = adcStateIdentify + } + + case "ISID": + if this.state != adcStateIdentify { + this.malformed(parts) + return + } + this.sid = parts[1] + + case "IINF": + flags := make(map[string]string) + for _, flag := range parts[1:] { + if len(flag) < 2 { + this.malformed(parts) + return + } + flags[flag[0:2]] = flag[2:] + } + + // Possibilities: User MyINFO (Normal state), or, hub telling information about itself (Identify->Normal state) + if (this.state == adcStateIdentify || this.state == adcStateVerify) && flags["CT"] == "32" { + // Hub telling information about itself + this.handleInfo(flags) + + // Transition to state VERIFY and send our own info + this.hc.SayRaw("BINF " + this.escape(this.sid) + " GARBAGE") // FIXME send a real info string + this.state = adcStateVerify + + } else if this.state == adcStateNormal { + // OK + this.handleInfo(flags) + + } else { + this.malformed(parts) + return + } + + case "IGPA": + // + + default: + this.malformed(parts) + } +} + +func (this *AdcProtocol) handleInfo(flags map[string]string) { + if flags["CT"] == "32" { + + // IINF DEADCH++\sTest\shub VE2.12.1\s(r"[unknown]")\sRelease HI1 NIADCH++ APADCH++ CT32 + // AP: extension 3.24 "Application and version separation in INF" + // HI: + + // Hub properties updated + hubName, ok := flags["DE"] + if ok { + this.hc.HubName = this.unescape(hubName) + this.hc.processEvent(HubEvent{EventType: EVENT_HUBNAME_CHANGED, Nick: this.hc.HubName}) + } + + } else { + // User MyINFO + // TODO + + } +} + +func (this *AdcProtocol) enterNormalState() { + this.state = adcStateNormal + this.hc.processEvent(HubEvent{EventType: EVENT_CONNECTION_STATE_CHANGED, StateChange: CONNECTIONSTATE_CONNECTED}) + this.hc.State = CONNECTIONSTATE_CONNECTED +} + +func (this *AdcProtocol) malformed(parts []string) { + this.hc.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: "Ignoring malformed, unhandled, or out-of-state protocol command '" + parts[0] + "'"}) +} + +func (this *AdcProtocol) escape(plaintext string) string { + // The string "\s" escapes space, "\n" newline and "\\" backslash. This version of the protocol reserves all other escapes for future use; any message containing unknown escapes must be discarded. + v1 := strings.Replace(plaintext, `\`, `\\`, -1) + v2 := strings.Replace(v1, "\n", `\n`, -1) + return strings.Replace(v2, " ", `\s`, -1) +} + +func (this *AdcProtocol) unescape(encoded string) string { + v1 := strings.Replace(encoded, `\s`, " ", -1) + v2 := strings.Replace(v1, `\n`, "\n", -1) + return strings.Replace(v2, `\\`, `\`, -1) } func (this *AdcProtocol) SayPublic(msg string) {