adc: parse user info messages, user<-->sid mapping
--HG-- branch : adc
This commit is contained in:
parent
b1c6a5f56a
commit
a5b773952c
233
AdcProtocol.go
233
AdcProtocol.go
@ -77,6 +77,7 @@ func (this *AdcProtocol) ProcessCommand(msg string) {
|
||||
return
|
||||
}
|
||||
|
||||
this.hc.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: msg})
|
||||
parts := strings.Split(msg, " ")
|
||||
switch parts[0] {
|
||||
|
||||
@ -112,33 +113,104 @@ func (this *AdcProtocol) ProcessCommand(msg string) {
|
||||
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:]
|
||||
// Hub telling information about itself
|
||||
|
||||
flags, err := this.parts2flags(parts[1:])
|
||||
if err != nil {
|
||||
this.logError(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 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)
|
||||
if flags["CT"] != "32" {
|
||||
this.malformed(parts)
|
||||
return
|
||||
}
|
||||
|
||||
err = this.handleHubInfo(flags)
|
||||
if err != nil {
|
||||
this.logError(err)
|
||||
return
|
||||
}
|
||||
|
||||
if this.state == adcStateIdentify {
|
||||
// Transition to state VERIFY and send our own info
|
||||
this.hc.SayRaw("BINF " + this.escape(this.sid) + " " + this.ourINFO(true) + "\n")
|
||||
this.state = adcStateVerify
|
||||
|
||||
} else if this.state == adcStateNormal {
|
||||
} else if this.state == adcStateNormal || this.state == adcStateVerify {
|
||||
// OK
|
||||
this.handleInfo(flags)
|
||||
|
||||
} else {
|
||||
// Bad state to be in
|
||||
this.malformed(parts)
|
||||
return
|
||||
}
|
||||
|
||||
case "BINF":
|
||||
if this.state != adcStateNormal {
|
||||
this.enterNormalState() // successful login
|
||||
}
|
||||
|
||||
sid := parts[1]
|
||||
flags, err := this.parts2flags(parts[2:])
|
||||
if err != nil {
|
||||
this.logError(err)
|
||||
return
|
||||
}
|
||||
|
||||
uinfo, err := this.handleUserInfo(flags)
|
||||
if err != nil {
|
||||
this.logError(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Log this user in, and associate this SID with this user
|
||||
this.hc.usersMut.Lock()
|
||||
defer this.hc.usersMut.Unlock()
|
||||
|
||||
newNick := uinfo.Nick
|
||||
|
||||
oldNick, sidExists := this.hc.userSIDs[sid]
|
||||
|
||||
handleNewUser := func() {
|
||||
// Install this SID as pointing to this nick
|
||||
this.hc.userSIDs[sid] = uinfo.Nick
|
||||
|
||||
// Check if this nick was in use by any other SID already
|
||||
for otherSid, otherSidNick := range this.hc.userSIDs {
|
||||
if otherSidNick == newNick && otherSid != sid {
|
||||
this.hc.processEvent(HubEvent{
|
||||
EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN,
|
||||
Message: fmt.Sprintf("Hub connection corrupted (duplicate SIDs '%s' and '%s' for nick '%s'), disconnecting", sid, otherSid, newNick),
|
||||
})
|
||||
this.hc.Disconnect()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Notifications
|
||||
this.hc.users[newNick] = *uinfo
|
||||
this.hc.processEvent(HubEvent{EventType: EVENT_USER_JOINED, Nick: newNick})
|
||||
}
|
||||
|
||||
if sidExists && oldNick != newNick {
|
||||
// Nick change = delete all trace of this user first, treat as new
|
||||
|
||||
delete(this.hc.users, oldNick)
|
||||
delete(this.hc.userSIDs, sid)
|
||||
this.hc.processEvent(HubEvent{EventType: EVENT_USER_PART, Nick: oldNick})
|
||||
handleNewUser()
|
||||
|
||||
} else if sidExists && oldNick == newNick {
|
||||
// Updating existing user
|
||||
this.hc.users[newNick] = *uinfo
|
||||
this.hc.processEvent(HubEvent{EventType: EVENT_USER_UPDATED_INFO, Nick: newNick})
|
||||
|
||||
} else if !sidExists {
|
||||
// User joined
|
||||
handleNewUser()
|
||||
}
|
||||
|
||||
case "ISTA":
|
||||
// Message from the hub
|
||||
if len(parts) < 3 {
|
||||
@ -154,7 +226,6 @@ func (this *AdcProtocol) ProcessCommand(msg string) {
|
||||
//
|
||||
|
||||
default:
|
||||
this.hc.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: msg})
|
||||
this.malformed(parts)
|
||||
}
|
||||
}
|
||||
@ -167,7 +238,6 @@ func (this *AdcProtocol) infoFlagsFor(u *UserInfo) map[string]string {
|
||||
"US": fmt.Sprintf("%d", u.UploadSpeedBps),
|
||||
"DS": fmt.Sprintf("%d", u.DownloadSpeedBps),
|
||||
"SL": fmt.Sprintf("%d", u.Slots),
|
||||
"FS": fmt.Sprintf("%d", u.Slots), // Free slots - NOT IN ADC DOCUMENTATION
|
||||
"HN": fmt.Sprintf("%d", u.HubsUnregistered),
|
||||
"HR": fmt.Sprintf("%d", u.HubsRegistered),
|
||||
"HO": fmt.Sprintf("%d", u.HubsOperator),
|
||||
@ -215,30 +285,123 @@ func (this *AdcProtocol) ourINFO(includePid bool) string {
|
||||
return ret[1:]
|
||||
}
|
||||
|
||||
func (this *AdcProtocol) handleInfo(flags map[string]string) {
|
||||
if flags["CT"] == "32" {
|
||||
func (this *AdcProtocol) parts2flags(parts []string) (map[string]string, error) {
|
||||
flags := make(map[string]string, len(parts))
|
||||
for _, flag := range parts {
|
||||
if len(flag) < 2 {
|
||||
return nil, fmt.Errorf("Malformed flag '%s'", flag)
|
||||
}
|
||||
flags[flag[0:2]] = this.unescape(flag[2:])
|
||||
}
|
||||
|
||||
// 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:
|
||||
return flags, nil
|
||||
}
|
||||
|
||||
// Hub properties updated
|
||||
func (this *AdcProtocol) handleHubInfo(flags map[string]string) error {
|
||||
if flags["CT"] != "32" {
|
||||
return fmt.Errorf("Expected CT==32")
|
||||
}
|
||||
|
||||
// Special SUPPORT that is only indicated in IINF
|
||||
if _, ok := flags["AP"]; ok {
|
||||
this.supports[adcSeparateApVe] = struct{}{}
|
||||
// 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
|
||||
|
||||
// Special SUPPORT that is only indicated in IINF
|
||||
if _, ok := flags["AP"]; ok {
|
||||
this.supports[adcSeparateApVe] = struct{}{}
|
||||
}
|
||||
|
||||
// Hub's name is in "NI", hub description in "DE"
|
||||
hubName, ok := flags["NI"]
|
||||
if ok {
|
||||
if hubDesc, ok := flags["DE"]; ok && len(hubDesc) > 0 {
|
||||
hubName += " - " + hubDesc
|
||||
}
|
||||
|
||||
hubName, ok := flags["DE"]
|
||||
if ok {
|
||||
this.hc.HubName = this.unescape(hubName)
|
||||
this.hc.processEvent(HubEvent{EventType: EVENT_HUBNAME_CHANGED, Nick: this.hc.HubName})
|
||||
}
|
||||
this.hc.HubName = hubName
|
||||
this.hc.processEvent(HubEvent{EventType: EVENT_HUBNAME_CHANGED, Nick: this.hc.HubName})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *AdcProtocol) handleUserInfo(flags map[string]string) (*UserInfo, error) {
|
||||
|
||||
// User MyINFO
|
||||
// BINF GUPR IDFEARIFD33NTGC4YBEZ3UFQS5R4ZXXTFL2QN2GRY PDZMIFLG5EKZG3BDRRMIJPG7ARNA6KW3JVIH3DF7Q NIivysaur5 SL3 FS3 SS0 SF0 HN1 HR0 HO0 VEEiskaltDC++\s2.2.9 US2621440 KPSHA256/3UPRORG4BLJ4CG6TO6R3G75A67LXOGD437NALQALRWJF6XBOECTA I40.0.0.0 U418301
|
||||
// BINF GUPR I4172.17.0.1 U418301 IDFEARIFD33NTGC4YBEZ3UFQS5R4ZXXTFL2QN2GRY VEEiskaltDC++\s2.2.9 SF0 NIivysaur5 SL3 HN1 HO0 KPSHA256/3UPRORG4BLJ4CG6TO6R3G75A67LXOGD437NALQALRWJF6XBOECTA HR0 FS3 SS0 US2621440 SUSEGA,ADC0,TCP4,UDP4
|
||||
// TODO
|
||||
|
||||
u := UserInfo{}
|
||||
for prop, val := range flags {
|
||||
switch prop {
|
||||
case "ID":
|
||||
u.CID = val
|
||||
case "PD":
|
||||
// ignore PID - it will only appear if we're talking about our own user
|
||||
case "NI":
|
||||
u.Nick = val
|
||||
case "SL":
|
||||
u.Slots, _ = strconv.ParseUint(val, 10, 64)
|
||||
case "SS":
|
||||
u.ShareSize, _ = strconv.ParseUint(val, 10, 64)
|
||||
case "SF":
|
||||
u.SharedFiles, _ = strconv.ParseUint(val, 10, 64)
|
||||
case "HN":
|
||||
u.HubsUnregistered, _ = strconv.ParseUint(val, 10, 64)
|
||||
case "HR":
|
||||
u.HubsRegistered, _ = strconv.ParseUint(val, 10, 64)
|
||||
case "HO":
|
||||
u.HubsOperator, _ = strconv.ParseUint(val, 10, 64)
|
||||
case "US":
|
||||
u.UploadSpeedBps, _ = strconv.ParseUint(val, 10, 64)
|
||||
case "DS":
|
||||
u.DownloadSpeedBps, _ = strconv.ParseUint(val, 10, 64)
|
||||
case "KP":
|
||||
u.Keyprint = val
|
||||
case "I4":
|
||||
u.IPv4Address = val
|
||||
case "I6":
|
||||
u.IPv6Address = val
|
||||
case "U4":
|
||||
u.IPv4UDPPort, _ = strconv.ParseUint(val, 10, 64)
|
||||
case "U6":
|
||||
u.IPv6UDPPort, _ = strconv.ParseUint(val, 10, 64)
|
||||
case "SU":
|
||||
u.SupportFlags = make(map[string]struct{})
|
||||
for _, supportFlag := range strings.Split(val, ",") {
|
||||
u.SupportFlags[supportFlag] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// VE / AP
|
||||
AP, hasAP := flags["AP"]
|
||||
VE, hasVE := flags["VE"]
|
||||
if hasAP && hasVE {
|
||||
u.ClientTag = AP
|
||||
u.ClientVersion = VE
|
||||
} else if hasAP && !hasVE {
|
||||
u.ClientTag, u.ClientVersion = this.getAPVEFromSingle(AP)
|
||||
} else if !hasAP && hasVE {
|
||||
u.ClientTag, u.ClientVersion = this.getAPVEFromSingle(VE)
|
||||
}
|
||||
|
||||
if u.Nick == "" {
|
||||
return nil, fmt.Errorf("Malformed user missing nick")
|
||||
}
|
||||
|
||||
return &u, nil
|
||||
}
|
||||
|
||||
func (this *AdcProtocol) getAPVEFromSingle(term string) (string, string) {
|
||||
words := strings.Split(term, " ")
|
||||
if len(words) > 1 {
|
||||
return strings.Join(words[0:len(words)-1], " "), words[len(words)-1]
|
||||
} else {
|
||||
// User MyINFO
|
||||
// TODO
|
||||
|
||||
return term, "0"
|
||||
}
|
||||
}
|
||||
|
||||
@ -249,7 +412,11 @@ func (this *AdcProtocol) enterNormalState() {
|
||||
}
|
||||
|
||||
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] + "'"})
|
||||
this.logError(fmt.Errorf("Ignoring malformed, unhandled, or out-of-state protocol command %v", parts))
|
||||
}
|
||||
|
||||
func (this *AdcProtocol) logError(e error) {
|
||||
this.hc.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: "Protocol error: " + e.Error()})
|
||||
}
|
||||
|
||||
func (this *AdcProtocol) escape(plaintext string) string {
|
||||
|
@ -19,6 +19,7 @@ type HubConnection struct {
|
||||
|
||||
usersMut sync.RWMutex
|
||||
users map[string]UserInfo
|
||||
userSIDs map[string]string
|
||||
|
||||
proto Protocol
|
||||
|
||||
|
@ -43,10 +43,11 @@ func (this *HubConnectionOptions) prepareConnection() *HubConnection {
|
||||
}
|
||||
|
||||
hc := HubConnection{
|
||||
Hco: this,
|
||||
HubName: DEFAULT_HUB_NAME,
|
||||
State: CONNECTIONSTATE_DISCONNECTED,
|
||||
users: make(map[string]UserInfo),
|
||||
Hco: this,
|
||||
HubName: DEFAULT_HUB_NAME,
|
||||
State: CONNECTIONSTATE_DISCONNECTED,
|
||||
users: make(map[string]UserInfo),
|
||||
userSIDs: make(map[string]string),
|
||||
|
||||
autoReconnect: !this.SkipAutoReconnect,
|
||||
}
|
||||
|
@ -35,8 +35,11 @@ type UserInfo_ADCOnly struct {
|
||||
IsHubOwner bool
|
||||
IPv4Address string // Passive <==> these fields are not set
|
||||
IPv6Address string
|
||||
IPv4UDPPort uint
|
||||
IPv6UDPPort uint
|
||||
IPv4UDPPort uint64
|
||||
IPv6UDPPort uint64
|
||||
Keyprint string
|
||||
CID string
|
||||
SupportFlags map[string]struct{}
|
||||
}
|
||||
|
||||
func NewUserInfo(username string) *UserInfo {
|
||||
|
Loading…
Reference in New Issue
Block a user