Compare commits
29 Commits
v1.6.0
...
fix_whois#
Author | SHA1 | Date | |
---|---|---|---|
![]() |
165fa6ef5b | ||
![]() |
be246a3bc4 | ||
![]() |
4fb452b2c0 | ||
![]() |
d707382a78 | ||
![]() |
7620a3c282 | ||
![]() |
18a3e2f2c3 | ||
![]() |
d046a9863f | ||
![]() |
a1450a81d6 | ||
![]() |
d594386658 | ||
![]() |
89b512fc76 | ||
![]() |
d01bb4fe57 | ||
![]() |
2fef0feb5a | ||
![]() |
735458ffed | ||
![]() |
02427bcb3f | ||
![]() |
bdcb4c21a5 | ||
![]() |
0e3be3f34c | ||
![]() |
19e564ed2b | ||
![]() |
ef10282a37 | ||
![]() |
3a9d1fefc8 | ||
![]() |
f5d8f22220 | ||
![]() |
062e2546ab | ||
![]() |
8f269b5201 | ||
![]() |
d33d60353c | ||
![]() |
9a5862287b | ||
![]() |
46d22a71b3 | ||
![]() |
4d97e035d2 | ||
![]() |
41b6511cec | ||
![]() |
1cde7c6902 | ||
![]() |
edfd990d59 |
@@ -21,7 +21,7 @@ pipeline:
|
||||
from: drone@mills.io
|
||||
skip_verify: true
|
||||
when:
|
||||
status: [ changed, failure ]
|
||||
status: [ success, changed, failure ]
|
||||
|
||||
secrets:
|
||||
registry_username:
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
*~*
|
||||
bin
|
||||
*.db
|
||||
*.pem
|
||||
|
||||
|
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -43,3 +43,9 @@
|
||||
[submodule "vendor/github.com/prometheus/procfs"]
|
||||
path = vendor/github.com/prometheus/procfs
|
||||
url = https://github.com/prometheus/procfs
|
||||
[submodule "vendor/github.com/sasha-s/go-deadlock"]
|
||||
path = vendor/github.com/sasha-s/go-deadlock
|
||||
url = https://github.com/sasha-s/go-deadlock
|
||||
[submodule "vendor/github.com/petermattis/goid"]
|
||||
path = vendor/github.com/petermattis/goid
|
||||
url = https://github.com/petermattis/goid
|
||||
|
8
.travis.yml
Normal file
8
.travis.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
language: go
|
||||
sudo: false
|
||||
go:
|
||||
- tip
|
||||
before_install:
|
||||
- go get github.com/mattn/goveralls
|
||||
script:
|
||||
- $HOME/gopath/bin/goveralls -service=travis-ci
|
49
CONTRIBUTING.md
Normal file
49
CONTRIBUTING.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Welcome to the contributing guide for eris!
|
||||
|
||||
## Developers
|
||||
|
||||
- James Mills [@prologic](https://github.com/prologic) (*uthor / Maintainer*)
|
||||
|
||||
### Contributors
|
||||
|
||||
- [@bear](https://github.com/bear)
|
||||
- [@DanielOaks](https://github.com/DanielOaks)
|
||||
- [@kzisme](https://github.com/kzisme)
|
||||
|
||||
## New Features
|
||||
|
||||
* [Create an Issue](https://github.com/prologic/eris/issues/new)
|
||||
|
||||
* [Fork eris](https://github.com/prologic/eris#fork-destination-box)
|
||||
|
||||
```bash
|
||||
$ git clone git@github.com:myuser/eris.git
|
||||
```
|
||||
|
||||
* Create a new feature branch:
|
||||
|
||||
```bash
|
||||
$ git checkout -b myfeature#issueN master
|
||||
```
|
||||
|
||||
* Hack on your feature with your favorite editor
|
||||
* Commit and Push your changes up:
|
||||
|
||||
```bash
|
||||
$ git add -A
|
||||
$ git commit -m "my fancy new feature"
|
||||
$ git push -u origin my-feature
|
||||
```
|
||||
|
||||
* Create a new [Pull Request](https://github.com/prologic/eris/compare/)
|
||||
* Give the pull request an appropriate title possibly matching the issue
|
||||
* In the pull request's description include the text `Closes #N` or `Fixes #N`
|
||||
|
||||
# Reporting Bugs
|
||||
|
||||
* File a new [Bug Report](https://github.com/prologic/eris/issues/new)
|
||||
* Label it as a "Bug"
|
||||
|
||||
When describing your bug report; please be concise and as detailed as you can
|
||||
so we can easily work out what the problem is. It's also very helpful if you
|
||||
are able to provide a test case that repeatedly demonstrates the bug at hand.
|
2
Makefile
2
Makefile
@@ -6,7 +6,7 @@ APP=eris
|
||||
PACKAGE=irc
|
||||
REPO?=prologic/$(APP)
|
||||
TAG?=latest
|
||||
BUILD?=-dev
|
||||
BUILD?=dev
|
||||
|
||||
all: dev
|
||||
|
||||
|
81
README.md
81
README.md
@@ -1,5 +1,11 @@
|
||||
# eris - IRC Server / Daemon written in Go
|
||||
|
||||
[](https://travis-ci.org/prologic/eris)
|
||||
[](https://goreportcard.com/report/github.com/prologic/eris)
|
||||
[](https://coveralls.io/r/prologic/eris)
|
||||
[](https://godoc.org/github.com/prologic/eris)
|
||||
[](https://github.com/prologic/eris/wiki)
|
||||
|
||||
> This project and repository is based off of [ergonomadic](https://github.com/edmund-huber/ergonomadic)
|
||||
> and much of my original contributions were made in my [fork of ergonomadic](https://github.com/prologic/ergonomadic)
|
||||
> but the upstream project was ultimately shutdown.
|
||||
@@ -26,7 +32,12 @@ Pull requests and issues are welcome.
|
||||
|
||||
Discussion at:
|
||||
|
||||
* /server irc.mills.io:6697 (*use SSL*)
|
||||
* /server irc.mills.io +6697 (*use TLS/SSL*)
|
||||
* /join #lobby
|
||||
|
||||
Or (*not recommended*)P
|
||||
|
||||
* /server irc.mills.io (*default port 6667, non-TLS)
|
||||
* /join #lobby
|
||||
|
||||
## Features
|
||||
@@ -42,6 +53,41 @@ Discussion at:
|
||||
* SSL/TLS support
|
||||
* Simple IRC operator privileges (*overrides most things*)
|
||||
* Secure connection tracking (+z) and SecureOnly user mode (+Z)
|
||||
* Secure channels (+Z)
|
||||
|
||||
## Quick Start
|
||||
|
||||
```#!bash
|
||||
$ go get github.com/prologic/eris
|
||||
$ cat > ircd.yml <<EOF
|
||||
network:
|
||||
name: Test
|
||||
server:
|
||||
name: Test
|
||||
listen:
|
||||
- ":6667"
|
||||
EOF
|
||||
$ eris
|
||||
```
|
||||
|
||||
If you want TLS (**recommended**) then:
|
||||
|
||||
```#!bash
|
||||
$ go get github.com/prologic/mksslcert
|
||||
$ mksslcert
|
||||
```
|
||||
|
||||
This generates a self-signed cert `cert.pem` and `key.pem` into the `$PWD`.
|
||||
|
||||
Then add a `tlslisten` block to your config:
|
||||
|
||||
```#!yaml
|
||||
server:
|
||||
tlslisten:
|
||||
":6697":
|
||||
key: key.pem
|
||||
cert: cert.pem
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -61,12 +107,43 @@ $ go install github.com/prologic/mkpasswd
|
||||
$ mkpasswd
|
||||
```
|
||||
|
||||
## Running the server
|
||||
Self-signed certificates can also be generated using the `mksslcert` tool
|
||||
from [prologic/mksslcert](https://github.com/prologic/mksslcert):
|
||||
|
||||
```#!bash
|
||||
$ go install github.com/prologic/mksslcert
|
||||
$ mksslcert
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
To run simply run the `eris` binary (*assuming a `ircd.yml` in the current directory*):
|
||||
|
||||
```#!bash
|
||||
$ eris
|
||||
```
|
||||
|
||||
Or you can deploy with [Docker](https://www.docker.com) using the prebuilt [prologic/eris](https://hub.docker.com/r/prologic/eris/):
|
||||
|
||||
```#!bash
|
||||
docker run -d -p 6667:6667 -p 6697:6697 prologic/eris
|
||||
```
|
||||
|
||||
You may want to customize the configuration however and create your own image based off of this; or deploy with `docker stack deploy` on a [Docker Swarm](https://docs.docker.com/engine/swarm/) clsuter like this:
|
||||
|
||||
```#!bash
|
||||
$ docker stack deploy -c docker-compose.yml eris
|
||||
```
|
||||
|
||||
Which assumes a `ircd.yml` coniguration file in the current directory which Docker will use to distribute as the configuration. The `docker-compose.yml` (*Docker Stackfile*) is available at the root of this repository.
|
||||
|
||||
## Related Proejcts
|
||||
|
||||
There are a number of supported accompanying services that are being developed alongside Eris:
|
||||
|
||||
* [Soter](https://github.com/prologic/soter) -- An IRC Bot that persists channel modes and topics.
|
||||
* [Cadmus](https://github.com/prologic/cadmus) -- An IRC Bot that logs channels and provides an interface for viewing and searching logs
|
||||
|
||||
## License
|
||||
|
||||
eris is licensed under the MIT License.
|
||||
|
@@ -27,6 +27,7 @@ const (
|
||||
var (
|
||||
SupportedCapabilities = CapabilitySet{
|
||||
MultiPrefix: true,
|
||||
SASL: true,
|
||||
}
|
||||
)
|
||||
|
||||
|
@@ -155,16 +155,16 @@ func (channel *Channel) Join(client *Client, key Text) {
|
||||
return
|
||||
}
|
||||
|
||||
isInvited := channel.lists[InviteMask].Match(client.UserHost())
|
||||
isInvited := channel.lists[InviteMask].Match(client.UserHost(false))
|
||||
if !isOperator && channel.flags.Has(InviteOnly) && !isInvited {
|
||||
client.ErrInviteOnlyChan(channel)
|
||||
return
|
||||
}
|
||||
|
||||
if channel.lists[BanMask].Match(client.UserHost()) &&
|
||||
if channel.lists[BanMask].Match(client.UserHost(false)) &&
|
||||
!isInvited &&
|
||||
!isOperator &&
|
||||
!channel.lists[ExceptMask].Match(client.UserHost()) {
|
||||
!channel.lists[ExceptMask].Match(client.UserHost(false)) {
|
||||
client.ErrBannedFromChan(channel)
|
||||
return
|
||||
}
|
||||
@@ -244,6 +244,9 @@ func (channel *Channel) CanSpeak(client *Client) bool {
|
||||
channel.members.HasMode(client, ChannelOperator)) {
|
||||
return false
|
||||
}
|
||||
if channel.flags.Has(SecureChan) && !client.flags[SecureConn] {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -371,7 +374,7 @@ func (channel *Channel) applyMode(client *Client, change *ChannelModeChange) boo
|
||||
return channel.applyModeMask(client, change.mode, change.op,
|
||||
NewName(change.arg))
|
||||
|
||||
case InviteOnly, Moderated, NoOutside, OpOnlyTopic, Private:
|
||||
case InviteOnly, Moderated, NoOutside, OpOnlyTopic, Private, SecureChan:
|
||||
return channel.applyModeFlag(client, change.mode, change.op)
|
||||
|
||||
case Key:
|
||||
@@ -505,7 +508,7 @@ func (channel *Channel) Invite(invitee *Client, inviter *Client) {
|
||||
}
|
||||
|
||||
if channel.flags.Has(InviteOnly) {
|
||||
channel.lists[InviteMask].Add(invitee.UserHost())
|
||||
channel.lists[InviteMask].Add(invitee.UserHost(false))
|
||||
}
|
||||
|
||||
inviter.RplInviting(invitee, channel.name)
|
||||
|
@@ -26,12 +26,14 @@ type Client struct {
|
||||
hasQuit bool
|
||||
hops uint
|
||||
hostname Name
|
||||
hostmask Name // Cloacked hostname (SHA256)
|
||||
pingTime time.Time
|
||||
idleTimer *time.Timer
|
||||
nick Name
|
||||
quitTimer *time.Timer
|
||||
realname Text
|
||||
registered bool
|
||||
sasl *SaslState
|
||||
server *Server
|
||||
socket *Socket
|
||||
replies chan string
|
||||
@@ -48,6 +50,7 @@ func NewClient(server *Server, conn net.Conn) *Client {
|
||||
channels: NewChannelSet(),
|
||||
ctime: now,
|
||||
flags: make(map[UserMode]bool),
|
||||
sasl: NewSaslState(),
|
||||
server: server,
|
||||
socket: NewSocket(conn),
|
||||
replies: make(chan string),
|
||||
@@ -55,7 +58,6 @@ func NewClient(server *Server, conn net.Conn) *Client {
|
||||
|
||||
if _, ok := conn.(*tls.Conn); ok {
|
||||
client.flags[SecureConn] = true
|
||||
client.flags[SecureOnly] = true
|
||||
}
|
||||
|
||||
client.Touch()
|
||||
@@ -82,6 +84,7 @@ func (client *Client) readloop() {
|
||||
|
||||
// Set the hostname for this client.
|
||||
client.hostname = AddrLookupHostname(client.socket.conn.RemoteAddr())
|
||||
client.hostmask = NewName(SHA256(client.hostname.String()))
|
||||
|
||||
for err == nil {
|
||||
if line, err = client.socket.Read(); err != nil {
|
||||
@@ -231,6 +234,8 @@ func (client *Client) destroy() {
|
||||
client.quitTimer.Stop()
|
||||
}
|
||||
|
||||
close(client.replies)
|
||||
|
||||
client.socket.Close()
|
||||
|
||||
log.Debugf("%s: destroyed", client)
|
||||
@@ -276,11 +281,14 @@ func (c *Client) ModeString() (str string) {
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Client) UserHost() Name {
|
||||
func (c *Client) UserHost(cloacked bool) Name {
|
||||
username := "*"
|
||||
if c.HasUsername() {
|
||||
username = c.username.String()
|
||||
}
|
||||
if cloacked {
|
||||
return Name(fmt.Sprintf("%s!%s@%s", c.Nick(), username, c.hostmask))
|
||||
}
|
||||
return Name(fmt.Sprintf("%s!%s@%s", c.Nick(), username, c.hostname))
|
||||
}
|
||||
|
||||
@@ -300,7 +308,7 @@ func (c *Client) Nick() Name {
|
||||
}
|
||||
|
||||
func (c *Client) Id() Name {
|
||||
return c.UserHost()
|
||||
return c.UserHost(true)
|
||||
}
|
||||
|
||||
func (c *Client) String() string {
|
||||
|
@@ -105,7 +105,7 @@ func (clients *ClientLookupSet) FindAll(userhost Name) *ClientSet {
|
||||
|
||||
var casemappedNickMask string
|
||||
for _, client := range clients.nicks {
|
||||
casemappedNickMask = client.UserHost().String()
|
||||
casemappedNickMask = client.UserHost(false).String()
|
||||
if matcher.Match(casemappedNickMask) {
|
||||
set.Add(client)
|
||||
}
|
||||
@@ -123,7 +123,7 @@ func (clients *ClientLookupSet) Find(userhost Name) *Client {
|
||||
|
||||
var casemappedNickMask string
|
||||
for _, client := range clients.nicks {
|
||||
casemappedNickMask = client.UserHost().String()
|
||||
casemappedNickMask = client.UserHost(false).String()
|
||||
if matcher.Match(casemappedNickMask) {
|
||||
return client
|
||||
}
|
||||
|
@@ -26,37 +26,38 @@ var (
|
||||
NotEnoughArgsError = errors.New("not enough arguments")
|
||||
ErrParseCommand = errors.New("failed to parse message")
|
||||
parseCommandFuncs = map[StringCode]parseCommandFunc{
|
||||
AWAY: ParseAwayCommand,
|
||||
CAP: ParseCapCommand,
|
||||
INVITE: ParseInviteCommand,
|
||||
ISON: ParseIsOnCommand,
|
||||
JOIN: ParseJoinCommand,
|
||||
KICK: ParseKickCommand,
|
||||
KILL: ParseKillCommand,
|
||||
LIST: ParseListCommand,
|
||||
MODE: ParseModeCommand,
|
||||
MOTD: ParseMOTDCommand,
|
||||
NAMES: ParseNamesCommand,
|
||||
NICK: ParseNickCommand,
|
||||
NOTICE: ParseNoticeCommand,
|
||||
ONICK: ParseOperNickCommand,
|
||||
OPER: ParseOperCommand,
|
||||
REHASH: ParseRehashCommand,
|
||||
PART: ParsePartCommand,
|
||||
PASS: ParsePassCommand,
|
||||
PING: ParsePingCommand,
|
||||
PONG: ParsePongCommand,
|
||||
PRIVMSG: ParsePrivMsgCommand,
|
||||
QUIT: ParseQuitCommand,
|
||||
TIME: ParseTimeCommand,
|
||||
LUSERS: ParseLUsersCommand,
|
||||
TOPIC: ParseTopicCommand,
|
||||
USER: ParseUserCommand,
|
||||
VERSION: ParseVersionCommand,
|
||||
WALLOPS: ParseWallopsCommand,
|
||||
WHO: ParseWhoCommand,
|
||||
WHOIS: ParseWhoisCommand,
|
||||
WHOWAS: ParseWhoWasCommand,
|
||||
AUTHENTICATE: ParseAuthenticateCommand,
|
||||
AWAY: ParseAwayCommand,
|
||||
CAP: ParseCapCommand,
|
||||
INVITE: ParseInviteCommand,
|
||||
ISON: ParseIsOnCommand,
|
||||
JOIN: ParseJoinCommand,
|
||||
KICK: ParseKickCommand,
|
||||
KILL: ParseKillCommand,
|
||||
LIST: ParseListCommand,
|
||||
MODE: ParseModeCommand,
|
||||
MOTD: ParseMOTDCommand,
|
||||
NAMES: ParseNamesCommand,
|
||||
NICK: ParseNickCommand,
|
||||
NOTICE: ParseNoticeCommand,
|
||||
ONICK: ParseOperNickCommand,
|
||||
OPER: ParseOperCommand,
|
||||
REHASH: ParseRehashCommand,
|
||||
PART: ParsePartCommand,
|
||||
PASS: ParsePassCommand,
|
||||
PING: ParsePingCommand,
|
||||
PONG: ParsePongCommand,
|
||||
PRIVMSG: ParsePrivMsgCommand,
|
||||
QUIT: ParseQuitCommand,
|
||||
TIME: ParseTimeCommand,
|
||||
LUSERS: ParseLUsersCommand,
|
||||
TOPIC: ParseTopicCommand,
|
||||
USER: ParseUserCommand,
|
||||
VERSION: ParseVersionCommand,
|
||||
WALLOPS: ParseWallopsCommand,
|
||||
WHO: ParseWhoCommand,
|
||||
WHOIS: ParseWhoisCommand,
|
||||
WHOWAS: ParseWhoWasCommand,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -183,6 +184,22 @@ func ParsePongCommand(args []string) (Command, error) {
|
||||
return message, nil
|
||||
}
|
||||
|
||||
// AUTHENTICATE <arg>
|
||||
|
||||
type AuthenticateCommand struct {
|
||||
BaseCommand
|
||||
arg string
|
||||
}
|
||||
|
||||
func ParseAuthenticateCommand(args []string) (Command, error) {
|
||||
if len(args) < 1 {
|
||||
return nil, NotEnoughArgsError
|
||||
}
|
||||
return &AuthenticateCommand{
|
||||
arg: args[0],
|
||||
}, nil
|
||||
}
|
||||
|
||||
// PASS <password>
|
||||
|
||||
type PassCommand struct {
|
||||
|
@@ -33,6 +33,10 @@ type Config struct {
|
||||
sync.Mutex
|
||||
filename string
|
||||
|
||||
Network struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
Server struct {
|
||||
PassConfig `yaml:",inline"`
|
||||
Listen []string
|
||||
@@ -44,6 +48,7 @@ type Config struct {
|
||||
}
|
||||
|
||||
Operator map[string]*PassConfig
|
||||
Account map[string]*PassConfig
|
||||
}
|
||||
|
||||
func (conf *Config) Operators() map[Name][]byte {
|
||||
@@ -54,6 +59,14 @@ func (conf *Config) Operators() map[Name][]byte {
|
||||
return operators
|
||||
}
|
||||
|
||||
func (conf *Config) Accounts() map[string][]byte {
|
||||
accounts := make(map[string][]byte)
|
||||
for name, account := range conf.Account {
|
||||
accounts[name] = []byte(account.Password)
|
||||
}
|
||||
return accounts
|
||||
}
|
||||
|
||||
func (conf *Config) Name() string {
|
||||
return conf.filename
|
||||
}
|
||||
@@ -88,6 +101,10 @@ func LoadConfig(filename string) (config *Config, err error) {
|
||||
|
||||
config.filename = filename
|
||||
|
||||
if config.Network.Name == "" {
|
||||
return nil, errors.New("Network name missing")
|
||||
}
|
||||
|
||||
if config.Server.Name == "" {
|
||||
return nil, errors.New("Server name missing")
|
||||
}
|
||||
|
@@ -5,38 +5,39 @@ const (
|
||||
MAX_REPLY_LEN = 512 - len(CRLF)
|
||||
|
||||
// string codes
|
||||
AWAY StringCode = "AWAY"
|
||||
CAP StringCode = "CAP"
|
||||
ERROR StringCode = "ERROR"
|
||||
INVITE StringCode = "INVITE"
|
||||
ISON StringCode = "ISON"
|
||||
JOIN StringCode = "JOIN"
|
||||
KICK StringCode = "KICK"
|
||||
KILL StringCode = "KILL"
|
||||
LIST StringCode = "LIST"
|
||||
MODE StringCode = "MODE"
|
||||
MOTD StringCode = "MOTD"
|
||||
NAMES StringCode = "NAMES"
|
||||
NICK StringCode = "NICK"
|
||||
NOTICE StringCode = "NOTICE"
|
||||
ONICK StringCode = "ONICK"
|
||||
OPER StringCode = "OPER"
|
||||
REHASH StringCode = "REHASH"
|
||||
PART StringCode = "PART"
|
||||
PASS StringCode = "PASS"
|
||||
PING StringCode = "PING"
|
||||
PONG StringCode = "PONG"
|
||||
PRIVMSG StringCode = "PRIVMSG"
|
||||
QUIT StringCode = "QUIT"
|
||||
TIME StringCode = "TIME"
|
||||
LUSERS StringCode = "LUSERS"
|
||||
TOPIC StringCode = "TOPIC"
|
||||
USER StringCode = "USER"
|
||||
VERSION StringCode = "VERSION"
|
||||
WALLOPS StringCode = "WALLOPS"
|
||||
WHO StringCode = "WHO"
|
||||
WHOIS StringCode = "WHOIS"
|
||||
WHOWAS StringCode = "WHOWAS"
|
||||
AUTHENTICATE StringCode = "AUTHENTICATE" // SASL
|
||||
AWAY StringCode = "AWAY"
|
||||
CAP StringCode = "CAP"
|
||||
ERROR StringCode = "ERROR"
|
||||
INVITE StringCode = "INVITE"
|
||||
ISON StringCode = "ISON"
|
||||
JOIN StringCode = "JOIN"
|
||||
KICK StringCode = "KICK"
|
||||
KILL StringCode = "KILL"
|
||||
LIST StringCode = "LIST"
|
||||
MODE StringCode = "MODE"
|
||||
MOTD StringCode = "MOTD"
|
||||
NAMES StringCode = "NAMES"
|
||||
NICK StringCode = "NICK"
|
||||
NOTICE StringCode = "NOTICE"
|
||||
ONICK StringCode = "ONICK"
|
||||
OPER StringCode = "OPER"
|
||||
REHASH StringCode = "REHASH"
|
||||
PART StringCode = "PART"
|
||||
PASS StringCode = "PASS"
|
||||
PING StringCode = "PING"
|
||||
PONG StringCode = "PONG"
|
||||
PRIVMSG StringCode = "PRIVMSG"
|
||||
QUIT StringCode = "QUIT"
|
||||
TIME StringCode = "TIME"
|
||||
LUSERS StringCode = "LUSERS"
|
||||
TOPIC StringCode = "TOPIC"
|
||||
USER StringCode = "USER"
|
||||
VERSION StringCode = "VERSION"
|
||||
WALLOPS StringCode = "WALLOPS"
|
||||
WHO StringCode = "WHO"
|
||||
WHOIS StringCode = "WHOIS"
|
||||
WHOWAS StringCode = "WHOWAS"
|
||||
|
||||
// numeric codes
|
||||
RPL_WELCOME NumericCode = 1
|
||||
@@ -92,6 +93,7 @@ const (
|
||||
RPL_LISTEND NumericCode = 323
|
||||
RPL_CHANNELMODEIS NumericCode = 324
|
||||
RPL_UNIQOPIS NumericCode = 325
|
||||
RPL_WHOISLOGGEDIN NumericCode = 330
|
||||
RPL_NOTOPIC NumericCode = 331
|
||||
RPL_TOPIC NumericCode = 332
|
||||
RPL_INVITING NumericCode = 341
|
||||
@@ -178,4 +180,15 @@ const (
|
||||
ERR_UMODEUNKNOWNFLAG NumericCode = 501
|
||||
ERR_USERSDONTMATCH NumericCode = 502
|
||||
RPL_WHOISSECURE NumericCode = 671
|
||||
|
||||
// SASL
|
||||
RPL_LOGGEDIN NumericCode = 900
|
||||
RPL_LOGGEDOUT NumericCode = 901
|
||||
ERR_NICKLOCKED NumericCode = 902
|
||||
RPL_SASLSUCCESS NumericCode = 903
|
||||
ERR_SASLFAIL NumericCode = 904
|
||||
ERR_SASLTOOLONG NumericCode = 905
|
||||
ERR_SASLABORTED NumericCode = 906
|
||||
ERR_SASLALREADY NumericCode = 907
|
||||
RPL_SASLMECHS NumericCode = 908
|
||||
)
|
||||
|
@@ -55,6 +55,7 @@ const (
|
||||
Invisible UserMode = 'i'
|
||||
Operator UserMode = 'o'
|
||||
WallOps UserMode = 'w'
|
||||
Registered UserMode = 'r' // not a real user mode (flag)
|
||||
SecureConn UserMode = 'z'
|
||||
SecureOnly UserMode = 'Z'
|
||||
)
|
||||
@@ -86,12 +87,13 @@ const (
|
||||
Secret ChannelMode = 's' // flag, deprecated
|
||||
UserLimit ChannelMode = 'l' // flag arg
|
||||
Voice ChannelMode = 'v' // arg
|
||||
SecureChan ChannelMode = 'Z' // arg
|
||||
)
|
||||
|
||||
var (
|
||||
SupportedChannelModes = ChannelModes{
|
||||
BanMask, ExceptMask, InviteMask, InviteOnly, Key, NoOutside,
|
||||
OpOnlyTopic, Private, UserLimit,
|
||||
OpOnlyTopic, Private, UserLimit, SecureChan,
|
||||
}
|
||||
)
|
||||
|
||||
|
117
irc/password.go
117
irc/password.go
@@ -2,17 +2,124 @@ package irc
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
//"sync"
|
||||
|
||||
sync "github.com/sasha-s/go-deadlock"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
EmptyPasswordError = errors.New("empty password")
|
||||
)
|
||||
var DefaultPasswordHasher = &Base64BCryptPasswordHasher{}
|
||||
|
||||
type PasswordHasher interface {
|
||||
Decode(encoded []byte) (decoded []byte, err error)
|
||||
Encode(password []byte) (encoded []byte, err error)
|
||||
Compare(encoded []byte, password []byte) error
|
||||
}
|
||||
|
||||
type PasswordStore interface {
|
||||
Get(username string) ([]byte, bool)
|
||||
Set(username, password string) error
|
||||
Verify(username, password string) error
|
||||
}
|
||||
|
||||
type PasswordStoreOpts struct {
|
||||
hasher PasswordHasher
|
||||
}
|
||||
|
||||
type MemoryPasswordStore struct {
|
||||
sync.RWMutex
|
||||
passwords map[string][]byte
|
||||
hasher PasswordHasher
|
||||
}
|
||||
|
||||
func NewMemoryPasswordStore(passwords map[string][]byte, opts PasswordStoreOpts) *MemoryPasswordStore {
|
||||
var hasher PasswordHasher
|
||||
|
||||
if opts.hasher != nil {
|
||||
hasher = opts.hasher
|
||||
} else {
|
||||
hasher = DefaultPasswordHasher
|
||||
}
|
||||
|
||||
return &MemoryPasswordStore{
|
||||
passwords: passwords,
|
||||
hasher: hasher,
|
||||
}
|
||||
}
|
||||
|
||||
func (store *MemoryPasswordStore) Get(username string) ([]byte, bool) {
|
||||
store.RLock()
|
||||
defer store.RUnlock()
|
||||
|
||||
hash, ok := store.passwords[username]
|
||||
return hash, ok
|
||||
}
|
||||
|
||||
func (store *MemoryPasswordStore) Set(username, password string) error {
|
||||
// Not Implemented
|
||||
return nil
|
||||
}
|
||||
|
||||
func (store *MemoryPasswordStore) Verify(username, password string) error {
|
||||
log.Debugf("looking up: %s", username)
|
||||
log.Debugf("%v", store.passwords)
|
||||
hash, ok := store.Get(username)
|
||||
if !ok {
|
||||
log.Debugf("username %s not found", username)
|
||||
return fmt.Errorf("account not found: %s", username)
|
||||
}
|
||||
|
||||
return store.hasher.Compare(hash, []byte(password))
|
||||
}
|
||||
|
||||
type Base64BCryptPasswordHasher struct{}
|
||||
|
||||
func (hasher *Base64BCryptPasswordHasher) Decode(encoded []byte) (decoded []byte, err error) {
|
||||
if encoded == nil {
|
||||
err = fmt.Errorf("empty password")
|
||||
return
|
||||
}
|
||||
decoded = make([]byte, base64.StdEncoding.DecodedLen(len(encoded)))
|
||||
log.Debugf("Decode:")
|
||||
log.Debugf("decoded: %v", decoded)
|
||||
log.Debugf("encoded: %v", encoded)
|
||||
_, err = base64.StdEncoding.Decode(decoded, encoded)
|
||||
return
|
||||
}
|
||||
|
||||
func (hasher *Base64BCryptPasswordHasher) Encode(password []byte) (encoded []byte, err error) {
|
||||
if password == nil {
|
||||
err = fmt.Errorf("empty password")
|
||||
return
|
||||
}
|
||||
bcrypted, err := bcrypt.GenerateFromPassword(password, bcrypt.MinCost)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
base64.StdEncoding.Encode(encoded, bcrypted)
|
||||
return
|
||||
}
|
||||
|
||||
func (hasher *Base64BCryptPasswordHasher) Compare(encoded, password []byte) error {
|
||||
log.Debugf("encoded: %s", encoded)
|
||||
log.Debugf("password: %s", password)
|
||||
decoded, err := hasher.Decode(encoded)
|
||||
log.Debugf("decoded: %s", decoded)
|
||||
log.Debugf("err: %s", err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return bcrypt.CompareHashAndPassword(decoded, []byte(password))
|
||||
}
|
||||
|
||||
// DEPRECATED
|
||||
|
||||
func DecodePassword(encoded string) (decoded []byte, err error) {
|
||||
if encoded == "" {
|
||||
err = EmptyPasswordError
|
||||
err = fmt.Errorf("empty password")
|
||||
return
|
||||
}
|
||||
decoded, err = base64.StdEncoding.DecodeString(encoded)
|
||||
|
175
irc/reply.go
175
irc/reply.go
@@ -173,14 +173,18 @@ func RplCap(client *Client, subCommand CapSubCommand, arg interface{}) string {
|
||||
// numeric replies
|
||||
|
||||
func (target *Client) RplWelcome() {
|
||||
target.NumericReply(RPL_WELCOME,
|
||||
":Welcome to the Internet Relay Network %s", target.Id())
|
||||
target.NumericReply(
|
||||
RPL_WELCOME,
|
||||
":Welcome to the %s Internet Relay Network %s",
|
||||
target.server.Network(),
|
||||
target.Id(),
|
||||
)
|
||||
}
|
||||
|
||||
func (target *Client) RplYourHost() {
|
||||
target.NumericReply(
|
||||
RPL_YOURHOST,
|
||||
":Your host is %s, running eris version %s",
|
||||
":Your host is %s, running %s",
|
||||
target.server.name,
|
||||
FullVersion(),
|
||||
)
|
||||
@@ -255,13 +259,27 @@ func (target *Client) RplWhois(client *Client) {
|
||||
target.RplWhoisSecure(client)
|
||||
}
|
||||
target.RplWhoisServer(client)
|
||||
target.RplEndOfWhois()
|
||||
target.RplWhoisLoggedIn(client)
|
||||
target.RplEndOfWhois(client)
|
||||
}
|
||||
|
||||
func (target *Client) RplWhoisUser(client *Client) {
|
||||
target.NumericReply(RPL_WHOISUSER,
|
||||
"%s %s %s * :%s", client.Nick(), client.username, client.hostname,
|
||||
client.realname)
|
||||
var clientHost Name
|
||||
|
||||
if target.flags[Operator] {
|
||||
clientHost = client.hostname
|
||||
} else {
|
||||
clientHost = client.hostmask
|
||||
}
|
||||
|
||||
target.NumericReply(
|
||||
RPL_WHOISUSER,
|
||||
"%s %s %s * :%s",
|
||||
client.Nick(),
|
||||
client.username,
|
||||
clientHost,
|
||||
client.realname,
|
||||
)
|
||||
}
|
||||
|
||||
func (target *Client) RplWhoisOperator(client *Client) {
|
||||
@@ -283,6 +301,19 @@ func (target *Client) RplWhoisIdle(client *Client) {
|
||||
client.Nick(), client.IdleSeconds(), client.SignonTime())
|
||||
}
|
||||
|
||||
func (target *Client) RplWhoisLoggedIn(client *Client) {
|
||||
if client.sasl.Id() == "" {
|
||||
return
|
||||
}
|
||||
|
||||
target.NumericReply(
|
||||
RPL_WHOISLOGGEDIN,
|
||||
"%s %s :Is logged in as",
|
||||
client.Nick(),
|
||||
client.sasl.Id(),
|
||||
)
|
||||
}
|
||||
|
||||
func (target *Client) RplWhoisServer(client *Client) {
|
||||
target.NumericReply(
|
||||
RPL_WHOISSERVER,
|
||||
@@ -293,9 +324,12 @@ func (target *Client) RplWhoisServer(client *Client) {
|
||||
)
|
||||
}
|
||||
|
||||
func (target *Client) RplEndOfWhois() {
|
||||
target.NumericReply(RPL_ENDOFWHOIS,
|
||||
":End of WHOIS list")
|
||||
func (target *Client) RplEndOfWhois(client *Client) {
|
||||
target.NumericReply(
|
||||
RPL_ENDOFWHOIS,
|
||||
"%s :End of WHOIS list",
|
||||
client.Nick(),
|
||||
)
|
||||
}
|
||||
|
||||
func (target *Client) RplChannelModeIs(channel *Channel) {
|
||||
@@ -306,6 +340,14 @@ func (target *Client) RplChannelModeIs(channel *Channel) {
|
||||
// <channel> <user> <host> <server> <nick> ( "H" / "G" ) ["*"] [ ( "@" / "+" ) ]
|
||||
// :<hopcount> <real name>
|
||||
func (target *Client) RplWhoReply(channel *Channel, client *Client) {
|
||||
var clientHost Name
|
||||
|
||||
if target.flags[Operator] {
|
||||
clientHost = client.hostname
|
||||
} else {
|
||||
clientHost = client.hostmask
|
||||
}
|
||||
|
||||
channelName := "*"
|
||||
flags := ""
|
||||
|
||||
@@ -335,9 +377,18 @@ func (target *Client) RplWhoReply(channel *Channel, client *Client) {
|
||||
}
|
||||
}
|
||||
}
|
||||
target.NumericReply(RPL_WHOREPLY,
|
||||
"%s %s %s %s %s %s :%d %s", channelName, client.username, client.hostname,
|
||||
client.server.name, client.Nick(), flags, client.hops, client.realname)
|
||||
target.NumericReply(
|
||||
RPL_WHOREPLY,
|
||||
"%s %s %s %s %s %s :%d %s",
|
||||
channelName,
|
||||
client.username,
|
||||
clientHost,
|
||||
client.server.name,
|
||||
client.Nick(),
|
||||
flags,
|
||||
client.hops,
|
||||
client.realname,
|
||||
)
|
||||
}
|
||||
|
||||
// <name> :End of WHO list
|
||||
@@ -548,9 +599,22 @@ func (target *Client) RplLUserMe() {
|
||||
}
|
||||
|
||||
func (target *Client) RplWhoWasUser(whoWas *WhoWas) {
|
||||
target.NumericReply(RPL_WHOWASUSER,
|
||||
var whoWasHost Name
|
||||
|
||||
if target.flags[Operator] {
|
||||
whoWasHost = whoWas.hostname
|
||||
} else {
|
||||
whoWasHost = whoWas.hostmask
|
||||
}
|
||||
|
||||
target.NumericReply(
|
||||
RPL_WHOWASUSER,
|
||||
"%s %s %s * :%s",
|
||||
whoWas.nickname, whoWas.username, whoWas.hostname, whoWas.realname)
|
||||
whoWas.nickname,
|
||||
whoWas.username,
|
||||
whoWasHost,
|
||||
whoWas.realname,
|
||||
)
|
||||
}
|
||||
|
||||
func (target *Client) RplEndOfWhoWas(nickname Name) {
|
||||
@@ -710,3 +774,84 @@ func (target *Client) ErrInviteOnlyChan(channel *Channel) {
|
||||
target.NumericReply(ERR_INVITEONLYCHAN,
|
||||
"%s :Cannot join channel (+i)", channel)
|
||||
}
|
||||
|
||||
//
|
||||
// SASL Errors / Replies
|
||||
//
|
||||
|
||||
func RplAuthenticate(client *Client, arg string) string {
|
||||
return NewStringReply(client.server, AUTHENTICATE, arg)
|
||||
}
|
||||
|
||||
func (target *Client) RplLoggedIn(authcid string) {
|
||||
target.NumericReply(
|
||||
RPL_LOGGEDIN,
|
||||
"%s %s :You are now logged in as %s",
|
||||
target, authcid, authcid,
|
||||
)
|
||||
}
|
||||
|
||||
func (target *Client) RplLoggedOut() {
|
||||
target.NumericReply(
|
||||
RPL_LOGGEDIN,
|
||||
"%s :You are now logged out",
|
||||
target,
|
||||
)
|
||||
}
|
||||
|
||||
func (target *Client) ErrNickLocked() {
|
||||
target.NumericReply(
|
||||
ERR_NICKLOCKED,
|
||||
"%s :You must use a nick assigned to you",
|
||||
target.Nick(),
|
||||
)
|
||||
}
|
||||
|
||||
func (target *Client) RplSaslSuccess() {
|
||||
target.NumericReply(
|
||||
RPL_SASLSUCCESS,
|
||||
"%s :SASL authentication successful",
|
||||
target.Nick(),
|
||||
)
|
||||
}
|
||||
|
||||
func (target *Client) ErrSaslFail(message string) {
|
||||
target.NumericReply(
|
||||
ERR_SASLFAIL,
|
||||
"%s :SASL authentication failed: %s",
|
||||
target.Nick(), message,
|
||||
)
|
||||
}
|
||||
|
||||
func (target *Client) ErrSaslTooLong() {
|
||||
target.NumericReply(
|
||||
ERR_SASLFAIL,
|
||||
"%s :SASL message too long",
|
||||
target.Nick(),
|
||||
)
|
||||
}
|
||||
|
||||
func (target *Client) ErrSaslAborted() {
|
||||
target.NumericReply(
|
||||
ERR_SASLABORTED,
|
||||
"%s :SASL authentication aborted",
|
||||
target.Nick(),
|
||||
)
|
||||
}
|
||||
|
||||
func (target *Client) ErrSaslAlready() {
|
||||
target.NumericReply(
|
||||
ERR_SASLALREADY,
|
||||
"%s :You have already authenticated using SASL",
|
||||
target.Nick(),
|
||||
)
|
||||
}
|
||||
|
||||
func (target *Client) RplSaslMechs(mechs ...string) {
|
||||
target.NumericReply(
|
||||
RPL_SASLMECHS,
|
||||
"%s %s :are available SASL mechanisms",
|
||||
target.Nick(),
|
||||
strings.Join(mechs, ","),
|
||||
)
|
||||
}
|
||||
|
86
irc/sasl.go
Normal file
86
irc/sasl.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package irc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
//"sync"
|
||||
|
||||
sync "github.com/sasha-s/go-deadlock"
|
||||
)
|
||||
|
||||
type SaslState struct {
|
||||
sync.RWMutex
|
||||
|
||||
started bool
|
||||
|
||||
buffer *bytes.Buffer
|
||||
mech string
|
||||
|
||||
authcid string
|
||||
}
|
||||
|
||||
func NewSaslState() *SaslState {
|
||||
return &SaslState{buffer: &bytes.Buffer{}}
|
||||
}
|
||||
|
||||
func (s *SaslState) Reset() {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
s.started = false
|
||||
s.buffer.Reset()
|
||||
s.mech = ""
|
||||
s.authcid = ""
|
||||
}
|
||||
|
||||
func (s *SaslState) Started() bool {
|
||||
s.RLock()
|
||||
defer s.RUnlock()
|
||||
|
||||
return s.started
|
||||
}
|
||||
|
||||
func (s *SaslState) Start() {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
s.started = true
|
||||
}
|
||||
|
||||
func (s *SaslState) WriteString(data string) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
s.buffer.WriteString(data)
|
||||
}
|
||||
|
||||
func (s SaslState) Len() int {
|
||||
s.RLock()
|
||||
defer s.RUnlock()
|
||||
|
||||
return s.buffer.Len()
|
||||
}
|
||||
|
||||
func (s *SaslState) String() string {
|
||||
s.RLock()
|
||||
defer s.RUnlock()
|
||||
|
||||
return s.buffer.String()
|
||||
}
|
||||
|
||||
func (s *SaslState) Login(authcid string) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
s.started = false
|
||||
s.buffer.Reset()
|
||||
s.mech = ""
|
||||
|
||||
s.authcid = authcid
|
||||
}
|
||||
|
||||
func (s *SaslState) Id() string {
|
||||
s.RLock()
|
||||
defer s.RUnlock()
|
||||
|
||||
return s.authcid
|
||||
}
|
104
irc/server.go
104
irc/server.go
@@ -2,8 +2,10 @@ package irc
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
@@ -35,9 +37,11 @@ type Server struct {
|
||||
idle chan *Client
|
||||
motdFile string
|
||||
name Name
|
||||
network Name
|
||||
description string
|
||||
newConns chan net.Conn
|
||||
operators map[Name][]byte
|
||||
accounts PasswordStore
|
||||
password []byte
|
||||
signals chan os.Signal
|
||||
whoWas *WhoWasList
|
||||
@@ -60,14 +64,18 @@ func NewServer(config *Config) *Server {
|
||||
idle: make(chan *Client),
|
||||
motdFile: config.Server.MOTD,
|
||||
name: NewName(config.Server.Name),
|
||||
network: NewName(config.Network.Name),
|
||||
description: config.Server.Description,
|
||||
newConns: make(chan net.Conn),
|
||||
operators: config.Operators(),
|
||||
accounts: NewMemoryPasswordStore(config.Accounts(), PasswordStoreOpts{}),
|
||||
signals: make(chan os.Signal, len(SERVER_SIGNALS)),
|
||||
whoWas: NewWhoWasList(100),
|
||||
ids: make(map[string]*Identity),
|
||||
}
|
||||
|
||||
log.Debugf("accounts: %v", config.Accounts())
|
||||
|
||||
// TODO: Make this configurabel?
|
||||
server.ids["global"] = NewIdentity(config.Server.Name, "global")
|
||||
|
||||
@@ -308,6 +316,7 @@ func (s *Server) Rehash() error {
|
||||
|
||||
s.motdFile = s.config.Server.MOTD
|
||||
s.name = NewName(s.config.Server.Name)
|
||||
s.network = NewName(s.config.Network.Name)
|
||||
s.description = s.config.Server.Description
|
||||
s.operators = s.config.Operators()
|
||||
|
||||
@@ -318,6 +327,10 @@ func (s *Server) Id() Name {
|
||||
return s.name
|
||||
}
|
||||
|
||||
func (s *Server) Network() Name {
|
||||
return s.network
|
||||
}
|
||||
|
||||
func (s *Server) String() string {
|
||||
return s.name.String()
|
||||
}
|
||||
@@ -376,6 +389,97 @@ func (msg *RFC2812UserCommand) HandleRegServer(server *Server) {
|
||||
msg.setUserInfo(server)
|
||||
}
|
||||
|
||||
func (msg *AuthenticateCommand) HandleRegServer(server *Server) {
|
||||
client := msg.Client()
|
||||
if !client.authorized {
|
||||
client.ErrPasswdMismatch()
|
||||
client.Quit("bad password")
|
||||
return
|
||||
}
|
||||
|
||||
if msg.arg == "*" {
|
||||
client.ErrSaslAborted()
|
||||
return
|
||||
}
|
||||
|
||||
if !client.sasl.Started() {
|
||||
if msg.arg == "PLAIN" {
|
||||
client.sasl.Start()
|
||||
client.Reply(RplAuthenticate(client, "+"))
|
||||
} else {
|
||||
client.RplSaslMechs("PLAIN")
|
||||
client.ErrSaslFail("Unknown authentication mechanism")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if len(msg.arg) > 400 {
|
||||
client.ErrSaslTooLong()
|
||||
return
|
||||
}
|
||||
|
||||
if len(msg.arg) == 400 {
|
||||
client.sasl.WriteString(msg.arg)
|
||||
return
|
||||
}
|
||||
|
||||
if msg.arg != "+" {
|
||||
client.sasl.WriteString(msg.arg)
|
||||
}
|
||||
|
||||
data, err := base64.StdEncoding.DecodeString(client.sasl.String())
|
||||
if err != nil {
|
||||
client.ErrSaslFail("Invalid base64 encoding")
|
||||
client.sasl.Reset()
|
||||
return
|
||||
}
|
||||
|
||||
// Do authentication
|
||||
|
||||
var (
|
||||
authcid string
|
||||
authzid string
|
||||
password string
|
||||
)
|
||||
|
||||
tokens := bytes.Split(data, []byte{'\000'})
|
||||
if len(tokens) == 3 {
|
||||
authcid = string(tokens[0])
|
||||
authzid = string(tokens[1])
|
||||
password = string(tokens[2])
|
||||
|
||||
if authzid == "" {
|
||||
authzid = authcid
|
||||
} else if authzid != authcid {
|
||||
client.ErrSaslFail("authzid and authcid should be the same")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
client.ErrSaslFail("invalid authentication blob")
|
||||
return
|
||||
}
|
||||
|
||||
err = server.accounts.Verify(authcid, password)
|
||||
if err != nil {
|
||||
client.ErrSaslFail("invalid authentication")
|
||||
return
|
||||
}
|
||||
|
||||
client.sasl.Login(authcid)
|
||||
client.RplLoggedIn(authcid)
|
||||
client.RplSaslSuccess()
|
||||
|
||||
client.flags[Registered] = true
|
||||
client.Reply(
|
||||
RplModeChanges(
|
||||
client, client,
|
||||
ModeChanges{
|
||||
&ModeChange{mode: Registered, op: Add},
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func (msg *UserCommand) setUserInfo(server *Server) {
|
||||
client := msg.Client()
|
||||
|
||||
|
11
irc/utils.go
Normal file
11
irc/utils.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package irc
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func SHA256(data string) string {
|
||||
hash := sha256.Sum256([]byte(data))
|
||||
return fmt.Sprintf("%x", hash)
|
||||
}
|
@@ -1,11 +1,18 @@
|
||||
package irc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
//PackageName package name
|
||||
Package = "eris"
|
||||
|
||||
// Version release version
|
||||
Version = "1.6.0"
|
||||
Version = "1.6.2"
|
||||
|
||||
// Build will be overwritten automatically by the build system
|
||||
Build = "-dev"
|
||||
Build = "dev"
|
||||
|
||||
// GitCommit will be overwritten automatically by the build system
|
||||
GitCommit = "HEAD"
|
||||
@@ -13,5 +20,5 @@ var (
|
||||
|
||||
// FullVersion display the full version and build
|
||||
func FullVersion() string {
|
||||
return Version + Build + " (" + GitCommit + ")"
|
||||
return fmt.Sprintf("%s-%s-%s@%s", Package, Version, Build, GitCommit)
|
||||
}
|
||||
|
@@ -17,6 +17,7 @@ type WhoWas struct {
|
||||
nickname Name
|
||||
username Name
|
||||
hostname Name
|
||||
hostmask Name
|
||||
realname Text
|
||||
}
|
||||
|
||||
@@ -33,6 +34,7 @@ func (list *WhoWasList) Append(client *Client) {
|
||||
nickname: client.Nick(),
|
||||
username: client.username,
|
||||
hostname: client.hostname,
|
||||
hostmask: client.hostmask,
|
||||
realname: client.realname,
|
||||
}
|
||||
list.end = (list.end + 1) % len(list.buffer)
|
||||
|
15
ircd.yml
15
ircd.yml
@@ -1,3 +1,7 @@
|
||||
network:
|
||||
# network name
|
||||
name: Local
|
||||
|
||||
server:
|
||||
# server name
|
||||
name: localhost.localdomain
|
||||
@@ -16,7 +20,7 @@ server:
|
||||
cert: cert.pem
|
||||
|
||||
# password to login to the server
|
||||
# generated using "ircd genpasswd"
|
||||
# generated using "mkpasswd" (from https://github.com/prologic/mkpasswd)
|
||||
#password: ""
|
||||
|
||||
# motd filename
|
||||
@@ -27,5 +31,12 @@ operator:
|
||||
# operator named 'admin' with password 'password'
|
||||
admin:
|
||||
# password to login with /OPER command
|
||||
# generated using "ircd genpasswd"
|
||||
# generated using "mkpasswd" (from https://github.com/prologic/mkpasswd)
|
||||
password: JDJhJDA0JE1vZmwxZC9YTXBhZ3RWT2xBbkNwZnV3R2N6VFUwQUI0RUJRVXRBRHliZVVoa0VYMnlIaGsu
|
||||
|
||||
# accounts (SASL)
|
||||
account:
|
||||
# username 'admin'
|
||||
admin:
|
||||
# password 'admin'
|
||||
password: JDJhJDA0JGtUU1JVc1JOUy9DbEh1WEdvYVlMdGVnclp6YnA3NDBOZGY1WUZhdTZtRzVmb1VKdXQ5ckZD
|
||||
|
2
main.go
2
main.go
@@ -23,7 +23,7 @@ func main() {
|
||||
flag.Parse()
|
||||
|
||||
if version {
|
||||
fmt.Printf("eris v%s", irc.FullVersion())
|
||||
fmt.Printf(irc.FullVersion())
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
|
41
scripts/release.sh
Executable file
41
scripts/release.sh
Executable file
@@ -0,0 +1,41 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo -n "Version to tag: "
|
||||
read TAG
|
||||
|
||||
echo -n "Name of release: "
|
||||
read NAME
|
||||
|
||||
echo -n "Desc of release: "
|
||||
read DESC
|
||||
|
||||
git tag ${TAG}
|
||||
git push --tags
|
||||
|
||||
if [ ! -d ./bin ]; then
|
||||
mkdir bin
|
||||
else
|
||||
rm -rf ./bin/*
|
||||
fi
|
||||
|
||||
echo -n "Building binaries ... "
|
||||
|
||||
GOOS=linux GOARCH=amd64 go build -o ./bin/eris-Linux-x86_64 .
|
||||
GOOS=linux GOARCH=arm64 go build -o ./bin/eris-Linux-arm_64 .
|
||||
GOOS=darwin GOARCH=amd64 go build -o ./bin/eris-Darwin-x86_64 .
|
||||
GOOS=windows GOARCH=amd64 go build -o ./bin/eris-Windows-x86_64.exe .
|
||||
|
||||
echo "DONE"
|
||||
|
||||
echo -n "Uploading binaries ... "
|
||||
|
||||
github-release release \
|
||||
-u prologic -p -r eris \
|
||||
-t ${TAG} -n "${NAME}" -d "${DESC}"
|
||||
|
||||
for file in bin/*; do
|
||||
name="$(echo $file | sed -e 's|bin/||g')"
|
||||
github-release upload -u prologic -r eris -t ${TAG} -n $name -f $file
|
||||
done
|
||||
|
||||
echo "DONE"
|
1
vendor/github.com/petermattis/goid
generated
vendored
Submodule
1
vendor/github.com/petermattis/goid
generated
vendored
Submodule
Submodule vendor/github.com/petermattis/goid added at 3db12ebb2a
2
vendor/github.com/prometheus/client_golang
generated
vendored
2
vendor/github.com/prometheus/client_golang
generated
vendored
Submodule vendor/github.com/prometheus/client_golang updated: 5cec1d0429...1cdba8fdde
2
vendor/github.com/prometheus/client_model
generated
vendored
2
vendor/github.com/prometheus/client_model
generated
vendored
Submodule vendor/github.com/prometheus/client_model updated: 6f38060186...99fa1f4be8
2
vendor/github.com/prometheus/common
generated
vendored
2
vendor/github.com/prometheus/common
generated
vendored
Submodule vendor/github.com/prometheus/common updated: e3fb1a1acd...2e54d0b93c
1
vendor/github.com/sasha-s/go-deadlock
generated
vendored
Submodule
1
vendor/github.com/sasha-s/go-deadlock
generated
vendored
Submodule
Submodule vendor/github.com/sasha-s/go-deadlock added at 565eb44395
2
vendor/github.com/sirupsen/logrus
generated
vendored
2
vendor/github.com/sirupsen/logrus
generated
vendored
Submodule vendor/github.com/sirupsen/logrus updated: 89742aefa4...95cd2b9c79
2
vendor/golang.org/x/crypto
generated
vendored
2
vendor/golang.org/x/crypto
generated
vendored
Submodule vendor/golang.org/x/crypto updated: 9f005a07e0...b080dc9a8c
2
vendor/golang.org/x/sys
generated
vendored
2
vendor/golang.org/x/sys
generated
vendored
Submodule vendor/golang.org/x/sys updated: 0dd5e194bb...4ff8c001ce
Reference in New Issue
Block a user