Compare commits
No commits in common. "master" and "v1.0.0" have entirely different histories.
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
_dist/
|
|
142
README.md
142
README.md
@ -1,142 +0,0 @@
|
|||||||
# nmdc-ircfrontend
|
|
||||||
|
|
||||||
An IRC protocol frontend for an NMDC server.
|
|
||||||
|
|
||||||
This program 'wraps' an NMDC server to make it available to IRC clients. It implements the IRC server protocol, but is not itself an IRC server, deferring to the upstream NMDC server in all salient cases. From a layering perspective it occupies the same position as the web interface.
|
|
||||||
|
|
||||||
The intent of this project is to expand client library and application support on platforms where the NMDC community has not been able to spend effort (e.g. Apple iOS client, ruby/rust/erlang libraries, greater choice in web interfaces).
|
|
||||||
|
|
||||||
TLS (SSL) support is not integrated. To host the IRC frontend over TLS, please use a reverse proxy in much the same manner as for NMDCS.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
This program uses some code from the AGPLv3 project https://github.com/eXeC64/Rosella , from which it inherits the Affero GPLv3 license. Anyone hosting a modified version of this software is required to release their changes under the terms of the AGPLv3.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- Seamless roundtrip transition between NMDC `/me` and IRC `CTCP ACTION`
|
|
||||||
- IRC client's "Real Name" exposed as NMDC Description
|
|
||||||
- IRC client's `CTCP VERSION` exposed as NMDC client tag, with heuristic truncation
|
|
||||||
- NMDC server's title exposed as IRC channel topic
|
|
||||||
- Login with passworded nick (classic `PASS`, not `SASL`)
|
|
||||||
- Full nick list integration including op status
|
|
||||||
- Support IRC client changing nick via NMDC upstream reconnection
|
|
||||||
- Automatic join to single-enforced chatroom
|
|
||||||
- Multithreaded
|
|
||||||
- Single binary deployment
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```
|
|
||||||
Usage of nmdc-ircfrontend:
|
|
||||||
-autojoin
|
|
||||||
Automatically join clients to the channel (default true)
|
|
||||||
-bind string
|
|
||||||
The address:port to bind to and listen for clients on (default ":6667")
|
|
||||||
-hubsecurity string
|
|
||||||
Nick used for administrative events (default "Hub-Security")
|
|
||||||
-servername string
|
|
||||||
Server name displayed to clients (default "nmdc-ircfrontend")
|
|
||||||
-upstream string
|
|
||||||
Upstream NMDC/ADC server (default "127.0.0.1:411")
|
|
||||||
-verbose
|
|
||||||
Display debugging information
|
|
||||||
```
|
|
||||||
|
|
||||||
## Compatibility
|
|
||||||
|
|
||||||
*This section was last updated on or around the release of 1.3.0. Current compatibility may differ.*
|
|
||||||
|
|
||||||
NMDC's smaller community has standardised around comparatively few protocol implementations by means of necessity. In comparison, there are a lot of IRC client implementations with slightly differing interpretations of the protocol.
|
|
||||||
|
|
||||||
For passworded DC usernames, your IRC PASS username must be the same as your IRC nickname.
|
|
||||||
|
|
||||||
Tested working:
|
|
||||||
- AndChat
|
|
||||||
- AndroIRC
|
|
||||||
- Atomic
|
|
||||||
- Hexchat
|
|
||||||
- HoloIRC
|
|
||||||
- Irssi
|
|
||||||
- LiteIRC
|
|
||||||
- Mango IRC
|
|
||||||
- mIRC 7
|
|
||||||
- Mutter
|
|
||||||
- Revolution IRC
|
|
||||||
- Weechat
|
|
||||||
- Yaaic
|
|
||||||
|
|
||||||
Unusable:
|
|
||||||
- Rocket.chat 0.6x IRC plugin (can join room and recieve PMs after some regex tweaking)
|
|
||||||
|
|
||||||
## Changelog
|
|
||||||
|
|
||||||
2018-03-24 1.3.0
|
|
||||||
- Feature: Support ADC hubs
|
|
||||||
- Compatibility: Fix missing userlist in LiteIRC (since 1.2.2)
|
|
||||||
- Compatibility: Avoid sending empty room list (fixes Yaaic, atomic, Revolution IRC)
|
|
||||||
- Update libnmdc to 0.17
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.3.0-win64.7z](https://git.ivysaur.me/attachments/5da75934-863b-4476-86e3-c7da219ee93b) *(818.71 KiB)*
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.3.0-win32.7z](https://git.ivysaur.me/attachments/7d5b2f9e-6f82-42be-b3ac-5473817cd662) *(766.17 KiB)*
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.3.0-src.zip](https://git.ivysaur.me/attachments/25b14ccb-82ae-402c-8110-6026b086f4aa) *(23.00 KiB)*
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.3.0-linux64.tar.xz](https://git.ivysaur.me/attachments/8f4f7592-7070-459f-a2ff-4db910433b60) *(937.04 KiB)*
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.3.0-linux32.tar.xz](https://git.ivysaur.me/attachments/3ea281d7-2e04-484c-8f0f-70ecfb2918ae) *(876.77 KiB)*
|
|
||||||
|
|
||||||
2017-05-28 1.2.3
|
|
||||||
- Fix a regression with userlist display on HexChat (other IRC client compatibility unknown)
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.2.3-win64.7z](https://git.ivysaur.me/attachments/19b20903-f173-498a-81bc-e87ab8c22a07) *(751.55 KiB)*
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.2.3-win32.7z](https://git.ivysaur.me/attachments/30f0a2b7-1f7a-4d15-b70e-bd52f3dde93a) *(687.16 KiB)*
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.2.3-src.zip](https://git.ivysaur.me/attachments/55a73ae2-96db-4c20-9185-4ce38617053c) *(21.03 KiB)*
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.2.3-linux64.tar.xz](https://git.ivysaur.me/attachments/ee6992f1-897f-4c8d-bb29-4b9c2a4330df) *(865.07 KiB)*
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.2.3-linux32.tar.xz](https://git.ivysaur.me/attachments/82f9260c-cfe6-49ea-9a5a-8d6463a399d9) *(799.88 KiB)*
|
|
||||||
|
|
||||||
2017-05-27 1.2.2
|
|
||||||
- Update libnmdc to 0.14
|
|
||||||
- Fix a crash that could occur if the server is scanned by a non-irc client
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.2.2-win64.7z](https://git.ivysaur.me/attachments/92ff9cdf-01a7-41bf-82c1-150295509df0) *(751.37 KiB)*
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.2.2-win32.7z](https://git.ivysaur.me/attachments/450cae51-4d30-46fc-8631-682bbf570128) *(686.93 KiB)*
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.2.2-src.zip](https://git.ivysaur.me/attachments/a70426b2-d34f-4eef-87a9-3541f28eb04d) *(19.78 KiB)*
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.2.2-linux64.tar.xz](https://git.ivysaur.me/attachments/d2a2c832-7c1f-4319-bd10-0b7718b56146) *(865.31 KiB)*
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.2.2-linux32.tar.xz](https://git.ivysaur.me/attachments/1ce22001-dc7f-495b-b384-0e3089392e18) *(799.69 KiB)*
|
|
||||||
|
|
||||||
2016-11-29 1.2.1
|
|
||||||
- Update libnmdc to 0.11
|
|
||||||
- Fix an issue with -devel version tag in 1.2.0 release binaries
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.2.1-win64.7z](https://git.ivysaur.me/attachments/a2c89ef5-36c1-41fb-a724-b6665c7ac226) *(750.20 KiB)*
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.2.1-win32.7z](https://git.ivysaur.me/attachments/3648db7b-12cc-4496-9f0b-ec3dada1a8ff) *(685.56 KiB)*
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.2.1-src.zip](https://git.ivysaur.me/attachments/97b49b6c-3c36-4eb2-8ca6-b4c9b78ed876) *(20.88 KiB)*
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.2.1-linux64.tar.xz](https://git.ivysaur.me/attachments/a52a32b1-aca2-4471-99cc-71517e32461f) *(855.09 KiB)*
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.2.1-linux32.tar.xz](https://git.ivysaur.me/attachments/9b46ee3f-b9d3-4a80-b434-f07952669728) *(798.09 KiB)*
|
|
||||||
|
|
||||||
2016-08-27 1.2.0
|
|
||||||
- Feature: Support WHOIS (display NMDC user's description + client software)
|
|
||||||
- Feature: `-version` command-line option
|
|
||||||
- Compatibility: Demote 'Lite IRC' to 'Usable with bugs' section
|
|
||||||
- Update libnmdc to r9 (fix protocol issues)
|
|
||||||
- Update golang to 1.7 (smaller binary size)
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.2.0-win64.7z](https://git.ivysaur.me/attachments/9c35784f-be38-4796-bef1-7fb9a370c2a1) *(747.07 KiB)*
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.2.0-win32.7z](https://git.ivysaur.me/attachments/f49ff51b-1981-4e26-ad87-80def4a5cd57) *(682.37 KiB)*
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.2.0-src.zip](https://git.ivysaur.me/attachments/921acc9b-1df9-4445-8192-04effa94cb87) *(20.84 KiB)*
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.2.0-linux64.tar.xz](https://git.ivysaur.me/attachments/38bdc89f-3519-4217-8950-c13366c1e9c2) *(851.35 KiB)*
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.2.0-linux32.tar.xz](https://git.ivysaur.me/attachments/1258ef99-e598-4a26-b287-83d0e2cd76cc) *(794.09 KiB)*
|
|
||||||
|
|
||||||
2016-05-10 1.1.0
|
|
||||||
- Feature: Support renaming own client during connection (`/nick`)
|
|
||||||
- Enhancement: Option to set Hub-Security nick (needed for initial CTCP, upgraded after upstream connection)
|
|
||||||
- Compatibility: Apply user-agent-specific hacks for mIRC and Atomic clients not nicely handling PRIVMSG with blank sender
|
|
||||||
- Compatibility: Better heuristic detection for client tag extraction
|
|
||||||
- Fix an issue causing the default client tag (nmdc-ircfrontend) to briefly appear in the NMDC user list, by (briefly) delaying the upstream join unless CTCP VERSION response comes in
|
|
||||||
- Fix an issue generating malformed version numbers in NMDC client tag
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.1.0-win64.7z](https://git.ivysaur.me/attachments/ab9a32da-cff7-4de9-91d5-fcb7244646c2) *(896.08 KiB)*
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.1.0-win32.7z](https://git.ivysaur.me/attachments/032a195f-585d-4d85-a904-1f5c4556ac75) *(819.77 KiB)*
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.1.0-src.zip](https://git.ivysaur.me/attachments/61ae2b92-6502-419b-9d73-79733a820a75) *(20.46 KiB)*
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.1.0-linux64.tar.xz](https://git.ivysaur.me/attachments/dd4a073d-fe01-4c5d-9eac-27d7c580931c) *(989.59 KiB)*
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.1.0-linux32.tar.xz](https://git.ivysaur.me/attachments/de5e5bdd-2407-4765-917c-6d7c5df40d82) *(924.09 KiB)*
|
|
||||||
|
|
||||||
2016-05-08 1.0.0
|
|
||||||
- Initial public release
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.0.0-win64.7z](https://git.ivysaur.me/attachments/a6f42ccb-6b62-408b-af25-7dcfa73d669c) *(890.16 KiB)*
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.0.0-win32.7z](https://git.ivysaur.me/attachments/b1735675-bbad-473f-9141-c890cac71471) *(817.71 KiB)*
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.0.0-src.zip](https://git.ivysaur.me/attachments/64c2a8a7-5b7e-46f5-8f30-959f665b616f) *(20.98 KiB)*
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.0.0-linux64.tar.xz](https://git.ivysaur.me/attachments/0f862b24-f0bb-41a6-bdea-2d27b95a1d00) *(987.32 KiB)*
|
|
||||||
- [⬇️ nmdc-ircfrontend-1.0.0-linux32.tar.xz](https://git.ivysaur.me/attachments/84471f39-bc5a-47ed-a2ab-0eab66c24ee3) *(922.65 KiB)*
|
|
63
README.txt
Normal file
63
README.txt
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
An IRC protocol frontend for an NMDC server.
|
||||||
|
|
||||||
|
This program 'wraps' an NMDC server to make it available to IRC clients. It implements the IRC server protocol, but is not itself an IRC server, deferring to the upstream NMDC server in all salient cases. From a layering perspective it occupies the same position as the web interface.
|
||||||
|
|
||||||
|
The intent of this project is to expand client library and application support on platforms where the NMDC community has not been able to spend effort (e.g. Apple iOS client, ruby/rust/erlang libraries, greater choice in web interfaces).
|
||||||
|
|
||||||
|
TLS (SSL) support is not integrated. To host the IRC frontend over TLS, please use a reverse proxy in much the same manner as for NMDCS.
|
||||||
|
|
||||||
|
This program uses some code from the AGPLv3 project https://github.com/eXeC64/Rosella , from which it inherits the Affero GPLv3 license. Anyone hosting a modified version of this software is required to release their changes under the terms of the AGPLv3.
|
||||||
|
|
||||||
|
Tags: NMDC AGPLv3
|
||||||
|
|
||||||
|
=FEATURES=
|
||||||
|
|
||||||
|
- Seamless roundtrip transition between NMDC `/me` and IRC `CTCP ACTION`
|
||||||
|
- IRC client's "Real Name" exposed as NMDC Description
|
||||||
|
- IRC client's `CTCP VERSION` exposed as NMDC client tag, with heuristic truncation
|
||||||
|
- NMDC server's title exposed as IRC channel topic
|
||||||
|
- Login with passworded nick (classic `PASS`, not `SASL`)
|
||||||
|
- Full nick list integration including op status
|
||||||
|
- Automatic join to single-enforced chatroom
|
||||||
|
- Multithreaded
|
||||||
|
- Single binary deployment
|
||||||
|
|
||||||
|
=USAGE=
|
||||||
|
|
||||||
|
`Usage of nmdc-ircfrontend:
|
||||||
|
-autojoin
|
||||||
|
Automatically join clients to the channel (default true)
|
||||||
|
-bind string
|
||||||
|
The address:port to bind to and listen for clients on (default ":6667")
|
||||||
|
-servername string
|
||||||
|
Server name displayed to clients (default "nmdc-ircfrontend")
|
||||||
|
-upstream string
|
||||||
|
Upstream NMDC server (default "127.0.0.1:411")
|
||||||
|
-verbose
|
||||||
|
Display debugging information`
|
||||||
|
|
||||||
|
=COMPATIBILITY=
|
||||||
|
|
||||||
|
NMDC's smaller community has standardised around comparatively few protocol implementations by means of necessity. In comparison, there are a lot of IRC client implementations with slightly differing interpretations of the protocol.
|
||||||
|
|
||||||
|
[b]Everything works:[/b]
|
||||||
|
- Hexchat
|
||||||
|
- Mango IRC
|
||||||
|
- AndroIRC
|
||||||
|
- Lite IRC
|
||||||
|
- Mutter
|
||||||
|
- Weechat
|
||||||
|
|
||||||
|
[b]Usable, with bugs:[/b]
|
||||||
|
- HoloIRC - Can't parse client tag, upstream bug https://github.com/tilal6991/HoloIRC/issues/140
|
||||||
|
- AndChat - Duplicate usernames appear, upstream bug https://github.com/znc/znc/issues/424
|
||||||
|
- Irssi - Ignorable warning "critical nicklist_set_host: assertion 'host != NULL' failed"
|
||||||
|
|
||||||
|
[b]Unusable:[/b]
|
||||||
|
- Yaaic - doesn't properly understand/parse the room join
|
||||||
|
- Atomic - doesn't properly understand/parse the room join, crashes on `PRIVMSG` with blank sender
|
||||||
|
|
||||||
|
=CHANGELOG=
|
||||||
|
|
||||||
|
2016-05-08 1.0.0
|
||||||
|
- Initial public release
|
23
REFERENCE.md
23
REFERENCE.md
@ -1,23 +0,0 @@
|
|||||||
# References
|
|
||||||
|
|
||||||
https://www.alien.net.au/irc/irc2numerics.html
|
|
||||||
|
|
||||||
http://faerion.sourceforge.net/doc/irc/whox.var
|
|
||||||
|
|
||||||
http://www.anta.net/misc/telnet-troubleshooting/irc.shtml
|
|
||||||
|
|
||||||
https://tools.ietf.org/html/rfc2812
|
|
||||||
|
|
||||||
http://www.irchelp.org/irchelp/rfc/ctcpspec.html
|
|
||||||
|
|
||||||
https://en.wikipedia.org/wiki/List_of_Internet_Relay_Chat_commands
|
|
||||||
|
|
||||||
https://en.wikipedia.org/wiki/Client-to-client_protocol
|
|
||||||
|
|
||||||
https://wiki.mibbit.com/index.php/Ctcp_(version)
|
|
||||||
|
|
||||||
https://github.com/eXeC64/Rosella AGPLv3 license
|
|
||||||
|
|
||||||
https://github.com/edmund-huber/ergonomadic MIT license
|
|
||||||
|
|
||||||
http://en.wikichip.org/wiki/irc/colors
|
|
61
TODO.txt
Normal file
61
TODO.txt
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
|
||||||
|
WISHLIST
|
||||||
|
========
|
||||||
|
|
||||||
|
- verify PING responses (application-layer timeout protocol)
|
||||||
|
|
||||||
|
- automatic markdown bold/italic formatting
|
||||||
|
|
||||||
|
- support changing nick (via reconnecting)
|
||||||
|
|
||||||
|
- support WHOIS
|
||||||
|
|
||||||
|
- respond to CTCP VERSION with the clienttag on behalf of other users
|
||||||
|
|
||||||
|
- support USERIP/KILL/KICK for ops
|
||||||
|
|
||||||
|
- use CTCP chat to support irc-special characters in chat messages (colon, newline)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
WONTFIX
|
||||||
|
=======
|
||||||
|
|
||||||
|
There's no additional client compatibility gained by implementing the following
|
||||||
|
|
||||||
|
- support USERHOST (used by androirc)
|
||||||
|
2016/05/08 13:43:55 >>> 'USERHOST' [squirtle90]
|
||||||
|
2016/05/08 13:43:55 <<< :pokehub.info 421 squirtle90 USERHOST :Unknown command
|
||||||
|
|
||||||
|
- support WHOX
|
||||||
|
|
||||||
|
- support SASL PLAIN authentication as well as just classic PASS
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
REF
|
||||||
|
===
|
||||||
|
|
||||||
|
https://www.alien.net.au/irc/irc2numerics.html
|
||||||
|
|
||||||
|
http://faerion.sourceforge.net/doc/irc/whox.var
|
||||||
|
|
||||||
|
http://www.anta.net/misc/telnet-troubleshooting/irc.shtml
|
||||||
|
|
||||||
|
https://tools.ietf.org/html/rfc2812
|
||||||
|
|
||||||
|
http://www.irchelp.org/irchelp/rfc/ctcpspec.html
|
||||||
|
|
||||||
|
https://en.wikipedia.org/wiki/List_of_Internet_Relay_Chat_commands
|
||||||
|
|
||||||
|
https://en.wikipedia.org/wiki/Client-to-client_protocol
|
||||||
|
|
||||||
|
https://wiki.mibbit.com/index.php/Ctcp_(version)
|
||||||
|
|
||||||
|
https://github.com/eXeC64/Rosella AGPLv3 license
|
||||||
|
|
||||||
|
https://github.com/edmund-huber/ergonomadic MIT license
|
||||||
|
|
||||||
|
http://en.wikichip.org/wiki/irc/colors
|
@ -1,33 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
rx_colorControlCode *regexp.Regexp = regexp.MustCompilePOSIX("\x03[0-9]+(,[0-9]+)?")
|
|
||||||
rx_formattingControlCode *regexp.Regexp = regexp.MustCompile("(\x02|\x1D|\x1F|\x16|\x0F)")
|
|
||||||
)
|
|
||||||
|
|
||||||
func reformatOutgoingMessageBody(body string) string {
|
|
||||||
|
|
||||||
// Strip color codes
|
|
||||||
noColors := rx_colorControlCode.ReplaceAllString(body, "")
|
|
||||||
|
|
||||||
// Strip formatting codes
|
|
||||||
return rx_formattingControlCode.ReplaceAllString(noColors, "")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func reformatIncomingMessageBody(body string) string {
|
|
||||||
|
|
||||||
// "Greentext" filter
|
|
||||||
// TODO markdown filters
|
|
||||||
if len(body) > 0 && body[0] == '>' {
|
|
||||||
return "\x033" + strings.Replace(body, "implying", "\x02implying\x02", -1)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
return body
|
|
||||||
}
|
|
||||||
}
|
|
68
clientTag.go
68
clientTag.go
@ -1,68 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type clientTag struct {
|
|
||||||
AppName string
|
|
||||||
Version string
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
rx_bestNumberPart = regexp.MustCompile(`[0-9\.]+`)
|
|
||||||
)
|
|
||||||
|
|
||||||
func parseVersion(ver string) clientTag {
|
|
||||||
// Try our best to turn the supplied text into a structured version
|
|
||||||
ret := clientTag{
|
|
||||||
AppName: APP_NAME,
|
|
||||||
Version: APP_VERSION,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Special case: Some clients use a structured version AppName:Version:Metadata
|
|
||||||
// If we check for that, we can support clients with spaces in the name
|
|
||||||
if cParts := strings.Split(ver, ":"); len(cParts) == 3 {
|
|
||||||
ret.AppName = cParts[0]
|
|
||||||
ret.Version = cParts[1]
|
|
||||||
|
|
||||||
} else if ver == "Atomic - An IRC client for Android https://indrora.github.io/Atomic" {
|
|
||||||
ret.AppName = "Atomic"
|
|
||||||
ret.Version = "2.1" // Abandonware. Last available version
|
|
||||||
|
|
||||||
} else if ver == "Yaaic - Yet Another Android IRC Client - http://www.yaaic.org" {
|
|
||||||
ret.AppName = "Yaaic"
|
|
||||||
ret.Version = "1.1" // Abandonware. Last available version
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// Special cases all failed, time for heuristics
|
|
||||||
// Turn colons to spaces; keep the first word, and the the first word containing digits
|
|
||||||
ver = strings.Trim(strings.Replace(ver, ":", " ", -1), " ")
|
|
||||||
|
|
||||||
words := strings.Split(ver, " ")
|
|
||||||
|
|
||||||
for _, word := range words[1:] {
|
|
||||||
if strings.ContainsAny(word, "0123456789") {
|
|
||||||
ret.AppName = words[0]
|
|
||||||
ret.Version = strings.Replace(word, "(", "", -1) // AndroIRC
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We only support digits, periods, and hyphens in the number part
|
|
||||||
// This removes the leading v from mIRC and the trailing deb** from irssi
|
|
||||||
if submatch := rx_bestNumberPart.FindStringSubmatch(ret.Version); len(submatch) == 1 {
|
|
||||||
ret.Version = submatch[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Special case: "Relay" means "HoloIRC"
|
|
||||||
if ret.AppName == "Relay" && ret.Version == "1.0" && strings.Contains(ver, "Android") {
|
|
||||||
ret.AppName = "HoloIRC"
|
|
||||||
ret.Version = "4"
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
@ -1,73 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestParseVersion(t *testing.T) {
|
|
||||||
|
|
||||||
tests := [][3]string{
|
|
||||||
|
|
||||||
[3]string{
|
|
||||||
"HexChat 2.12.1 [x64] / Microsoft Windows 10 Pro (x64) [Intel(R) Core(TM) i5-2500 CPU @ 3.30GHz (3.60GHz)]",
|
|
||||||
"HexChat", "2.12.1",
|
|
||||||
},
|
|
||||||
|
|
||||||
[3]string{
|
|
||||||
"AndroIRC - Android IRC Client (5.2 - Build 6830152) - http://www.androirc.com",
|
|
||||||
"AndroIRC", "5.2",
|
|
||||||
},
|
|
||||||
|
|
||||||
[3]string{
|
|
||||||
"AndChat 1.4.3.2 http://www.andchat.ne",
|
|
||||||
"AndChat", "1.4.3.2",
|
|
||||||
},
|
|
||||||
|
|
||||||
[3]string{
|
|
||||||
"liteIRC for Android 1.1.8",
|
|
||||||
"liteIRC", "1.1.8",
|
|
||||||
},
|
|
||||||
|
|
||||||
[3]string{
|
|
||||||
":Relay:1.0:Android", // HoloIRC before 4.1.0
|
|
||||||
"HoloIRC", "4",
|
|
||||||
},
|
|
||||||
|
|
||||||
[3]string{
|
|
||||||
"Relay:1.0:Android", // HoloIRC after 4.1.0
|
|
||||||
"HoloIRC", "4",
|
|
||||||
},
|
|
||||||
|
|
||||||
[3]string{
|
|
||||||
"mIRC v7.45",
|
|
||||||
"mIRC", "7.45",
|
|
||||||
},
|
|
||||||
|
|
||||||
[3]string{
|
|
||||||
"Revolution IRC:0.3.2:Android",
|
|
||||||
"Revolution IRC", "0.3.2",
|
|
||||||
},
|
|
||||||
|
|
||||||
[3]string{
|
|
||||||
"irssi v1.0.2-1+deb9u3",
|
|
||||||
"irssi", "1.0.2",
|
|
||||||
},
|
|
||||||
|
|
||||||
[3]string{
|
|
||||||
"Atomic - An IRC client for Android https://indrora.github.io/Atomic",
|
|
||||||
"Atomic", "2.1",
|
|
||||||
},
|
|
||||||
|
|
||||||
[3]string{
|
|
||||||
"Yaaic - Yet Another Android IRC Client - http://www.yaaic.org",
|
|
||||||
"Yaaic", "1.1",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
result := parseVersion(test[0])
|
|
||||||
if result.AppName != test[1] || result.Version != test[2] {
|
|
||||||
t.Fatalf("Got <%s,%s> expecting <%s,%s>\n", result.AppName, result.Version, test[1], test[2])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
6
go.mod
6
go.mod
@ -1,6 +0,0 @@
|
|||||||
module code.ivysaur.me/nmdc-ircfrontend
|
|
||||||
|
|
||||||
require (
|
|
||||||
code.ivysaur.me/libnmdc v0.17.0
|
|
||||||
github.com/cxmcc/tiger v0.0.0-20170524142333-bde35e2713d7 // indirect
|
|
||||||
)
|
|
4
go.sum
4
go.sum
@ -1,4 +0,0 @@
|
|||||||
code.ivysaur.me/libnmdc v0.17.0 h1:51QlGxldeo7DKZGE7koiU8MA2ehwB9FtQLog/LHuOeg=
|
|
||||||
code.ivysaur.me/libnmdc v0.17.0/go.mod h1:ZHCjIX/zm29hd2H8YtzOBHiSyowZNOHvgoMRjKbBLLg=
|
|
||||||
github.com/cxmcc/tiger v0.0.0-20170524142333-bde35e2713d7 h1:jBEtq1t2gpn2kEzvRlCUxvvrxl5aSWkXNPwe/hwvSNQ=
|
|
||||||
github.com/cxmcc/tiger v0.0.0-20170524142333-bde35e2713d7/go.mod h1:ruCYvt9rtYymAr4rNmfYJrl1dz8HSXUFP7cufqKOsDI=
|
|
175
godist.sh
Normal file
175
godist.sh
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# godist.sh is a template build script for golang applications.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016, The godist.sh Author(s)
|
||||||
|
#
|
||||||
|
# Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
# purpose with or without fee is hereby granted, provided that the above
|
||||||
|
# copyright notice and this permission notice appear in all copies.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
|
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
DIST_DIR=./_dist
|
||||||
|
|
||||||
|
sanitise() {
|
||||||
|
TARGETFILE="$1" php <<'EOD'
|
||||||
|
<?php
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
ini_set('display_errors', 'On');
|
||||||
|
$targetfile = getenv("TARGETFILE");
|
||||||
|
$data = file_get_contents($targetfile);
|
||||||
|
$gopath = getenv("GOPATH");
|
||||||
|
$newpath = preg_replace('~[a-zA-Z0-9]~', 'x', $gopath);
|
||||||
|
$data = str_replace([$gopath, str_replace('\\', '/', $gopath)], [$newpath, $newpath], $data);
|
||||||
|
file_put_contents($targetfile, $data);
|
||||||
|
EOD
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
get_package_name() {
|
||||||
|
# Binaries are named after the containing folder.
|
||||||
|
pwd | tr / $'\n' | tail -n1
|
||||||
|
}
|
||||||
|
|
||||||
|
check_dependencies() {
|
||||||
|
# `set -e` will take care of killing the application if the dependency
|
||||||
|
# goes unresolved.
|
||||||
|
which 7z > /dev/null
|
||||||
|
which php > /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
get_platform_description() {
|
||||||
|
if [[ $GOOS == windows ]] ; then
|
||||||
|
echo -n "win"
|
||||||
|
else
|
||||||
|
echo -n "${GOOS}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $GOARCH == 386 ]] ; then
|
||||||
|
echo -n 32
|
||||||
|
elif [[ $GOARCH == amd64 ]] ; then
|
||||||
|
echo -n 64
|
||||||
|
else
|
||||||
|
echo -n "-${GOARCH}"
|
||||||
|
if [[ -v $GOARM ]] ; then
|
||||||
|
echo -n "v${GOARM}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
pathfix() {
|
||||||
|
if [[ $(uname -o) == Cygwin ]] ; then
|
||||||
|
cygpath -w "$1"
|
||||||
|
else
|
||||||
|
echo "$1"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
single_build() {
|
||||||
|
local version="$1"
|
||||||
|
|
||||||
|
# Determine build output name
|
||||||
|
local bin_name=''
|
||||||
|
if [[ $GOOS == windows ]] ; then
|
||||||
|
local_bin_name="$(get_package_name).exe"
|
||||||
|
else
|
||||||
|
local_bin_name="$(get_package_name)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -f ./$local_bin_name ]] ; then
|
||||||
|
rm -f "./$local_bin_name"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local tmpdir=$(mktemp -d)
|
||||||
|
|
||||||
|
# Platform identifier
|
||||||
|
local platform="$(get_platform_description)"
|
||||||
|
echo "[INFO] Building ${version} for ${platform}..."
|
||||||
|
|
||||||
|
# Build.
|
||||||
|
# GOARCH/GOOS supplied in function env
|
||||||
|
go build -a -ldflags '-s -w' -o "$(pathfix "${tmpdir}/${local_bin_name}")"
|
||||||
|
|
||||||
|
# Sanitise.
|
||||||
|
sanitise "${tmpdir}/${local_bin_name}"
|
||||||
|
|
||||||
|
# Archive.
|
||||||
|
if [[ ! -d $DIST_DIR ]] ; then
|
||||||
|
mkdir "$DIST_DIR"
|
||||||
|
fi
|
||||||
|
local archive_name="${DIST_DIR}/$(get_package_name)-${version}-${platform}"
|
||||||
|
if [[ $GOOS == windows ]] ; then
|
||||||
|
archive_name="${archive_name}.7z"
|
||||||
|
7z a -mx9 "$archive_name" "${tmpdir}/${local_bin_name}" >/dev/null
|
||||||
|
else
|
||||||
|
archive_name="${archive_name}.tar.xz"
|
||||||
|
XZ_OPT='-9' tar caf "$archive_name" -C "${tmpdir}" "${local_bin_name}" --owner=0 --group=0 >/dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
rm -f "${tmpdir}/${local_bin_name}"
|
||||||
|
rmdir "$tmpdir"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
datestamp() {
|
||||||
|
( TZ=UTC date +%Y%m%d%H%M%SZ )
|
||||||
|
}
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<EOD
|
||||||
|
Usage: ./godist.sh [-v VERSION]
|
||||||
|
EOD
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
|
||||||
|
check_dependencies
|
||||||
|
|
||||||
|
if [[ ! -v GOPATH ]] ; then
|
||||||
|
echo "Please set GOPATH." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ ! -d $GOPATH ]] ; then
|
||||||
|
echo "Invalid GOPATH set." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local version=""
|
||||||
|
while getopts ':v:' flag ; do
|
||||||
|
case "$flag" in
|
||||||
|
v)
|
||||||
|
version="$OPTARG"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
usage
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ $version == "" ]] ; then
|
||||||
|
read -p "Enter version string (blank for timestamp)> " version
|
||||||
|
if [[ $version == "" ]] ; then
|
||||||
|
version=$(datestamp)
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
GOARCH=amd64 GOOS=windows single_build "$version"
|
||||||
|
GOARCH=386 GOOS=windows single_build "$version"
|
||||||
|
GOARCH=amd64 GOOS=linux single_build "$version"
|
||||||
|
GOARCH=386 GOOS=linux single_build "$version"
|
||||||
|
|
||||||
|
echo "[INFO] Build complete."
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
18
main.go
18
main.go
@ -1,7 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2016-2018 The `nmdc-ircfrontend' author(s)
|
Copyright (C) 2016 The `nmdc-ircfrontend' author(s)
|
||||||
Copyright (C) 2013 Harry Jeffery
|
Copyright (C) 2013 Harry Jeffery
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
@ -20,29 +20,20 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
|
"libnmdc"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"code.ivysaur.me/libnmdc"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
ircAddress := flag.String("bind", ":6667", "The address:port to bind to and listen for clients on")
|
ircAddress := flag.String("bind", ":6667", "The address:port to bind to and listen for clients on")
|
||||||
dcAddress := flag.String("upstream", "127.0.0.1:411", "Upstream NMDC/ADC server")
|
dcAddress := flag.String("upstream", "127.0.0.1:411", "Upstream NMDC server")
|
||||||
serverName := flag.String("servername", APP_NAME, "Server name displayed to clients")
|
serverName := flag.String("servername", "nmdc-ircfrontend", "Server name displayed to clients")
|
||||||
hubsec := flag.String("hubsecurity", "Hub-Security", "Nick used for administrative events")
|
|
||||||
verbose := flag.Bool("verbose", false, "Display debugging information")
|
verbose := flag.Bool("verbose", false, "Display debugging information")
|
||||||
autojoin := flag.Bool("autojoin", true, "Automatically join clients to the channel")
|
autojoin := flag.Bool("autojoin", true, "Automatically join clients to the channel")
|
||||||
version := flag.Bool("version", false, "Display version and exit")
|
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if *version {
|
|
||||||
log.Printf("%s version %s\n", APP_NAME, APP_VERSION)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("Listening on '%s'...", *ircAddress)
|
log.Printf("Listening on '%s'...", *ircAddress)
|
||||||
if *autojoin {
|
if *autojoin {
|
||||||
log.Printf("Clients will be automatically joined to the channel.")
|
log.Printf("Clients will be automatically joined to the channel.")
|
||||||
@ -65,7 +56,6 @@ func main() {
|
|||||||
server := NewServer(*serverName, libnmdc.HubAddress(*dcAddress), conn)
|
server := NewServer(*serverName, libnmdc.HubAddress(*dcAddress), conn)
|
||||||
server.verbose = *verbose
|
server.verbose = *verbose
|
||||||
server.autojoin = *autojoin
|
server.autojoin = *autojoin
|
||||||
server.hubSecNick = *hubsec
|
|
||||||
go server.RunWorker()
|
go server.RunWorker()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
20
quirks.go
20
quirks.go
@ -1,20 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Quirks struct {
|
|
||||||
RequireNickForGeneralMessages bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetQuirksForClient(ver string) Quirks {
|
|
||||||
if strings.Contains(ver, "mIRC") || strings.Contains(ver, "Atomic") || strings.Contains(ver, "Yaaic") {
|
|
||||||
return Quirks{
|
|
||||||
RequireNickForGeneralMessages: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
return Quirks{}
|
|
||||||
}
|
|
||||||
}
|
|
374
server.go
374
server.go
@ -1,7 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2016-2018 The `nmdc-ircfrontend' author(s)
|
Copyright (C) 2016 The `nmdc-ircfrontend' author(s)
|
||||||
Copyright (C) 2013 Harry Jeffery
|
Copyright (C) 2013 Harry Jeffery
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
@ -22,13 +22,12 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"libnmdc"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.ivysaur.me/libnmdc"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ClientState int
|
type ClientState int
|
||||||
@ -42,32 +41,23 @@ const (
|
|||||||
type Server struct {
|
type Server struct {
|
||||||
name string
|
name string
|
||||||
motd string
|
motd string
|
||||||
hubSecNick string
|
|
||||||
|
|
||||||
clientConn net.Conn
|
clientConn net.Conn
|
||||||
clientState ClientState
|
clientState ClientState
|
||||||
ClientStateLock sync.Mutex
|
|
||||||
|
|
||||||
upstreamLauncher libnmdc.HubConnectionOptions
|
upstreamLauncher libnmdc.HubConnectionOptions
|
||||||
upstreamCloser chan struct{}
|
upstreamCloser chan struct{}
|
||||||
upstreamEvents chan libnmdc.HubEvent
|
|
||||||
upstream *libnmdc.HubConnection
|
upstream *libnmdc.HubConnection
|
||||||
|
|
||||||
verbose bool
|
verbose bool
|
||||||
autojoin bool
|
autojoin bool
|
||||||
|
|
||||||
recievedFirstServerMessage bool
|
sentCtcpVersion bool
|
||||||
recievedCtcpVersion bool
|
|
||||||
nickChangeAttempt int
|
|
||||||
sentFakeSelfJoin bool
|
|
||||||
|
|
||||||
quirks Quirks
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer(name string, upstream libnmdc.HubAddress, conn net.Conn) *Server {
|
func NewServer(name string, upstream libnmdc.HubAddress, conn net.Conn) *Server {
|
||||||
self := libnmdc.NewUserInfo("")
|
self := libnmdc.NewUserInfo("")
|
||||||
self.ClientTag = APP_NAME
|
self.ClientTag = APP_DESCRIPTION
|
||||||
self.ClientVersion = APP_VERSION
|
|
||||||
|
|
||||||
return &Server{
|
return &Server{
|
||||||
name: name,
|
name: name,
|
||||||
@ -76,12 +66,10 @@ func NewServer(name string, upstream libnmdc.HubAddress, conn net.Conn) *Server
|
|||||||
motd: "Connected to " + name + ". You /must/ join " + BLESSED_CHANNEL + " to continue.",
|
motd: "Connected to " + name + ". You /must/ join " + BLESSED_CHANNEL + " to continue.",
|
||||||
upstreamLauncher: libnmdc.HubConnectionOptions{
|
upstreamLauncher: libnmdc.HubConnectionOptions{
|
||||||
Address: upstream,
|
Address: upstream,
|
||||||
Self: self,
|
Self: *self,
|
||||||
SkipAutoReconnect: true,
|
SkipAutoReconnect: true,
|
||||||
},
|
},
|
||||||
upstreamEvents: make(chan libnmdc.HubEvent, 0), // unbuffered
|
|
||||||
upstreamCloser: make(chan struct{}, 1),
|
upstreamCloser: make(chan struct{}, 1),
|
||||||
quirks: Quirks{},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +107,7 @@ func (s *Server) RunWorker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If this was a /timeout/, send a KA and continue.
|
// If this was a /timeout/, send a KA and continue.
|
||||||
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
|
if libnmdc.CheckIsNetTimeout(err) {
|
||||||
s.writeClient("PING :" + s.name)
|
s.writeClient("PING :" + s.name)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -138,12 +126,12 @@ func (s *Server) RunWorker() {
|
|||||||
|
|
||||||
// Client sent a command
|
// Client sent a command
|
||||||
fields := strings.Fields(string(line))
|
fields := strings.Fields(string(line))
|
||||||
if len(fields) == 0 {
|
if len(fields) < 1 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(fields[0], ":") {
|
if strings.HasPrefix(fields[0], ":") {
|
||||||
fields[0] = fields[0][1:]
|
fields = fields[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
s.handleCommand(strings.ToUpper(fields[0]), fields[1:])
|
s.handleCommand(strings.ToUpper(fields[0]), fields[1:])
|
||||||
@ -164,14 +152,10 @@ func (s *Server) RunWorker() {
|
|||||||
|
|
||||||
func (s *Server) postGeneralMessageInRoom(msg string) {
|
func (s *Server) postGeneralMessageInRoom(msg string) {
|
||||||
|
|
||||||
var sendAs = ""
|
// FIXME blank names work well in hexchat, but not in yaaic
|
||||||
|
var BLANK_NICK = "" // "!@" + s.name // "PtokaX" // "\xC2\xA0"
|
||||||
|
|
||||||
// Some clients can't handle blank nicks very well
|
// CTCP action conversion
|
||||||
if s.quirks.RequireNickForGeneralMessages {
|
|
||||||
sendAs = s.hubSecNick + "!" + s.hubSecNick + "@" + s.hubSecNick
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect pseudo-system message for potential CTCP ACTION conversion
|
|
||||||
words := strings.Split(msg, " ")
|
words := strings.Split(msg, " ")
|
||||||
firstWord := words[0]
|
firstWord := words[0]
|
||||||
remainder := strings.Join(words[1:], " ")
|
remainder := strings.Join(words[1:], " ")
|
||||||
@ -192,11 +176,36 @@ func (s *Server) postGeneralMessageInRoom(msg string) {
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
// genuine system message
|
// genuine system message
|
||||||
s.reply(rplMsg, sendAs, BLESSED_CHANNEL, msg)
|
s.reply(rplMsg, BLANK_NICK, BLESSED_CHANNEL, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var rx_colorControlCode *regexp.Regexp = regexp.MustCompilePOSIX("\x03[0-9]+(,[0-9]+)?")
|
||||||
|
var rx_formattingControlCode *regexp.Regexp = regexp.MustCompile("(\x02|\x1D|\x1F|\x16|\x0F)")
|
||||||
|
|
||||||
|
func reformatOutgoingMessageBody(body string) string {
|
||||||
|
|
||||||
|
// Strip color codes
|
||||||
|
noColors := rx_colorControlCode.ReplaceAllString(body, "")
|
||||||
|
|
||||||
|
// Strip formatting codes
|
||||||
|
return rx_formattingControlCode.ReplaceAllString(noColors, "")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func reformatIncomingMessageBody(body string) string {
|
||||||
|
|
||||||
|
// "Greentext" filter
|
||||||
|
// TODO markdown filters
|
||||||
|
if len(body) > 0 && body[0] == '>' {
|
||||||
|
return "\x033" + strings.Replace(body, "implying", "\x02implying\x02", -1)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) upstreamWorker() {
|
func (s *Server) upstreamWorker() {
|
||||||
|
|
||||||
// Read loop
|
// Read loop
|
||||||
@ -208,15 +217,11 @@ func (s *Server) upstreamWorker() {
|
|||||||
s.upstream.Disconnect()
|
s.upstream.Disconnect()
|
||||||
return
|
return
|
||||||
|
|
||||||
case hubEvent := <-s.upstreamEvents:
|
case hubEvent := <-s.upstream.OnEvent:
|
||||||
switch hubEvent.EventType {
|
switch hubEvent.EventType {
|
||||||
case libnmdc.EVENT_USER_JOINED:
|
case libnmdc.EVENT_USER_JOINED:
|
||||||
if hubEvent.Nick == s.clientNick() && s.sentFakeSelfJoin {
|
|
||||||
s.sentFakeSelfJoin = false
|
|
||||||
} else {
|
|
||||||
// If we want to JOIN with the full power of the supplied nick!user@host, then we'll need to actually remember the active client's USER parameters
|
|
||||||
s.reply(rplJoin, hubEvent.Nick, BLESSED_CHANNEL)
|
s.reply(rplJoin, hubEvent.Nick, BLESSED_CHANNEL)
|
||||||
}
|
// If we want to JOIN with the full power of the supplied nick!user@host, then we'll need to actually remember the active client's USER parameters
|
||||||
|
|
||||||
case libnmdc.EVENT_USER_PART:
|
case libnmdc.EVENT_USER_PART:
|
||||||
s.reply(rplPart, hubEvent.Nick, BLESSED_CHANNEL, "Disconnected")
|
s.reply(rplPart, hubEvent.Nick, BLESSED_CHANNEL, "Disconnected")
|
||||||
@ -225,24 +230,14 @@ func (s *Server) upstreamWorker() {
|
|||||||
// description change - no relevance for IRC users
|
// description change - no relevance for IRC users
|
||||||
|
|
||||||
case libnmdc.EVENT_CONNECTION_STATE_CHANGED:
|
case libnmdc.EVENT_CONNECTION_STATE_CHANGED:
|
||||||
s.postGeneralMessageInRoom("* Upstream: " + hubEvent.StateChange.String())
|
s.postGeneralMessageInRoom("* Upstream: " + hubEvent.StateChange.Format())
|
||||||
|
|
||||||
if hubEvent.StateChange == libnmdc.CONNECTIONSTATE_CONNECTED {
|
if hubEvent.StateChange == libnmdc.CONNECTIONSTATE_CONNECTED {
|
||||||
s.sendNames() // delay doing this until now
|
s.sendNames() // delay doing this until now
|
||||||
}
|
}
|
||||||
if hubEvent.StateChange == libnmdc.CONNECTIONSTATE_DISCONNECTED {
|
if hubEvent.StateChange == libnmdc.CONNECTIONSTATE_DISCONNECTED {
|
||||||
if s.nickChangeAttempt > 0 {
|
|
||||||
// If this was a nick change, reconnect /immediately/
|
|
||||||
s.upstream = nil
|
|
||||||
s.clientState = CSRegistered
|
|
||||||
s.maybeStartUpstream() // launches new goroutine
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Abandon thread. Don't try to autoreconnect at our level, the remote client can be responsible for that
|
// Abandon thread. Don't try to autoreconnect at our level, the remote client can be responsible for that
|
||||||
s.DisconnectClient()
|
s.DisconnectClient()
|
||||||
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case libnmdc.EVENT_HUBNAME_CHANGED:
|
case libnmdc.EVENT_HUBNAME_CHANGED:
|
||||||
@ -257,10 +252,12 @@ func (s *Server) upstreamWorker() {
|
|||||||
} else {
|
} else {
|
||||||
// nick!username@userhost, but for us all three of those are always identical
|
// nick!username@userhost, but for us all three of those are always identical
|
||||||
s.reply(rplMsg, hubEvent.Nick+"!"+hubEvent.Nick+"@"+hubEvent.Nick, BLESSED_CHANNEL, reformatIncomingMessageBody(hubEvent.Message))
|
s.reply(rplMsg, hubEvent.Nick+"!"+hubEvent.Nick+"@"+hubEvent.Nick, BLESSED_CHANNEL, reformatIncomingMessageBody(hubEvent.Message))
|
||||||
}
|
|
||||||
|
|
||||||
if !s.recievedFirstServerMessage {
|
if !s.sentCtcpVersion {
|
||||||
s.hubSecNick = hubEvent.Nick // Replace with the hub's real Hub-Security nick, although we shouldn't need it again
|
// Abuse this first username in order to also send a CTCP version request
|
||||||
|
s.reply(rplMsg, hubEvent.Nick+"!"+hubEvent.Nick+"@"+hubEvent.Nick, BLESSED_CHANNEL, "\x01VERSION\x01")
|
||||||
|
s.sentCtcpVersion = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case libnmdc.EVENT_SYSTEM_MESSAGE_FROM_CONN, libnmdc.EVENT_SYSTEM_MESSAGE_FROM_HUB:
|
case libnmdc.EVENT_SYSTEM_MESSAGE_FROM_CONN, libnmdc.EVENT_SYSTEM_MESSAGE_FROM_HUB:
|
||||||
@ -283,10 +280,10 @@ func (s *Server) handleCommand(command string, args []string) {
|
|||||||
// do nothing
|
// do nothing
|
||||||
|
|
||||||
case "INFO":
|
case "INFO":
|
||||||
s.reply(rplInfo, APP_NAME+" v"+APP_VERSION)
|
s.reply(rplInfo, APP_DESCRIPTION)
|
||||||
|
|
||||||
case "VERSION":
|
case "VERSION":
|
||||||
s.reply(rplVersion, APP_VERSION)
|
s.reply(rplVersion, VERSION)
|
||||||
|
|
||||||
case "MOTD":
|
case "MOTD":
|
||||||
s.sendMOTD(s.motd)
|
s.sendMOTD(s.motd)
|
||||||
@ -320,38 +317,17 @@ func (s *Server) handleCommand(command string, args []string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// mIRC puts a colon in first place when changing nick, hexchat doesn't
|
|
||||||
suppliedNick := args[0]
|
|
||||||
if len(suppliedNick) >= 2 && suppliedNick[0] == ':' {
|
|
||||||
suppliedNick = suppliedNick[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.clientNick() == "" {
|
if s.clientNick() == "" {
|
||||||
// allow set, as part of the login phase
|
// allow set, as part of the login phase
|
||||||
s.upstreamLauncher.Self.Nick = suppliedNick
|
s.upstreamLauncher.Self.Nick = args[0]
|
||||||
|
|
||||||
} else if suppliedNick == s.clientNick() {
|
} else if args[0] == s.clientNick() {
|
||||||
// Ignore
|
// Ignore
|
||||||
// Required for compatibility with Lite IRC, which sends USER/NICK in the wrong order
|
// Required for compatibility with Lite IRC, which sends USER/NICK in the wrong order
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
s.reply(rplKill, "Can't change nicks on this server.")
|
||||||
s.ClientStateLock.Lock()
|
s.DisconnectClient()
|
||||||
defer s.ClientStateLock.Unlock()
|
|
||||||
if s.upstream == nil {
|
|
||||||
// Not yet connected, should be safe to change nick
|
|
||||||
s.upstreamLauncher.Self.Nick = suppliedNick
|
|
||||||
} else {
|
|
||||||
// Need to disconnect/reconnect the upstream
|
|
||||||
s.writeClient(fmt.Sprintf(":%s!%s@%s NICK %s", s.clientNick(), s.clientNick(), s.clientNick(), suppliedNick)) // notify client about what they've done
|
|
||||||
s.upstreamLauncher.Self.Nick = suppliedNick
|
|
||||||
|
|
||||||
s.nickChangeAttempt++
|
|
||||||
s.upstream.Disconnect()
|
|
||||||
}
|
|
||||||
|
|
||||||
//s.reply(rplKill, "Can't change nicks on this server.")
|
|
||||||
//s.DisconnectClient()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case "USER":
|
case "USER":
|
||||||
@ -379,9 +355,7 @@ func (s *Server) handleCommand(command string, args []string) {
|
|||||||
s.reply(rplWelcome)
|
s.reply(rplWelcome)
|
||||||
s.clientState = CSRegistered
|
s.clientState = CSRegistered
|
||||||
|
|
||||||
// Send CTCP VERSION request immediately
|
// TODO tell the client that they /must/ join #chat
|
||||||
s.reply(rplMsg, s.hubSecNick+"!"+s.hubSecNick+"@"+s.hubSecNick, BLESSED_CHANNEL, "\x01VERSION\x01")
|
|
||||||
|
|
||||||
if s.autojoin {
|
if s.autojoin {
|
||||||
s.handleCommand("JOIN", []string{BLESSED_CHANNEL})
|
s.handleCommand("JOIN", []string{BLESSED_CHANNEL})
|
||||||
}
|
}
|
||||||
@ -407,14 +381,98 @@ func (s *Server) handleRegisteredCommand(command string, args []string) {
|
|||||||
}
|
}
|
||||||
s.reply(rplListEnd)
|
s.reply(rplListEnd)
|
||||||
|
|
||||||
|
case "JOIN":
|
||||||
|
if len(args) < 1 {
|
||||||
|
s.reply(errMoreArgs)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch args[0] {
|
||||||
|
case BLESSED_CHANNEL:
|
||||||
|
if s.clientState != CSJoined {
|
||||||
|
// Join for the first time
|
||||||
|
s.clientState = CSJoined
|
||||||
|
|
||||||
|
// Acknowledge
|
||||||
|
s.reply(rplJoin, s.clientNick(), BLESSED_CHANNEL)
|
||||||
|
|
||||||
|
// Send (initially just us) nicklist for the chat channel
|
||||||
|
//s.sendNames()
|
||||||
|
|
||||||
|
// Spawn upstream connection
|
||||||
|
s.upstream = s.upstreamLauncher.Connect()
|
||||||
|
go s.upstreamWorker()
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// They're already here, ignore
|
||||||
|
// Can happen if autojoin is enabled but the client already requested a login
|
||||||
|
}
|
||||||
|
|
||||||
|
case "0":
|
||||||
|
// Quitting all channels? Drop client
|
||||||
|
s.reply(rplKill, "Bye.")
|
||||||
|
s.DisconnectClient()
|
||||||
|
|
||||||
|
default:
|
||||||
|
s.reply(rplKill, "There is only '"+BLESSED_CHANNEL+"'.")
|
||||||
|
s.DisconnectClient()
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
s.handleJoinedCommand(command, args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) SetClientSoftwareVersion(ver string) {
|
||||||
|
// "HexChat 2.12.1 [x64] / Microsoft Windows 10 Pro (x64) [Intel(R) Core(TM) i5-2500 CPU @ 3.30GHz (3.60GHz)]"
|
||||||
|
// "AndroIRC - Android IRC Client (5.2 - Build 6830152) - http://www.androirc.com"
|
||||||
|
// "AndChat 1.4.3.2 http://www.andchat.net"
|
||||||
|
// "liteIRC for Android 1.1.8"
|
||||||
|
// ":Relay:1.0:Android"
|
||||||
|
|
||||||
|
// A bit long and unwieldy. Heuristic: stop after the first word containing digits
|
||||||
|
|
||||||
|
parts := regexp.MustCompilePOSIX("^([^0-9]*[0-9]+[^ ]*) ").FindStringSubmatch(ver)
|
||||||
|
|
||||||
|
if len(parts) > 0 {
|
||||||
|
ver = strings.TrimRight(parts[0], " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
s.verbosef("Replacing client tag with '%s'...", ver)
|
||||||
|
|
||||||
|
s.upstreamLauncher.Self.ClientTag = ver
|
||||||
|
s.upstream.Hco.Self.ClientTag = ver
|
||||||
|
|
||||||
|
s.upstream.SayInfo()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) handleJoinedCommand(command string, args []string) {
|
||||||
|
|
||||||
|
if s.clientState != CSJoined {
|
||||||
|
s.reply(errNotReg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch command {
|
||||||
|
case "PART":
|
||||||
|
if len(args) < 1 {
|
||||||
|
s.reply(errMoreArgs)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if args[0] == BLESSED_CHANNEL {
|
||||||
|
// You can check out any time you like, but you can never leave
|
||||||
|
s.reply(rplJoin, s.clientNick(), BLESSED_CHANNEL)
|
||||||
|
s.sendNames()
|
||||||
|
}
|
||||||
|
|
||||||
case "PRIVMSG", "NOTICE":
|
case "PRIVMSG", "NOTICE":
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
s.reply(errMoreArgs)
|
s.reply(errMoreArgs)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Strip leading colon (work around a bug in HoloIRC 4.1.0 and older)
|
message := strings.Join(args[1:], " ")[1:] // strip leading colon
|
||||||
message := strings.Join(args[1:], " ")[1:]
|
|
||||||
|
|
||||||
if strings.HasPrefix(message, "\x01VERSION ") {
|
if strings.HasPrefix(message, "\x01VERSION ") {
|
||||||
// Not a real message - a reply to our internal request. Change the user's tag to match the actual client software
|
// Not a real message - a reply to our internal request. Change the user's tag to match the actual client software
|
||||||
@ -424,7 +482,6 @@ func (s *Server) handleRegisteredCommand(command string, args []string) {
|
|||||||
versionString := message[9:]
|
versionString := message[9:]
|
||||||
versionString = versionString[:len(versionString)-1]
|
versionString = versionString[:len(versionString)-1]
|
||||||
s.SetClientSoftwareVersion(versionString)
|
s.SetClientSoftwareVersion(versionString)
|
||||||
s.quirks = GetQuirksForClient(versionString)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -452,105 +509,6 @@ func (s *Server) handleRegisteredCommand(command string, args []string) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case "JOIN":
|
|
||||||
if len(args) < 1 {
|
|
||||||
s.reply(errMoreArgs)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch args[0] {
|
|
||||||
case BLESSED_CHANNEL:
|
|
||||||
// Give it a few seconds - it's better to hear the CTCP VERSION
|
|
||||||
// response first. Once we get that, it'll connect instantly
|
|
||||||
go func() {
|
|
||||||
<-time.After(WAIT_FOR_VERSION * time.Second)
|
|
||||||
|
|
||||||
s.ClientStateLock.Lock()
|
|
||||||
defer s.ClientStateLock.Unlock()
|
|
||||||
|
|
||||||
s.maybeStartUpstream()
|
|
||||||
}()
|
|
||||||
|
|
||||||
case "0":
|
|
||||||
// Quitting all channels? Drop client
|
|
||||||
s.reply(rplKill, "Bye.")
|
|
||||||
s.DisconnectClient()
|
|
||||||
|
|
||||||
default:
|
|
||||||
s.reply(rplKill, "There is only '"+BLESSED_CHANNEL+"'.")
|
|
||||||
s.DisconnectClient()
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
s.handleJoinedCommand(command, args)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) maybeStartUpstream() {
|
|
||||||
if s.clientState != CSJoined {
|
|
||||||
// Join for the first time
|
|
||||||
s.clientState = CSJoined
|
|
||||||
|
|
||||||
// Acknowledge
|
|
||||||
s.reply(rplJoin, s.clientNick(), BLESSED_CHANNEL)
|
|
||||||
|
|
||||||
// Spawn upstream connection
|
|
||||||
s.upstream = libnmdc.ConnectAsync(&s.upstreamLauncher, s.upstreamEvents)
|
|
||||||
go s.upstreamWorker()
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// They're already here, ignore
|
|
||||||
// Can happen if autojoin is enabled but the client already requested a login
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) SetClientSoftwareVersion(ver string) {
|
|
||||||
|
|
||||||
ct := parseVersion(ver)
|
|
||||||
|
|
||||||
s.verbosef("Replacing client tag with '%s' version '%s'", ct.AppName, ct.Version)
|
|
||||||
|
|
||||||
s.upstreamLauncher.Self.ClientTag = ct.AppName
|
|
||||||
s.upstreamLauncher.Self.ClientVersion = ct.Version
|
|
||||||
s.recievedCtcpVersion = true
|
|
||||||
|
|
||||||
s.ClientStateLock.Lock()
|
|
||||||
defer s.ClientStateLock.Unlock()
|
|
||||||
|
|
||||||
if s.upstream != nil {
|
|
||||||
s.upstream.Hco.Self.ClientTag = ct.AppName
|
|
||||||
s.upstream.Hco.Self.ClientVersion = ct.Version
|
|
||||||
s.upstream.SayInfo()
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Connected for the first time (order was CTCP VERSION --> JOIN)
|
|
||||||
|
|
||||||
s.maybeStartUpstream()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) handleJoinedCommand(command string, args []string) {
|
|
||||||
|
|
||||||
if s.clientState != CSJoined {
|
|
||||||
s.reply(errNotReg)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch command {
|
|
||||||
case "PART":
|
|
||||||
if len(args) < 1 {
|
|
||||||
s.reply(errMoreArgs)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if args[0] == BLESSED_CHANNEL {
|
|
||||||
// You can check out any time you like, but you can never leave
|
|
||||||
s.reply(rplJoin, s.clientNick(), BLESSED_CHANNEL)
|
|
||||||
s.sendNames()
|
|
||||||
}
|
|
||||||
|
|
||||||
case "QUIT":
|
case "QUIT":
|
||||||
s.DisconnectClient()
|
s.DisconnectClient()
|
||||||
|
|
||||||
@ -594,7 +552,8 @@ func (s *Server) handleJoinedCommand(command string, args []string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore this command
|
// s.sendWho(args[0])
|
||||||
|
// s.sendNames() // fixes hexchat, but andchat always sends WHO /immediately/ after NAMES end, causing an infinite loop
|
||||||
|
|
||||||
case "MODE":
|
case "MODE":
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
@ -618,35 +577,6 @@ func (s *Server) handleJoinedCommand(command string, args []string) {
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
case "WHOIS":
|
|
||||||
if len(args) < 1 {
|
|
||||||
s.reply(errMoreArgs)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// WHOIS [target] nick[,nick2[,nick...]]
|
|
||||||
nicklist := args[0] // Assume WHOIS ${nick} only,
|
|
||||||
if len(args) >= 2 {
|
|
||||||
nicklist = args[1] // It was WHOIS ${target} ${nick} instead
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, targetnick := range strings.Split(nicklist, ",") {
|
|
||||||
// tell the client something about it
|
|
||||||
// The protocol does ostensibly support wildcard WHOIS, but we don't (yet)
|
|
||||||
s.upstream.Users(func(u *map[string]libnmdc.UserInfo) error {
|
|
||||||
for nick, nickinfo := range *u {
|
|
||||||
if nick == targetnick {
|
|
||||||
s.reply(rplWhoisUser, nick, nickinfo.Description+" <"+nickinfo.ClientTag+" V:"+nickinfo.ClientVersion+">")
|
|
||||||
if nickinfo.IsOperator {
|
|
||||||
s.reply(rplWhoisOperator, nick)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
s.reply(rplEndOfWhois)
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
s.reply(errUnknownCommand, command)
|
s.reply(errUnknownCommand, command)
|
||||||
}
|
}
|
||||||
@ -679,15 +609,6 @@ func (s *Server) sendNames() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(nameList) == 0 {
|
|
||||||
// We don't have a nick list yet. Many clients can't handle a blank list
|
|
||||||
// We could delay until we do have a nick list
|
|
||||||
// Or, we could send our nick only, and filter it out of the next join
|
|
||||||
|
|
||||||
nameList = append(nameList, s.clientNick())
|
|
||||||
s.sentFakeSelfJoin = true
|
|
||||||
}
|
|
||||||
|
|
||||||
s.reply(rplNames, BLESSED_CHANNEL, strings.Join(nameList, " "))
|
s.reply(rplNames, BLESSED_CHANNEL, strings.Join(nameList, " "))
|
||||||
s.reply(rplEndOfNames, BLESSED_CHANNEL)
|
s.reply(rplEndOfNames, BLESSED_CHANNEL)
|
||||||
}
|
}
|
||||||
@ -758,9 +679,9 @@ func (s *Server) reply(code replyCode, args ...string) {
|
|||||||
s.writeClient(fmt.Sprintf(":%s 005 %s NAMESX CHANTYPES=# :are supported by this server", s.name, s.clientNick()))
|
s.writeClient(fmt.Sprintf(":%s 005 %s NAMESX CHANTYPES=# :are supported by this server", s.name, s.clientNick()))
|
||||||
|
|
||||||
case rplJoin:
|
case rplJoin:
|
||||||
s.writeClient(fmt.Sprintf(":%s!%s@%s JOIN %s", args[0], args[0], args[0], args[1]))
|
s.writeClient(fmt.Sprintf(":%s JOIN %s", args[0], args[1]))
|
||||||
case rplPart:
|
case rplPart:
|
||||||
s.writeClient(fmt.Sprintf(":%s!%s@%s PART %s %s", args[0], args[0], args[0], args[1], args[2]))
|
s.writeClient(fmt.Sprintf(":%s PART %s %s", args[0], args[1], args[2]))
|
||||||
case rplTopic:
|
case rplTopic:
|
||||||
s.writeClient(fmt.Sprintf(":%s 332 %s %s :%s", s.name, s.clientNick(), args[0], args[1]))
|
s.writeClient(fmt.Sprintf(":%s 332 %s %s :%s", s.name, s.clientNick(), args[0], args[1]))
|
||||||
case rplNoTopic:
|
case rplNoTopic:
|
||||||
@ -769,7 +690,7 @@ func (s *Server) reply(code replyCode, args ...string) {
|
|||||||
case rplNames:
|
case rplNames:
|
||||||
s.writeClient(fmt.Sprintf(":%s 353 %s = %s :%s", s.name, s.clientNick(), args[0], args[1]))
|
s.writeClient(fmt.Sprintf(":%s 353 %s = %s :%s", s.name, s.clientNick(), args[0], args[1]))
|
||||||
case rplEndOfNames:
|
case rplEndOfNames:
|
||||||
s.writeClient(fmt.Sprintf(":%s 366 %s %s :End of /NAMES list.", s.name, s.clientNick(), args[0]))
|
s.writeClient(fmt.Sprintf(":%s 366 %s %s :End of NAMES list", s.name, s.clientNick(), args[0]))
|
||||||
|
|
||||||
case rplWho:
|
case rplWho:
|
||||||
s.writeClient(fmt.Sprintf(":%s 352 %s %s %s %s %s %s H :0 %s", s.name, s.clientNick(), args[1], args[0], args[0], s.name, args[0], args[0]))
|
s.writeClient(fmt.Sprintf(":%s 352 %s %s %s %s %s %s H :0 %s", s.name, s.clientNick(), args[1], args[0], args[0], s.name, args[0], args[0]))
|
||||||
@ -814,13 +735,6 @@ func (s *Server) reply(code replyCode, args ...string) {
|
|||||||
case rplPong:
|
case rplPong:
|
||||||
s.writeClient(fmt.Sprintf(":%s PONG %s %s", s.name, s.clientNick(), args[0]))
|
s.writeClient(fmt.Sprintf(":%s PONG %s %s", s.name, s.clientNick(), args[0]))
|
||||||
|
|
||||||
case rplWhoisUser:
|
|
||||||
s.writeClient(fmt.Sprintf(":%s 311 %s %s %s %s * :%s", s.name, args[0], args[0], args[0], s.name, args[1])) // caller should supply nick,description
|
|
||||||
case rplWhoisOperator:
|
|
||||||
s.writeClient(fmt.Sprintf(":%s 313 %s :is an IRC operator", s.name, args[0]))
|
|
||||||
case rplEndOfWhois:
|
|
||||||
s.writeClient(fmt.Sprintf(":%s 318 :End of WHOIS list", s.name))
|
|
||||||
|
|
||||||
case errMoreArgs:
|
case errMoreArgs:
|
||||||
s.writeClient(fmt.Sprintf(":%s 461 %s :Not enough params", s.name, s.clientNick()))
|
s.writeClient(fmt.Sprintf(":%s 461 %s :Not enough params", s.name, s.clientNick()))
|
||||||
case errNoNick:
|
case errNoNick:
|
||||||
|
13
typedefs.go
13
typedefs.go
@ -1,7 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2016-2018 The `nmdc-ircfrontend' author(s)
|
Copyright (C) 2016 The `nmdc-ircfrontend' author(s)
|
||||||
Copyright (C) 2013 Harry Jeffery
|
Copyright (C) 2013 Harry Jeffery
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
@ -18,19 +18,15 @@ You should have received a copy of the GNU Affero General Public License
|
|||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var (
|
|
||||||
APP_VERSION = "0.0" // overridden with build ldflags
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
APP_NAME = "nmdc-ircfrontend"
|
VERSION = "1.0.0"
|
||||||
|
APP_DESCRIPTION = "nmdc-ircfrontend v" + VERSION
|
||||||
BLESSED_CHANNEL = "#chat" // must be lowercase
|
BLESSED_CHANNEL = "#chat" // must be lowercase
|
||||||
BLESSED_CHANNEL_MODE = "n" // means that you have to be in the channel to chat, but that's it
|
BLESSED_CHANNEL_MODE = "n" // means that you have to be in the channel to chat, but that's it
|
||||||
|
|
||||||
NICKS_PER_PROTOMSG = 128 // Max number of nicks to send per message
|
NICKS_PER_PROTOMSG = 128 // Max number of nicks to send per message
|
||||||
CLIENT_READ_BUFFSIZE = 512
|
CLIENT_READ_BUFFSIZE = 512
|
||||||
CLIENT_KEEPALIVE_EVERY = 60 // should be longer than the client's one, hexchat is 30s
|
CLIENT_KEEPALIVE_EVERY = 60 // should be longer than the client's one, hexchat is 30s
|
||||||
WAIT_FOR_VERSION = 3 // Seconds to wait for the CTCP VERSION response before connecting with default MyINFO
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type replyCode int
|
type replyCode int
|
||||||
@ -59,9 +55,6 @@ const (
|
|||||||
rplMOTD
|
rplMOTD
|
||||||
rplEndOfMOTD
|
rplEndOfMOTD
|
||||||
rplPong
|
rplPong
|
||||||
rplWhoisUser
|
|
||||||
rplWhoisOperator
|
|
||||||
rplEndOfWhois
|
|
||||||
errMoreArgs
|
errMoreArgs
|
||||||
errNoNick
|
errNoNick
|
||||||
errInvalidNick
|
errInvalidNick
|
||||||
|
14
vendor/code.ivysaur.me/libnmdc/.hgignore
generated
vendored
14
vendor/code.ivysaur.me/libnmdc/.hgignore
generated
vendored
@ -1,14 +0,0 @@
|
|||||||
mode:regex
|
|
||||||
|
|
||||||
# Compilation output
|
|
||||||
\.(?:exe|a)$
|
|
||||||
^pkg/
|
|
||||||
|
|
||||||
# Dependencies
|
|
||||||
^src/(?:github.com|gopkg.in|golang.org)/
|
|
||||||
|
|
||||||
# Scratch space
|
|
||||||
^src/nmdc/
|
|
||||||
|
|
||||||
# Binary release artefacts
|
|
||||||
/?__dist/
|
|
21
vendor/code.ivysaur.me/libnmdc/.hgtags
generated
vendored
21
vendor/code.ivysaur.me/libnmdc/.hgtags
generated
vendored
@ -1,21 +0,0 @@
|
|||||||
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
|
|
||||||
a48811ff2cfe5246c26801cf27da25bf56fec8bb v0.16.0
|
|
715
vendor/code.ivysaur.me/libnmdc/AdcProtocol.go
generated
vendored
715
vendor/code.ivysaur.me/libnmdc/AdcProtocol.go
generated
vendored
@ -1,715 +0,0 @@
|
|||||||
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.SayInfo()
|
|
||||||
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) SayInfo() {
|
|
||||||
this.hc.SayRaw("BINF " + this.escape(this.sid) + " " + this.ourINFO(true) + "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
}
|
|
90
vendor/code.ivysaur.me/libnmdc/AutodetectProtocol.go
generated
vendored
90
vendor/code.ivysaur.me/libnmdc/AutodetectProtocol.go
generated
vendored
@ -1,90 +0,0 @@
|
|||||||
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) SayInfo() {
|
|
||||||
this.realProtoMut.Lock()
|
|
||||||
defer this.realProtoMut.Unlock()
|
|
||||||
|
|
||||||
if this.realProto == nil {
|
|
||||||
this.realProto = NewNmdcProtocol(this.hc)
|
|
||||||
}
|
|
||||||
this.realProto.SayInfo()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *AutodetectProtocol) ProtoMessageSeparator() string {
|
|
||||||
this.realProtoMut.Lock()
|
|
||||||
defer this.realProtoMut.Unlock()
|
|
||||||
|
|
||||||
if this.realProto == nil {
|
|
||||||
return "|"
|
|
||||||
}
|
|
||||||
return this.realProto.ProtoMessageSeparator()
|
|
||||||
}
|
|
29
vendor/code.ivysaur.me/libnmdc/ConnectionMode.go
generated
vendored
29
vendor/code.ivysaur.me/libnmdc/ConnectionMode.go
generated
vendored
@ -1,29 +0,0 @@
|
|||||||
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
vendor/code.ivysaur.me/libnmdc/ConnectionState.go
generated
vendored
40
vendor/code.ivysaur.me/libnmdc/ConnectionState.go
generated
vendored
@ -1,40 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
15
vendor/code.ivysaur.me/libnmdc/Gopkg.lock
generated
vendored
15
vendor/code.ivysaur.me/libnmdc/Gopkg.lock
generated
vendored
@ -1,15 +0,0 @@
|
|||||||
# 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
vendor/code.ivysaur.me/libnmdc/Gopkg.toml
generated
vendored
26
vendor/code.ivysaur.me/libnmdc/Gopkg.toml
generated
vendored
@ -1,26 +0,0 @@
|
|||||||
|
|
||||||
# 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
vendor/code.ivysaur.me/libnmdc/HubAddress.go
generated
vendored
48
vendor/code.ivysaur.me/libnmdc/HubAddress.go
generated
vendored
@ -1,48 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
233
vendor/code.ivysaur.me/libnmdc/HubConnection.go
generated
vendored
233
vendor/code.ivysaur.me/libnmdc/HubConnection.go
generated
vendored
@ -1,233 +0,0 @@
|
|||||||
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) SayInfo() {
|
|
||||||
this.proto.SayInfo()
|
|
||||||
}
|
|
||||||
|
|
||||||
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
vendor/code.ivysaur.me/libnmdc/HubConnectionOptions.go
generated
vendored
79
vendor/code.ivysaur.me/libnmdc/HubConnectionOptions.go
generated
vendored
@ -1,79 +0,0 @@
|
|||||||
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
vendor/code.ivysaur.me/libnmdc/HubEvent.go
generated
vendored
25
vendor/code.ivysaur.me/libnmdc/HubEvent.go
generated
vendored
@ -1,25 +0,0 @@
|
|||||||
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
vendor/code.ivysaur.me/libnmdc/NmdcProtocol.go
generated
vendored
407
vendor/code.ivysaur.me/libnmdc/NmdcProtocol.go
generated
vendored
@ -1,407 +0,0 @@
|
|||||||
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||
|
|
||||||
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, "&", "&", -1)
|
|
||||||
v2 := strings.Replace(v1, "|", "|", -1)
|
|
||||||
return strings.Replace(v2, "$", "$", -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *NmdcProtocol) unescape(encoded string) string {
|
|
||||||
v1 := strings.Replace(encoded, "$", "$", -1)
|
|
||||||
v2 := strings.Replace(v1, "|", "|", -1)
|
|
||||||
return strings.Replace(v2, "&", "&", -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
|
|
||||||
}
|
|
33
vendor/code.ivysaur.me/libnmdc/Protocol.go
generated
vendored
33
vendor/code.ivysaur.me/libnmdc/Protocol.go
generated
vendored
@ -1,33 +0,0 @@
|
|||||||
package libnmdc
|
|
||||||
|
|
||||||
type Protocol interface {
|
|
||||||
ProcessCommand(msg string)
|
|
||||||
|
|
||||||
SayPublic(string)
|
|
||||||
|
|
||||||
SayPrivate(user, message string)
|
|
||||||
|
|
||||||
SayInfo()
|
|
||||||
|
|
||||||
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
vendor/code.ivysaur.me/libnmdc/TODO.txt
generated
vendored
6
vendor/code.ivysaur.me/libnmdc/TODO.txt
generated
vendored
@ -1,6 +0,0 @@
|
|||||||
NMDC:
|
|
||||||
- Implement ZPipe ($ZOn)
|
|
||||||
|
|
||||||
ADC:
|
|
||||||
- Usercommands
|
|
||||||
- ???
|
|
27
vendor/code.ivysaur.me/libnmdc/UserCommand.go
generated
vendored
27
vendor/code.ivysaur.me/libnmdc/UserCommand.go
generated
vendored
@ -1,27 +0,0 @@
|
|||||||
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
vendor/code.ivysaur.me/libnmdc/UserFlag.go
generated
vendored
17
vendor/code.ivysaur.me/libnmdc/UserFlag.go
generated
vendored
@ -1,17 +0,0 @@
|
|||||||
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
vendor/code.ivysaur.me/libnmdc/UserInfo.go
generated
vendored
53
vendor/code.ivysaur.me/libnmdc/UserInfo.go
generated
vendored
@ -1,53 +0,0 @@
|
|||||||
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,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
28
vendor/code.ivysaur.me/libnmdc/libnmdc.go
generated
vendored
28
vendor/code.ivysaur.me/libnmdc/libnmdc.go
generated
vendored
@ -1,28 +0,0 @@
|
|||||||
package libnmdc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
DEFAULT_CLIENT_TAG string = "libnmdc.go"
|
|
||||||
DEFAULT_CLIENT_VERSION string = "0.17"
|
|
||||||
DEFAULT_HUB_NAME string = "(unknown)"
|
|
||||||
SEND_KEEPALIVE_EVERY time.Duration = 29 * time.Second
|
|
||||||
AUTO_RECONNECT_AFTER time.Duration = 30 * time.Second
|
|
||||||
RECONNECT_IF_NO_DATA_RECIEVED_IN time.Duration = 24 * time.Hour // we expect keepalives wayyyy more frequently than this
|
|
||||||
AUTODETECT_ADC_NMDC_TIMEOUT time.Duration = 3 * time.Second
|
|
||||||
)
|
|
||||||
|
|
||||||
var ErrNotConnected error = errors.New("Not connected")
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
41
vendor/code.ivysaur.me/libnmdc/tth.go
generated
vendored
41
vendor/code.ivysaur.me/libnmdc/tth.go
generated
vendored
@ -1,41 +0,0 @@
|
|||||||
package libnmdc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base32"
|
|
||||||
"errors"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/cxmcc/tiger"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Base32 encodes the input slice in BASE32 string format without any trailing
|
|
||||||
// padding equals characters.
|
|
||||||
func Base32(input []byte) string {
|
|
||||||
return strings.TrimRight(base32.StdEncoding.EncodeToString(input), "=")
|
|
||||||
}
|
|
||||||
|
|
||||||
// TTH returns the TTH hash of a string in raw byte format. Use the Base32()
|
|
||||||
// function to convert it to normal string format.
|
|
||||||
// This is a basic implementation that only supports content up to 1024 bytes in length.
|
|
||||||
func TTH(input string) ([]byte, error) {
|
|
||||||
|
|
||||||
// Short segments do not need to be padded.
|
|
||||||
// Content above 1024 bytes needs tree handling (0x00 prefix for leaf nodes,
|
|
||||||
// 0x01 prefix for hash nodes) but for content less than 1024 bytes, just
|
|
||||||
// return the leaf hash
|
|
||||||
// @ref http://adc.sourceforge.net/draft-jchapweske-thex-02.html
|
|
||||||
if len(input) > 1024 {
|
|
||||||
return nil, errors.New("TTH content exceeded 1024 bytes")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Single leaf hash only
|
|
||||||
leafHash := tiger.New()
|
|
||||||
leafHash.Write([]byte("\x00" + input))
|
|
||||||
return leafHash.Sum(nil), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Tiger(input string) []byte {
|
|
||||||
leafHash := tiger.New()
|
|
||||||
leafHash.Write([]byte(input))
|
|
||||||
return leafHash.Sum(nil)
|
|
||||||
}
|
|
22
vendor/github.com/cxmcc/tiger/.gitignore
generated
vendored
22
vendor/github.com/cxmcc/tiger/.gitignore
generated
vendored
@ -1,22 +0,0 @@
|
|||||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
|
||||||
*.o
|
|
||||||
*.a
|
|
||||||
*.so
|
|
||||||
|
|
||||||
# Folders
|
|
||||||
_obj
|
|
||||||
_test
|
|
||||||
|
|
||||||
# Architecture specific extensions/prefixes
|
|
||||||
*.[568vq]
|
|
||||||
[568vq].out
|
|
||||||
|
|
||||||
*.cgo1.go
|
|
||||||
*.cgo2.c
|
|
||||||
_cgo_defun.c
|
|
||||||
_cgo_gotypes.go
|
|
||||||
_cgo_export.*
|
|
||||||
|
|
||||||
_testmain.go
|
|
||||||
|
|
||||||
*.exe
|
|
7
vendor/github.com/cxmcc/tiger/.travis.yml
generated
vendored
7
vendor/github.com/cxmcc/tiger/.travis.yml
generated
vendored
@ -1,7 +0,0 @@
|
|||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.x
|
|
||||||
- 1.6
|
|
||||||
- 1.7.x
|
|
||||||
- master
|
|
20
vendor/github.com/cxmcc/tiger/LICENSE
generated
vendored
20
vendor/github.com/cxmcc/tiger/LICENSE
generated
vendored
@ -1,20 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2013 Xiuming Chen
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
this software and associated documentation files (the "Software"), to deal in
|
|
||||||
the Software without restriction, including without limitation the rights to
|
|
||||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
||||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
48
vendor/github.com/cxmcc/tiger/README.md
generated
vendored
48
vendor/github.com/cxmcc/tiger/README.md
generated
vendored
@ -1,48 +0,0 @@
|
|||||||
Tiger cryptographic hash function for Go
|
|
||||||
|
|
||||||
-----
|
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/cxmcc/tiger.svg?branch=master)](https://travis-ci.org/cxmcc/tiger)
|
|
||||||
[![GoDoc](http://godoc.org/github.com/cxmcc/tiger?status.png)](http://godoc.org/github.com/cxmcc/tiger)
|
|
||||||
|
|
||||||
|
|
||||||
### About Tiger
|
|
||||||
|
|
||||||
* Tiger cryptographic hash function is designed by Ross Anderson and Eli Biham in 1995.
|
|
||||||
* The size of a Tiger hash value is 192 bits. Truncated versions (Tiger/128, Tiger/160) are simply prefixes of Tiger/192.
|
|
||||||
* Tiger2 is a variant where the message is padded by first appending a byte 0x80, rather than 0x01 as in the case of Tiger.
|
|
||||||
* Links: [paper](http://www.cs.technion.ac.il/~biham/Reports/Tiger/), [wikipedia](http://en.wikipedia.org/wiki/Tiger_\(cryptography\))
|
|
||||||
|
|
||||||
### API Documentation
|
|
||||||
|
|
||||||
Implementing [hash.Hash](http://golang.org/pkg/hash/#Hash). Usage is pretty much the same as other stanard hashing libraries.
|
|
||||||
Documentation currently available at Godoc: [http://godoc.org/github.com/cxmcc/tiger](http://godoc.org/github.com/cxmcc/tiger)
|
|
||||||
|
|
||||||
|
|
||||||
### Installing
|
|
||||||
~~~
|
|
||||||
go get github.com/cxmcc/tiger
|
|
||||||
~~~
|
|
||||||
|
|
||||||
### Example
|
|
||||||
~~~ go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"github.com/cxmcc/tiger"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
h := tiger.New()
|
|
||||||
io.WriteString(h, "Example for tiger")
|
|
||||||
fmt.Printf("Output: %x\n", h.Sum(nil))
|
|
||||||
// Output: 82bd060e19f945014f0063e8f0e6d7decfa9edfd97e76743
|
|
||||||
}
|
|
||||||
~~~
|
|
||||||
|
|
||||||
|
|
||||||
### License
|
|
||||||
|
|
||||||
It's MIT License
|
|
96
vendor/github.com/cxmcc/tiger/compress.go
generated
vendored
96
vendor/github.com/cxmcc/tiger/compress.go
generated
vendored
@ -1,96 +0,0 @@
|
|||||||
package tiger
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
var littleEndian bool
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
x := uint32(0x04030201)
|
|
||||||
y := [4]byte{0x1, 0x2, 0x3, 0x4}
|
|
||||||
littleEndian = *(*[4]byte)(unsafe.Pointer(&x)) == y
|
|
||||||
}
|
|
||||||
|
|
||||||
func pass(a, b, c uint64, x []uint64, mul uint64) (uint64, uint64, uint64) {
|
|
||||||
a, b, c = round(a, b, c, x[0], mul)
|
|
||||||
b, c, a = round(b, c, a, x[1], mul)
|
|
||||||
c, a, b = round(c, a, b, x[2], mul)
|
|
||||||
a, b, c = round(a, b, c, x[3], mul)
|
|
||||||
b, c, a = round(b, c, a, x[4], mul)
|
|
||||||
c, a, b = round(c, a, b, x[5], mul)
|
|
||||||
a, b, c = round(a, b, c, x[6], mul)
|
|
||||||
b, c, a = round(b, c, a, x[7], mul)
|
|
||||||
return a, b, c
|
|
||||||
}
|
|
||||||
|
|
||||||
func round(a, b, c, x, mul uint64) (uint64, uint64, uint64) {
|
|
||||||
c ^= x
|
|
||||||
a -= t1[c&0xff] ^ t2[(c>>16)&0xff] ^ t3[(c>>32)&0xff] ^ t4[(c>>48)&0xff]
|
|
||||||
b += t4[(c>>8)&0xff] ^ t3[(c>>24)&0xff] ^ t2[(c>>40)&0xff] ^ t1[(c>>56)&0xff]
|
|
||||||
b *= mul
|
|
||||||
return a, b, c
|
|
||||||
}
|
|
||||||
|
|
||||||
func keySchedule(x []uint64) {
|
|
||||||
x[0] -= x[7] ^ 0xa5a5a5a5a5a5a5a5
|
|
||||||
x[1] ^= x[0]
|
|
||||||
x[2] += x[1]
|
|
||||||
x[3] -= x[2] ^ ((^x[1]) << 19)
|
|
||||||
x[4] ^= x[3]
|
|
||||||
x[5] += x[4]
|
|
||||||
x[6] -= x[5] ^ ((^x[4]) >> 23)
|
|
||||||
x[7] ^= x[6]
|
|
||||||
x[0] += x[7]
|
|
||||||
x[1] -= x[0] ^ ((^x[7]) << 19)
|
|
||||||
x[2] ^= x[1]
|
|
||||||
x[3] += x[2]
|
|
||||||
x[4] -= x[3] ^ ((^x[2]) >> 23)
|
|
||||||
x[5] ^= x[4]
|
|
||||||
x[6] += x[5]
|
|
||||||
x[7] -= x[6] ^ 0x0123456789abcdef
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *digest) compress(data []byte) {
|
|
||||||
// save_abc
|
|
||||||
a := d.a
|
|
||||||
b := d.b
|
|
||||||
c := d.c
|
|
||||||
|
|
||||||
var x []uint64
|
|
||||||
if littleEndian {
|
|
||||||
x = []uint64{
|
|
||||||
binary.LittleEndian.Uint64(data[0:8]),
|
|
||||||
binary.LittleEndian.Uint64(data[8:16]),
|
|
||||||
binary.LittleEndian.Uint64(data[16:24]),
|
|
||||||
binary.LittleEndian.Uint64(data[24:32]),
|
|
||||||
binary.LittleEndian.Uint64(data[32:40]),
|
|
||||||
binary.LittleEndian.Uint64(data[40:48]),
|
|
||||||
binary.LittleEndian.Uint64(data[48:56]),
|
|
||||||
binary.LittleEndian.Uint64(data[56:64]),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
x = []uint64{
|
|
||||||
binary.BigEndian.Uint64(data[0:8]),
|
|
||||||
binary.BigEndian.Uint64(data[8:16]),
|
|
||||||
binary.BigEndian.Uint64(data[16:24]),
|
|
||||||
binary.BigEndian.Uint64(data[24:32]),
|
|
||||||
binary.BigEndian.Uint64(data[32:40]),
|
|
||||||
binary.BigEndian.Uint64(data[40:48]),
|
|
||||||
binary.BigEndian.Uint64(data[48:56]),
|
|
||||||
binary.BigEndian.Uint64(data[56:64]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
d.a, d.b, d.c = pass(d.a, d.b, d.c, x, 5)
|
|
||||||
keySchedule(x)
|
|
||||||
d.c, d.a, d.b = pass(d.c, d.a, d.b, x, 7)
|
|
||||||
keySchedule(x)
|
|
||||||
d.b, d.c, d.a = pass(d.b, d.c, d.a, x, 9)
|
|
||||||
|
|
||||||
// feedforward
|
|
||||||
d.a ^= a
|
|
||||||
d.b -= b
|
|
||||||
d.c += c
|
|
||||||
}
|
|
269
vendor/github.com/cxmcc/tiger/sboxes.go
generated
vendored
269
vendor/github.com/cxmcc/tiger/sboxes.go
generated
vendored
@ -1,269 +0,0 @@
|
|||||||
package tiger
|
|
||||||
|
|
||||||
var t1 = [...]uint64{
|
|
||||||
0x02aab17cf7e90c5e, 0xac424b03e243a8ec, 0x72cd5be30dd5fcd3, 0x6d019b93f6f97f3a,
|
|
||||||
0xcd9978ffd21f9193, 0x7573a1c9708029e2, 0xb164326b922a83c3, 0x46883eee04915870,
|
|
||||||
0xeaace3057103ece6, 0xc54169b808a3535c, 0x4ce754918ddec47c, 0x0aa2f4dfdc0df40c,
|
|
||||||
0x10b76f18a74dbefa, 0xc6ccb6235ad1ab6a, 0x13726121572fe2ff, 0x1a488c6f199d921e,
|
|
||||||
0x4bc9f9f4da0007ca, 0x26f5e6f6e85241c7, 0x859079dbea5947b6, 0x4f1885c5c99e8c92,
|
|
||||||
0xd78e761ea96f864b, 0x8e36428c52b5c17d, 0x69cf6827373063c1, 0xb607c93d9bb4c56e,
|
|
||||||
0x7d820e760e76b5ea, 0x645c9cc6f07fdc42, 0xbf38a078243342e0, 0x5f6b343c9d2e7d04,
|
|
||||||
0xf2c28aeb600b0ec6, 0x6c0ed85f7254bcac, 0x71592281a4db4fe5, 0x1967fa69ce0fed9f,
|
|
||||||
0xfd5293f8b96545db, 0xc879e9d7f2a7600b, 0x860248920193194e, 0xa4f9533b2d9cc0b3,
|
|
||||||
0x9053836c15957613, 0xdb6dcf8afc357bf1, 0x18beea7a7a370f57, 0x037117ca50b99066,
|
|
||||||
0x6ab30a9774424a35, 0xf4e92f02e325249b, 0x7739db07061ccae1, 0xd8f3b49ceca42a05,
|
|
||||||
0xbd56be3f51382f73, 0x45faed5843b0bb28, 0x1c813d5c11bf1f83, 0x8af0e4b6d75fa169,
|
|
||||||
0x33ee18a487ad9999, 0x3c26e8eab1c94410, 0xb510102bc0a822f9, 0x141eef310ce6123b,
|
|
||||||
0xfc65b90059ddb154, 0xe0158640c5e0e607, 0x884e079826c3a3cf, 0x930d0d9523c535fd,
|
|
||||||
0x35638d754e9a2b00, 0x4085fccf40469dd5, 0xc4b17ad28be23a4c, 0xcab2f0fc6a3e6a2e,
|
|
||||||
0x2860971a6b943fcd, 0x3dde6ee212e30446, 0x6222f32ae01765ae, 0x5d550bb5478308fe,
|
|
||||||
0xa9efa98da0eda22a, 0xc351a71686c40da7, 0x1105586d9c867c84, 0xdcffee85fda22853,
|
|
||||||
0xccfbd0262c5eef76, 0xbaf294cb8990d201, 0xe69464f52afad975, 0x94b013afdf133e14,
|
|
||||||
0x06a7d1a32823c958, 0x6f95fe5130f61119, 0xd92ab34e462c06c0, 0xed7bde33887c71d2,
|
|
||||||
0x79746d6e6518393e, 0x5ba419385d713329, 0x7c1ba6b948a97564, 0x31987c197bfdac67,
|
|
||||||
0xde6c23c44b053d02, 0x581c49fed002d64d, 0xdd474d6338261571, 0xaa4546c3e473d062,
|
|
||||||
0x928fce349455f860, 0x48161bbacaab94d9, 0x63912430770e6f68, 0x6ec8a5e602c6641c,
|
|
||||||
0x87282515337ddd2b, 0x2cda6b42034b701b, 0xb03d37c181cb096d, 0xe108438266c71c6f,
|
|
||||||
0x2b3180c7eb51b255, 0xdf92b82f96c08bbc, 0x5c68c8c0a632f3ba, 0x5504cc861c3d0556,
|
|
||||||
0xabbfa4e55fb26b8f, 0x41848b0ab3baceb4, 0xb334a273aa445d32, 0xbca696f0a85ad881,
|
|
||||||
0x24f6ec65b528d56c, 0x0ce1512e90f4524a, 0x4e9dd79d5506d35a, 0x258905fac6ce9779,
|
|
||||||
0x2019295b3e109b33, 0xf8a9478b73a054cc, 0x2924f2f934417eb0, 0x3993357d536d1bc4,
|
|
||||||
0x38a81ac21db6ff8b, 0x47c4fbf17d6016bf, 0x1e0faadd7667e3f5, 0x7abcff62938beb96,
|
|
||||||
0xa78dad948fc179c9, 0x8f1f98b72911e50d, 0x61e48eae27121a91, 0x4d62f7ad31859808,
|
|
||||||
0xeceba345ef5ceaeb, 0xf5ceb25ebc9684ce, 0xf633e20cb7f76221, 0xa32cdf06ab8293e4,
|
|
||||||
0x985a202ca5ee2ca4, 0xcf0b8447cc8a8fb1, 0x9f765244979859a3, 0xa8d516b1a1240017,
|
|
||||||
0x0bd7ba3ebb5dc726, 0xe54bca55b86adb39, 0x1d7a3afd6c478063, 0x519ec608e7669edd,
|
|
||||||
0x0e5715a2d149aa23, 0x177d4571848ff194, 0xeeb55f3241014c22, 0x0f5e5ca13a6e2ec2,
|
|
||||||
0x8029927b75f5c361, 0xad139fabc3d6e436, 0x0d5df1a94ccf402f, 0x3e8bd948bea5dfc8,
|
|
||||||
0xa5a0d357bd3ff77e, 0xa2d12e251f74f645, 0x66fd9e525e81a082, 0x2e0c90ce7f687a49,
|
|
||||||
0xc2e8bcbeba973bc5, 0x000001bce509745f, 0x423777bbe6dab3d6, 0xd1661c7eaef06eb5,
|
|
||||||
0xa1781f354daacfd8, 0x2d11284a2b16affc, 0xf1fc4f67fa891d1f, 0x73ecc25dcb920ada,
|
|
||||||
0xae610c22c2a12651, 0x96e0a810d356b78a, 0x5a9a381f2fe7870f, 0xd5ad62ede94e5530,
|
|
||||||
0xd225e5e8368d1427, 0x65977b70c7af4631, 0x99f889b2de39d74f, 0x233f30bf54e1d143,
|
|
||||||
0x9a9675d3d9a63c97, 0x5470554ff334f9a8, 0x166acb744a4f5688, 0x70c74caab2e4aead,
|
|
||||||
0xf0d091646f294d12, 0x57b82a89684031d1, 0xefd95a5a61be0b6b, 0x2fbd12e969f2f29a,
|
|
||||||
0x9bd37013feff9fe8, 0x3f9b0404d6085a06, 0x4940c1f3166cfe15, 0x09542c4dcdf3defb,
|
|
||||||
0xb4c5218385cd5ce3, 0xc935b7dc4462a641, 0x3417f8a68ed3b63f, 0xb80959295b215b40,
|
|
||||||
0xf99cdaef3b8c8572, 0x018c0614f8fcb95d, 0x1b14accd1a3acdf3, 0x84d471f200bb732d,
|
|
||||||
0xc1a3110e95e8da16, 0x430a7220bf1a82b8, 0xb77e090d39df210e, 0x5ef4bd9f3cd05e9d,
|
|
||||||
0x9d4ff6da7e57a444, 0xda1d60e183d4a5f8, 0xb287c38417998e47, 0xfe3edc121bb31886,
|
|
||||||
0xc7fe3ccc980ccbef, 0xe46fb590189bfd03, 0x3732fd469a4c57dc, 0x7ef700a07cf1ad65,
|
|
||||||
0x59c64468a31d8859, 0x762fb0b4d45b61f6, 0x155baed099047718, 0x68755e4c3d50baa6,
|
|
||||||
0xe9214e7f22d8b4df, 0x2addbf532eac95f4, 0x32ae3909b4bd0109, 0x834df537b08e3450,
|
|
||||||
0xfa209da84220728d, 0x9e691d9b9efe23f7, 0x0446d288c4ae8d7f, 0x7b4cc524e169785b,
|
|
||||||
0x21d87f0135ca1385, 0xcebb400f137b8aa5, 0x272e2b66580796be, 0x3612264125c2b0de,
|
|
||||||
0x057702bdad1efbb2, 0xd4babb8eacf84be9, 0x91583139641bc67b, 0x8bdc2de08036e024,
|
|
||||||
0x603c8156f49f68ed, 0xf7d236f7dbef5111, 0x9727c4598ad21e80, 0xa08a0896670a5fd7,
|
|
||||||
0xcb4a8f4309eba9cb, 0x81af564b0f7036a1, 0xc0b99aa778199abd, 0x959f1ec83fc8e952,
|
|
||||||
0x8c505077794a81b9, 0x3acaaf8f056338f0, 0x07b43f50627a6778, 0x4a44ab49f5eccc77,
|
|
||||||
0x3bc3d6e4b679ee98, 0x9cc0d4d1cf14108c, 0x4406c00b206bc8a0, 0x82a18854c8d72d89,
|
|
||||||
0x67e366b35c3c432c, 0xb923dd61102b37f2, 0x56ab2779d884271d, 0xbe83e1b0ff1525af,
|
|
||||||
0xfb7c65d4217e49a9, 0x6bdbe0e76d48e7d4, 0x08df828745d9179e, 0x22ea6a9add53bd34,
|
|
||||||
0xe36e141c5622200a, 0x7f805d1b8cb750ee, 0xafe5c7a59f58e837, 0xe27f996a4fb1c23c,
|
|
||||||
0xd3867dfb0775f0d0, 0xd0e673de6e88891a, 0x123aeb9eafb86c25, 0x30f1d5d5c145b895,
|
|
||||||
0xbb434a2dee7269e7, 0x78cb67ecf931fa38, 0xf33b0372323bbf9c, 0x52d66336fb279c74,
|
|
||||||
0x505f33ac0afb4eaa, 0xe8a5cd99a2cce187, 0x534974801e2d30bb, 0x8d2d5711d5876d90,
|
|
||||||
0x1f1a412891bc038e, 0xd6e2e71d82e56648, 0x74036c3a497732b7, 0x89b67ed96361f5ab,
|
|
||||||
0xffed95d8f1ea02a2, 0xe72b3bd61464d43d, 0xa6300f170bdc4820, 0xebc18760ed78a77a,
|
|
||||||
}
|
|
||||||
|
|
||||||
var t2 = [...]uint64{
|
|
||||||
0xe6a6be5a05a12138, 0xb5a122a5b4f87c98, 0x563c6089140b6990, 0x4c46cb2e391f5dd5,
|
|
||||||
0xd932addbc9b79434, 0x08ea70e42015aff5, 0xd765a6673e478cf1, 0xc4fb757eab278d99,
|
|
||||||
0xdf11c6862d6e0692, 0xddeb84f10d7f3b16, 0x6f2ef604a665ea04, 0x4a8e0f0ff0e0dfb3,
|
|
||||||
0xa5edeef83dbcba51, 0xfc4f0a2a0ea4371e, 0xe83e1da85cb38429, 0xdc8ff882ba1b1ce2,
|
|
||||||
0xcd45505e8353e80d, 0x18d19a00d4db0717, 0x34a0cfeda5f38101, 0x0be77e518887caf2,
|
|
||||||
0x1e341438b3c45136, 0xe05797f49089ccf9, 0xffd23f9df2591d14, 0x543dda228595c5cd,
|
|
||||||
0x661f81fd99052a33, 0x8736e641db0f7b76, 0x15227725418e5307, 0xe25f7f46162eb2fa,
|
|
||||||
0x48a8b2126c13d9fe, 0xafdc541792e76eea, 0x03d912bfc6d1898f, 0x31b1aafa1b83f51b,
|
|
||||||
0xf1ac2796e42ab7d9, 0x40a3a7d7fcd2ebac, 0x1056136d0afbbcc5, 0x7889e1dd9a6d0c85,
|
|
||||||
0xd33525782a7974aa, 0xa7e25d09078ac09b, 0xbd4138b3eac6edd0, 0x920abfbe71eb9e70,
|
|
||||||
0xa2a5d0f54fc2625c, 0xc054e36b0b1290a3, 0xf6dd59ff62fe932b, 0x3537354511a8ac7d,
|
|
||||||
0xca845e9172fadcd4, 0x84f82b60329d20dc, 0x79c62ce1cd672f18, 0x8b09a2add124642c,
|
|
||||||
0xd0c1e96a19d9e726, 0x5a786a9b4ba9500c, 0x0e020336634c43f3, 0xc17b474aeb66d822,
|
|
||||||
0x6a731ae3ec9baac2, 0x8226667ae0840258, 0x67d4567691caeca5, 0x1d94155c4875adb5,
|
|
||||||
0x6d00fd985b813fdf, 0x51286efcb774cd06, 0x5e8834471fa744af, 0xf72ca0aee761ae2e,
|
|
||||||
0xbe40e4cdaee8e09a, 0xe9970bbb5118f665, 0x726e4beb33df1964, 0x703b000729199762,
|
|
||||||
0x4631d816f5ef30a7, 0xb880b5b51504a6be, 0x641793c37ed84b6c, 0x7b21ed77f6e97d96,
|
|
||||||
0x776306312ef96b73, 0xae528948e86ff3f4, 0x53dbd7f286a3f8f8, 0x16cadce74cfc1063,
|
|
||||||
0x005c19bdfa52c6dd, 0x68868f5d64d46ad3, 0x3a9d512ccf1e186a, 0x367e62c2385660ae,
|
|
||||||
0xe359e7ea77dcb1d7, 0x526c0773749abe6e, 0x735ae5f9d09f734b, 0x493fc7cc8a558ba8,
|
|
||||||
0xb0b9c1533041ab45, 0x321958ba470a59bd, 0x852db00b5f46c393, 0x91209b2bd336b0e5,
|
|
||||||
0x6e604f7d659ef19f, 0xb99a8ae2782ccb24, 0xccf52ab6c814c4c7, 0x4727d9afbe11727b,
|
|
||||||
0x7e950d0c0121b34d, 0x756f435670ad471f, 0xf5add442615a6849, 0x4e87e09980b9957a,
|
|
||||||
0x2acfa1df50aee355, 0xd898263afd2fd556, 0xc8f4924dd80c8fd6, 0xcf99ca3d754a173a,
|
|
||||||
0xfe477bacaf91bf3c, 0xed5371f6d690c12d, 0x831a5c285e687094, 0xc5d3c90a3708a0a4,
|
|
||||||
0x0f7f903717d06580, 0x19f9bb13b8fdf27f, 0xb1bd6f1b4d502843, 0x1c761ba38fff4012,
|
|
||||||
0x0d1530c4e2e21f3b, 0x8943ce69a7372c8a, 0xe5184e11feb5ce66, 0x618bdb80bd736621,
|
|
||||||
0x7d29bad68b574d0b, 0x81bb613e25e6fe5b, 0x071c9c10bc07913f, 0xc7beeb7909ac2d97,
|
|
||||||
0xc3e58d353bc5d757, 0xeb017892f38f61e8, 0xd4effb9c9b1cc21a, 0x99727d26f494f7ab,
|
|
||||||
0xa3e063a2956b3e03, 0x9d4a8b9a4aa09c30, 0x3f6ab7d500090fb4, 0x9cc0f2a057268ac0,
|
|
||||||
0x3dee9d2dedbf42d1, 0x330f49c87960a972, 0xc6b2720287421b41, 0x0ac59ec07c00369c,
|
|
||||||
0xef4eac49cb353425, 0xf450244eef0129d8, 0x8acc46e5caf4deb6, 0x2ffeab63989263f7,
|
|
||||||
0x8f7cb9fe5d7a4578, 0x5bd8f7644e634635, 0x427a7315bf2dc900, 0x17d0c4aa2125261c,
|
|
||||||
0x3992486c93518e50, 0xb4cbfee0a2d7d4c3, 0x7c75d6202c5ddd8d, 0xdbc295d8e35b6c61,
|
|
||||||
0x60b369d302032b19, 0xce42685fdce44132, 0x06f3ddb9ddf65610, 0x8ea4d21db5e148f0,
|
|
||||||
0x20b0fce62fcd496f, 0x2c1b912358b0ee31, 0xb28317b818f5a308, 0xa89c1e189ca6d2cf,
|
|
||||||
0x0c6b18576aaadbc8, 0xb65deaa91299fae3, 0xfb2b794b7f1027e7, 0x04e4317f443b5beb,
|
|
||||||
0x4b852d325939d0a6, 0xd5ae6beefb207ffc, 0x309682b281c7d374, 0xbae309a194c3b475,
|
|
||||||
0x8cc3f97b13b49f05, 0x98a9422ff8293967, 0x244b16b01076ff7c, 0xf8bf571c663d67ee,
|
|
||||||
0x1f0d6758eee30da1, 0xc9b611d97adeb9b7, 0xb7afd5887b6c57a2, 0x6290ae846b984fe1,
|
|
||||||
0x94df4cdeacc1a5fd, 0x058a5bd1c5483aff, 0x63166cc142ba3c37, 0x8db8526eb2f76f40,
|
|
||||||
0xe10880036f0d6d4e, 0x9e0523c9971d311d, 0x45ec2824cc7cd691, 0x575b8359e62382c9,
|
|
||||||
0xfa9e400dc4889995, 0xd1823ecb45721568, 0xdafd983b8206082f, 0xaa7d29082386a8cb,
|
|
||||||
0x269fcd4403b87588, 0x1b91f5f728bdd1e0, 0xe4669f39040201f6, 0x7a1d7c218cf04ade,
|
|
||||||
0x65623c29d79ce5ce, 0x2368449096c00bb1, 0xab9bf1879da503ba, 0xbc23ecb1a458058e,
|
|
||||||
0x9a58df01bb401ecc, 0xa070e868a85f143d, 0x4ff188307df2239e, 0x14d565b41a641183,
|
|
||||||
0xee13337452701602, 0x950e3dcf3f285e09, 0x59930254b9c80953, 0x3bf299408930da6d,
|
|
||||||
0xa955943f53691387, 0xa15edecaa9cb8784, 0x29142127352be9a0, 0x76f0371fff4e7afb,
|
|
||||||
0x0239f450274f2228, 0xbb073af01d5e868b, 0xbfc80571c10e96c1, 0xd267088568222e23,
|
|
||||||
0x9671a3d48e80b5b0, 0x55b5d38ae193bb81, 0x693ae2d0a18b04b8, 0x5c48b4ecadd5335f,
|
|
||||||
0xfd743b194916a1ca, 0x2577018134be98c4, 0xe77987e83c54a4ad, 0x28e11014da33e1b9,
|
|
||||||
0x270cc59e226aa213, 0x71495f756d1a5f60, 0x9be853fb60afef77, 0xadc786a7f7443dbf,
|
|
||||||
0x0904456173b29a82, 0x58bc7a66c232bd5e, 0xf306558c673ac8b2, 0x41f639c6b6c9772a,
|
|
||||||
0x216defe99fda35da, 0x11640cc71c7be615, 0x93c43694565c5527, 0xea038e6246777839,
|
|
||||||
0xf9abf3ce5a3e2469, 0x741e768d0fd312d2, 0x0144b883ced652c6, 0xc20b5a5ba33f8552,
|
|
||||||
0x1ae69633c3435a9d, 0x97a28ca4088cfdec, 0x8824a43c1e96f420, 0x37612fa66eeea746,
|
|
||||||
0x6b4cb165f9cf0e5a, 0x43aa1c06a0abfb4a, 0x7f4dc26ff162796b, 0x6cbacc8e54ed9b0f,
|
|
||||||
0xa6b7ffefd2bb253e, 0x2e25bc95b0a29d4f, 0x86d6a58bdef1388c, 0xded74ac576b6f054,
|
|
||||||
0x8030bdbc2b45805d, 0x3c81af70e94d9289, 0x3eff6dda9e3100db, 0xb38dc39fdfcc8847,
|
|
||||||
0x123885528d17b87e, 0xf2da0ed240b1b642, 0x44cefadcd54bf9a9, 0x1312200e433c7ee6,
|
|
||||||
0x9ffcc84f3a78c748, 0xf0cd1f72248576bb, 0xec6974053638cfe4, 0x2ba7b67c0cec4e4c,
|
|
||||||
0xac2f4df3e5ce32ed, 0xcb33d14326ea4c11, 0xa4e9044cc77e58bc, 0x5f513293d934fcef,
|
|
||||||
0x5dc9645506e55444, 0x50de418f317de40a, 0x388cb31a69dde259, 0x2db4a83455820a86,
|
|
||||||
0x9010a91e84711ae9, 0x4df7f0b7b1498371, 0xd62a2eabc0977179, 0x22fac097aa8d5c0e,
|
|
||||||
}
|
|
||||||
|
|
||||||
var t3 = [...]uint64{
|
|
||||||
0xf49fcc2ff1daf39b, 0x487fd5c66ff29281, 0xe8a30667fcdca83f, 0x2c9b4be3d2fcce63,
|
|
||||||
0xda3ff74b93fbbbc2, 0x2fa165d2fe70ba66, 0xa103e279970e93d4, 0xbecdec77b0e45e71,
|
|
||||||
0xcfb41e723985e497, 0xb70aaa025ef75017, 0xd42309f03840b8e0, 0x8efc1ad035898579,
|
|
||||||
0x96c6920be2b2abc5, 0x66af4163375a9172, 0x2174abdcca7127fb, 0xb33ccea64a72ff41,
|
|
||||||
0xf04a4933083066a5, 0x8d970acdd7289af5, 0x8f96e8e031c8c25e, 0xf3fec02276875d47,
|
|
||||||
0xec7bf310056190dd, 0xf5adb0aebb0f1491, 0x9b50f8850fd58892, 0x4975488358b74de8,
|
|
||||||
0xa3354ff691531c61, 0x0702bbe481d2c6ee, 0x89fb24057deded98, 0xac3075138596e902,
|
|
||||||
0x1d2d3580172772ed, 0xeb738fc28e6bc30d, 0x5854ef8f63044326, 0x9e5c52325add3bbe,
|
|
||||||
0x90aa53cf325c4623, 0xc1d24d51349dd067, 0x2051cfeea69ea624, 0x13220f0a862e7e4f,
|
|
||||||
0xce39399404e04864, 0xd9c42ca47086fcb7, 0x685ad2238a03e7cc, 0x066484b2ab2ff1db,
|
|
||||||
0xfe9d5d70efbf79ec, 0x5b13b9dd9c481854, 0x15f0d475ed1509ad, 0x0bebcd060ec79851,
|
|
||||||
0xd58c6791183ab7f8, 0xd1187c5052f3eee4, 0xc95d1192e54e82ff, 0x86eea14cb9ac6ca2,
|
|
||||||
0x3485beb153677d5d, 0xdd191d781f8c492a, 0xf60866baa784ebf9, 0x518f643ba2d08c74,
|
|
||||||
0x8852e956e1087c22, 0xa768cb8dc410ae8d, 0x38047726bfec8e1a, 0xa67738b4cd3b45aa,
|
|
||||||
0xad16691cec0dde19, 0xc6d4319380462e07, 0xc5a5876d0ba61938, 0x16b9fa1fa58fd840,
|
|
||||||
0x188ab1173ca74f18, 0xabda2f98c99c021f, 0x3e0580ab134ae816, 0x5f3b05b773645abb,
|
|
||||||
0x2501a2be5575f2f6, 0x1b2f74004e7e8ba9, 0x1cd7580371e8d953, 0x7f6ed89562764e30,
|
|
||||||
0xb15926ff596f003d, 0x9f65293da8c5d6b9, 0x6ecef04dd690f84c, 0x4782275fff33af88,
|
|
||||||
0xe41433083f820801, 0xfd0dfe409a1af9b5, 0x4325a3342cdb396b, 0x8ae77e62b301b252,
|
|
||||||
0xc36f9e9f6655615a, 0x85455a2d92d32c09, 0xf2c7dea949477485, 0x63cfb4c133a39eba,
|
|
||||||
0x83b040cc6ebc5462, 0x3b9454c8fdb326b0, 0x56f56a9e87ffd78c, 0x2dc2940d99f42bc6,
|
|
||||||
0x98f7df096b096e2d, 0x19a6e01e3ad852bf, 0x42a99ccbdbd4b40b, 0xa59998af45e9c559,
|
|
||||||
0x366295e807d93186, 0x6b48181bfaa1f773, 0x1fec57e2157a0a1d, 0x4667446af6201ad5,
|
|
||||||
0xe615ebcacfb0f075, 0xb8f31f4f68290778, 0x22713ed6ce22d11e, 0x3057c1a72ec3c93b,
|
|
||||||
0xcb46acc37c3f1f2f, 0xdbb893fd02aaf50e, 0x331fd92e600b9fcf, 0xa498f96148ea3ad6,
|
|
||||||
0xa8d8426e8b6a83ea, 0xa089b274b7735cdc, 0x87f6b3731e524a11, 0x118808e5cbc96749,
|
|
||||||
0x9906e4c7b19bd394, 0xafed7f7e9b24a20c, 0x6509eadeeb3644a7, 0x6c1ef1d3e8ef0ede,
|
|
||||||
0xb9c97d43e9798fb4, 0xa2f2d784740c28a3, 0x7b8496476197566f, 0x7a5be3e6b65f069d,
|
|
||||||
0xf96330ed78be6f10, 0xeee60de77a076a15, 0x2b4bee4aa08b9bd0, 0x6a56a63ec7b8894e,
|
|
||||||
0x02121359ba34fef4, 0x4cbf99f8283703fc, 0x398071350caf30c8, 0xd0a77a89f017687a,
|
|
||||||
0xf1c1a9eb9e423569, 0x8c7976282dee8199, 0x5d1737a5dd1f7abd, 0x4f53433c09a9fa80,
|
|
||||||
0xfa8b0c53df7ca1d9, 0x3fd9dcbc886ccb77, 0xc040917ca91b4720, 0x7dd00142f9d1dcdf,
|
|
||||||
0x8476fc1d4f387b58, 0x23f8e7c5f3316503, 0x032a2244e7e37339, 0x5c87a5d750f5a74b,
|
|
||||||
0x082b4cc43698992e, 0xdf917becb858f63c, 0x3270b8fc5bf86dda, 0x10ae72bb29b5dd76,
|
|
||||||
0x576ac94e7700362b, 0x1ad112dac61efb8f, 0x691bc30ec5faa427, 0xff246311cc327143,
|
|
||||||
0x3142368e30e53206, 0x71380e31e02ca396, 0x958d5c960aad76f1, 0xf8d6f430c16da536,
|
|
||||||
0xc8ffd13f1be7e1d2, 0x7578ae66004ddbe1, 0x05833f01067be646, 0xbb34b5ad3bfe586d,
|
|
||||||
0x095f34c9a12b97f0, 0x247ab64525d60ca8, 0xdcdbc6f3017477d1, 0x4a2e14d4decad24d,
|
|
||||||
0xbdb5e6d9be0a1eeb, 0x2a7e70f7794301ab, 0xdef42d8a270540fd, 0x01078ec0a34c22c1,
|
|
||||||
0xe5de511af4c16387, 0x7ebb3a52bd9a330a, 0x77697857aa7d6435, 0x004e831603ae4c32,
|
|
||||||
0xe7a21020ad78e312, 0x9d41a70c6ab420f2, 0x28e06c18ea1141e6, 0xd2b28cbd984f6b28,
|
|
||||||
0x26b75f6c446e9d83, 0xba47568c4d418d7f, 0xd80badbfe6183d8e, 0x0e206d7f5f166044,
|
|
||||||
0xe258a43911cbca3e, 0x723a1746b21dc0bc, 0xc7caa854f5d7cdd3, 0x7cac32883d261d9c,
|
|
||||||
0x7690c26423ba942c, 0x17e55524478042b8, 0xe0be477656a2389f, 0x4d289b5e67ab2da0,
|
|
||||||
0x44862b9c8fbbfd31, 0xb47cc8049d141365, 0x822c1b362b91c793, 0x4eb14655fb13dfd8,
|
|
||||||
0x1ecbba0714e2a97b, 0x6143459d5cde5f14, 0x53a8fbf1d5f0ac89, 0x97ea04d81c5e5b00,
|
|
||||||
0x622181a8d4fdb3f3, 0xe9bcd341572a1208, 0x1411258643cce58a, 0x9144c5fea4c6e0a4,
|
|
||||||
0x0d33d06565cf620f, 0x54a48d489f219ca1, 0xc43e5eac6d63c821, 0xa9728b3a72770daf,
|
|
||||||
0xd7934e7b20df87ef, 0xe35503b61a3e86e5, 0xcae321fbc819d504, 0x129a50b3ac60bfa6,
|
|
||||||
0xcd5e68ea7e9fb6c3, 0xb01c90199483b1c7, 0x3de93cd5c295376c, 0xaed52edf2ab9ad13,
|
|
||||||
0x2e60f512c0a07884, 0xbc3d86a3e36210c9, 0x35269d9b163951ce, 0x0c7d6e2ad0cdb5fa,
|
|
||||||
0x59e86297d87f5733, 0x298ef221898db0e7, 0x55000029d1a5aa7e, 0x8bc08ae1b5061b45,
|
|
||||||
0xc2c31c2b6c92703a, 0x94cc596baf25ef42, 0x0a1d73db22540456, 0x04b6a0f9d9c4179a,
|
|
||||||
0xeffdafa2ae3d3c60, 0xf7c8075bb49496c4, 0x9cc5c7141d1cd4e3, 0x78bd1638218e5534,
|
|
||||||
0xb2f11568f850246a, 0xedfabcfa9502bc29, 0x796ce5f2da23051b, 0xaae128b0dc93537c,
|
|
||||||
0x3a493da0ee4b29ae, 0xb5df6b2c416895d7, 0xfcabbd25122d7f37, 0x70810b58105dc4b1,
|
|
||||||
0xe10fdd37f7882a90, 0x524dcab5518a3f5c, 0x3c9e85878451255b, 0x4029828119bd34e2,
|
|
||||||
0x74a05b6f5d3ceccb, 0xb610021542e13eca, 0x0ff979d12f59e2ac, 0x6037da27e4f9cc50,
|
|
||||||
0x5e92975a0df1847d, 0xd66de190d3e623fe, 0x5032d6b87b568048, 0x9a36b7ce8235216e,
|
|
||||||
0x80272a7a24f64b4a, 0x93efed8b8c6916f7, 0x37ddbff44cce1555, 0x4b95db5d4b99bd25,
|
|
||||||
0x92d3fda169812fc0, 0xfb1a4a9a90660bb6, 0x730c196946a4b9b2, 0x81e289aa7f49da68,
|
|
||||||
0x64669a0f83b1a05f, 0x27b3ff7d9644f48b, 0xcc6b615c8db675b3, 0x674f20b9bcebbe95,
|
|
||||||
0x6f31238275655982, 0x5ae488713e45cf05, 0xbf619f9954c21157, 0xeabac46040a8eae9,
|
|
||||||
0x454c6fe9f2c0c1cd, 0x419cf6496412691c, 0xd3dc3bef265b0f70, 0x6d0e60f5c3578a9e,
|
|
||||||
}
|
|
||||||
|
|
||||||
var t4 = [...]uint64{
|
|
||||||
0x5b0e608526323c55, 0x1a46c1a9fa1b59f5, 0xa9e245a17c4c8ffa, 0x65ca5159db2955d7,
|
|
||||||
0x05db0a76ce35afc2, 0x81eac77ea9113d45, 0x528ef88ab6ac0a0d, 0xa09ea253597be3ff,
|
|
||||||
0x430ddfb3ac48cd56, 0xc4b3a67af45ce46f, 0x4ececfd8fbe2d05e, 0x3ef56f10b39935f0,
|
|
||||||
0x0b22d6829cd619c6, 0x17fd460a74df2069, 0x6cf8cc8e8510ed40, 0xd6c824bf3a6ecaa7,
|
|
||||||
0x61243d581a817049, 0x048bacb6bbc163a2, 0xd9a38ac27d44cc32, 0x7fddff5baaf410ab,
|
|
||||||
0xad6d495aa804824b, 0xe1a6a74f2d8c9f94, 0xd4f7851235dee8e3, 0xfd4b7f886540d893,
|
|
||||||
0x247c20042aa4bfda, 0x096ea1c517d1327c, 0xd56966b4361a6685, 0x277da5c31221057d,
|
|
||||||
0x94d59893a43acff7, 0x64f0c51ccdc02281, 0x3d33bcc4ff6189db, 0xe005cb184ce66af1,
|
|
||||||
0xff5ccd1d1db99bea, 0xb0b854a7fe42980f, 0x7bd46a6a718d4b9f, 0xd10fa8cc22a5fd8c,
|
|
||||||
0xd31484952be4bd31, 0xc7fa975fcb243847, 0x4886ed1e5846c407, 0x28cddb791eb70b04,
|
|
||||||
0xc2b00be2f573417f, 0x5c9590452180f877, 0x7a6bddfff370eb00, 0xce509e38d6d9d6a4,
|
|
||||||
0xebeb0f00647fa702, 0x1dcc06cf76606f06, 0xe4d9f28ba286ff0a, 0xd85a305dc918c262,
|
|
||||||
0x475b1d8732225f54, 0x2d4fb51668ccb5fe, 0xa679b9d9d72bba20, 0x53841c0d912d43a5,
|
|
||||||
0x3b7eaa48bf12a4e8, 0x781e0e47f22f1ddf, 0xeff20ce60ab50973, 0x20d261d19dffb742,
|
|
||||||
0x16a12b03062a2e39, 0x1960eb2239650495, 0x251c16fed50eb8b8, 0x9ac0c330f826016e,
|
|
||||||
0xed152665953e7671, 0x02d63194a6369570, 0x5074f08394b1c987, 0x70ba598c90b25ce1,
|
|
||||||
0x794a15810b9742f6, 0x0d5925e9fcaf8c6c, 0x3067716cd868744e, 0x910ab077e8d7731b,
|
|
||||||
0x6a61bbdb5ac42f61, 0x93513efbf0851567, 0xf494724b9e83e9d5, 0xe887e1985c09648d,
|
|
||||||
0x34b1d3c675370cfd, 0xdc35e433bc0d255d, 0xd0aab84234131be0, 0x08042a50b48b7eaf,
|
|
||||||
0x9997c4ee44a3ab35, 0x829a7b49201799d0, 0x263b8307b7c54441, 0x752f95f4fd6a6ca6,
|
|
||||||
0x927217402c08c6e5, 0x2a8ab754a795d9ee, 0xa442f7552f72943d, 0x2c31334e19781208,
|
|
||||||
0x4fa98d7ceaee6291, 0x55c3862f665db309, 0xbd0610175d53b1f3, 0x46fe6cb840413f27,
|
|
||||||
0x3fe03792df0cfa59, 0xcfe700372eb85e8f, 0xa7be29e7adbce118, 0xe544ee5cde8431dd,
|
|
||||||
0x8a781b1b41f1873e, 0xa5c94c78a0d2f0e7, 0x39412e2877b60728, 0xa1265ef3afc9a62c,
|
|
||||||
0xbcc2770c6a2506c5, 0x3ab66dd5dce1ce12, 0xe65499d04a675b37, 0x7d8f523481bfd216,
|
|
||||||
0x0f6f64fcec15f389, 0x74efbe618b5b13c8, 0xacdc82b714273e1d, 0xdd40bfe003199d17,
|
|
||||||
0x37e99257e7e061f8, 0xfa52626904775aaa, 0x8bbbf63a463d56f9, 0xf0013f1543a26e64,
|
|
||||||
0xa8307e9f879ec898, 0xcc4c27a4150177cc, 0x1b432f2cca1d3348, 0xde1d1f8f9f6fa013,
|
|
||||||
0x606602a047a7ddd6, 0xd237ab64cc1cb2c7, 0x9b938e7225fcd1d3, 0xec4e03708e0ff476,
|
|
||||||
0xfeb2fbda3d03c12d, 0xae0bced2ee43889a, 0x22cb8923ebfb4f43, 0x69360d013cf7396d,
|
|
||||||
0x855e3602d2d4e022, 0x073805bad01f784c, 0x33e17a133852f546, 0xdf4874058ac7b638,
|
|
||||||
0xba92b29c678aa14a, 0x0ce89fc76cfaadcd, 0x5f9d4e0908339e34, 0xf1afe9291f5923b9,
|
|
||||||
0x6e3480f60f4a265f, 0xeebf3a2ab29b841c, 0xe21938a88f91b4ad, 0x57dfeff845c6d3c3,
|
|
||||||
0x2f006b0bf62caaf2, 0x62f479ef6f75ee78, 0x11a55ad41c8916a9, 0xf229d29084fed453,
|
|
||||||
0x42f1c27b16b000e6, 0x2b1f76749823c074, 0x4b76eca3c2745360, 0x8c98f463b91691bd,
|
|
||||||
0x14bcc93cf1ade66a, 0x8885213e6d458397, 0x8e177df0274d4711, 0xb49b73b5503f2951,
|
|
||||||
0x10168168c3f96b6b, 0x0e3d963b63cab0ae, 0x8dfc4b5655a1db14, 0xf789f1356e14de5c,
|
|
||||||
0x683e68af4e51dac1, 0xc9a84f9d8d4b0fd9, 0x3691e03f52a0f9d1, 0x5ed86e46e1878e80,
|
|
||||||
0x3c711a0e99d07150, 0x5a0865b20c4e9310, 0x56fbfc1fe4f0682e, 0xea8d5de3105edf9b,
|
|
||||||
0x71abfdb12379187a, 0x2eb99de1bee77b9c, 0x21ecc0ea33cf4523, 0x59a4d7521805c7a1,
|
|
||||||
0x3896f5eb56ae7c72, 0xaa638f3db18f75dc, 0x9f39358dabe9808e, 0xb7defa91c00b72ac,
|
|
||||||
0x6b5541fd62492d92, 0x6dc6dee8f92e4d5b, 0x353f57abc4beea7e, 0x735769d6da5690ce,
|
|
||||||
0x0a234aa642391484, 0xf6f9508028f80d9d, 0xb8e319a27ab3f215, 0x31ad9c1151341a4d,
|
|
||||||
0x773c22a57bef5805, 0x45c7561a07968633, 0xf913da9e249dbe36, 0xda652d9b78a64c68,
|
|
||||||
0x4c27a97f3bc334ef, 0x76621220e66b17f4, 0x967743899acd7d0b, 0xf3ee5bcae0ed6782,
|
|
||||||
0x409f753600c879fc, 0x06d09a39b5926db6, 0x6f83aeb0317ac588, 0x01e6ca4a86381f21,
|
|
||||||
0x66ff3462d19f3025, 0x72207c24ddfd3bfb, 0x4af6b6d3e2ece2eb, 0x9c994dbec7ea08de,
|
|
||||||
0x49ace597b09a8bc4, 0xb38c4766cf0797ba, 0x131b9373c57c2a75, 0xb1822cce61931e58,
|
|
||||||
0x9d7555b909ba1c0c, 0x127fafdd937d11d2, 0x29da3badc66d92e4, 0xa2c1d57154c2ecbc,
|
|
||||||
0x58c5134d82f6fe24, 0x1c3ae3515b62274f, 0xe907c82e01cb8126, 0xf8ed091913e37fcb,
|
|
||||||
0x3249d8f9c80046c9, 0x80cf9bede388fb63, 0x1881539a116cf19e, 0x5103f3f76bd52457,
|
|
||||||
0x15b7e6f5ae47f7a8, 0xdbd7c6ded47e9ccf, 0x44e55c410228bb1a, 0xb647d4255edb4e99,
|
|
||||||
0x5d11882bb8aafc30, 0xf5098bbb29d3212a, 0x8fb5ea14e90296b3, 0x677b942157dd025a,
|
|
||||||
0xfb58e7c0a390acb5, 0x89d3674c83bd4a01, 0x9e2da4df4bf3b93b, 0xfcc41e328cab4829,
|
|
||||||
0x03f38c96ba582c52, 0xcad1bdbd7fd85db2, 0xbbb442c16082ae83, 0xb95fe86ba5da9ab0,
|
|
||||||
0xb22e04673771a93f, 0x845358c9493152d8, 0xbe2a488697b4541e, 0x95a2dc2dd38e6966,
|
|
||||||
0xc02c11ac923c852b, 0x2388b1990df2a87b, 0x7c8008fa1b4f37be, 0x1f70d0c84d54e503,
|
|
||||||
0x5490adec7ece57d4, 0x002b3c27d9063a3a, 0x7eaea3848030a2bf, 0xc602326ded2003c0,
|
|
||||||
0x83a7287d69a94086, 0xc57a5fcb30f57a8a, 0xb56844e479ebe779, 0xa373b40f05dcbce9,
|
|
||||||
0xd71a786e88570ee2, 0x879cbacdbde8f6a0, 0x976ad1bcc164a32f, 0xab21e25e9666d78b,
|
|
||||||
0x901063aae5e5c33c, 0x9818b34448698d90, 0xe36487ae3e1e8abb, 0xafbdf931893bdcb4,
|
|
||||||
0x6345a0dc5fbbd519, 0x8628fe269b9465ca, 0x1e5d01603f9c51ec, 0x4de44006a15049b7,
|
|
||||||
0xbf6c70e5f776cbb1, 0x411218f2ef552bed, 0xcb0c0708705a36a3, 0xe74d14754f986044,
|
|
||||||
0xcd56d9430ea8280e, 0xc12591d7535f5065, 0xc83223f1720aef96, 0xc3a0396f7363a51f,
|
|
||||||
}
|
|
117
vendor/github.com/cxmcc/tiger/tiger.go
generated
vendored
117
vendor/github.com/cxmcc/tiger/tiger.go
generated
vendored
@ -1,117 +0,0 @@
|
|||||||
// Package tiger implements the Tiger hash algorithm
|
|
||||||
// https://github.com/cxmcc/tiger
|
|
||||||
package tiger
|
|
||||||
|
|
||||||
import "hash"
|
|
||||||
|
|
||||||
// The size of a Tiger hash value in bytes
|
|
||||||
const Size = 24
|
|
||||||
|
|
||||||
// The blocksize of Tiger hash function in bytes
|
|
||||||
const BlockSize = 64
|
|
||||||
|
|
||||||
const (
|
|
||||||
chunk = 64
|
|
||||||
initA = 0x0123456789abcdef
|
|
||||||
initB = 0xfedcba9876543210
|
|
||||||
initC = 0xf096a5b4c3b2e187
|
|
||||||
)
|
|
||||||
|
|
||||||
type digest struct {
|
|
||||||
a uint64
|
|
||||||
b uint64
|
|
||||||
c uint64
|
|
||||||
x [chunk]byte
|
|
||||||
nx int
|
|
||||||
length uint64
|
|
||||||
ver int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *digest) Reset() {
|
|
||||||
d.a = initA
|
|
||||||
d.b = initB
|
|
||||||
d.c = initC
|
|
||||||
d.nx = 0
|
|
||||||
d.length = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// New returns a new hash.Hash computing the Tiger hash value
|
|
||||||
func New() hash.Hash {
|
|
||||||
d := new(digest)
|
|
||||||
d.Reset()
|
|
||||||
d.ver = 1
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
|
|
||||||
// New returns a new hash.Hash computing the Tiger2 hash value
|
|
||||||
func New2() hash.Hash {
|
|
||||||
d := new(digest)
|
|
||||||
d.Reset()
|
|
||||||
d.ver = 2
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *digest) BlockSize() int {
|
|
||||||
return BlockSize
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *digest) Size() int {
|
|
||||||
return Size
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *digest) Write(p []byte) (length int, err error) {
|
|
||||||
length = len(p)
|
|
||||||
d.length += uint64(length)
|
|
||||||
if d.nx > 0 {
|
|
||||||
n := len(p)
|
|
||||||
if n > chunk-d.nx {
|
|
||||||
n = chunk - d.nx
|
|
||||||
}
|
|
||||||
copy(d.x[d.nx:d.nx+n], p[:n])
|
|
||||||
d.nx += n
|
|
||||||
if d.nx == chunk {
|
|
||||||
d.compress(d.x[:chunk])
|
|
||||||
d.nx = 0
|
|
||||||
}
|
|
||||||
p = p[n:]
|
|
||||||
}
|
|
||||||
for len(p) >= chunk {
|
|
||||||
d.compress(p[:chunk])
|
|
||||||
p = p[chunk:]
|
|
||||||
}
|
|
||||||
if len(p) > 0 {
|
|
||||||
d.nx = copy(d.x[:], p)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d digest) Sum(in []byte) []byte {
|
|
||||||
length := d.length
|
|
||||||
var tmp [64]byte
|
|
||||||
if d.ver == 1 {
|
|
||||||
tmp[0] = 0x01
|
|
||||||
} else {
|
|
||||||
tmp[0] = 0x80
|
|
||||||
}
|
|
||||||
|
|
||||||
size := length & 0x3f
|
|
||||||
if size < 56 {
|
|
||||||
d.Write(tmp[:56-size])
|
|
||||||
} else {
|
|
||||||
d.Write(tmp[:64+56-size])
|
|
||||||
}
|
|
||||||
|
|
||||||
length <<= 3
|
|
||||||
for i := uint(0); i < 8; i++ {
|
|
||||||
tmp[i] = byte(length >> (8 * i))
|
|
||||||
}
|
|
||||||
d.Write(tmp[:8])
|
|
||||||
|
|
||||||
for i := uint(0); i < 8; i++ {
|
|
||||||
tmp[i] = byte(d.a >> (8 * i))
|
|
||||||
tmp[i+8] = byte(d.b >> (8 * i))
|
|
||||||
tmp[i+16] = byte(d.c >> (8 * i))
|
|
||||||
}
|
|
||||||
|
|
||||||
return append(in, tmp[:24]...)
|
|
||||||
}
|
|
4
vendor/modules.txt
vendored
4
vendor/modules.txt
vendored
@ -1,4 +0,0 @@
|
|||||||
# code.ivysaur.me/libnmdc v0.17.0
|
|
||||||
code.ivysaur.me/libnmdc
|
|
||||||
# github.com/cxmcc/tiger v0.0.0-20170524142333-bde35e2713d7
|
|
||||||
github.com/cxmcc/tiger
|
|
Loading…
Reference in New Issue
Block a user