adc: PID/CID, info generation+parsing, decode adc error messages
--HG-- branch : adc
This commit is contained in:
parent
a3963f196a
commit
3dc4ede0d8
211
AdcProtocol.go
211
AdcProtocol.go
@ -1,6 +1,10 @@
|
||||
package libnmdc
|
||||
|
||||
import (
|
||||
"encoding/base32"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@ -17,10 +21,15 @@ const (
|
||||
type AdcProtocol struct {
|
||||
hc *HubConnection
|
||||
state adcState
|
||||
sid string
|
||||
sid, pid, cid string // all in base32 encoding
|
||||
supports map[string]struct{}
|
||||
}
|
||||
|
||||
const (
|
||||
// extra extensions that aren't flagged in SUPPORTS
|
||||
adcSeparateApVe string = "SEPARATE_AP_VE" // we invented this string
|
||||
)
|
||||
|
||||
func NewAdcProtocol(hc *HubConnection) Protocol {
|
||||
proto := AdcProtocol{
|
||||
hc: hc,
|
||||
@ -28,12 +37,53 @@ func NewAdcProtocol(hc *HubConnection) Protocol {
|
||||
supports: make(map[string]struct{}),
|
||||
}
|
||||
|
||||
rxPid := regexp.MustCompile("^[A-Z2-7]{39}$")
|
||||
if !rxPid.MatchString(hc.Hco.AdcPID) {
|
||||
hc.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN, Message: "Invalid custom PID, regenerating"})
|
||||
hc.Hco.AdcPID = NewPID()
|
||||
}
|
||||
|
||||
pid_base32 := hc.Hco.AdcPID
|
||||
cid_base32, err := proto.pid2cid(pid_base32)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
proto.cid = cid_base32
|
||||
proto.pid = pid_base32
|
||||
|
||||
// Start logging in
|
||||
hc.SayRaw("HSUP ADBASE ADTIGR\n")
|
||||
|
||||
return &proto
|
||||
}
|
||||
|
||||
func (this *AdcProtocol) pid2cid(pid_base32 string) (string, error) {
|
||||
|
||||
/*
|
||||
Generate random data and store it in PID_raw, then;
|
||||
PID = Base32( PID_raw )
|
||||
CID = Base32( Hash( PID_raw ) )
|
||||
|
||||
For GPA/PAS, assuming that '12345' is the random data supplied in GPA, then;
|
||||
PAS = Base32( Hash( password + '12345' ) )
|
||||
*/
|
||||
|
||||
pid_raw, err := base32.StdEncoding.DecodeString(pid_base32 + "=")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
cid_raw, err := TTH(string(pid_raw))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
cid_base32 := Base32(cid_raw)
|
||||
|
||||
return cid_base32, nil
|
||||
}
|
||||
|
||||
func (this *AdcProtocol) ProcessCommand(msg string) {
|
||||
this.hc.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: msg})
|
||||
|
||||
@ -91,7 +141,7 @@ func (this *AdcProtocol) ProcessCommand(msg string) {
|
||||
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.hc.SayRaw("BINF " + this.escape(this.sid) + " " + this.ourINFO(true) + "\n")
|
||||
this.state = adcStateVerify
|
||||
|
||||
} else if this.state == adcStateNormal {
|
||||
@ -103,6 +153,17 @@ func (this *AdcProtocol) ProcessCommand(msg string) {
|
||||
return
|
||||
}
|
||||
|
||||
case "ISTA":
|
||||
// Message from the hub
|
||||
if len(parts) < 3 {
|
||||
this.malformed(parts)
|
||||
return
|
||||
}
|
||||
|
||||
code, _ := strconv.Atoi(parts[1])
|
||||
msg := this.unescape(parts[2])
|
||||
this.hc.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_HUB, Message: this.ErrorMessage(code, msg)})
|
||||
|
||||
case "IGPA":
|
||||
//
|
||||
|
||||
@ -111,6 +172,62 @@ func (this *AdcProtocol) ProcessCommand(msg string) {
|
||||
}
|
||||
}
|
||||
|
||||
func (this *AdcProtocol) infoFlagsFor(u *UserInfo) map[string]string {
|
||||
parts := map[string]string{
|
||||
"NI": u.Nick,
|
||||
"SS": fmt.Sprintf("%d", u.ShareSize),
|
||||
"SF": fmt.Sprintf("%d", u.SharedFiles),
|
||||
"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),
|
||||
}
|
||||
|
||||
if _, ok := this.supports[adcSeparateApVe]; ok {
|
||||
parts["AP"] = u.ClientTag
|
||||
parts["VE"] = u.ClientVersion
|
||||
} else {
|
||||
parts["VE"] = fmt.Sprintf("%s %s", u.ClientVersion, u.ClientTag)
|
||||
}
|
||||
|
||||
ct := 0 // 1=bot, 2=registered user, 4=operator, 8=super user, 16=hub owner, 32=hub
|
||||
if u.IsBot {
|
||||
ct |= 1
|
||||
}
|
||||
if u.IsRegistered {
|
||||
ct |= 2
|
||||
}
|
||||
if u.IsOperator {
|
||||
ct |= 4
|
||||
}
|
||||
if u.IsSuperUser {
|
||||
ct |= 8
|
||||
}
|
||||
if u.IsHubOwner {
|
||||
ct |= 16
|
||||
}
|
||||
parts["CT"] = fmt.Sprintf("%d", ct)
|
||||
|
||||
return parts
|
||||
}
|
||||
|
||||
func (this *AdcProtocol) ourINFO(includePid bool) string {
|
||||
parts := this.infoFlagsFor(this.hc.Hco.Self)
|
||||
parts["ID"] = this.cid
|
||||
if includePid {
|
||||
parts["PD"] = this.pid
|
||||
}
|
||||
|
||||
ret := ""
|
||||
for k, v := range parts {
|
||||
ret += " " + k + this.escape(v)
|
||||
}
|
||||
return ret[1:]
|
||||
}
|
||||
|
||||
func (this *AdcProtocol) handleInfo(flags map[string]string) {
|
||||
if flags["CT"] == "32" {
|
||||
|
||||
@ -119,6 +236,12 @@ func (this *AdcProtocol) handleInfo(flags map[string]string) {
|
||||
// HI:
|
||||
|
||||
// Hub properties updated
|
||||
|
||||
// Special SUPPORT that is only indicated in IINF
|
||||
if _, ok := flags["AP"]; ok {
|
||||
this.supports[adcSeparateApVe] = struct{}{}
|
||||
}
|
||||
|
||||
hubName, ok := flags["DE"]
|
||||
if ok {
|
||||
this.hc.HubName = this.unescape(hubName)
|
||||
@ -164,3 +287,87 @@ func (this *AdcProtocol) SayPrivate(user, message string) {
|
||||
func (this *AdcProtocol) ProtoMessageSeparator() string {
|
||||
return "\n"
|
||||
}
|
||||
|
||||
func (this *AdcProtocol) ErrorMessage(code int, msg string) string {
|
||||
severity := code / 100
|
||||
category := (code % 100) / 10
|
||||
cat_sub := (code % 100)
|
||||
|
||||
formatSeverity := func(severity int) string {
|
||||
switch severity {
|
||||
case 0:
|
||||
return "OK"
|
||||
case 1:
|
||||
return "Warning"
|
||||
case 2:
|
||||
return "Error"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
formatCategory := func(category int) string {
|
||||
switch category {
|
||||
case 0:
|
||||
return ""
|
||||
case 1:
|
||||
return "Hub not accepting users"
|
||||
case 2:
|
||||
return "Login failed"
|
||||
case 3:
|
||||
return "Access denied"
|
||||
case 4:
|
||||
return "Protocol error"
|
||||
case 5:
|
||||
return "Transfer error"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
formatCatSub := func(cat_sub int) string {
|
||||
switch cat_sub {
|
||||
case 11:
|
||||
return "Hub is full"
|
||||
case 12:
|
||||
return "Hub is disabled"
|
||||
case 21:
|
||||
return "Invalid nick"
|
||||
case 22:
|
||||
return "Nick is already in use"
|
||||
case 23:
|
||||
return "Invalid password"
|
||||
case 24:
|
||||
return "CID already connected"
|
||||
case 25:
|
||||
return "Access denied"
|
||||
case 26:
|
||||
return "Registered users only"
|
||||
case 27:
|
||||
return "Invalid PID"
|
||||
case 31:
|
||||
return "Permanently banned"
|
||||
case 32:
|
||||
return "Temporarily banned"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
parts := make([]string, 0, 4)
|
||||
if fs := formatSeverity(severity); len(fs) > 0 {
|
||||
parts = append(parts, fs)
|
||||
}
|
||||
if fc := formatCategory(category); len(fc) > 0 {
|
||||
parts = append(parts, fc)
|
||||
}
|
||||
if fcs := formatCatSub(cat_sub); len(fcs) > 0 {
|
||||
parts = append(parts, fcs)
|
||||
}
|
||||
if len(msg) > 0 {
|
||||
parts = append(parts, msg)
|
||||
}
|
||||
|
||||
return strings.Join(parts, ": ") + fmt.Sprintf(" (code %d)", code)
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user