Compare commits
1 Commits
v1.6.6
...
RplWelcome
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d3fd6ec764 |
@@ -6,15 +6,9 @@ pipeline:
|
|||||||
build:
|
build:
|
||||||
image: golang
|
image: golang
|
||||||
commands:
|
commands:
|
||||||
- go get -d ./...
|
- go get -d
|
||||||
- go build .
|
- go build .
|
||||||
|
|
||||||
test:
|
|
||||||
image: golang
|
|
||||||
commands:
|
|
||||||
- go get -d ./...
|
|
||||||
- go test ./...
|
|
||||||
|
|
||||||
docker:
|
docker:
|
||||||
image: plugins/docker
|
image: plugins/docker
|
||||||
repo: r.mills.io/prologic/eris
|
repo: r.mills.io/prologic/eris
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,6 +1,5 @@
|
|||||||
*~*
|
*~*
|
||||||
bin
|
bin
|
||||||
dist
|
|
||||||
*.db
|
*.db
|
||||||
*.pem
|
*.pem
|
||||||
|
|
||||||
|
12
.gitmodules
vendored
12
.gitmodules
vendored
@@ -43,15 +43,3 @@
|
|||||||
[submodule "vendor/github.com/prometheus/procfs"]
|
[submodule "vendor/github.com/prometheus/procfs"]
|
||||||
path = vendor/github.com/prometheus/procfs
|
path = vendor/github.com/prometheus/procfs
|
||||||
url = https://github.com/prometheus/procfs
|
url = https://github.com/prometheus/procfs
|
||||||
[submodule "vendor/github.com/thoj/go-ircevent"]
|
|
||||||
path = vendor/github.com/thoj/go-ircevent
|
|
||||||
url = https://github.com/thoj/go-ircevent
|
|
||||||
[submodule "vendor/github.com/stretchr/testify"]
|
|
||||||
path = vendor/github.com/stretchr/testify
|
|
||||||
url = https://github.com/stretchr/testify
|
|
||||||
[submodule "vendor/github.com/renstrom/shortuuid"]
|
|
||||||
path = vendor/github.com/renstrom/shortuuid
|
|
||||||
url = https://github.com/renstrom/shortuuid
|
|
||||||
[submodule "vendor/github.com/satori/go.uuid"]
|
|
||||||
path = vendor/github.com/satori/go.uuid
|
|
||||||
url = https://github.com/satori/go.uuid
|
|
||||||
|
@@ -1,31 +0,0 @@
|
|||||||
builds:
|
|
||||||
- binary: eris
|
|
||||||
flags: -tags "static_build"
|
|
||||||
ldflags: -w -X mail.Version={{.Version}} -X main.Commit={{.Commit}}
|
|
||||||
env:
|
|
||||||
- CGO_ENABLED=0
|
|
||||||
goos:
|
|
||||||
- darwin
|
|
||||||
- freebsd
|
|
||||||
- linux
|
|
||||||
- windows
|
|
||||||
goarch:
|
|
||||||
- i386
|
|
||||||
- amd64
|
|
||||||
- arm
|
|
||||||
- amd64
|
|
||||||
goarm:
|
|
||||||
- 6
|
|
||||||
- 7
|
|
||||||
sign:
|
|
||||||
artifacts: checksum
|
|
||||||
archive:
|
|
||||||
wrap_in_directory: true
|
|
||||||
format_overrides:
|
|
||||||
- goos: windows
|
|
||||||
format: zip
|
|
||||||
files:
|
|
||||||
- "*.pem"
|
|
||||||
- "*.yml"
|
|
||||||
- "LICENSE"
|
|
||||||
- "README.md"
|
|
@@ -46,4 +46,4 @@ $ git push -u origin my-feature
|
|||||||
|
|
||||||
When describing your bug report; please be concise and as detailed as you can
|
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
|
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.
|
are able to provide a test case that repeatedly demonstrates the bug at hand:
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
FROM golang:alpine AS build
|
FROM golang:alpine AS build
|
||||||
|
|
||||||
ARG TAG
|
ARG TAG
|
||||||
|
ARG BUILD
|
||||||
|
|
||||||
ENV APP eris
|
ENV APP eris
|
||||||
ENV REPO prologic/$APP
|
ENV REPO prologic/$APP
|
||||||
@@ -11,7 +12,7 @@ RUN apk add --update git make build-base && \
|
|||||||
|
|
||||||
WORKDIR /go/src/github.com/$REPO
|
WORKDIR /go/src/github.com/$REPO
|
||||||
COPY . /go/src/github.com/$REPO
|
COPY . /go/src/github.com/$REPO
|
||||||
RUN make TAG=$TAG build
|
RUN make TAG=$TAG BUILD=$BUILD build
|
||||||
|
|
||||||
# Runtime
|
# Runtime
|
||||||
FROM alpine
|
FROM alpine
|
||||||
|
7
Makefile
7
Makefile
@@ -6,6 +6,7 @@ APP=eris
|
|||||||
PACKAGE=irc
|
PACKAGE=irc
|
||||||
REPO?=prologic/$(APP)
|
REPO?=prologic/$(APP)
|
||||||
TAG?=latest
|
TAG?=latest
|
||||||
|
BUILD?=-dev
|
||||||
|
|
||||||
all: dev
|
all: dev
|
||||||
|
|
||||||
@@ -16,13 +17,13 @@ deps:
|
|||||||
@go get ./...
|
@go get ./...
|
||||||
|
|
||||||
build: clean deps
|
build: clean deps
|
||||||
@echo " -> Building $(REPO) v$(TAG)-@$(COMMIT)"
|
@echo " -> Building $(TAG)$(BUILD)"
|
||||||
@go build -tags "netgo static_build" -installsuffix netgo \
|
@go build -tags "netgo static_build" -installsuffix netgo \
|
||||||
-ldflags "-w -X github.com/$(REPO)/${PACKAGE}.GitCommit=$(COMMIT)
|
-ldflags "-w -X github.com/$(REPO)/${PACKAGE}.GitCommit=$(COMMIT) -X github.com/$(REPO)/${PACKAGE}.Build=$(BUILD)" .
|
||||||
@echo "Built $$(./$(APP) -v)"
|
@echo "Built $$(./$(APP) -v)"
|
||||||
|
|
||||||
image:
|
image:
|
||||||
@docker build --build-arg TAG=$(TAG) -t $(REPO):$(TAG) .
|
@docker build --build-arg TAG=$(TAG) --build-arg BUILD=$(BUILD) -t $(REPO):$(TAG) .
|
||||||
@echo "Image created: $(REPO):$(TAG)"
|
@echo "Image created: $(REPO):$(TAG)"
|
||||||
|
|
||||||
test:
|
test:
|
||||||
|
65
README.md
65
README.md
@@ -1,10 +1,4 @@
|
|||||||
:set spell eris - IRC Server / Daemon written in Go
|
# 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)
|
> 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)
|
> and much of my original contributions were made in my [fork of ergonomadic](https://github.com/prologic/ergonomadic)
|
||||||
@@ -25,7 +19,7 @@ The connotation here is that IRC (*Internet Relay Chat*) is a place of chaos,
|
|||||||
strife and discord. IRC is a place where you argue and get into arguments for
|
strife and discord. IRC is a place where you argue and get into arguments for
|
||||||
the sake of argument.
|
the sake of argument.
|
||||||
|
|
||||||
So `eris` is an IRC daemon written from scratch in Go to facilitate discord
|
So `eris` is an IRC daemon written from scratch in Go to factiliate discord
|
||||||
and have arguments for the sake of argument!
|
and have arguments for the sake of argument!
|
||||||
|
|
||||||
Pull requests and issues are welcome.
|
Pull requests and issues are welcome.
|
||||||
@@ -35,9 +29,9 @@ Discussion at:
|
|||||||
* /server irc.mills.io +6697 (*use TLS/SSL*)
|
* /server irc.mills.io +6697 (*use TLS/SSL*)
|
||||||
* /join #lobby
|
* /join #lobby
|
||||||
|
|
||||||
Or (**not recommended**):
|
Or (*not recommended*)P
|
||||||
|
|
||||||
* /server irc.mills.io (*default port 6667, non-TLS*)
|
* /server irc.mills.io (*default port 6667, non-TLS)
|
||||||
* /join #lobby
|
* /join #lobby
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
@@ -54,41 +48,6 @@ Or (**not recommended**):
|
|||||||
* Simple IRC operator privileges (*overrides most things*)
|
* Simple IRC operator privileges (*overrides most things*)
|
||||||
* Secure connection tracking (+z) and SecureOnly user mode (+Z)
|
* Secure connection tracking (+z) and SecureOnly user mode (+Z)
|
||||||
* Secure channels (+Z)
|
* Secure channels (+Z)
|
||||||
* Three layers of channel privacy, Public, Private (+p) and Secret (s)
|
|
||||||
|
|
||||||
## 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
|
## Installation
|
||||||
|
|
||||||
@@ -108,14 +67,6 @@ $ go install github.com/prologic/mkpasswd
|
|||||||
$ mkpasswd
|
$ mkpasswd
|
||||||
```
|
```
|
||||||
|
|
||||||
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
|
## Deployment
|
||||||
|
|
||||||
To run simply run the `eris` binary (*assuming a `ircd.yml` in the current directory*):
|
To run simply run the `eris` binary (*assuming a `ircd.yml` in the current directory*):
|
||||||
@@ -136,19 +87,15 @@ You may want to customize the configuration however and create your own image ba
|
|||||||
$ docker stack deploy -c docker-compose.yml eris
|
$ 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.
|
Which assumes a `ircd.yml` coniguration fiel int he 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 Projects
|
## Related Proejcts
|
||||||
|
|
||||||
There are a number of supported accompanying services that are being developed alongside Eris:
|
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.
|
* [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
|
* [Cadmus](https://github.com/prologic/cadmus) -- An IRC Bot that logs channels and provides an interface for viewing and searching logs
|
||||||
|
|
||||||
## Recommended Mobile clients
|
|
||||||
|
|
||||||
* [Palaver (iOS)](https://palaverapp.com/) -- SASL, TLS, Server Password, Push Notifications, IRCv3 (*Also supports custom image upload service(s) for better privacy of shared photos/images over IRC*)
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
eris is licensed under the MIT License.
|
eris is licensed under the MIT License.
|
||||||
|
@@ -155,16 +155,16 @@ func (channel *Channel) Join(client *Client, key Text) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
isInvited := channel.lists[InviteMask].Match(client.UserHost(false))
|
isInvited := channel.lists[InviteMask].Match(client.UserHost())
|
||||||
if !isOperator && channel.flags.Has(InviteOnly) && !isInvited {
|
if !isOperator && channel.flags.Has(InviteOnly) && !isInvited {
|
||||||
client.ErrInviteOnlyChan(channel)
|
client.ErrInviteOnlyChan(channel)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if channel.lists[BanMask].Match(client.UserHost(false)) &&
|
if channel.lists[BanMask].Match(client.UserHost()) &&
|
||||||
!isInvited &&
|
!isInvited &&
|
||||||
!isOperator &&
|
!isOperator &&
|
||||||
!channel.lists[ExceptMask].Match(client.UserHost(false)) {
|
!channel.lists[ExceptMask].Match(client.UserHost()) {
|
||||||
client.ErrBannedFromChan(channel)
|
client.ErrBannedFromChan(channel)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -374,7 +374,7 @@ func (channel *Channel) applyMode(client *Client, change *ChannelModeChange) boo
|
|||||||
return channel.applyModeMask(client, change.mode, change.op,
|
return channel.applyModeMask(client, change.mode, change.op,
|
||||||
NewName(change.arg))
|
NewName(change.arg))
|
||||||
|
|
||||||
case InviteOnly, Moderated, NoOutside, OpOnlyTopic, Private, Secret, SecureChan:
|
case InviteOnly, Moderated, NoOutside, OpOnlyTopic, Private, SecureChan:
|
||||||
return channel.applyModeFlag(client, change.mode, change.op)
|
return channel.applyModeFlag(client, change.mode, change.op)
|
||||||
|
|
||||||
case Key:
|
case Key:
|
||||||
@@ -508,7 +508,7 @@ func (channel *Channel) Invite(invitee *Client, inviter *Client) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if channel.flags.Has(InviteOnly) {
|
if channel.flags.Has(InviteOnly) {
|
||||||
channel.lists[InviteMask].Add(invitee.UserHost(false))
|
channel.lists[InviteMask].Add(invitee.UserHost())
|
||||||
}
|
}
|
||||||
|
|
||||||
inviter.RplInviting(invitee, channel.name)
|
inviter.RplInviting(invitee, channel.name)
|
||||||
|
@@ -26,7 +26,6 @@ type Client struct {
|
|||||||
hasQuit bool
|
hasQuit bool
|
||||||
hops uint
|
hops uint
|
||||||
hostname Name
|
hostname Name
|
||||||
hostmask Name // Cloacked hostname (SHA256)
|
|
||||||
pingTime time.Time
|
pingTime time.Time
|
||||||
idleTimer *time.Timer
|
idleTimer *time.Timer
|
||||||
nick Name
|
nick Name
|
||||||
@@ -72,17 +71,8 @@ func NewClient(server *Server, conn net.Conn) *Client {
|
|||||||
//
|
//
|
||||||
|
|
||||||
func (client *Client) writeloop() {
|
func (client *Client) writeloop() {
|
||||||
for {
|
for reply := range client.replies {
|
||||||
select {
|
client.socket.Write(reply)
|
||||||
case reply, ok := <-client.replies:
|
|
||||||
if !ok || reply == "" || client.socket == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
client.socket.Write(reply)
|
|
||||||
}
|
|
||||||
if client.replies == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,7 +83,6 @@ func (client *Client) readloop() {
|
|||||||
|
|
||||||
// Set the hostname for this client.
|
// Set the hostname for this client.
|
||||||
client.hostname = AddrLookupHostname(client.socket.conn.RemoteAddr())
|
client.hostname = AddrLookupHostname(client.socket.conn.RemoteAddr())
|
||||||
client.hostmask = NewName(SHA256(client.hostname.String()))
|
|
||||||
|
|
||||||
for err == nil {
|
for err == nil {
|
||||||
if line, err = client.socket.Read(); err != nil {
|
if line, err = client.socket.Read(); err != nil {
|
||||||
@@ -128,6 +117,13 @@ func (client *Client) readloop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) processCommand(cmd Command) {
|
func (client *Client) processCommand(cmd Command) {
|
||||||
|
client.server.metrics.Counter("client", "commands").Inc()
|
||||||
|
|
||||||
|
defer func(t time.Time) {
|
||||||
|
v := client.server.metrics.SummaryVec("client", "command_duration_seconds")
|
||||||
|
v.WithLabelValues(cmd.Code().String()).Observe(time.Now().Sub(t).Seconds())
|
||||||
|
}(time.Now())
|
||||||
|
|
||||||
cmd.SetClient(client)
|
cmd.SetClient(client)
|
||||||
|
|
||||||
if !client.registered {
|
if !client.registered {
|
||||||
@@ -146,13 +142,6 @@ func (client *Client) processCommand(cmd Command) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
client.server.metrics.Counter("client", "commands").Inc()
|
|
||||||
|
|
||||||
defer func(t time.Time) {
|
|
||||||
v := client.server.metrics.SummaryVec("client", "command_duration_seconds")
|
|
||||||
v.WithLabelValues(cmd.Code().String()).Observe(time.Now().Sub(t).Seconds())
|
|
||||||
}(time.Now())
|
|
||||||
|
|
||||||
switch srvCmd.(type) {
|
switch srvCmd.(type) {
|
||||||
case *PingCommand, *PongCommand:
|
case *PingCommand, *PongCommand:
|
||||||
client.Touch()
|
client.Touch()
|
||||||
@@ -218,7 +207,6 @@ func (client *Client) Register() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
client.registered = true
|
client.registered = true
|
||||||
client.flags[HostMask] = true
|
|
||||||
client.Touch()
|
client.Touch()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,12 +220,6 @@ func (client *Client) destroy() {
|
|||||||
|
|
||||||
// clean up server
|
// clean up server
|
||||||
|
|
||||||
if _, ok := client.socket.conn.(*tls.Conn); ok {
|
|
||||||
client.server.metrics.GaugeVec("server", "clients").WithLabelValues("secure").Dec()
|
|
||||||
} else {
|
|
||||||
client.server.metrics.GaugeVec("server", "clients").WithLabelValues("insecure").Dec()
|
|
||||||
}
|
|
||||||
|
|
||||||
client.server.connections.Dec()
|
client.server.connections.Dec()
|
||||||
client.server.clients.Remove(client)
|
client.server.clients.Remove(client)
|
||||||
|
|
||||||
@@ -251,7 +233,6 @@ func (client *Client) destroy() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
close(client.replies)
|
close(client.replies)
|
||||||
client.replies = nil
|
|
||||||
|
|
||||||
client.socket.Close()
|
client.socket.Close()
|
||||||
|
|
||||||
@@ -298,14 +279,11 @@ func (c *Client) ModeString() (str string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) UserHost(cloacked bool) Name {
|
func (c *Client) UserHost() Name {
|
||||||
username := "*"
|
username := "*"
|
||||||
if c.HasUsername() {
|
if c.HasUsername() {
|
||||||
username = c.username.String()
|
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))
|
return Name(fmt.Sprintf("%s!%s@%s", c.Nick(), username, c.hostname))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,7 +303,7 @@ func (c *Client) Nick() Name {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Id() Name {
|
func (c *Client) Id() Name {
|
||||||
return c.UserHost(true)
|
return c.UserHost()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) String() string {
|
func (c *Client) String() string {
|
||||||
@@ -368,9 +346,7 @@ func (client *Client) ChangeNickname(nickname Name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) Reply(reply string) {
|
func (client *Client) Reply(reply string) {
|
||||||
if client.replies != nil {
|
client.replies <- reply
|
||||||
client.replies <- reply
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) Quit(message Text) {
|
func (client *Client) Quit(message Text) {
|
||||||
|
@@ -4,7 +4,9 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
//"sync"
|
||||||
|
|
||||||
|
sync "github.com/sasha-s/go-deadlock"
|
||||||
|
|
||||||
"github.com/DanielOaks/girc-go/ircmatch"
|
"github.com/DanielOaks/girc-go/ircmatch"
|
||||||
)
|
)
|
||||||
@@ -103,7 +105,7 @@ func (clients *ClientLookupSet) FindAll(userhost Name) *ClientSet {
|
|||||||
|
|
||||||
var casemappedNickMask string
|
var casemappedNickMask string
|
||||||
for _, client := range clients.nicks {
|
for _, client := range clients.nicks {
|
||||||
casemappedNickMask = client.UserHost(false).String()
|
casemappedNickMask = client.UserHost().String()
|
||||||
if matcher.Match(casemappedNickMask) {
|
if matcher.Match(casemappedNickMask) {
|
||||||
set.Add(client)
|
set.Add(client)
|
||||||
}
|
}
|
||||||
@@ -121,7 +123,7 @@ func (clients *ClientLookupSet) Find(userhost Name) *Client {
|
|||||||
|
|
||||||
var casemappedNickMask string
|
var casemappedNickMask string
|
||||||
for _, client := range clients.nicks {
|
for _, client := range clients.nicks {
|
||||||
casemappedNickMask = client.UserHost(false).String()
|
casemappedNickMask = client.UserHost().String()
|
||||||
if matcher.Match(casemappedNickMask) {
|
if matcher.Match(casemappedNickMask) {
|
||||||
return client
|
return client
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,9 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"sync"
|
//"sync"
|
||||||
|
|
||||||
|
sync "github.com/sasha-s/go-deadlock"
|
||||||
|
|
||||||
"github.com/imdario/mergo"
|
"github.com/imdario/mergo"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
|
@@ -20,7 +20,6 @@ var DefObjectives = map[float64]float64{
|
|||||||
type Metrics struct {
|
type Metrics struct {
|
||||||
namespace string
|
namespace string
|
||||||
metrics map[string]prometheus.Metric
|
metrics map[string]prometheus.Metric
|
||||||
gaugevecs map[string]*prometheus.GaugeVec
|
|
||||||
sumvecs map[string]*prometheus.SummaryVec
|
sumvecs map[string]*prometheus.SummaryVec
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,7 +27,6 @@ func NewMetrics(namespace string) *Metrics {
|
|||||||
return &Metrics{
|
return &Metrics{
|
||||||
namespace: namespace,
|
namespace: namespace,
|
||||||
metrics: make(map[string]prometheus.Metric),
|
metrics: make(map[string]prometheus.Metric),
|
||||||
gaugevecs: make(map[string]*prometheus.GaugeVec),
|
|
||||||
sumvecs: make(map[string]*prometheus.SummaryVec),
|
sumvecs: make(map[string]*prometheus.SummaryVec),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,24 +101,6 @@ func (m *Metrics) NewGaugeFunc(subsystem, name, help string, f func() float64) p
|
|||||||
return guage
|
return guage
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Metrics) NewGaugeVec(subsystem, name, help string, labels []string) *prometheus.GaugeVec {
|
|
||||||
gauagevec := prometheus.NewGaugeVec(
|
|
||||||
prometheus.GaugeOpts{
|
|
||||||
Namespace: m.namespace,
|
|
||||||
Subsystem: subsystem,
|
|
||||||
Name: name,
|
|
||||||
Help: help,
|
|
||||||
},
|
|
||||||
labels,
|
|
||||||
)
|
|
||||||
|
|
||||||
key := fmt.Sprintf("%s_%s", subsystem, name)
|
|
||||||
m.gaugevecs[key] = gauagevec
|
|
||||||
prometheus.MustRegister(gauagevec)
|
|
||||||
|
|
||||||
return gauagevec
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Metrics) NewSummary(subsystem, name, help string) prometheus.Summary {
|
func (m *Metrics) NewSummary(subsystem, name, help string) prometheus.Summary {
|
||||||
summary := prometheus.NewSummary(
|
summary := prometheus.NewSummary(
|
||||||
prometheus.SummaryOpts{
|
prometheus.SummaryOpts{
|
||||||
@@ -168,11 +148,6 @@ func (m *Metrics) Gauge(subsystem, name string) prometheus.Gauge {
|
|||||||
return m.metrics[key].(prometheus.Gauge)
|
return m.metrics[key].(prometheus.Gauge)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Metrics) GaugeVec(subsystem, name string) *prometheus.GaugeVec {
|
|
||||||
key := fmt.Sprintf("%s_%s", subsystem, name)
|
|
||||||
return m.gaugevecs[key]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Metrics) Summary(subsystem, name string) prometheus.Summary {
|
func (m *Metrics) Summary(subsystem, name string) prometheus.Summary {
|
||||||
key := fmt.Sprintf("%s_%s", subsystem, name)
|
key := fmt.Sprintf("%s_%s", subsystem, name)
|
||||||
return m.metrics[key].(prometheus.Summary)
|
return m.metrics[key].(prometheus.Summary)
|
||||||
|
10
irc/modes.go
10
irc/modes.go
@@ -58,12 +58,11 @@ const (
|
|||||||
Registered UserMode = 'r' // not a real user mode (flag)
|
Registered UserMode = 'r' // not a real user mode (flag)
|
||||||
SecureConn UserMode = 'z'
|
SecureConn UserMode = 'z'
|
||||||
SecureOnly UserMode = 'Z'
|
SecureOnly UserMode = 'Z'
|
||||||
HostMask UserMode = 'x'
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
SupportedUserModes = UserModes{
|
SupportedUserModes = UserModes{
|
||||||
Invisible, Operator, HostMask,
|
Invisible, Operator,
|
||||||
}
|
}
|
||||||
DefaultChannelModes = ChannelModes{
|
DefaultChannelModes = ChannelModes{
|
||||||
NoOutside, OpOnlyTopic,
|
NoOutside, OpOnlyTopic,
|
||||||
@@ -71,6 +70,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
Anonymous ChannelMode = 'a' // flag
|
||||||
BanMask ChannelMode = 'b' // arg
|
BanMask ChannelMode = 'b' // arg
|
||||||
ChannelCreator ChannelMode = 'O' // flag
|
ChannelCreator ChannelMode = 'O' // flag
|
||||||
ChannelOperator ChannelMode = 'o' // arg
|
ChannelOperator ChannelMode = 'o' // arg
|
||||||
@@ -82,6 +82,8 @@ const (
|
|||||||
NoOutside ChannelMode = 'n' // flag
|
NoOutside ChannelMode = 'n' // flag
|
||||||
OpOnlyTopic ChannelMode = 't' // flag
|
OpOnlyTopic ChannelMode = 't' // flag
|
||||||
Private ChannelMode = 'p' // flag
|
Private ChannelMode = 'p' // flag
|
||||||
|
Quiet ChannelMode = 'q' // flag
|
||||||
|
ReOp ChannelMode = 'r' // flag
|
||||||
Secret ChannelMode = 's' // flag, deprecated
|
Secret ChannelMode = 's' // flag, deprecated
|
||||||
UserLimit ChannelMode = 'l' // flag arg
|
UserLimit ChannelMode = 'l' // flag arg
|
||||||
Voice ChannelMode = 'v' // arg
|
Voice ChannelMode = 'v' // arg
|
||||||
@@ -91,7 +93,7 @@ const (
|
|||||||
var (
|
var (
|
||||||
SupportedChannelModes = ChannelModes{
|
SupportedChannelModes = ChannelModes{
|
||||||
BanMask, ExceptMask, InviteMask, InviteOnly, Key, NoOutside,
|
BanMask, ExceptMask, InviteMask, InviteOnly, Key, NoOutside,
|
||||||
OpOnlyTopic, Private, UserLimit, Secret, SecureChan,
|
OpOnlyTopic, Private, UserLimit, SecureChan,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -117,7 +119,7 @@ func (m *ModeCommand) HandleServer(s *Server) {
|
|||||||
|
|
||||||
for _, change := range m.changes {
|
for _, change := range m.changes {
|
||||||
switch change.mode {
|
switch change.mode {
|
||||||
case Invisible, HostMask, WallOps, SecureOnly:
|
case Invisible, WallOps, SecureOnly:
|
||||||
switch change.op {
|
switch change.op {
|
||||||
case Add:
|
case Add:
|
||||||
if target.flags[change.mode] {
|
if target.flags[change.mode] {
|
||||||
|
@@ -4,8 +4,9 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
"sync"
|
//"sync"
|
||||||
|
|
||||||
|
sync "github.com/sasha-s/go-deadlock"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -67,7 +68,7 @@ func (store *MemoryPasswordStore) Verify(username, password string) error {
|
|||||||
hash, ok := store.Get(username)
|
hash, ok := store.Get(username)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Debugf("username %s not found", username)
|
log.Debugf("username %s not found", username)
|
||||||
return fmt.Errorf("account not found: %s", username)
|
return fmt.Errorf("account not found: %S", username)
|
||||||
}
|
}
|
||||||
|
|
||||||
return store.hasher.Compare(hash, []byte(password))
|
return store.hasher.Compare(hash, []byte(password))
|
||||||
|
@@ -1,22 +0,0 @@
|
|||||||
package irc
|
|
||||||
|
|
||||||
func CanSeeChannel(client *Client, channel *Channel) bool {
|
|
||||||
isPrivate := channel.flags.Has(Private)
|
|
||||||
isSecret := channel.flags.Has(Secret)
|
|
||||||
|
|
||||||
isMember := channel.members.Has(client)
|
|
||||||
isOperator := client.flags[Operator]
|
|
||||||
isRegistered := client.flags[Registered]
|
|
||||||
isSecure := client.flags[SecureConn]
|
|
||||||
|
|
||||||
if !(isSecret || isPrivate) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if isSecret && (isMember || isOperator) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if isPrivate && (isMember || isOperator || (isRegistered && isSecure)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
72
irc/reply.go
72
irc/reply.go
@@ -260,16 +260,16 @@ func (target *Client) RplWhois(client *Client) {
|
|||||||
}
|
}
|
||||||
target.RplWhoisServer(client)
|
target.RplWhoisServer(client)
|
||||||
target.RplWhoisLoggedIn(client)
|
target.RplWhoisLoggedIn(client)
|
||||||
target.RplEndOfWhois(client)
|
target.RplEndOfWhois()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (target *Client) RplWhoisUser(client *Client) {
|
func (target *Client) RplWhoisUser(client *Client) {
|
||||||
var clientHost Name
|
var clientHost Name
|
||||||
|
|
||||||
if target.flags[Operator] || !client.flags[HostMask] {
|
if client.flags[SecureConn] {
|
||||||
clientHost = client.hostname
|
clientHost = client.hostname
|
||||||
} else {
|
} else {
|
||||||
clientHost = client.hostmask
|
clientHost = NewName("SECURED")
|
||||||
}
|
}
|
||||||
|
|
||||||
target.NumericReply(
|
target.NumericReply(
|
||||||
@@ -324,12 +324,9 @@ func (target *Client) RplWhoisServer(client *Client) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (target *Client) RplEndOfWhois(client *Client) {
|
func (target *Client) RplEndOfWhois() {
|
||||||
target.NumericReply(
|
target.NumericReply(RPL_ENDOFWHOIS,
|
||||||
RPL_ENDOFWHOIS,
|
":End of WHOIS list")
|
||||||
"%s :End of WHOIS list",
|
|
||||||
client.Nick(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (target *Client) RplChannelModeIs(channel *Channel) {
|
func (target *Client) RplChannelModeIs(channel *Channel) {
|
||||||
@@ -340,14 +337,6 @@ func (target *Client) RplChannelModeIs(channel *Channel) {
|
|||||||
// <channel> <user> <host> <server> <nick> ( "H" / "G" ) ["*"] [ ( "@" / "+" ) ]
|
// <channel> <user> <host> <server> <nick> ( "H" / "G" ) ["*"] [ ( "@" / "+" ) ]
|
||||||
// :<hopcount> <real name>
|
// :<hopcount> <real name>
|
||||||
func (target *Client) RplWhoReply(channel *Channel, client *Client) {
|
func (target *Client) RplWhoReply(channel *Channel, client *Client) {
|
||||||
var clientHost Name
|
|
||||||
|
|
||||||
if target.flags[Operator] || !client.flags[HostMask] {
|
|
||||||
clientHost = client.hostname
|
|
||||||
} else {
|
|
||||||
clientHost = client.hostmask
|
|
||||||
}
|
|
||||||
|
|
||||||
channelName := "*"
|
channelName := "*"
|
||||||
flags := ""
|
flags := ""
|
||||||
|
|
||||||
@@ -377,18 +366,9 @@ func (target *Client) RplWhoReply(channel *Channel, client *Client) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
target.NumericReply(
|
target.NumericReply(RPL_WHOREPLY,
|
||||||
RPL_WHOREPLY,
|
"%s %s %s %s %s %s :%d %s", channelName, client.username, client.hostname,
|
||||||
"%s %s %s %s %s %s :%d %s",
|
client.server.name, client.Nick(), flags, client.hops, client.realname)
|
||||||
channelName,
|
|
||||||
client.username,
|
|
||||||
clientHost,
|
|
||||||
client.server.name,
|
|
||||||
client.Nick(),
|
|
||||||
flags,
|
|
||||||
client.hops,
|
|
||||||
client.realname,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// <name> :End of WHO list
|
// <name> :End of WHO list
|
||||||
@@ -489,13 +469,8 @@ func (target *Client) RplMOTDEnd() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (target *Client) RplList(channel *Channel) {
|
func (target *Client) RplList(channel *Channel) {
|
||||||
target.NumericReply(
|
target.NumericReply(RPL_LIST,
|
||||||
RPL_LIST,
|
"%s %d :%s", channel, channel.members.Count(), channel.topic)
|
||||||
"%s %d :%s",
|
|
||||||
channel,
|
|
||||||
channel.members.Count(),
|
|
||||||
channel.topic,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (target *Client) RplListEnd(server *Server) {
|
func (target *Client) RplListEnd(server *Server) {
|
||||||
@@ -509,12 +484,8 @@ func (target *Client) RplNamReply(channel *Channel) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (target *Client) RplWhoisChannels(client *Client) {
|
func (target *Client) RplWhoisChannels(client *Client) {
|
||||||
target.MultilineReply(
|
target.MultilineReply(client.WhoisChannelsNames(), RPL_WHOISCHANNELS,
|
||||||
client.WhoisChannelsNames(target),
|
"%s :%s", client.Nick())
|
||||||
RPL_WHOISCHANNELS,
|
|
||||||
"%s :%s",
|
|
||||||
client.Nick(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (target *Client) RplVersion() {
|
func (target *Client) RplVersion() {
|
||||||
@@ -608,22 +579,9 @@ func (target *Client) RplLUserMe() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (target *Client) RplWhoWasUser(whoWas *WhoWas) {
|
func (target *Client) RplWhoWasUser(whoWas *WhoWas) {
|
||||||
var whoWasHost Name
|
target.NumericReply(RPL_WHOWASUSER,
|
||||||
|
|
||||||
if target.flags[Operator] {
|
|
||||||
whoWasHost = whoWas.hostname
|
|
||||||
} else {
|
|
||||||
whoWasHost = whoWas.hostmask
|
|
||||||
}
|
|
||||||
|
|
||||||
target.NumericReply(
|
|
||||||
RPL_WHOWASUSER,
|
|
||||||
"%s %s %s * :%s",
|
"%s %s %s * :%s",
|
||||||
whoWas.nickname,
|
whoWas.nickname, whoWas.username, whoWas.hostname, whoWas.realname)
|
||||||
whoWas.username,
|
|
||||||
whoWasHost,
|
|
||||||
whoWas.realname,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (target *Client) RplEndOfWhoWas(nickname Name) {
|
func (target *Client) RplEndOfWhoWas(nickname Name) {
|
||||||
|
@@ -2,7 +2,9 @@ package irc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"sync"
|
//"sync"
|
||||||
|
|
||||||
|
sync "github.com/sasha-s/go-deadlock"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SaslState struct {
|
type SaslState struct {
|
||||||
|
@@ -44,16 +44,13 @@ type Server struct {
|
|||||||
accounts PasswordStore
|
accounts PasswordStore
|
||||||
password []byte
|
password []byte
|
||||||
signals chan os.Signal
|
signals chan os.Signal
|
||||||
done chan bool
|
|
||||||
whoWas *WhoWasList
|
whoWas *WhoWasList
|
||||||
ids map[string]*Identity
|
ids map[string]*Identity
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
SERVER_SIGNALS = []os.Signal{
|
SERVER_SIGNALS = []os.Signal{syscall.SIGINT, syscall.SIGHUP,
|
||||||
syscall.SIGINT, syscall.SIGHUP,
|
syscall.SIGTERM, syscall.SIGQUIT}
|
||||||
syscall.SIGTERM, syscall.SIGQUIT,
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewServer(config *Config) *Server {
|
func NewServer(config *Config) *Server {
|
||||||
@@ -73,14 +70,13 @@ func NewServer(config *Config) *Server {
|
|||||||
operators: config.Operators(),
|
operators: config.Operators(),
|
||||||
accounts: NewMemoryPasswordStore(config.Accounts(), PasswordStoreOpts{}),
|
accounts: NewMemoryPasswordStore(config.Accounts(), PasswordStoreOpts{}),
|
||||||
signals: make(chan os.Signal, len(SERVER_SIGNALS)),
|
signals: make(chan os.Signal, len(SERVER_SIGNALS)),
|
||||||
done: make(chan bool),
|
|
||||||
whoWas: NewWhoWasList(100),
|
whoWas: NewWhoWasList(100),
|
||||||
ids: make(map[string]*Identity),
|
ids: make(map[string]*Identity),
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("accounts: %v", config.Accounts())
|
log.Debugf("accounts: %v", config.Accounts())
|
||||||
|
|
||||||
// TODO: Make this configureable?
|
// TODO: Make this configurabel?
|
||||||
server.ids["global"] = NewIdentity(config.Server.Name, "global")
|
server.ids["global"] = NewIdentity(config.Server.Name, "global")
|
||||||
|
|
||||||
if config.Server.Password != "" {
|
if config.Server.Password != "" {
|
||||||
@@ -127,22 +123,15 @@ func NewServer(config *Config) *Server {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// server registered (clients) gauge
|
// server clients gauge
|
||||||
server.metrics.NewGaugeFunc(
|
server.metrics.NewGaugeFunc(
|
||||||
"server", "registered",
|
"server", "clients",
|
||||||
"Number of registered clients connected",
|
"Number of registered clients connected",
|
||||||
func() float64 {
|
func() float64 {
|
||||||
return float64(server.clients.Count())
|
return float64(server.clients.Count())
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// server clients gauge (by secure/insecure)
|
|
||||||
server.metrics.NewGaugeVec(
|
|
||||||
"server", "clients",
|
|
||||||
"Number of registered clients connected (by secure/insecure)",
|
|
||||||
[]string{"secure"},
|
|
||||||
)
|
|
||||||
|
|
||||||
// server channels gauge
|
// server channels gauge
|
||||||
server.metrics.NewGaugeFunc(
|
server.metrics.NewGaugeFunc(
|
||||||
"server", "channels",
|
"server", "channels",
|
||||||
@@ -202,22 +191,13 @@ func (server *Server) Shutdown() {
|
|||||||
server.Global("shutting down...")
|
server.Global("shutting down...")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *Server) Stop() {
|
|
||||||
server.done <- true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (server *Server) Run() {
|
func (server *Server) Run() {
|
||||||
for {
|
done := false
|
||||||
|
for !done {
|
||||||
select {
|
select {
|
||||||
case <-server.done:
|
|
||||||
return
|
|
||||||
case <-server.signals:
|
case <-server.signals:
|
||||||
server.Shutdown()
|
server.Shutdown()
|
||||||
// Give at least 1s for clients to see the shutdown
|
done = true
|
||||||
go func() {
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
server.Stop()
|
|
||||||
}()
|
|
||||||
|
|
||||||
case conn := <-server.newConns:
|
case conn := <-server.newConns:
|
||||||
go NewClient(server, conn)
|
go NewClient(server, conn)
|
||||||
@@ -237,12 +217,6 @@ func (s *Server) acceptor(listener net.Listener) {
|
|||||||
}
|
}
|
||||||
log.Debugf("%s accept: %s", s, conn.RemoteAddr())
|
log.Debugf("%s accept: %s", s, conn.RemoteAddr())
|
||||||
|
|
||||||
if _, ok := conn.(*tls.Conn); ok {
|
|
||||||
s.metrics.GaugeVec("server", "clients").WithLabelValues("secure").Inc()
|
|
||||||
} else {
|
|
||||||
s.metrics.GaugeVec("server", "clients").WithLabelValues("insecure").Inc()
|
|
||||||
}
|
|
||||||
|
|
||||||
s.connections.Inc()
|
s.connections.Inc()
|
||||||
s.newConns <- conn
|
s.newConns <- conn
|
||||||
}
|
}
|
||||||
@@ -629,14 +603,10 @@ func (msg *PrivMsgCommand) HandleServer(server *Server) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) WhoisChannelsNames(target *Client) []string {
|
func (client *Client) WhoisChannelsNames() []string {
|
||||||
chstrs := make([]string, client.channels.Count())
|
chstrs := make([]string, client.channels.Count())
|
||||||
index := 0
|
index := 0
|
||||||
client.channels.Range(func(channel *Channel) bool {
|
client.channels.Range(func(channel *Channel) bool {
|
||||||
if !CanSeeChannel(target, channel) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case channel.members.Get(client).Has(ChannelOperator):
|
case channel.members.Get(client).Has(ChannelOperator):
|
||||||
chstrs[index] = "@" + channel.name.String()
|
chstrs[index] = "@" + channel.name.String()
|
||||||
@@ -843,7 +813,7 @@ func (msg *ListCommand) HandleServer(server *Server) {
|
|||||||
|
|
||||||
if len(msg.channels) == 0 {
|
if len(msg.channels) == 0 {
|
||||||
server.channels.Range(func(name Name, channel *Channel) bool {
|
server.channels.Range(func(name Name, channel *Channel) bool {
|
||||||
if !CanSeeChannel(client, channel) {
|
if !client.flags[Operator] && channel.flags.Has(Private) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
client.RplList(channel)
|
client.RplList(channel)
|
||||||
@@ -852,7 +822,7 @@ func (msg *ListCommand) HandleServer(server *Server) {
|
|||||||
} else {
|
} else {
|
||||||
for _, chname := range msg.channels {
|
for _, chname := range msg.channels {
|
||||||
channel := server.channels.Get(chname)
|
channel := server.channels.Get(chname)
|
||||||
if channel == nil || !CanSeeChannel(client, channel) {
|
if channel == nil || (!client.flags[Operator] && channel.flags.Has(Private)) {
|
||||||
client.ErrNoSuchChannel(chname)
|
client.ErrNoSuchChannel(chname)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,9 @@ package irc
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
//"sync"
|
||||||
|
|
||||||
|
sync "github.com/sasha-s/go-deadlock"
|
||||||
)
|
)
|
||||||
|
|
||||||
//
|
//
|
||||||
|
11
irc/utils.go
11
irc/utils.go
@@ -1,11 +0,0 @@
|
|||||||
package irc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/sha256"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func SHA256(data string) string {
|
|
||||||
hash := sha256.Sum256([]byte(data))
|
|
||||||
return fmt.Sprintf("%x", hash)
|
|
||||||
}
|
|
@@ -1,21 +1,20 @@
|
|||||||
package irc
|
package irc
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Package package name
|
//PackageName package name
|
||||||
Package = "eris"
|
Package = "eris"
|
||||||
|
|
||||||
// Version release version
|
// Version release version
|
||||||
Version = "1.6.4"
|
Version = "1.6.0"
|
||||||
|
|
||||||
// Commit will be overwritten automatically by the build system
|
// Build will be overwritten automatically by the build system
|
||||||
Commit = "HEAD"
|
Build = "-dev"
|
||||||
|
|
||||||
|
// GitCommit will be overwritten automatically by the build system
|
||||||
|
GitCommit = "HEAD"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FullVersion display the full version and build
|
// FullVersion display the full version and build
|
||||||
func FullVersion() string {
|
func FullVersion() string {
|
||||||
return fmt.Sprintf("%s-%s@%s", Package, Version, Commit)
|
return Package + " v" + Version + Build + " (" + GitCommit + ")"
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,9 @@
|
|||||||
package irc
|
package irc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
//"sync"
|
||||||
|
|
||||||
|
sync "github.com/sasha-s/go-deadlock"
|
||||||
)
|
)
|
||||||
|
|
||||||
type WhoWasList struct {
|
type WhoWasList struct {
|
||||||
@@ -15,7 +17,6 @@ type WhoWas struct {
|
|||||||
nickname Name
|
nickname Name
|
||||||
username Name
|
username Name
|
||||||
hostname Name
|
hostname Name
|
||||||
hostmask Name
|
|
||||||
realname Text
|
realname Text
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +33,6 @@ func (list *WhoWasList) Append(client *Client) {
|
|||||||
nickname: client.Nick(),
|
nickname: client.Nick(),
|
||||||
username: client.username,
|
username: client.username,
|
||||||
hostname: client.hostname,
|
hostname: client.hostname,
|
||||||
hostmask: client.hostmask,
|
|
||||||
realname: client.realname,
|
realname: client.realname,
|
||||||
}
|
}
|
||||||
list.end = (list.end + 1) % len(list.buffer)
|
list.end = (list.end + 1) % len(list.buffer)
|
||||||
|
575
main_test.go
575
main_test.go
@@ -1,575 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/sha256"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
"github.com/renstrom/shortuuid"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/thoj/go-ircevent"
|
|
||||||
|
|
||||||
eris "github.com/prologic/eris/irc"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
TIMEOUT = 3 * time.Second
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
server *eris.Server
|
|
||||||
|
|
||||||
debug = flag.Bool("d", false, "enable debug logging")
|
|
||||||
)
|
|
||||||
|
|
||||||
func setupServer() *eris.Server {
|
|
||||||
config := &eris.Config{}
|
|
||||||
|
|
||||||
config.Network.Name = "Test"
|
|
||||||
config.Server.Name = "test"
|
|
||||||
config.Server.Description = "Test"
|
|
||||||
config.Server.Listen = []string{":6667"}
|
|
||||||
|
|
||||||
// SASL
|
|
||||||
config.Account = map[string]*eris.PassConfig{
|
|
||||||
"admin": &eris.PassConfig{"JDJhJDA0JGtUU1JVc1JOUy9DbEh1WEdvYVlMdGVnclp6YnA3NDBOZGY1WUZhdTZtRzVmb1VKdXQ5ckZD"},
|
|
||||||
}
|
|
||||||
|
|
||||||
server := eris.NewServer(config)
|
|
||||||
|
|
||||||
go server.Run()
|
|
||||||
|
|
||||||
return server
|
|
||||||
}
|
|
||||||
|
|
||||||
func randomValidName() string {
|
|
||||||
var name eris.Name
|
|
||||||
for {
|
|
||||||
name = eris.NewName(shortuuid.New())
|
|
||||||
if name.IsNickname() {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return name.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func newClient(start bool) *irc.Connection {
|
|
||||||
name := randomValidName()
|
|
||||||
client := irc.IRC(name, name)
|
|
||||||
client.RealName = fmt.Sprintf("Test Client: %s", name)
|
|
||||||
|
|
||||||
err := client.Connect("localhost:6667")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("error setting up test client: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if start {
|
|
||||||
go client.Loop()
|
|
||||||
}
|
|
||||||
|
|
||||||
return client
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
if *debug {
|
|
||||||
log.SetLevel(log.DebugLevel)
|
|
||||||
} else {
|
|
||||||
log.SetLevel(log.WarnLevel)
|
|
||||||
}
|
|
||||||
|
|
||||||
server = setupServer()
|
|
||||||
|
|
||||||
result := m.Run()
|
|
||||||
|
|
||||||
server.Stop()
|
|
||||||
|
|
||||||
os.Exit(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConnection(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
expected := true
|
|
||||||
actual := make(chan bool)
|
|
||||||
|
|
||||||
client := newClient(false)
|
|
||||||
|
|
||||||
client.AddCallback("001", func(e *irc.Event) {
|
|
||||||
actual <- true
|
|
||||||
})
|
|
||||||
|
|
||||||
defer client.Quit()
|
|
||||||
go client.Loop()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case res := <-actual:
|
|
||||||
assert.Equal(expected, res)
|
|
||||||
case <-time.After(TIMEOUT):
|
|
||||||
assert.Fail("timeout")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSASL(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
expected := true
|
|
||||||
actual := make(chan bool)
|
|
||||||
|
|
||||||
client := newClient(false)
|
|
||||||
client.SASLLogin = "admin"
|
|
||||||
client.SASLPassword = "admin"
|
|
||||||
|
|
||||||
client.AddCallback("001", func(e *irc.Event) {
|
|
||||||
actual <- true
|
|
||||||
})
|
|
||||||
|
|
||||||
defer client.Quit()
|
|
||||||
go client.Loop()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case res := <-actual:
|
|
||||||
assert.Equal(expected, res)
|
|
||||||
case <-time.After(TIMEOUT):
|
|
||||||
assert.Fail("timeout")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRplWelcome(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
expected := "Welcome to the .* Internet Relay Network .*!.*@.*"
|
|
||||||
actual := make(chan string)
|
|
||||||
|
|
||||||
client := newClient(false)
|
|
||||||
|
|
||||||
client.AddCallback("001", func(e *irc.Event) {
|
|
||||||
actual <- e.Message()
|
|
||||||
})
|
|
||||||
|
|
||||||
defer client.Quit()
|
|
||||||
go client.Loop()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case res := <-actual:
|
|
||||||
assert.Regexp(expected, res)
|
|
||||||
case <-time.After(TIMEOUT):
|
|
||||||
assert.Fail("timeout")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUser_JOIN(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
client := newClient(false)
|
|
||||||
|
|
||||||
expected := []string{client.GetNick(), "=", "#join", fmt.Sprintf("@%s", client.GetNick())}
|
|
||||||
actual := make(chan string)
|
|
||||||
|
|
||||||
client.AddCallback("353", func(e *irc.Event) {
|
|
||||||
for i := range e.Arguments {
|
|
||||||
actual <- e.Arguments[i]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
defer client.Quit()
|
|
||||||
go client.Loop()
|
|
||||||
|
|
||||||
client.Join("#join")
|
|
||||||
client.SendRaw("NAMES #join")
|
|
||||||
|
|
||||||
for i := range expected {
|
|
||||||
select {
|
|
||||||
case res := <-actual:
|
|
||||||
assert.Equal(expected[i], res)
|
|
||||||
case <-time.After(TIMEOUT):
|
|
||||||
assert.Fail("timeout")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestChannel_InviteOnly(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
expected := true
|
|
||||||
actual := make(chan bool)
|
|
||||||
|
|
||||||
client1 := newClient(false)
|
|
||||||
client2 := newClient(false)
|
|
||||||
|
|
||||||
client1.AddCallback("324", func(e *irc.Event) {
|
|
||||||
if strings.Contains(e.Arguments[2], "i") {
|
|
||||||
client2.Join("#inviteonly")
|
|
||||||
} else {
|
|
||||||
client1.Mode("#inviteonly")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
client2.AddCallback("473", func(e *irc.Event) {
|
|
||||||
actual <- true
|
|
||||||
})
|
|
||||||
client2.AddCallback("JOIN", func(e *irc.Event) {
|
|
||||||
actual <- false
|
|
||||||
})
|
|
||||||
|
|
||||||
defer client1.Quit()
|
|
||||||
defer client2.Quit()
|
|
||||||
go client1.Loop()
|
|
||||||
go client2.Loop()
|
|
||||||
|
|
||||||
client1.Join("#inviteonly")
|
|
||||||
client1.Mode("#inviteonly", "+i")
|
|
||||||
client1.Mode("#inviteonly")
|
|
||||||
|
|
||||||
select {
|
|
||||||
case res := <-actual:
|
|
||||||
assert.Equal(expected, res)
|
|
||||||
case <-time.After(TIMEOUT):
|
|
||||||
assert.Fail("timeout")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUser_WithHostMask(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
client1 := newClient(false)
|
|
||||||
client2 := newClient(false)
|
|
||||||
|
|
||||||
expected := fmt.Sprintf("%x", sha256.Sum256([]byte("localhost")))
|
|
||||||
actual := make(chan string)
|
|
||||||
|
|
||||||
client1.AddCallback("001", func(e *irc.Event) {
|
|
||||||
client1.Mode(client1.GetNick(), "+x")
|
|
||||||
})
|
|
||||||
|
|
||||||
client2.AddCallback("001", func(e *irc.Event) {
|
|
||||||
client2.Whois(client1.GetNick())
|
|
||||||
})
|
|
||||||
|
|
||||||
client2.AddCallback("401", func(e *irc.Event) {
|
|
||||||
client2.Whois(client1.GetNick())
|
|
||||||
})
|
|
||||||
|
|
||||||
client2.AddCallback("311", func(e *irc.Event) {
|
|
||||||
actual <- e.Arguments[3]
|
|
||||||
})
|
|
||||||
|
|
||||||
defer client1.Quit()
|
|
||||||
defer client2.Quit()
|
|
||||||
go client1.Loop()
|
|
||||||
go client2.Loop()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case res := <-actual:
|
|
||||||
assert.Equal(expected, res)
|
|
||||||
case <-time.After(TIMEOUT):
|
|
||||||
assert.Fail("timeout")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUser_WithoutHostMask(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
client1 := newClient(false)
|
|
||||||
client2 := newClient(false)
|
|
||||||
|
|
||||||
expected := "localhost"
|
|
||||||
actual := make(chan string)
|
|
||||||
|
|
||||||
client1.AddCallback("001", func(e *irc.Event) {
|
|
||||||
client1.Mode(client1.GetNick(), "-x")
|
|
||||||
})
|
|
||||||
|
|
||||||
client2.AddCallback("001", func(e *irc.Event) {
|
|
||||||
client2.Whois(client1.GetNick())
|
|
||||||
})
|
|
||||||
|
|
||||||
client2.AddCallback("401", func(e *irc.Event) {
|
|
||||||
client2.Whois(client1.GetNick())
|
|
||||||
})
|
|
||||||
|
|
||||||
client2.AddCallback("311", func(e *irc.Event) {
|
|
||||||
actual <- e.Arguments[3]
|
|
||||||
})
|
|
||||||
|
|
||||||
defer client1.Quit()
|
|
||||||
defer client2.Quit()
|
|
||||||
go client1.Loop()
|
|
||||||
go client2.Loop()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case res := <-actual:
|
|
||||||
assert.Equal(expected, res)
|
|
||||||
case <-time.After(TIMEOUT):
|
|
||||||
assert.Fail("timeout")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUser_PRIVMSG(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
expected := "Hello World!"
|
|
||||||
actual := make(chan string)
|
|
||||||
|
|
||||||
client1 := newClient(false)
|
|
||||||
client2 := newClient(false)
|
|
||||||
|
|
||||||
client1.AddCallback("001", func(e *irc.Event) {
|
|
||||||
client1.Privmsg(client2.GetNick(), expected)
|
|
||||||
|
|
||||||
})
|
|
||||||
client1.AddCallback("PRIVMSG", func(e *irc.Event) {
|
|
||||||
actual <- e.Message()
|
|
||||||
})
|
|
||||||
|
|
||||||
client2.AddCallback("001", func(e *irc.Event) {
|
|
||||||
client2.Privmsg(client1.GetNick(), expected)
|
|
||||||
})
|
|
||||||
client2.AddCallback("PRIVMSG", func(e *irc.Event) {
|
|
||||||
actual <- e.Message()
|
|
||||||
})
|
|
||||||
|
|
||||||
defer client1.Quit()
|
|
||||||
defer client2.Quit()
|
|
||||||
go client1.Loop()
|
|
||||||
go client2.Loop()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case res := <-actual:
|
|
||||||
assert.Equal(expected, res)
|
|
||||||
case <-time.After(TIMEOUT):
|
|
||||||
assert.Fail("timeout")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestChannel_PRIVMSG(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
expected := "Hello World!"
|
|
||||||
actual := make(chan string)
|
|
||||||
|
|
||||||
client1 := newClient(false)
|
|
||||||
client2 := newClient(false)
|
|
||||||
|
|
||||||
client1.AddCallback("JOIN", func(e *irc.Event) {
|
|
||||||
client1.Privmsg(e.Arguments[0], expected)
|
|
||||||
})
|
|
||||||
client2.AddCallback("JOIN", func(e *irc.Event) {
|
|
||||||
client2.Privmsg(e.Arguments[0], expected)
|
|
||||||
})
|
|
||||||
|
|
||||||
client1.AddCallback("PRIVMSG", func(e *irc.Event) {
|
|
||||||
actual <- e.Message()
|
|
||||||
})
|
|
||||||
client2.AddCallback("PRIVMSG", func(e *irc.Event) {
|
|
||||||
actual <- e.Message()
|
|
||||||
})
|
|
||||||
|
|
||||||
defer client1.Quit()
|
|
||||||
defer client2.Quit()
|
|
||||||
go client1.Loop()
|
|
||||||
go client2.Loop()
|
|
||||||
|
|
||||||
client1.Join("#channelprivmsg")
|
|
||||||
client2.Join("#channelprivmsg")
|
|
||||||
|
|
||||||
select {
|
|
||||||
case res := <-actual:
|
|
||||||
assert.Equal(expected, res)
|
|
||||||
case <-time.After(TIMEOUT):
|
|
||||||
assert.Fail("timeout")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestChannel_NoExternal(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
expected := true
|
|
||||||
actual := make(chan bool)
|
|
||||||
|
|
||||||
client1 := newClient(true)
|
|
||||||
client2 := newClient(true)
|
|
||||||
|
|
||||||
client1.AddCallback("JOIN", func(e *irc.Event) {
|
|
||||||
channel := e.Arguments[0]
|
|
||||||
if channel == "#noexternal" {
|
|
||||||
if e.Nick == client1.GetNick() {
|
|
||||||
client2.Privmsg("#noexternal", "FooBar!")
|
|
||||||
} else {
|
|
||||||
assert.Fail(fmt.Sprintf("unexpected user %s joined %s", e.Nick, channel))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
assert.Fail(fmt.Sprintf("unexpected channel %s", channel))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
client2.AddCallback("PRIVMSG", func(e *irc.Event) {
|
|
||||||
if e.Arguments[0] == "#noexternal" {
|
|
||||||
actual <- false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
client2.AddCallback("404", func(e *irc.Event) {
|
|
||||||
actual <- true
|
|
||||||
})
|
|
||||||
|
|
||||||
defer client1.Quit()
|
|
||||||
defer client2.Quit()
|
|
||||||
go client1.Loop()
|
|
||||||
go client2.Loop()
|
|
||||||
|
|
||||||
client1.Join("#noexternal")
|
|
||||||
|
|
||||||
select {
|
|
||||||
case res := <-actual:
|
|
||||||
assert.Equal(expected, res)
|
|
||||||
case <-time.After(TIMEOUT):
|
|
||||||
assert.Fail("timeout")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestChannel_SetTopic_InvalidChannel(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
expected := true
|
|
||||||
actual := make(chan bool)
|
|
||||||
|
|
||||||
client1 := newClient(false)
|
|
||||||
|
|
||||||
client1.AddCallback("403", func(e *irc.Event) {
|
|
||||||
actual <- true
|
|
||||||
})
|
|
||||||
|
|
||||||
defer client1.Quit()
|
|
||||||
go client1.Loop()
|
|
||||||
|
|
||||||
client1.SendRaw("TOPIC #invalidchannel :FooBar")
|
|
||||||
|
|
||||||
select {
|
|
||||||
case res := <-actual:
|
|
||||||
assert.Equal(expected, res)
|
|
||||||
case <-time.After(TIMEOUT):
|
|
||||||
assert.Fail("timeout")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestChannel_SetTopic_NotOnChannel(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
expected := true
|
|
||||||
actual := make(chan bool)
|
|
||||||
|
|
||||||
client1 := newClient(false)
|
|
||||||
client2 := newClient(false)
|
|
||||||
|
|
||||||
client1.AddCallback("442", func(e *irc.Event) {
|
|
||||||
actual <- true
|
|
||||||
})
|
|
||||||
client2.AddCallback("JOIN", func(e *irc.Event) {
|
|
||||||
client1.SendRaw("TOPIC #notonchannel :FooBar")
|
|
||||||
})
|
|
||||||
|
|
||||||
defer client1.Quit()
|
|
||||||
go client1.Loop()
|
|
||||||
|
|
||||||
client2.Join("#notonchannel")
|
|
||||||
|
|
||||||
select {
|
|
||||||
case res := <-actual:
|
|
||||||
assert.Equal(expected, res)
|
|
||||||
case <-time.After(TIMEOUT):
|
|
||||||
assert.Fail("timeout")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestChannel_BadChannelKey(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
expected := true
|
|
||||||
actual := make(chan bool)
|
|
||||||
|
|
||||||
client1 := newClient(false)
|
|
||||||
client2 := newClient(false)
|
|
||||||
|
|
||||||
client1.AddCallback("324", func(e *irc.Event) {
|
|
||||||
if strings.Contains(e.Arguments[2], "k") {
|
|
||||||
client2.Join(e.Arguments[1])
|
|
||||||
} else {
|
|
||||||
client1.Mode("#badchannelkey")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
client2.AddCallback("JOIN", func(e *irc.Event) {
|
|
||||||
if e.Nick == client2.GetNick() && e.Arguments[0] == "#badchannelkey" {
|
|
||||||
actual <- false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
client2.AddCallback("475", func(e *irc.Event) {
|
|
||||||
actual <- true
|
|
||||||
})
|
|
||||||
|
|
||||||
defer client1.Quit()
|
|
||||||
defer client2.Quit()
|
|
||||||
go client1.Loop()
|
|
||||||
go client2.Loop()
|
|
||||||
|
|
||||||
client1.Join("#badchannelkey")
|
|
||||||
client1.Mode("#badchannelkey", "+k", "opensesame")
|
|
||||||
client1.Mode("#badchannelkey")
|
|
||||||
|
|
||||||
select {
|
|
||||||
case res := <-actual:
|
|
||||||
assert.Equal(expected, res)
|
|
||||||
case <-time.After(TIMEOUT):
|
|
||||||
assert.Fail("timeout")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestChannel_GoodChannelKey(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
expected := true
|
|
||||||
actual := make(chan bool)
|
|
||||||
|
|
||||||
client1 := newClient(true)
|
|
||||||
client2 := newClient(true)
|
|
||||||
|
|
||||||
client1.AddCallback("324", func(e *irc.Event) {
|
|
||||||
if strings.Contains(e.Arguments[2], "k") {
|
|
||||||
client2.SendRawf("JOIN %s :opensesame", e.Arguments[1])
|
|
||||||
} else {
|
|
||||||
client1.Mode("#goodchannelkey")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
client2.AddCallback("JOIN", func(e *irc.Event) {
|
|
||||||
if e.Nick == client2.GetNick() && e.Arguments[0] == "#goodchannelkey" {
|
|
||||||
actual <- true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
client2.AddCallback("475", func(e *irc.Event) {
|
|
||||||
actual <- false
|
|
||||||
})
|
|
||||||
|
|
||||||
defer client1.Quit()
|
|
||||||
defer client2.Quit()
|
|
||||||
go client1.Loop()
|
|
||||||
go client2.Loop()
|
|
||||||
|
|
||||||
client1.Join("#goodchannelkey")
|
|
||||||
client1.Mode("#goodchannelkey", "+k", "opensesame")
|
|
||||||
client1.Mode("#goodchannelkey")
|
|
||||||
|
|
||||||
select {
|
|
||||||
case res := <-actual:
|
|
||||||
assert.Equal(expected, res)
|
|
||||||
case <-time.After(TIMEOUT):
|
|
||||||
assert.Fail("timeout")
|
|
||||||
}
|
|
||||||
}
|
|
@@ -21,7 +21,7 @@ fi
|
|||||||
echo -n "Building binaries ... "
|
echo -n "Building binaries ... "
|
||||||
|
|
||||||
GOOS=linux GOARCH=amd64 go build -o ./bin/eris-Linux-x86_64 .
|
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=linux GOARCH=arm64 go build -o ./bin/eris-Linux-x86_64 .
|
||||||
GOOS=darwin GOARCH=amd64 go build -o ./bin/eris-Darwin-x86_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 .
|
GOOS=windows GOARCH=amd64 go build -o ./bin/eris-Windows-x86_64.exe .
|
||||||
|
|
||||||
|
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: 661e31bf84...5cec1d0429
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: 99fa1f4be8...6f38060186
2
vendor/github.com/prometheus/common
generated
vendored
2
vendor/github.com/prometheus/common
generated
vendored
Submodule vendor/github.com/prometheus/common updated: 2e54d0b93c...e3fb1a1acd
1
vendor/github.com/renstrom/shortuuid
generated
vendored
1
vendor/github.com/renstrom/shortuuid
generated
vendored
Submodule vendor/github.com/renstrom/shortuuid deleted from d728e00b72
1
vendor/github.com/satori/go.uuid
generated
vendored
1
vendor/github.com/satori/go.uuid
generated
vendored
Submodule vendor/github.com/satori/go.uuid deleted from 5bf94b69c6
2
vendor/github.com/sirupsen/logrus
generated
vendored
2
vendor/github.com/sirupsen/logrus
generated
vendored
Submodule vendor/github.com/sirupsen/logrus updated: 95cd2b9c79...89742aefa4
1
vendor/github.com/stretchr/testify
generated
vendored
1
vendor/github.com/stretchr/testify
generated
vendored
Submodule vendor/github.com/stretchr/testify deleted from 2aa2c176b9
1
vendor/github.com/thoj/go-ircevent
generated
vendored
1
vendor/github.com/thoj/go-ircevent
generated
vendored
Submodule vendor/github.com/thoj/go-ircevent deleted from db5bd176f7
2
vendor/golang.org/x/crypto
generated
vendored
2
vendor/golang.org/x/crypto
generated
vendored
Submodule vendor/golang.org/x/crypto updated: 94eea52f7b...9f005a07e0
2
vendor/golang.org/x/sys
generated
vendored
2
vendor/golang.org/x/sys
generated
vendored
Submodule vendor/golang.org/x/sys updated: 8b4580aae2...0dd5e194bb
2
vendor/golang.org/x/text
generated
vendored
2
vendor/golang.org/x/text
generated
vendored
Submodule vendor/golang.org/x/text updated: 556d234e9c...88f656faf3
Reference in New Issue
Block a user