adc: PID/CID, info generation+parsing, decode adc error messages
--HG-- branch : adc
This commit is contained in:
parent
a3963f196a
commit
3dc4ede0d8
217
AdcProtocol.go
217
AdcProtocol.go
@ -1,6 +1,10 @@
|
|||||||
package libnmdc
|
package libnmdc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base32"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -15,12 +19,17 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type AdcProtocol struct {
|
type AdcProtocol struct {
|
||||||
hc *HubConnection
|
hc *HubConnection
|
||||||
state adcState
|
state adcState
|
||||||
sid string
|
sid, pid, cid string // all in base32 encoding
|
||||||
supports map[string]struct{}
|
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 {
|
func NewAdcProtocol(hc *HubConnection) Protocol {
|
||||||
proto := AdcProtocol{
|
proto := AdcProtocol{
|
||||||
hc: hc,
|
hc: hc,
|
||||||
@ -28,12 +37,53 @@ func NewAdcProtocol(hc *HubConnection) Protocol {
|
|||||||
supports: make(map[string]struct{}),
|
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
|
// Start logging in
|
||||||
hc.SayRaw("HSUP ADBASE ADTIGR\n")
|
hc.SayRaw("HSUP ADBASE ADTIGR\n")
|
||||||
|
|
||||||
return &proto
|
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) {
|
func (this *AdcProtocol) ProcessCommand(msg string) {
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: msg})
|
this.hc.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: msg})
|
||||||
|
|
||||||
@ -91,7 +141,7 @@ func (this *AdcProtocol) ProcessCommand(msg string) {
|
|||||||
this.handleInfo(flags)
|
this.handleInfo(flags)
|
||||||
|
|
||||||
// Transition to state VERIFY and send our own info
|
// 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
|
this.state = adcStateVerify
|
||||||
|
|
||||||
} else if this.state == adcStateNormal {
|
} else if this.state == adcStateNormal {
|
||||||
@ -103,6 +153,17 @@ func (this *AdcProtocol) ProcessCommand(msg string) {
|
|||||||
return
|
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":
|
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) {
|
func (this *AdcProtocol) handleInfo(flags map[string]string) {
|
||||||
if flags["CT"] == "32" {
|
if flags["CT"] == "32" {
|
||||||
|
|
||||||
@ -119,6 +236,12 @@ func (this *AdcProtocol) handleInfo(flags map[string]string) {
|
|||||||
// HI:
|
// HI:
|
||||||
|
|
||||||
// Hub properties updated
|
// Hub properties updated
|
||||||
|
|
||||||
|
// Special SUPPORT that is only indicated in IINF
|
||||||
|
if _, ok := flags["AP"]; ok {
|
||||||
|
this.supports[adcSeparateApVe] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
hubName, ok := flags["DE"]
|
hubName, ok := flags["DE"]
|
||||||
if ok {
|
if ok {
|
||||||
this.hc.HubName = this.unescape(hubName)
|
this.hc.HubName = this.unescape(hubName)
|
||||||
@ -164,3 +287,87 @@ func (this *AdcProtocol) SayPrivate(user, message string) {
|
|||||||
func (this *AdcProtocol) ProtoMessageSeparator() string {
|
func (this *AdcProtocol) ProtoMessageSeparator() string {
|
||||||
return "\n"
|
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