Browse Source

vendor: commit existing golang vendor directory

master
mappu 9 months ago
parent
commit
8f443e1cfb
100 changed files with 24124 additions and 1 deletions
  1. 0
    1
      .gitignore
  2. 14
    0
      vendor/code.ivysaur.me/libnmdc/.hgignore
  3. 20
    0
      vendor/code.ivysaur.me/libnmdc/.hgtags
  4. 711
    0
      vendor/code.ivysaur.me/libnmdc/AdcProtocol.go
  5. 80
    0
      vendor/code.ivysaur.me/libnmdc/AutodetectProtocol.go
  6. 29
    0
      vendor/code.ivysaur.me/libnmdc/ConnectionMode.go
  7. 40
    0
      vendor/code.ivysaur.me/libnmdc/ConnectionState.go
  8. 58
    0
      vendor/code.ivysaur.me/libnmdc/Example_test.go
  9. 15
    0
      vendor/code.ivysaur.me/libnmdc/Gopkg.lock
  10. 26
    0
      vendor/code.ivysaur.me/libnmdc/Gopkg.toml
  11. 48
    0
      vendor/code.ivysaur.me/libnmdc/HubAddress.go
  12. 229
    0
      vendor/code.ivysaur.me/libnmdc/HubConnection.go
  13. 79
    0
      vendor/code.ivysaur.me/libnmdc/HubConnectionOptions.go
  14. 25
    0
      vendor/code.ivysaur.me/libnmdc/HubEvent.go
  15. 407
    0
      vendor/code.ivysaur.me/libnmdc/NmdcProtocol.go
  16. 97
    0
      vendor/code.ivysaur.me/libnmdc/NmdcProtocol_test.go
  17. 31
    0
      vendor/code.ivysaur.me/libnmdc/Protocol.go
  18. 6
    0
      vendor/code.ivysaur.me/libnmdc/TODO.txt
  19. 27
    0
      vendor/code.ivysaur.me/libnmdc/UserCommand.go
  20. 17
    0
      vendor/code.ivysaur.me/libnmdc/UserFlag.go
  21. 53
    0
      vendor/code.ivysaur.me/libnmdc/UserInfo.go
  22. 98
    0
      vendor/code.ivysaur.me/libnmdc/__dist/README.txt
  23. 28
    0
      vendor/code.ivysaur.me/libnmdc/libnmdc.go
  24. 41
    0
      vendor/code.ivysaur.me/libnmdc/tth.go
  25. 36
    0
      vendor/code.ivysaur.me/libnmdc/tth_test.go
  26. 22
    0
      vendor/github.com/cxmcc/tiger/.gitignore
  27. 7
    0
      vendor/github.com/cxmcc/tiger/.travis.yml
  28. 20
    0
      vendor/github.com/cxmcc/tiger/LICENSE
  29. 48
    0
      vendor/github.com/cxmcc/tiger/README.md
  30. 96
    0
      vendor/github.com/cxmcc/tiger/compress.go
  31. 269
    0
      vendor/github.com/cxmcc/tiger/sboxes.go
  32. 117
    0
      vendor/github.com/cxmcc/tiger/tiger.go
  33. 144
    0
      vendor/github.com/cxmcc/tiger/tiger_test.go
  34. 7
    0
      vendor/github.com/googollee/go-engine.io/.travis.yml
  35. 23
    0
      vendor/github.com/googollee/go-engine.io/LICENSE
  36. 78
    0
      vendor/github.com/googollee/go-engine.io/README.md
  37. 14
    0
      vendor/github.com/googollee/go-engine.io/example/asset/index.html
  38. 4137
    0
      vendor/github.com/googollee/go-engine.io/example/asset/index.js
  39. 5
    0
      vendor/github.com/googollee/go-engine.io/example/asset/style.css
  40. 60
    0
      vendor/github.com/googollee/go-engine.io/example/main.go
  41. 50
    0
      vendor/github.com/googollee/go-engine.io/ioutil.go
  42. 123
    0
      vendor/github.com/googollee/go-engine.io/ioutil_test.go
  43. 8
    0
      vendor/github.com/googollee/go-engine.io/message/message.go
  44. 45
    0
      vendor/github.com/googollee/go-engine.io/parser/limit_reader.go
  45. 58
    0
      vendor/github.com/googollee/go-engine.io/parser/limit_reader_test.go
  46. 191
    0
      vendor/github.com/googollee/go-engine.io/parser/packet.go
  47. 297
    0
      vendor/github.com/googollee/go-engine.io/parser/packet_test.go
  48. 3
    0
      vendor/github.com/googollee/go-engine.io/parser/parser.go
  49. 170
    0
      vendor/github.com/googollee/go-engine.io/parser/payload.go
  50. 212
    0
      vendor/github.com/googollee/go-engine.io/parser/payload_test.go
  51. 149
    0
      vendor/github.com/googollee/go-engine.io/polling/client.go
  52. 231
    0
      vendor/github.com/googollee/go-engine.io/polling/polling_test.go
  53. 197
    0
      vendor/github.com/googollee/go-engine.io/polling/server.go
  54. 507
    0
      vendor/github.com/googollee/go-engine.io/polling/server_test.go
  55. 28
    0
      vendor/github.com/googollee/go-engine.io/polling/try_locker.go
  56. 47
    0
      vendor/github.com/googollee/go-engine.io/polling/try_locker_test.go
  57. 33
    0
      vendor/github.com/googollee/go-engine.io/polling/writer.go
  58. 89
    0
      vendor/github.com/googollee/go-engine.io/polling/writer_test.go
  59. 12
    0
      vendor/github.com/googollee/go-engine.io/polling/xhr.go
  60. 188
    0
      vendor/github.com/googollee/go-engine.io/server.go
  61. 388
    0
      vendor/github.com/googollee/go-engine.io/server_conn.go
  62. 377
    0
      vendor/github.com/googollee/go-engine.io/server_conn_test.go
  63. 92
    0
      vendor/github.com/googollee/go-engine.io/server_test.go
  64. 47
    0
      vendor/github.com/googollee/go-engine.io/sessions.go
  65. 25
    0
      vendor/github.com/googollee/go-engine.io/sessions_test.go
  66. 50
    0
      vendor/github.com/googollee/go-engine.io/transport/transport.go
  67. 72
    0
      vendor/github.com/googollee/go-engine.io/websocket/client.go
  68. 81
    0
      vendor/github.com/googollee/go-engine.io/websocket/server.go
  69. 12
    0
      vendor/github.com/googollee/go-engine.io/websocket/websocket.go
  70. 458
    0
      vendor/github.com/googollee/go-engine.io/websocket/websocket_test.go
  71. 5
    0
      vendor/github.com/googollee/go-socket.io/.travis.yml
  72. 23
    0
      vendor/github.com/googollee/go-socket.io/LICENSE
  73. 127
    0
      vendor/github.com/googollee/go-socket.io/README.md
  74. 70
    0
      vendor/github.com/googollee/go-socket.io/adapter.go
  75. 168
    0
      vendor/github.com/googollee/go-socket.io/attachment.go
  76. 184
    0
      vendor/github.com/googollee/go-socket.io/attachment_test.go
  77. 82
    0
      vendor/github.com/googollee/go-socket.io/caller.go
  78. 38
    0
      vendor/github.com/googollee/go-socket.io/example/asset/index.html
  79. 10308
    0
      vendor/github.com/googollee/go-socket.io/example/asset/jquery-1.11.1.js
  80. 3
    0
      vendor/github.com/googollee/go-socket.io/example/asset/socket.io-1.3.7.js
  81. 53
    0
      vendor/github.com/googollee/go-socket.io/example/main.go
  82. 213
    0
      vendor/github.com/googollee/go-socket.io/handler.go
  83. 105
    0
      vendor/github.com/googollee/go-socket.io/handler_test.go
  84. 50
    0
      vendor/github.com/googollee/go-socket.io/helper_test.go
  85. 34
    0
      vendor/github.com/googollee/go-socket.io/ioutil.go
  86. 6
    0
      vendor/github.com/googollee/go-socket.io/main.go
  87. 60
    0
      vendor/github.com/googollee/go-socket.io/message_reader.go
  88. 54
    0
      vendor/github.com/googollee/go-socket.io/message_reader_test.go
  89. 47
    0
      vendor/github.com/googollee/go-socket.io/namespace.go
  90. 338
    0
      vendor/github.com/googollee/go-socket.io/parser.go
  91. 191
    0
      vendor/github.com/googollee/go-socket.io/parser_test.go
  92. 106
    0
      vendor/github.com/googollee/go-socket.io/server.go
  93. 174
    0
      vendor/github.com/googollee/go-socket.io/socket.go
  94. 45
    0
      vendor/github.com/googollee/go-socket.io/trim_writer.go
  95. 64
    0
      vendor/github.com/googollee/go-socket.io/trim_writer_test.go
  96. 25
    0
      vendor/github.com/gorilla/websocket/.gitignore
  97. 19
    0
      vendor/github.com/gorilla/websocket/.travis.yml
  98. 8
    0
      vendor/github.com/gorilla/websocket/AUTHORS
  99. 22
    0
      vendor/github.com/gorilla/websocket/LICENSE
  100. 0
    0
      vendor/github.com/gorilla/websocket/README.md

+ 0
- 1
.gitignore View File

@@ -4,4 +4,3 @@ nmdc-webfrontend.conf
clientpack/
_dist/
node_modules/
vendor/

+ 14
- 0
vendor/code.ivysaur.me/libnmdc/.hgignore View File

@@ -0,0 +1,14 @@
mode:regex

# Compilation output
\.(?:exe|a)$
^pkg/

# Dependencies
^src/(?:github.com|gopkg.in|golang.org)/

# Scratch space
^src/nmdc/

# Binary release artefacts
/?__dist/

+ 20
- 0
vendor/code.ivysaur.me/libnmdc/.hgtags View File

@@ -0,0 +1,20 @@
945ab4b16d05aa084f71bf5da9a3f687e0ec8bbd v0.1.0
02a360e95480b97ddad83add5db48b2766339a99 nmdc-log-service-1.0.0
137c1b65039e03c80379826a6efdfd808f6fbc8f v0.2.0
d8b64d5527c2a5e4d76872e5bc3d69f7646135c6 v0.3.0
fca41372e400853775b02e951f9db91d87f41adb nmdc-log-service-1.0.1
050b424a7c5d5a27c9323c8810f3afbead1f5b96 v0.4.0
da9f123633f9c28be6435ed7898139665d4c39d9 nmdc-log-service-1.0.2
75a78f6a78f249a2cd8aa3d29f7e5e6319b4e03b v0.5.0
4116422bb10229d887f9296970a166fa1ef8c5fd nmdc-log-service-1.0.3
cb86f3a40115cc46f450c0c83fd9b9d3b740e820 nmdc-log-service-1.0.4
cb86f3a40115cc46f450c0c83fd9b9d3b740e820 v0.6.0
71343a2c641a438206d30ea7e75dc89a11dbef00 v0.7.0
b0e57a5fcffdf4102d669db51a3648ddf66a0792 v0.8.0
e7c2c71ef24b386add728fad35fff4a996fccbac v0.9.0
3ecc037cf2d7080572fe87c2e39ecd153fb0e947 v0.10.0
5149ffe70ea8475e480b682345b31aa45a3352db v0.11.0
22b156a6fc2f6161765317f4ec9ab3731a26e0e2 v0.12.0
3ee0f4ea5142d66079a9500bdcd48a53bdcf362f v0.13.0
6422ed687cd308c339b6dc188bbe1034ed93f893 v0.14.0
84fb191007017862ffc37af68dcdace5d8c06eee v0.15.0

+ 711
- 0
vendor/code.ivysaur.me/libnmdc/AdcProtocol.go View File

@@ -0,0 +1,711 @@
package libnmdc

import (
"encoding/base32"
"fmt"
"regexp"
"strconv"
"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
state adcState
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,
state: adcStateProtocol,
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 ADUCMD\n")

return &proto
}

func (this *AdcProtocol) pid2cid(pid_base32 string) (string, error) {

pid_raw, err := base32.StdEncoding.DecodeString(pid_base32 + "=")
if err != nil {
return "", err
}

cid_raw := Tiger(string(pid_raw))
cid_base32 := Base32(cid_raw)

return cid_base32, nil
}

func (this *AdcProtocol) SID2Nick(sid string) (string, bool) {
this.hc.usersMut.Lock()
defer this.hc.usersMut.Unlock()

nick, ok := this.hc.userSIDs[sid]
return nick, ok
}

func (this *AdcProtocol) Nick2SID(targetNick string) (string, bool) {
this.hc.usersMut.Lock()
defer this.hc.usersMut.Unlock()

for sid, nick := range this.hc.userSIDs {
if nick == targetNick {
return sid, true
}
}

return "", false
}

func (this *AdcProtocol) ProcessCommand(msg string) {

if len(msg) == 0 {
return
}

this.hc.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: msg})

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]

// State transition IDENTIFY --> VERIFY and send our own info
this.hc.SayRaw("BINF " + this.escape(this.sid) + " " + this.ourINFO(true) + "\n")
this.state = adcStateVerify

case "IINF":
// Hub telling information about itself
// ADCH++ sends this once we are successfully logged in

flags, err := this.parts2flags(parts[1:])
if err != nil {
this.logError(err)
return
}

if flags["CT"] != "32" {
this.malformed(parts)
return
}

err = this.handleHubInfo(flags)
if err != nil {
this.logError(err)
return
}

if this.state != adcStateNormal {
this.enterNormalState() // successful login
}

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
}

// Log this user in, and associate this SID with this user
this.hc.usersMut.Lock()
defer this.hc.usersMut.Unlock()

oldNick, sidExists := this.hc.userSIDs[sid]

uinfo := UserInfo{}
if sidExists {
uinfo_lookup, ok := this.hc.users[oldNick]
if !ok {
// Shouldn't happen
this.hc.processEvent(HubEvent{
EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN,
Message: fmt.Sprintf("Hub connection corrupted (missing info for SID='%s' nick='%s'), disconnecting", sid, oldNick),
})
this.hc.Disconnect()
return
}

uinfo = uinfo_lookup
}

this.updateUserInfo(&uinfo, flags)
newNick := uinfo.Nick
if len(newNick) == 0 {
this.logError(fmt.Errorf("Zero-length nick for user (SID='%s')", sid))
}

shouldHandleNewUser := false
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})
shouldHandleNewUser = true

} 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
shouldHandleNewUser = true
}

//

if shouldHandleNewUser {
// 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})
}

case "IMSG":
// General message from the hub
if len(parts) < 2 {
this.malformed(parts)
return
}

this.hc.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_HUB, Message: this.unescape(parts[1])})

case "ISTA":
// Error 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 "IQUI":
// Error message from the hub
// IQUI V3M6 DI1 MSNick\staken,\splease\spick\sanother\sone TL-1
if len(parts) < 2 {
this.malformed(parts)
return
}

sid := parts[1]

flags, err := this.parts2flags(parts[2:])
if err != nil {
return
}

if sid == this.sid {
if msg, ok := flags["MS"]; ok {
this.hc.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_HUB, Message: "The hub is closing our connection because: " + this.unescape(msg)})
} else {
this.hc.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_HUB, Message: "The hub is closing our connection"})
}
} else {
this.hc.usersMut.Lock()
defer this.hc.usersMut.Unlock()
otherSidNick, ok := this.hc.userSIDs[sid]
if ok {
delete(this.hc.userSIDs, sid)
delete(this.hc.users, otherSidNick)
this.hc.processEvent(HubEvent{EventType: EVENT_USER_PART, Nick: otherSidNick})
} else {
// ??
this.logError(fmt.Errorf("An unknown user quit the hub (SID=%s)", sid))
}
}

case "BMSG":
// Message from a user
// BMSG ZVF4 hi
if len(parts) < 3 {
this.malformed(parts)
return
}

sid := this.unescape(parts[1])
msg := this.unescape(parts[2])

this.hc.usersMut.Lock()
defer this.hc.usersMut.Unlock()
nick, ok := this.hc.userSIDs[sid]
if !ok {
this.logError(fmt.Errorf("Recieved message from unknown SID '%s'", sid))
return
}

this.hc.processEvent(HubEvent{EventType: EVENT_PUBLIC, Nick: nick, Message: msg})

case "IGPA":
// Password is needed
// IGPA 7EIAAAECLMAAAPJQAAADQQYAAAWAYAAAKVFQAAF6EAAAAAYFAAAA
// HPAS LZDIJOTZDPWHINHGPT5RHT6WLU7DRME7DQO2O3Q
if len(parts) < 2 {
this.malformed(parts)
return
}

/*
For GPA/PAS, assuming that '12345' is the random data supplied in GPA, then;
PAS = Base32( Hash( password + '12345' ) )

GPA: The data parameter is at least 24 random bytes (base32 encoded).
*/

data_base32 := parts[1]
if len(data_base32)%8 != 0 {
data_base32 += strings.Repeat("=", 8-(len(data_base32)%8))
}

data_raw, err := base32.StdEncoding.DecodeString(data_base32)
if err != nil {
this.logError(err)
return
}

resp := Base32(Tiger(this.hc.Hco.NickPassword + string(data_raw)))
this.hc.SayRaw("HPAS " + resp + "\n")

case "EMSG":
// Private message from other user
// EMSG I5RO FMWH test\spm PMI5RO
// EMSG sender recip==us message [flags...]
if len(parts) < 4 {
this.malformed(parts)
return
}

if parts[2] != this.sid {
this.logError(fmt.Errorf("Recieved a PM intended for someone else (got SID=%s expected SID=%s)", parts[2], this.sid))
return
}

senderSid := parts[1]
senderNick, ok := this.SID2Nick(parts[1])
if !ok {
this.logError(fmt.Errorf("Recieved a PM from an unknown user (SID=%s)", senderSid))
return
}

msg := this.unescape(parts[3])
this.hc.processEvent(HubEvent{EventType: EVENT_PRIVATE, Nick: senderNick, Message: msg})

case "ICMD":
// Usercommand
// ICMD ADCH++/About\sthis\shub TTHMSG\s+about\n CT3

if len(parts) < 2 {
this.malformed(parts)
return
}

uc := UserCommand{
Message: this.unescape(parts[1]),
Type: USERCOMMAND_TYPE_RAW, // default
}

flags, err := this.parts2flags(parts[2:])
if err != nil {
this.malformed(parts)
return
}

if ct, ok := flags["CT"]; ok {
ct64, _ := strconv.ParseUint(ct, 10, 64)
uc.Context = UserCommandContext(ct64)
}

if tt, ok := flags["TT"]; ok {
uc.Command = tt
}

if sp, ok := flags["SP"]; ok && sp == "1" {
uc.Type = USERCOMMAND_TYPE_SEPARATOR
}

if co, ok := flags["CO"]; ok && co == "1" {
uc.Type = USERCOMMAND_TYPE_NICKLIMITED // "Constrained" in ADC parlance
}

if rm, ok := flags["RM"]; ok && rm == "1" {
uc.RemoveThis = true
}

this.hc.processEvent(HubEvent{EventType: EVENT_USERCOMMAND, UserCommand: &uc})

// Ignored messages
// ````````````````

case "DCTM": // Client-client ConnectToMe
case "BSCH": // Search

default:
this.malformed(parts)
}
}

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),
"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.ClientTag, u.ClientVersion)
}

// Do not send the hub a CT (it decides what type we are)

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) 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:])
}

return flags, nil
}

func (this *AdcProtocol) handleHubInfo(flags map[string]string) error {
if flags["CT"] != "32" {
return fmt.Errorf("Expected 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

// 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
}

this.hc.HubName = hubName
this.hc.processEvent(HubEvent{EventType: EVENT_HUBNAME_CHANGED, Nick: this.hc.HubName})
}

return nil
}

func (this *AdcProtocol) updateUserInfo(u *UserInfo, flags map[string]string) {

// 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
// Or maybe only incremental:
// BINF Z3BA HO1
// TODO

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)
}
}

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 {
return term, "0"
}
}

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.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 {
// 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) {
this.hc.SayRaw("BMSG " + this.sid + " " + this.escape(msg) + "\n")
}

func (this *AdcProtocol) SayPrivate(user, message string) {
if sid, ok := this.Nick2SID(user); ok {
this.hc.SayRaw("DMSG " + this.sid + " " + sid + " " + this.escape(message) + "\n")
} else {
this.logError(fmt.Errorf("Unknown user '%s'", user))
}
}

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)

}

+ 80
- 0
vendor/code.ivysaur.me/libnmdc/AutodetectProtocol.go View File

@@ -0,0 +1,80 @@
package libnmdc

import (
"sync"
"time"
)

type AutodetectProtocol struct {
hc *HubConnection

realProtoMut sync.Mutex
realProto Protocol
}

func NewAutodetectProtocol(hc *HubConnection) Protocol {
proto := AutodetectProtocol{
hc: hc,
realProto: nil,
}

go proto.timeout()

return &proto
}

func (this *AutodetectProtocol) timeout() {
time.Sleep(AUTODETECT_ADC_NMDC_TIMEOUT)

this.realProtoMut.Lock()
defer this.realProtoMut.Unlock()

if this.realProto == nil {
this.realProto = NewAdcProtocol(this.hc)
this.hc.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: "Detected ADC protocol"})
}
}

func (this *AutodetectProtocol) ProcessCommand(msg string) {
this.realProtoMut.Lock()
defer this.realProtoMut.Unlock()

if this.realProto == nil {
// We actually got some data using $ as the separator?
// Upgrade to a full NMDC protocol
this.realProto = NewNmdcProtocol(this.hc)
this.hc.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: "Detected NMDC protocol"})
}

this.realProto.ProcessCommand(msg)
}

func (this *AutodetectProtocol) SayPublic(msg string) {
this.realProtoMut.Lock()
defer this.realProtoMut.Unlock()

if this.realProto == nil {
this.realProto = NewNmdcProtocol(this.hc)
}
this.realProto.SayPublic(msg)
}

func (this *AutodetectProtocol) SayPrivate(user, message string) {
this.realProtoMut.Lock()
defer this.realProtoMut.Unlock()

if this.realProto == nil {
this.realProto = NewNmdcProtocol(this.hc)
}
this.realProto.SayPrivate(user, message)
}

func (this *AutodetectProtocol) ProtoMessageSeparator() string {
this.realProtoMut.Lock()
defer this.realProtoMut.Unlock()

if this.realProto == nil {
return "|"
}
return this.realProto.ProtoMessageSeparator()
}

+ 29
- 0
vendor/code.ivysaur.me/libnmdc/ConnectionMode.go View File

@@ -0,0 +1,29 @@
package libnmdc

import (
"fmt"
)

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))
}
}

+ 40
- 0
vendor/code.ivysaur.me/libnmdc/ConnectionState.go View File

@@ -0,0 +1,40 @@
package libnmdc

import (
"net"
)

type ConnectionState int

const (
CONNECTIONSTATE_DISCONNECTED = 1
CONNECTIONSTATE_CONNECTING = 2 // Handshake in progress
CONNECTIONSTATE_CONNECTED = 3
)

func (cs ConnectionState) String() string {
switch cs {
case CONNECTIONSTATE_DISCONNECTED:
return "Disconnected"
case CONNECTIONSTATE_CONNECTING:
return "Connecting"
case CONNECTIONSTATE_CONNECTED:
return "Connected"
default:
return "?"
}
}

func checkIsNetTimeout(err error) bool {
if err == nil {
return false
}

switch err.(type) {
case net.Error:
return err.(net.Error).Timeout()

default:
return false
}
}

+ 58
- 0
vendor/code.ivysaur.me/libnmdc/Example_test.go View File

@@ -0,0 +1,58 @@
package libnmdc
import (
"fmt"
)
func ExampleHubConnectionOptions_Connect() {
opts := HubConnectionOptions{
Address: "127.0.0.1",
Self: NewUserInfo("slowpoke9"),
}
events := make(chan HubEvent, 0)
hub := ConnectAsync(&opts, events)
for event := range events {
switch event.EventType {
case EVENT_CONNECTION_STATE_CHANGED:
fmt.Printf("Connection -- %s (%s)\n", event.StateChange, event.Message)
case EVENT_PUBLIC:
fmt.Printf("Message from '%s': '%s'\n", event.Nick, event.Message)
if event.Message == "how are you" {
hub.SayPublic("good thanks!")
}
default:
fmt.Printf("%+v\n", event)
}
}
}
func ExampleHubConnectionOptions_ConnectSync() {
cb := func(hub *HubConnection, event HubEvent) {
switch event.EventType {
case EVENT_CONNECTION_STATE_CHANGED:
fmt.Printf("Connection -- %s (%s)\n", event.StateChange, event.Message)
case EVENT_PUBLIC:
fmt.Printf("Message from '%s': '%s'\n", event.Nick, event.Message)
if event.Message == "how are you" {
hub.SayPublic("good thanks!")
}
default:
fmt.Printf("%+v\n", event)
}
}
opts := HubConnectionOptions{
Address: "127.0.0.1",
Self: NewUserInfo("slowpoke9"),
}
ConnectSync(&opts, cb) // blocking
}

+ 15
- 0
vendor/code.ivysaur.me/libnmdc/Gopkg.lock View File

@@ -0,0 +1,15 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.


[[projects]]
branch = "master"
name = "github.com/cxmcc/tiger"
packages = ["."]
revision = "bde35e2713d7f674987c2ecb21a6b0fc33749516"

[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "c88ee670a5600b482019325b6d6633bb6b5fe789596dc29ef809aa7bb013927b"
solver-name = "gps-cdcl"
solver-version = 1

+ 26
- 0
vendor/code.ivysaur.me/libnmdc/Gopkg.toml View File

@@ -0,0 +1,26 @@

# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"


[[constraint]]
branch = "master"
name = "github.com/cxmcc/tiger"

+ 48
- 0
vendor/code.ivysaur.me/libnmdc/HubAddress.go View File

@@ -0,0 +1,48 @@
package libnmdc

import (
"net/url"
"strings"
)

type HubAddress string

func (this *HubAddress) parse() url.URL {
parsed, err := url.Parse(strings.ToLower(string(*this)))
if err != nil || len(parsed.Host) == 0 {
parsed = &url.URL{
Scheme: "",
Host: string(*this),
}
}

// Add default port if not specified
if !strings.ContainsRune(parsed.Host, ':') {
parsed.Host = parsed.Host + ":411"
}

return *parsed
}

func (this *HubAddress) IsSecure() bool {
parsed := this.parse()

return parsed.Scheme == "nmdcs" || parsed.Scheme == "dchubs" || parsed.Scheme == "adcs"
}

func (this *HubAddress) GetHostOnly() string {
return this.parse().Host
}

func (this *HubAddress) GetProtocol() HubProtocol {
parsed := this.parse()

switch parsed.Scheme {
case "nmdc", "dchub", "nmdcs", "dchubs":
return HubProtocolNmdc
case "adc", "adcs":
return HubProtocolAdc
default:
return HubProtocolAutodetect
}
}

+ 229
- 0
vendor/code.ivysaur.me/libnmdc/HubConnection.go View File

@@ -0,0 +1,229 @@
package libnmdc

import (
"crypto/tls"
"fmt"
"net"
"regexp"
"sync"
"time"
)

type HubConnection struct {
// Supplied parameters
Hco *HubConnectionOptions

// Current remote status
HubName string
State ConnectionState

usersMut sync.RWMutex
users map[string]UserInfo
userSIDs map[string]string

proto Protocol

// Event callback
processEvent func(HubEvent)

// Private state
conn net.Conn // this is an interface
connValid bool
autoReconnect bool
lastDataRecieved time.Time
}

// Thread-safe user accessor.
func (this *HubConnection) Users(cb func(*map[string]UserInfo) error) error {
this.usersMut.Lock()
defer this.usersMut.Unlock()

return cb(&this.users)
}

func (this *HubConnection) SayPublic(message string) {
this.proto.SayPublic(message)
}

func (this *HubConnection) SayPrivate(recipient string, message string) {
this.proto.SayPrivate(recipient, message)
}

func (this *HubConnection) UserExists(nick string) bool {
this.usersMut.RLock()
defer this.usersMut.RUnlock()

_, already_existed := this.users[nick]
return already_existed
}

func (this *HubConnection) UserCount() int {
this.usersMut.RLock()
defer this.usersMut.RUnlock()

return len(this.users)
}

func (this *HubConnection) userJoined_NameOnly(nick string) {
if !this.UserExists(nick) {

this.usersMut.Lock()
this.users[nick] = *NewUserInfo(nick)
this.usersMut.Unlock() // Don't lock over a processEvent boundary

this.processEvent(HubEvent{EventType: EVENT_USER_JOINED, Nick: nick})
}
}

func (this *HubConnection) userJoined_Full(uinf *UserInfo) {
// n.b. also called when we get a replacement MyINFO for someone
this.usersMut.Lock()
_, userExisted := this.users[uinf.Nick] // don't use UserExists as it would deadlock the mutex
this.users[uinf.Nick] = *uinf
this.usersMut.Unlock() // Don't lock over a processEvent boundary

if !userExisted {
this.processEvent(HubEvent{EventType: EVENT_USER_JOINED, Nick: uinf.Nick})
} else {
this.processEvent(HubEvent{EventType: EVENT_USER_UPDATED_INFO, Nick: uinf.Nick})
}
}

// SayRaw sends raw bytes over the TCP socket. Callers should add the protocol
// terminating character themselves (e.g. `|` for NMDC).
// Note that protocol messages are transmitted on the caller thread, not from
// any internal libnmdc thread.
func (this *HubConnection) SayRaw(protocolCommand string) error {
if !this.connValid {
return ErrNotConnected
}

_, err := this.conn.Write([]byte(protocolCommand))
return err
}

func (this *HubConnection) SayKeepalive() error {
if !this.connValid {
return ErrNotConnected
}

return this.SayRaw(this.proto.ProtoMessageSeparator())
}

func (this *HubConnection) Disconnect() {
this.autoReconnect = false
if this.conn != nil {
this.conn.Close()
}
// A CONNECTIONSTATE_DISCONNECTED message will be emitted by the worker.
}

func (this *HubConnection) worker() {
var fullBuffer string
var err error = nil
var nbytes int = 0

for {

// If we're not connected, attempt reconnect
if this.conn == nil {

fullBuffer = "" // clear

if this.Hco.Address.IsSecure() {
this.conn, err = tls.Dial("tcp", this.Hco.Address.GetHostOnly(), &tls.Config{
InsecureSkipVerify: this.Hco.SkipVerifyTLS,
})
} else {
this.conn, err = net.Dial("tcp", this.Hco.Address.GetHostOnly())
}

if err != nil {
this.State = CONNECTIONSTATE_DISCONNECTED
this.connValid = false
this.proto = nil

} else {
this.State = CONNECTIONSTATE_CONNECTING
this.connValid = true
this.processEvent(HubEvent{EventType: EVENT_CONNECTION_STATE_CHANGED, StateChange: CONNECTIONSTATE_CONNECTING})
this.proto = this.Hco.Address.GetProtocol().Create(this)

}
}

// Read from socket into our local buffer (blocking)
if this.connValid {

readBuff := make([]byte, 1024)
this.conn.SetReadDeadline(time.Now().Add(SEND_KEEPALIVE_EVERY))

nbytes, err = this.conn.Read(readBuff)

if checkIsNetTimeout(err) {
// No data before read deadline
err = nil

if this.proto == nil {
// Autodetect: switch to ADC
this.proto = NewAdcProtocol(this)
} else {
// Normal
// Send KA packet
err = this.SayKeepalive()
}
}

if nbytes > 0 {
this.lastDataRecieved = time.Now()
fullBuffer += string(readBuff[0:nbytes])
}
}

if this.proto != nil {
rxSeparator := regexp.QuoteMeta(this.proto.ProtoMessageSeparator())
rxProtocolMessage := regexp.MustCompile(`(?ms)\A[^` + rxSeparator + `]*` + rxSeparator)

// Attempt to parse a message block
for len(fullBuffer) > 0 {

// FIXME nmdc
for len(fullBuffer) > 0 && fullBuffer[0] == '|' {
fullBuffer = fullBuffer[1:]
}

protocolMessage := rxProtocolMessage.FindString(fullBuffer)
if len(protocolMessage) > 0 {
this.proto.ProcessCommand(protocolMessage[:len(protocolMessage)-1])
fullBuffer = fullBuffer[len(protocolMessage):]
} else {
break
}
}

if err == nil && time.Now().Sub(this.lastDataRecieved) > RECONNECT_IF_NO_DATA_RECIEVED_IN {
err = fmt.Errorf("No packets recieved since %s, connection presumed lost", this.lastDataRecieved.Format(time.RFC3339))
}
}

// Maybe we disconnected
// Perform this check *last*, to ensure we've had a final shot at
// clearing out any queued messages
if err != nil {
this.State = CONNECTIONSTATE_DISCONNECTED
this.conn = nil
this.connValid = false
this.proto = nil
this.processEvent(HubEvent{EventType: EVENT_CONNECTION_STATE_CHANGED, StateChange: CONNECTIONSTATE_DISCONNECTED, Message: err.Error()})

if this.autoReconnect {
time.Sleep(AUTO_RECONNECT_AFTER) // Wait before reconnect
continue
} else {
return // leave the worker for good
}
}

}

}

+ 79
- 0
vendor/code.ivysaur.me/libnmdc/HubConnectionOptions.go View File

@@ -0,0 +1,79 @@
package libnmdc

import (
"crypto/rand"
)

type HubConnectionOptions struct {
Address HubAddress
SkipVerifyTLS bool // using a negative verb, because bools default to false
SkipAutoReconnect bool // as above
Self *UserInfo
NickPassword string
AdcPID string // blank: autogenerate
}

func NewPID() string {
pidBytes := make([]byte, 24)
n, err := rand.Read(pidBytes)
if err != nil {
panic(err) // Insufficient cryptographic randomness
}

if n != 24 {
panic("Insufficient cryptographic randomness")
}

return Base32(pidBytes)
}

func (this *HubConnectionOptions) prepareConnection() *HubConnection {
if this.Self.ClientTag == "" {
this.Self.ClientTag = DEFAULT_CLIENT_TAG
this.Self.ClientVersion = DEFAULT_CLIENT_VERSION
}

// Shouldn't be blank either
if this.Self.ClientVersion == "" {
this.Self.ClientVersion = "0"
}

if this.AdcPID == "" {
this.AdcPID = NewPID()
}

hc := HubConnection{
Hco: this,
HubName: DEFAULT_HUB_NAME,
State: CONNECTIONSTATE_DISCONNECTED,
users: make(map[string]UserInfo),
userSIDs: make(map[string]string),

autoReconnect: !this.SkipAutoReconnect,
}

return &hc
}

// ConnectAsync connects to a hub server, and spawns a background goroutine to handle
// protocol messages. Events will be sent by channel to the supplied onEvent channel,
// the client is responsible for selecting off this.
func ConnectAsync(opts *HubConnectionOptions, onEvent chan HubEvent) *HubConnection {
hc := opts.prepareConnection()
hc.processEvent = func(ev HubEvent) {
onEvent <- ev
}

go hc.worker()
return hc
}

// ConnectSync connects to a hub server, and blocks forever to handle protocol messages.
// Client code should supply an event handling function as hco.OnEventSync.
func ConnectSync(opts *HubConnectionOptions, onEvent func(hub *HubConnection, ev HubEvent)) {
hc := opts.prepareConnection()
hc.processEvent = func(ev HubEvent) {
onEvent(hc, ev)
}
hc.worker()
}

+ 25
- 0
vendor/code.ivysaur.me/libnmdc/HubEvent.go View File

@@ -0,0 +1,25 @@
package libnmdc

type HubEventType int

const (
EVENT_PUBLIC HubEventType = 1
EVENT_PRIVATE HubEventType = 2
EVENT_SYSTEM_MESSAGE_FROM_HUB HubEventType = 3
EVENT_SYSTEM_MESSAGE_FROM_CONN HubEventType = 4
EVENT_USER_JOINED HubEventType = 5
EVENT_USER_PART HubEventType = 6
EVENT_USER_UPDATED_INFO HubEventType = 7
EVENT_CONNECTION_STATE_CHANGED HubEventType = 8
EVENT_HUBNAME_CHANGED HubEventType = 9
EVENT_DEBUG_MESSAGE HubEventType = 10
EVENT_USERCOMMAND HubEventType = 11
)

type HubEvent struct {
EventType HubEventType
Nick string
Message string
StateChange ConnectionState
UserCommand *UserCommand
}

+ 407
- 0
vendor/code.ivysaur.me/libnmdc/NmdcProtocol.go View File

@@ -0,0 +1,407 @@
package libnmdc

import (
"errors"
"fmt"
"regexp"
"strconv"
"strings"
)

type NmdcProtocol struct {
hc *HubConnection
sentOurHello bool
supports map[string]struct{}

rxPublicChat *regexp.Regexp
rxIncomingTo *regexp.Regexp
rxUserCommand *regexp.Regexp
rxMyInfo *regexp.Regexp
rxMyInfoNoTag *regexp.Regexp
}

func NewNmdcProtocol(hc *HubConnection) Protocol {
proto := NmdcProtocol{}
proto.hc = hc

// With the `m` flag, use \A instead of ^ to anchor to start
// This fixes accidentally finding a better match in the middle of a multi-line message

proto.rxPublicChat = regexp.MustCompile(`(?ms)\A<([^>]*)> (.*)$`)
proto.rxIncomingTo = regexp.MustCompile(`(?ms)\A([^ ]+) From: ([^ ]+) \$<([^>]*)> (.*)`)
proto.rxUserCommand = regexp.MustCompile(`(?ms)\A(\d+) (\d+)\s?([^\$]*)\$?(.*)`)

// Format: $ALL <nick> <description>$ $<connection><flag>$<e-mail>$<sharesize>$

HEAD := `(?ms)^\$ALL ([^ ]+) `
FOOT := `\$.\$([^$]+)\$([^$]*)\$([0-9]*)\$$`

proto.rxMyInfo = regexp.MustCompile(HEAD + `([^<]*)<(.+?) V:([^,]+),M:(.),H:([0-9]+)/([0-9]+)/([0-9]+),S:([0-9]+)>` + FOOT)
proto.rxMyInfoNoTag = regexp.MustCompile(HEAD + `([^$]*)` + FOOT) // Fallback for no tag

// Done
return &proto
}

func (this *NmdcProtocol) ProcessCommand(message string) {

// Zero-length protocol message
// ````````````````````````````
if len(message) == 0 {
return
}

// Public chat
// ```````````
if this.rxPublicChat.MatchString(message) {
pubchat_parts := this.rxPublicChat.FindStringSubmatch(message)
this.hc.processEvent(HubEvent{EventType: EVENT_PUBLIC, Nick: pubchat_parts[1], Message: this.unescape(pubchat_parts[2])})
return
}

// System messages
// ```````````````
if message[0] != '$' {
this.hc.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_HUB, Nick: this.hc.HubName, Message: this.unescape(message)})
return
}

// Protocol messages
// `````````````````

commandParts := strings.SplitN(message, " ", 2)
switch commandParts[0] {

case "$Lock":
this.hc.SayRaw("$Supports NoHello NoGetINFO UserCommand UserIP2 QuickList ChatOnly|" +
"$Key " + this.unlock([]byte(commandParts[1])) + "|")
this.sentOurHello = false

case "$Hello":
if commandParts[1] == this.hc.Hco.Self.Nick && !this.sentOurHello {
this.hc.SayRaw("$Version 1,0091|")
this.hc.SayRaw("$GetNickList|")
this.sayInfo()
this.sentOurHello = true

} else {
this.hc.userJoined_NameOnly(commandParts[1])

}

case "$HubName":
this.hc.HubName = commandParts[1]
this.hc.processEvent(HubEvent{EventType: EVENT_HUBNAME_CHANGED, Nick: commandParts[1]})

case "$ValidateDenide": // sic
if len(this.hc.Hco.NickPassword) > 0 {
this.hc.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN, Message: "Incorrect password."})
} else {
this.hc.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN, Message: "Nick already in use."})
}

case "$HubIsFull":
this.hc.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN, Message: "Hub is full."})

case "$BadPass":
this.hc.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN, Message: "Incorrect password."})

case "$GetPass":
if len(this.hc.Hco.NickPassword) == 0 {
// We've got a problem. MyPass with no arguments is a syntax error with no message = instant close
// Just drop the connection
this.hc.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN, Message: "This account is passworded."})
this.hc.Disconnect()
} else {
this.hc.SayRaw("$MyPass " + this.escape(this.hc.Hco.NickPassword) + "|")
}

case "$Quit":
this.hc.usersMut.Lock()
delete(this.hc.users, commandParts[1])
this.hc.usersMut.Unlock() // Don't lock over a processEvent boundary

this.hc.processEvent(HubEvent{EventType: EVENT_USER_PART, Nick: commandParts[1]})

case "$MyINFO":
u, err := this.parseMyINFO(commandParts[1])
if err != nil {
this.hc.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: err.Error()})
return
}

this.hc.userJoined_Full(u)

case "$NickList":
nicklist := strings.Split(commandParts[1], "$$")
for _, nick := range nicklist {
if len(nick) > 0 {
this.hc.userJoined_NameOnly(nick)
}
}

case "$OpList":
oplist := strings.Split(commandParts[1], "$$")
opmap := map[string]struct{}{}

// Organise/sort the list, and ensure we're not meeting an operator for
// the first time
for _, nick := range oplist {
if len(nick) > 0 {
opmap[nick] = struct{}{}
this.hc.userJoined_NameOnly(nick) // assert existence; noop otherwise
}
}

// Mark all mentioned nicks as being operators, and all unmentioned nicks
// as being /not/ an operator. (second pass minimises RW mutex use)
func() {
this.hc.usersMut.Lock()
defer this.hc.usersMut.Unlock()

for nick, userinfo := range this.hc.users {
_, isop := opmap[nick]

userinfo.IsOperator = isop
this.hc.users[nick] = userinfo
}
}()

case "$To:":
valid := false
if this.rxIncomingTo.MatchString(commandParts[1]) {
txparts := this.rxIncomingTo.FindStringSubmatch(commandParts[1])
if txparts[1] == this.hc.Hco.Self.Nick && txparts[2] == txparts[3] {
this.hc.processEvent(HubEvent{EventType: EVENT_PRIVATE, Nick: txparts[2], Message: this.unescape(txparts[4])})
valid = true
}
}

if !valid {
this.hc.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: "Malformed private message '" + commandParts[1] + "'"})
}

case "$UserIP":
this.hc.usersMut.Lock()

pairs := strings.Split(commandParts[1], "$$")
notifyOfUpdate := make([]string, 0, len(pairs))

nextIPPair:
for _, pair := range pairs {
parts := strings.SplitN(pair, " ", 2)
if len(parts) != 2 {
// ????
continue nextIPPair
}

ip2nick := parts[0]
ip2addr := parts[1]

uinfo, ok := this.hc.users[ip2nick]
if !ok {
this.hc.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: "Recieved IP '" + ip2addr + "' for unknown user '" + ip2nick + "'"})
continue nextIPPair
}

if uinfo.IPAddress != ip2addr {
uinfo.IPAddress = ip2addr
notifyOfUpdate = append(notifyOfUpdate, ip2nick)
this.hc.users[ip2nick] = uinfo
}
}

this.hc.usersMut.Unlock()

for _, nick := range notifyOfUpdate {
this.hc.processEvent(HubEvent{EventType: EVENT_USER_UPDATED_INFO, Nick: nick})
}

case "$ForceMove":
this.hc.Hco.Address = HubAddress(commandParts[1])
this.hc.conn.Close() // we'll reconnect onto the new address

case "$UserCommand":
// $UserCommand 1 1 Group chat\New group chat$<%[mynick]> !groupchat_new&#124;|
if this.rxUserCommand.MatchString(commandParts[1]) {
usc := this.rxUserCommand.FindStringSubmatch(commandParts[1])

typeInt, _ := strconv.Atoi(usc[1])
contextInt, _ := strconv.Atoi(usc[2])

uscStruct := UserCommand{
Type: UserCommandType(typeInt),
Context: UserCommandContext(contextInt),
Message: usc[3],
Command: this.unescape(usc[4]),
}

this.hc.processEvent(HubEvent{EventType: EVENT_USERCOMMAND, UserCommand: &uscStruct})

} else {
this.hc.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: "Malformed usercommand '" + commandParts[1] + "'"})
}

case "$Supports":
this.supports = make(map[string]struct{})
for _, s := range strings.Split(commandParts[1], " ") {
this.supports[s] = struct{}{}
}

if !this.sentOurHello {

// Need to log in.
// If the hub supports QuickList, we can skip one network roundtrip
if _, ok := this.supports["QuickList"]; ok {
this.sayInfo()
this.hc.SayRaw("$GetNickList|")
} else {
this.hc.SayRaw("$ValidateNick " + this.escape(this.hc.Hco.Self.Nick) + "|")
}

// This also counts as the end of the handshake from our POV. Consider
// ourselves logged in
this.sentOurHello = true
if this.hc.State != CONNECTIONSTATE_CONNECTED {
this.hc.processEvent(HubEvent{EventType: EVENT_CONNECTION_STATE_CHANGED, StateChange: CONNECTIONSTATE_CONNECTED})
this.hc.State = CONNECTIONSTATE_CONNECTED
}

}

// IGNORABLE COMMANDS
case "$HubTopic":
case "$Search":
case "$ConnectToMe":

default:
this.hc.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: "Unhandled protocol command '" + commandParts[0] + "'"})

}
}

func (this *NmdcProtocol) escape(plaintext string) string {
v1 := strings.Replace(plaintext, "&", "&amp;", -1)
v2 := strings.Replace(v1, "|", "&#124;", -1)
return strings.Replace(v2, "$", "&#36;", -1)
}

func (this *NmdcProtocol) unescape(encoded string) string {
v1 := strings.Replace(encoded, "&#36;", "$", -1)
v2 := strings.Replace(v1, "&#124;", "|", -1)
return strings.Replace(v2, "&amp;", "&", -1)
}

func (this *NmdcProtocol) SayPublic(message string) {
this.hc.SayRaw("<" + this.hc.Hco.Self.Nick + "> " + this.escape(message) + "|")
}

func (this *NmdcProtocol) SayPrivate(recipient, message string) {
this.hc.SayRaw("$To: " + recipient + " From: " + this.hc.Hco.Self.Nick + " $<" + this.hc.Hco.Self.Nick + "> " + this.escape(message) + "|")
}

func (this *NmdcProtocol) sayInfo() {
this.hc.SayRaw(this.getUserMyINFO(this.hc.Hco.Self) + "|")
}

func (this *NmdcProtocol) parseMyINFO(protomsg string) (*UserInfo, error) {
ret := UserInfo{}

// Normal format (with tag in exact V/M/H/S order)
matches := this.rxMyInfo.FindStringSubmatch(protomsg)
if matches != nil {
ret.Nick = matches[1]
ret.Description = this.unescape(matches[2])
ret.ClientTag = this.unescape(matches[3])
ret.ClientVersion = matches[4]
ret.ConnectionMode = ConnectionMode(matches[5][0])
maybeParse(matches[6], &ret.HubsUnregistered, 0)
maybeParse(matches[7], &ret.HubsRegistered, 0)
maybeParse(matches[8], &ret.HubsOperator, 0)
maybeParse(matches[9], &ret.Slots, 0)
if len(matches[10]) > 1 {
ret.Speed = matches[10][:len(matches[10])-2]
} else {
ret.Speed = ""
}
ret.Flag = UserFlag(matches[10][len(matches[10])-1])
ret.Email = this.unescape(matches[11])
maybeParse(matches[12], &ret.ShareSize, 0)

return &ret, nil
}

// No-tag format, used in early connection
matches = this.rxMyInfoNoTag.FindStringSubmatch(protomsg)
if matches != nil {
ret.Nick = matches[1]
ret.Description = this.unescape(matches[2])
ret.ClientTag = ""
ret.ClientVersion = "0"
ret.ConnectionMode = CONNECTIONMODE_PASSIVE
ret.HubsUnregistered = 0
ret.HubsRegistered = 0
ret.HubsOperator = 0
ret.Slots = 0

if len(matches[3]) > 1 {
ret.Speed = matches[3][:len(matches[3])-2]
} else {
ret.Speed = ""
}
ret.Flag = UserFlag(matches[3][len(matches[3])-1])
ret.Email = this.unescape(matches[4])
maybeParse(matches[5], &ret.ShareSize, 0)

return &ret, nil
}

// Couldn't get anything out of it...
return nil, errors.New("Malformed MyINFO")
}

// Returns the MyINFO command, WITH leading $MyINFO, and WITHOUT trailing pipe
func (this *NmdcProtocol) getUserMyINFO(u *UserInfo) string {
return fmt.Sprintf(
"$MyINFO $ALL %s %s<%s V:%s,M:%c,H:%d/%d/%d,S:%d>$ $%s%c$%s$%d$",
u.Nick,
u.Description,
u.ClientTag,
strings.Replace(u.ClientVersion, ",", "-", -1), // just in case
u.ConnectionMode,
u.HubsUnregistered,
u.HubsRegistered,
u.HubsOperator,
u.Slots,
u.Speed,
u.Flag,
u.Email,
u.ShareSize,
)
}

func (this *NmdcProtocol) ProtoMessageSeparator() string {
return "|"
}

func (this *NmdcProtocol) unlock(lock []byte) string {

nibble_swap := func(b byte) byte {
return ((b << 4) & 0xF0) | ((b >> 4) & 0x0F)
}

chr := func(b byte) string {
if b == 0 || b == 5 || b == 36 || b == 96 || b == 124 || b == 126 {
return fmt.Sprintf("/%%DCN%04d%%/", b)
} else {
return string(b)
}
}

key := chr(nibble_swap(lock[0] ^ lock[len(lock)-2] ^ lock[len(lock)-3] ^ 5))
for i := 1; i < len(lock); i += 1 {
key += chr(nibble_swap(lock[i] ^ lock[i-1]))
}

return key
}

+ 97
- 0
vendor/code.ivysaur.me/libnmdc/NmdcProtocol_test.go View File

@@ -0,0 +1,97 @@
package libnmdc

import (
"testing"
)

func TestMyINFOParse(t *testing.T) {

np := NewNmdcProtocol(nil).(*NmdcProtocol)

type myInfoTestPair struct {
in string
expect UserInfo
}

cases := []myInfoTestPair{

myInfoTestPair{
in: "$ALL Bxxxy description<ApexDC++ V:1.4.3,M:P,H:9/0/2,S:1>$ $0.01\x01$xyz@example.com$53054999578$",
expect: UserInfo{
Nick: "Bxxxy",
Description: "description",
ClientTag: "ApexDC++",
ClientVersion: "1.4.3",
Email: "xyz@example.com",
ShareSize: 53054999578,
Flag: FLAG_NORMAL,
Slots: 1,
HubsUnregistered: 9,
HubsRegistered: 0,
HubsOperator: 2,
UserInfo_NMDCOnly: UserInfo_NMDCOnly{
ConnectionMode: CONNECTIONMODE_PASSIVE,
Speed: "0.0",
},
},
},
myInfoTestPair{
in: "$ALL ixxxxxxx0 $P$10A$$0$",
expect: UserInfo{
Nick: "ixxxxxxx0",
ClientVersion: "0", // Auto-inserted by the parser for short-format MyINFO strings
Flag: UserFlag(rune('A')),
UserInfo_NMDCOnly: UserInfo_NMDCOnly{
ConnectionMode: CONNECTIONMODE_PASSIVE,
Speed: "1",
},
},
},
myInfoTestPair{
in: "$ALL SXXXX_XXXXXXR <ncdc V:1.19.1-12-g5561,M:P,H:1/0/0,S:10>$ $0.005Q$$0$",
expect: UserInfo{
Nick: "SXXXX_XXXXXXR",
ClientTag: "ncdc",
ClientVersion: "1.19.1-12-g5561",
Flag: UserFlag(rune('Q')),
Slots: 10,
HubsUnregistered: 1,
UserInfo_NMDCOnly: UserInfo_NMDCOnly{
ConnectionMode: CONNECTIONMODE_PASSIVE,
Speed: "0.00",
},
},
},
myInfoTestPair{
in: "$ALL mxxxu desccccc<HexChat V:2.12.1,M:P,H:1/0/0,S:0>$ $p$$0$",
expect: UserInfo{
Nick: "mxxxu",
Description: "desccccc",
ClientTag: "HexChat",
ClientVersion: "2.12.1",
Flag: UserFlag(rune('p')),
HubsUnregistered: 1,
Slots: 0,
UserInfo_NMDCOnly: UserInfo_NMDCOnly{
ConnectionMode: CONNECTIONMODE_PASSIVE,
},
},
},
}

for _, v := range cases {

got, err := np.parseMyINFO(v.in)

if err != nil {
t.Errorf("MyINFO parse warning (%s)", err.Error())
continue
}

if *got != v.expect {
t.Errorf("MyINFO parse failure\nExpected:\n%+v\nGot:\n%+v\n", v.expect, got)
continue
}
}

}

+ 31
- 0
vendor/code.ivysaur.me/libnmdc/Protocol.go View File

@@ -0,0 +1,31 @@
package libnmdc

type Protocol interface {
ProcessCommand(msg string)

SayPublic(string)

SayPrivate(user, message string)

ProtoMessageSeparator() string
}

type HubProtocol int

const (
HubProtocolAutodetect HubProtocol = 0
HubProtocolNmdc HubProtocol = 1
HubProtocolAdc HubProtocol = 2
)

func (hp HubProtocol) Create(hc *HubConnection) Protocol {
if hp == HubProtocolNmdc {
return NewNmdcProtocol(hc)

} else if hp == HubProtocolAdc {
return NewAdcProtocol(hc)

} else {
return NewAutodetectProtocol(hc)
}
}

+ 6
- 0
vendor/code.ivysaur.me/libnmdc/TODO.txt View File

@@ -0,0 +1,6 @@
NMDC:
- Implement ZPipe ($ZOn)

ADC:
- Usercommands
- ???

+ 27
- 0
vendor/code.ivysaur.me/libnmdc/UserCommand.go View File

@@ -0,0 +1,27 @@
package libnmdc

type UserCommandType uint8

const (
USERCOMMAND_TYPE_SEPARATOR UserCommandType = 0
USERCOMMAND_TYPE_RAW UserCommandType = 1
USERCOMMAND_TYPE_NICKLIMITED UserCommandType = 2
USERCOMMAND_TYPE_CLEARALL UserCommandType = 255
)

type UserCommandContext uint8

const (
USERCOMMAND_CONTEXT_HUB UserCommandContext = 1
USERCOMMAND_CONTEXT_USER UserCommandContext = 2
USERCOMMAND_CONTEXT_SEARCH UserCommandContext = 4
USERCOMMAND_CONTEXT_FILELIST UserCommandContext = 8
)

type UserCommand struct {
Type UserCommandType
Context UserCommandContext
Message string
Command string
RemoveThis bool // Currently only set by ADC hubs
}

+ 17
- 0
vendor/code.ivysaur.me/libnmdc/UserFlag.go View File

@@ -0,0 +1,17 @@
package libnmdc

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
)

+ 53
- 0
vendor/code.ivysaur.me/libnmdc/UserInfo.go View File

@@ -0,0 +1,53 @@
package libnmdc

// This structure represents a user connected to a hub.
type UserInfo struct {
Nick string
Description string
ClientTag string
ClientVersion string
Email string
ShareSize uint64
Flag UserFlag
Slots uint64
HubsUnregistered uint64
HubsRegistered uint64
HubsOperator uint64
IsOperator bool

UserInfo_NMDCOnly
UserInfo_ADCOnly
}

type UserInfo_NMDCOnly struct {
Speed string
IPAddress string
ConnectionMode ConnectionMode
}

type UserInfo_ADCOnly struct {
SharedFiles uint64
UploadSpeedBps uint64
DownloadSpeedBps uint64
IsBot bool
IsRegistered bool
IsSuperUser bool
IsHubOwner bool
IPv4Address string // Passive <==> these fields are not set
IPv6Address string
IPv4UDPPort uint64
IPv6UDPPort uint64
Keyprint string
CID string
SupportFlags map[string]struct{}
}

func NewUserInfo(username string) *UserInfo {
return &UserInfo{
Nick: username,
HubsUnregistered: 1,
UserInfo_NMDCOnly: UserInfo_NMDCOnly{
ConnectionMode: CONNECTIONMODE_PASSIVE,
},
}
}

+ 98
- 0
vendor/code.ivysaur.me/libnmdc/__dist/README.txt View File

@@ -0,0 +1,98 @@
An NMDC / ADC client protocol library for Golang.

Written in golang
Tags: nmdc

=FEATURES=

- Connect to NMDC and ADC hubs
- SSL (NMDCS/ADCS) with option to ignore certificate validity
- Autodetect NMDC/ADC protocol by timeout
- Send public and private chat messages, UserCommand support
- Protocol keepalives
- Parse user details (including UserIP2 for NMDC)
- Fast NMDC login via NoHello and QuickList
- Both synchronous (callback) and asynchronous (channel) -based APIs, including example

=GO GET=

This package can be installed via go get: `go get code.ivysaur.me/libnmdc`
[go-get]code.ivysaur.me/libnmdc git https://git.ivysaur.me/code.ivysaur.me/libnmdc.git[/go-get]

=CHANGELOG=

2017-11-26 0.16
- Feature: Support connecting to ADC hubs
- BREAKING: Simplify connection API
- Vendor new dependency on github.com/cxmcc/tiger (MIT license)

2017-11-14 0.15
- Feature: Fallback reconnection if no data (not even keepalives) are recieved from the hub in 24 hours
- Fix an issue with detecting protocol messages inside multi-line chat messages
- Update examples and the default client version number

2017-02-09 0.14
- Fix an issue with crashing on malformed IP addresses supplied by the hub