Compare commits
15 Commits
graceful_s
...
v1.6.6
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d3285748f9 | ||
![]() |
283ef104a4 | ||
![]() |
04d907d1e9 | ||
![]() |
d74a6780fe | ||
![]() |
d7e9ef230a | ||
![]() |
75f224a7c0 | ||
![]() |
caab002d51 | ||
![]() |
7ff892bba9 | ||
![]() |
233238b709 | ||
![]() |
59e0792db1 | ||
![]() |
962b6645c1 | ||
![]() |
cee8bf9957 | ||
![]() |
9d93bca179 | ||
![]() |
ccae795335 | ||
![]() |
862eb429d4 |
@@ -6,9 +6,15 @@ 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,5 +1,6 @@
|
|||||||
*~*
|
*~*
|
||||||
bin
|
bin
|
||||||
|
dist
|
||||||
*.db
|
*.db
|
||||||
*.pem
|
*.pem
|
||||||
|
|
||||||
|
18
.gitmodules
vendored
18
.gitmodules
vendored
@@ -43,9 +43,15 @@
|
|||||||
[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/sasha-s/go-deadlock"]
|
[submodule "vendor/github.com/thoj/go-ircevent"]
|
||||||
path = vendor/github.com/sasha-s/go-deadlock
|
path = vendor/github.com/thoj/go-ircevent
|
||||||
url = https://github.com/sasha-s/go-deadlock
|
url = https://github.com/thoj/go-ircevent
|
||||||
[submodule "vendor/github.com/petermattis/goid"]
|
[submodule "vendor/github.com/stretchr/testify"]
|
||||||
path = vendor/github.com/petermattis/goid
|
path = vendor/github.com/stretchr/testify
|
||||||
url = https://github.com/petermattis/goid
|
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
|
||||||
|
31
.goreleaser.yml
Normal file
31
.goreleaser.yml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
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"
|
@@ -2,7 +2,6 @@
|
|||||||
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
|
||||||
@@ -12,7 +11,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=$BUILD build
|
RUN make TAG=$TAG build
|
||||||
|
|
||||||
# Runtime
|
# Runtime
|
||||||
FROM alpine
|
FROM alpine
|
||||||
|
7
Makefile
7
Makefile
@@ -6,7 +6,6 @@ APP=eris
|
|||||||
PACKAGE=irc
|
PACKAGE=irc
|
||||||
REPO?=prologic/$(APP)
|
REPO?=prologic/$(APP)
|
||||||
TAG?=latest
|
TAG?=latest
|
||||||
BUILD?=dev
|
|
||||||
|
|
||||||
all: dev
|
all: dev
|
||||||
|
|
||||||
@@ -17,13 +16,13 @@ deps:
|
|||||||
@go get ./...
|
@go get ./...
|
||||||
|
|
||||||
build: clean deps
|
build: clean deps
|
||||||
@echo " -> Building $(TAG)$(BUILD)"
|
@echo " -> Building $(REPO) v$(TAG)-@$(COMMIT)"
|
||||||
@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) -X github.com/$(REPO)/${PACKAGE}.Build=$(BUILD)" .
|
-ldflags "-w -X github.com/$(REPO)/${PACKAGE}.GitCommit=$(COMMIT)
|
||||||
@echo "Built $$(./$(APP) -v)"
|
@echo "Built $$(./$(APP) -v)"
|
||||||
|
|
||||||
image:
|
image:
|
||||||
@docker build --build-arg TAG=$(TAG) --build-arg BUILD=$(BUILD) -t $(REPO):$(TAG) .
|
@docker build --build-arg TAG=$(TAG) -t $(REPO):$(TAG) .
|
||||||
@echo "Image created: $(REPO):$(TAG)"
|
@echo "Image created: $(REPO):$(TAG)"
|
||||||
|
|
||||||
test:
|
test:
|
||||||
|
13
README.md
13
README.md
@@ -1,4 +1,4 @@
|
|||||||
# eris - IRC Server / Daemon written in Go
|
:set spell eris - IRC Server / Daemon written in Go
|
||||||
|
|
||||||
[](https://travis-ci.org/prologic/eris)
|
[](https://travis-ci.org/prologic/eris)
|
||||||
[](https://goreportcard.com/report/github.com/prologic/eris)
|
[](https://goreportcard.com/report/github.com/prologic/eris)
|
||||||
@@ -25,7 +25,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 factiliate discord
|
So `eris` is an IRC daemon written from scratch in Go to facilitate 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 +35,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*)P
|
Or (**not recommended**):
|
||||||
|
|
||||||
* /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,6 +54,7 @@ Or (*not recommended*)P
|
|||||||
* 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
|
## Quick Start
|
||||||
|
|
||||||
@@ -144,6 +145,10 @@ There are a number of supported accompanying services that are being developed a
|
|||||||
* [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.
|
||||||
|
@@ -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, SecureChan:
|
case InviteOnly, Moderated, NoOutside, OpOnlyTopic, Private, Secret, SecureChan:
|
||||||
return channel.applyModeFlag(client, change.mode, change.op)
|
return channel.applyModeFlag(client, change.mode, change.op)
|
||||||
|
|
||||||
case Key:
|
case Key:
|
||||||
|
@@ -72,8 +72,17 @@ func NewClient(server *Server, conn net.Conn) *Client {
|
|||||||
//
|
//
|
||||||
|
|
||||||
func (client *Client) writeloop() {
|
func (client *Client) writeloop() {
|
||||||
for reply := range client.replies {
|
for {
|
||||||
client.socket.Write(reply)
|
select {
|
||||||
|
case reply, ok := <-client.replies:
|
||||||
|
if !ok || reply == "" || client.socket == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
client.socket.Write(reply)
|
||||||
|
}
|
||||||
|
if client.replies == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,13 +128,6 @@ 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 {
|
||||||
@@ -144,6 +146,13 @@ 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()
|
||||||
@@ -209,6 +218,7 @@ func (client *Client) Register() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
client.registered = true
|
client.registered = true
|
||||||
|
client.flags[HostMask] = true
|
||||||
client.Touch()
|
client.Touch()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,6 +232,12 @@ 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)
|
||||||
|
|
||||||
|
@@ -4,9 +4,7 @@ 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"
|
||||||
)
|
)
|
||||||
|
@@ -4,9 +4,7 @@ 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,6 +20,7 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,6 +28,7 @@ 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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,6 +103,24 @@ 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{
|
||||||
@@ -148,6 +168,11 @@ 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,11 +58,12 @@ 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,
|
Invisible, Operator, HostMask,
|
||||||
}
|
}
|
||||||
DefaultChannelModes = ChannelModes{
|
DefaultChannelModes = ChannelModes{
|
||||||
NoOutside, OpOnlyTopic,
|
NoOutside, OpOnlyTopic,
|
||||||
@@ -70,7 +71,6 @@ 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,8 +82,6 @@ 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
|
||||||
@@ -93,7 +91,7 @@ const (
|
|||||||
var (
|
var (
|
||||||
SupportedChannelModes = ChannelModes{
|
SupportedChannelModes = ChannelModes{
|
||||||
BanMask, ExceptMask, InviteMask, InviteOnly, Key, NoOutside,
|
BanMask, ExceptMask, InviteMask, InviteOnly, Key, NoOutside,
|
||||||
OpOnlyTopic, Private, UserLimit, SecureChan,
|
OpOnlyTopic, Private, UserLimit, Secret, SecureChan,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -119,7 +117,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, WallOps, SecureOnly:
|
case Invisible, HostMask, WallOps, SecureOnly:
|
||||||
switch change.op {
|
switch change.op {
|
||||||
case Add:
|
case Add:
|
||||||
if target.flags[change.mode] {
|
if target.flags[change.mode] {
|
||||||
|
@@ -4,9 +4,8 @@ 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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
22
irc/privacy.go
Normal file
22
irc/privacy.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
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
|
||||||
|
}
|
21
irc/reply.go
21
irc/reply.go
@@ -266,7 +266,7 @@ func (target *Client) RplWhois(client *Client) {
|
|||||||
func (target *Client) RplWhoisUser(client *Client) {
|
func (target *Client) RplWhoisUser(client *Client) {
|
||||||
var clientHost Name
|
var clientHost Name
|
||||||
|
|
||||||
if target.flags[Operator] {
|
if target.flags[Operator] || !client.flags[HostMask] {
|
||||||
clientHost = client.hostname
|
clientHost = client.hostname
|
||||||
} else {
|
} else {
|
||||||
clientHost = client.hostmask
|
clientHost = client.hostmask
|
||||||
@@ -342,7 +342,7 @@ func (target *Client) RplChannelModeIs(channel *Channel) {
|
|||||||
func (target *Client) RplWhoReply(channel *Channel, client *Client) {
|
func (target *Client) RplWhoReply(channel *Channel, client *Client) {
|
||||||
var clientHost Name
|
var clientHost Name
|
||||||
|
|
||||||
if target.flags[Operator] {
|
if target.flags[Operator] || !client.flags[HostMask] {
|
||||||
clientHost = client.hostname
|
clientHost = client.hostname
|
||||||
} else {
|
} else {
|
||||||
clientHost = client.hostmask
|
clientHost = client.hostmask
|
||||||
@@ -489,8 +489,13 @@ func (target *Client) RplMOTDEnd() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (target *Client) RplList(channel *Channel) {
|
func (target *Client) RplList(channel *Channel) {
|
||||||
target.NumericReply(RPL_LIST,
|
target.NumericReply(
|
||||||
"%s %d :%s", channel, channel.members.Count(), channel.topic)
|
RPL_LIST,
|
||||||
|
"%s %d :%s",
|
||||||
|
channel,
|
||||||
|
channel.members.Count(),
|
||||||
|
channel.topic,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (target *Client) RplListEnd(server *Server) {
|
func (target *Client) RplListEnd(server *Server) {
|
||||||
@@ -504,8 +509,12 @@ func (target *Client) RplNamReply(channel *Channel) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (target *Client) RplWhoisChannels(client *Client) {
|
func (target *Client) RplWhoisChannels(client *Client) {
|
||||||
target.MultilineReply(client.WhoisChannelsNames(), RPL_WHOISCHANNELS,
|
target.MultilineReply(
|
||||||
"%s :%s", client.Nick())
|
client.WhoisChannelsNames(target),
|
||||||
|
RPL_WHOISCHANNELS,
|
||||||
|
"%s :%s",
|
||||||
|
client.Nick(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (target *Client) RplVersion() {
|
func (target *Client) RplVersion() {
|
||||||
|
@@ -2,9 +2,7 @@ package irc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
//"sync"
|
"sync"
|
||||||
|
|
||||||
sync "github.com/sasha-s/go-deadlock"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type SaslState struct {
|
type SaslState struct {
|
||||||
|
@@ -44,13 +44,16 @@ 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{syscall.SIGINT, syscall.SIGHUP,
|
SERVER_SIGNALS = []os.Signal{
|
||||||
syscall.SIGTERM, syscall.SIGQUIT}
|
syscall.SIGINT, syscall.SIGHUP,
|
||||||
|
syscall.SIGTERM, syscall.SIGQUIT,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewServer(config *Config) *Server {
|
func NewServer(config *Config) *Server {
|
||||||
@@ -70,13 +73,14 @@ 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 configurabel?
|
// TODO: Make this configureable?
|
||||||
server.ids["global"] = NewIdentity(config.Server.Name, "global")
|
server.ids["global"] = NewIdentity(config.Server.Name, "global")
|
||||||
|
|
||||||
if config.Server.Password != "" {
|
if config.Server.Password != "" {
|
||||||
@@ -123,15 +127,22 @@ func NewServer(config *Config) *Server {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// server clients gauge
|
// server registered (clients) gauge
|
||||||
server.metrics.NewGaugeFunc(
|
server.metrics.NewGaugeFunc(
|
||||||
"server", "clients",
|
"server", "registered",
|
||||||
"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",
|
||||||
@@ -191,13 +202,22 @@ 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() {
|
||||||
done := false
|
for {
|
||||||
for !done {
|
|
||||||
select {
|
select {
|
||||||
|
case <-server.done:
|
||||||
|
return
|
||||||
case <-server.signals:
|
case <-server.signals:
|
||||||
server.Shutdown()
|
server.Shutdown()
|
||||||
done = true
|
// Give at least 1s for clients to see the shutdown
|
||||||
|
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)
|
||||||
@@ -217,6 +237,12 @@ 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
|
||||||
}
|
}
|
||||||
@@ -603,10 +629,14 @@ func (msg *PrivMsgCommand) HandleServer(server *Server) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) WhoisChannelsNames() []string {
|
func (client *Client) WhoisChannelsNames(target *Client) []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()
|
||||||
@@ -813,7 +843,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 !client.flags[Operator] && channel.flags.Has(Private) {
|
if !CanSeeChannel(client, channel) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
client.RplList(channel)
|
client.RplList(channel)
|
||||||
@@ -822,7 +852,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 || (!client.flags[Operator] && channel.flags.Has(Private)) {
|
if channel == nil || !CanSeeChannel(client, channel) {
|
||||||
client.ErrNoSuchChannel(chname)
|
client.ErrNoSuchChannel(chname)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@@ -3,9 +3,7 @@ package irc
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
//"sync"
|
"sync"
|
||||||
|
|
||||||
sync "github.com/sasha-s/go-deadlock"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@@ -5,20 +5,17 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
//PackageName package name
|
// Package package name
|
||||||
Package = "eris"
|
Package = "eris"
|
||||||
|
|
||||||
// Version release version
|
// Version release version
|
||||||
Version = "1.6.2"
|
Version = "1.6.4"
|
||||||
|
|
||||||
// Build will be overwritten automatically by the build system
|
// Commit will be overwritten automatically by the build system
|
||||||
Build = "dev"
|
Commit = "HEAD"
|
||||||
|
|
||||||
// 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@%s", Package, Version, Build, GitCommit)
|
return fmt.Sprintf("%s-%s@%s", Package, Version, Commit)
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,7 @@
|
|||||||
package irc
|
package irc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
//"sync"
|
"sync"
|
||||||
|
|
||||||
sync "github.com/sasha-s/go-deadlock"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type WhoWasList struct {
|
type WhoWasList struct {
|
||||||
|
575
main_test.go
Normal file
575
main_test.go
Normal file
@@ -0,0 +1,575 @@
|
|||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
1
vendor/github.com/petermattis/goid
generated
vendored
1
vendor/github.com/petermattis/goid
generated
vendored
Submodule vendor/github.com/petermattis/goid deleted from 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: 1cdba8fdde...661e31bf84
1
vendor/github.com/renstrom/shortuuid
generated
vendored
Submodule
1
vendor/github.com/renstrom/shortuuid
generated
vendored
Submodule
Submodule vendor/github.com/renstrom/shortuuid added at d728e00b72
1
vendor/github.com/sasha-s/go-deadlock
generated
vendored
1
vendor/github.com/sasha-s/go-deadlock
generated
vendored
Submodule vendor/github.com/sasha-s/go-deadlock deleted from 565eb44395
1
vendor/github.com/satori/go.uuid
generated
vendored
Submodule
1
vendor/github.com/satori/go.uuid
generated
vendored
Submodule
Submodule vendor/github.com/satori/go.uuid added at 5bf94b69c6
1
vendor/github.com/stretchr/testify
generated
vendored
Submodule
1
vendor/github.com/stretchr/testify
generated
vendored
Submodule
Submodule vendor/github.com/stretchr/testify added at 2aa2c176b9
1
vendor/github.com/thoj/go-ircevent
generated
vendored
Submodule
1
vendor/github.com/thoj/go-ircevent
generated
vendored
Submodule
Submodule vendor/github.com/thoj/go-ircevent added at db5bd176f7
2
vendor/golang.org/x/crypto
generated
vendored
2
vendor/golang.org/x/crypto
generated
vendored
Submodule vendor/golang.org/x/crypto updated: b080dc9a8c...94eea52f7b
2
vendor/golang.org/x/sys
generated
vendored
2
vendor/golang.org/x/sys
generated
vendored
Submodule vendor/golang.org/x/sys updated: 4ff8c001ce...8b4580aae2
2
vendor/golang.org/x/text
generated
vendored
2
vendor/golang.org/x/text
generated
vendored
Submodule vendor/golang.org/x/text updated: 88f656faf3...556d234e9c
Reference in New Issue
Block a user