// libnmdc project UserInfo.go package libnmdc import ( "errors" "fmt" "regexp" "strconv" "strings" ) type UserFlag byte const ( FLAG_NORMAL UserFlag = 1 FLAG_AWAY_1 UserFlag = 2 FLAG_AWAY_2 UserFlag = 3 FLAG_SERVER_1 UserFlag = 4 FLAG_SERVER_2 UserFlag = 5 FLAG_SERVER_AWAY_1 UserFlag = 6 FLAG_SERVER_AWAY_2 UserFlag = 7 FLAG_FIREBALL_1 UserFlag = 8 FLAG_FIREBALL_2 UserFlag = 9 FLAG_FIREBALL_AWAY_1 UserFlag = 10 FLAG_FIREBALL_AWAY_2 UserFlag = 11 ) type ConnectionMode rune const ( CONNECTIONMODE_ACTIVE ConnectionMode = 'A' // 65 CONNECTIONMODE_PASSIVE ConnectionMode = 'P' // 49 CONNECTIONMODE_SOCKS5 ConnectionMode = '5' // 53 ) func (this ConnectionMode) String() string { switch this { case CONNECTIONMODE_ACTIVE: return "Active" case CONNECTIONMODE_PASSIVE: return "Passive" case CONNECTIONMODE_SOCKS5: return "SOCKS5" default: return fmt.Sprintf("ConnectionMode(\"%s\")", string(this)) } } // This structure represents a user connected to a hub. type UserInfo struct { Nick string Description string ClientTag string ClientVersion string Email string ShareSize uint64 ConnectionMode ConnectionMode Flag UserFlag Slots uint64 Speed string HubsUnregistered uint64 HubsRegistered uint64 HubsOperator uint64 IsOperator bool } var rx_myinfo *regexp.Regexp var rx_myinfo_notag *regexp.Regexp func init() { // $ALL $ $$$$ HEAD := `(?ms)^\$ALL ([^ ]+) ` FOOT := `\$.\$([^$]+)\$([^$]*)\$([0-9]*)\$$` rx_myinfo = regexp.MustCompile(HEAD + `([^<]*)<(.+?) V:([^,]+),M:(.),H:([0-9]+)/([0-9]+)/([0-9]+),S:([0-9]+)>` + FOOT) rx_myinfo_notag = regexp.MustCompile(HEAD + `([^$]*)` + FOOT) // Fallback for no tag } func NewUserInfo(username string) *UserInfo { return &UserInfo{ Nick: username, ConnectionMode: CONNECTIONMODE_PASSIVE, HubsUnregistered: 1, } } func maybeParse(str string, dest *uint64, default_val uint64) { sz, err := strconv.ParseUint(str, 10, 64) if err == nil { *dest = sz } else { *dest = default_val } } func (this *UserInfo) fromMyINFO(protomsg string) error { // Normal format (with tag in exact V/M/H/S order) matches := rx_myinfo.FindStringSubmatch(protomsg) if matches != nil { this.Nick = matches[1] this.Description = NMDCUnescape(matches[2]) this.ClientTag = NMDCUnescape(matches[3]) this.ClientVersion = matches[4] this.ConnectionMode = ConnectionMode(matches[4][0]) maybeParse(matches[6], &this.HubsUnregistered, 0) maybeParse(matches[7], &this.HubsRegistered, 0) maybeParse(matches[8], &this.HubsOperator, 0) maybeParse(matches[9], &this.Slots, 0) if len(matches[10]) > 1 { this.Speed = matches[10][:len(matches[10])-2] } else { this.Speed = "" } this.Flag = UserFlag(matches[10][len(matches[10])-1]) this.Email = NMDCUnescape(matches[11]) maybeParse(matches[12], &this.ShareSize, 0) return nil } // No-tag format, used in early connection matches = rx_myinfo_notag.FindStringSubmatch(protomsg) if matches != nil { this.Nick = matches[1] this.Description = NMDCUnescape(matches[2]) this.ClientTag = "" this.ClientVersion = "0" this.ConnectionMode = CONNECTIONMODE_PASSIVE this.HubsUnregistered = 0 this.HubsRegistered = 0 this.HubsOperator = 0 this.Slots = 0 if len(matches[3]) > 1 { this.Speed = matches[3][:len(matches[3])-2] } else { this.Speed = "" } this.Flag = UserFlag(matches[3][len(matches[3])-1]) this.Email = NMDCUnescape(matches[4]) maybeParse(matches[5], &this.ShareSize, 0) return nil } // Couldn't get anything out of it... return errors.New("Malformed MyINFO") } // Returns the MyINFO command, WITH leading $MyINFO, and WITHOUT trailing pipe func (this *UserInfo) toMyINFO() string { return fmt.Sprintf( "$MyINFO $ALL %s %s<%s V:%s,M:%c,H:%d/%d/%d,S:%d>$ $%s%c$%s$%d$", this.Nick, this.Description, this.ClientTag, strings.Replace(this.ClientVersion, ",", "-", -1), // just in case this.ConnectionMode, this.HubsUnregistered, this.HubsRegistered, this.HubsOperator, this.Slots, this.Speed, this.Flag, this.Email, this.ShareSize, ) }