57 Commits

Author SHA1 Message Date
idk
03006d070a Instead of writing to the ircd.yml file, write the reachable addresses out to a plain-text file, using the same value *.i2pkeys and *.torkeys as the place where they are stored. so it's like: addr.i2p.public.txt for reachable address, add.i2p.private for private keys 2020-11-11 00:50:38 -05:00
idk
d3c805f6f4 just use ioutil 2020-11-09 22:23:45 -05:00
idk
ebfb181a5b Remove the copypasta from the bine example that I didn't before, and which isn't accurate 2020-11-09 19:44:50 -05:00
idk
bb15e1db9e add Tor support as well 2020-11-09 18:36:24 -05:00
idk
984f067966 Fix it so that they key is generated right 2020-11-09 10:44:38 -05:00
idk
1f953d37fd key considerations 2020-11-09 02:22:18 -05:00
idk
942364517d key considerations 2020-11-09 02:12:23 -05:00
idk
31750200ea key considerations 2020-11-09 01:57:53 -05:00
idk
4b67a1e91d Add I2P Configuration support 2020-11-09 01:51:32 -05:00
dependabot-preview[bot]
eea24e3047 Bump golang.org/x/text from 0.3.3 to 0.3.4 (#79)
Bumps [golang.org/x/text](https://github.com/golang/text) from 0.3.3 to 0.3.4.
- [Release notes](https://github.com/golang/text/releases)
- [Commits](https://github.com/golang/text/compare/v0.3.3...v0.3.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-10-28 08:09:40 +10:00
dependabot-preview[bot]
e91312a468 Bump github.com/sirupsen/logrus from 1.6.0 to 1.7.0 (#78)
Bumps [github.com/sirupsen/logrus](https://github.com/sirupsen/logrus) from 1.6.0 to 1.7.0.
- [Release notes](https://github.com/sirupsen/logrus/releases)
- [Changelog](https://github.com/sirupsen/logrus/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sirupsen/logrus/compare/v1.6.0...v1.7.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-10-15 16:51:36 +10:00
dependabot-preview[bot]
065a93d56f Bump github.com/imdario/mergo from 0.3.9 to 0.3.11 (#77)
Bumps [github.com/imdario/mergo](https://github.com/imdario/mergo) from 0.3.9 to 0.3.11.
- [Release notes](https://github.com/imdario/mergo/releases)
- [Commits](https://github.com/imdario/mergo/compare/v0.3.9...v0.3.11)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-08-13 22:49:43 +10:00
dependabot-preview[bot]
fbbf36d1a1 Bump golang.org/x/text from 0.3.2 to 0.3.3 (#74)
Bumps [golang.org/x/text](https://github.com/golang/text) from 0.3.2 to 0.3.3.
- [Release notes](https://github.com/golang/text/releases)
- [Commits](https://github.com/golang/text/compare/v0.3.2...v0.3.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-17 14:40:05 +10:00
James Mills
3675251706 Refactored user flag/modes and fixed several data race bugs 2020-06-08 14:40:22 +10:00
James Mills
6f61b673a1 Refactor client 2020-06-08 14:20:21 +10:00
James Mills
4566b2021f Tidied up unused dependencies 2020-06-08 13:56:51 +10:00
James Mills
e7c5b96a6a Updated depednenices 2020-06-08 13:56:36 +10:00
James Mills
b18403ea71 Migrate to Github Actions Workflows 2020-06-08 13:47:06 +10:00
dependabot-preview[bot]
c9cbab6769 Bump github.com/stretchr/testify from 1.5.1 to 1.6.0 (#72)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.5.1 to 1.6.0.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.5.1...v1.6.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-01 11:21:26 +10:00
dependabot-preview[bot]
176aba3c99 Bump gopkg.in/yaml.v2 from 2.2.8 to 2.3.0 (#68)
Bumps [gopkg.in/yaml.v2](https://github.com/go-yaml/yaml) from 2.2.8 to 2.3.0.
- [Release notes](https://github.com/go-yaml/yaml/releases)
- [Commits](https://github.com/go-yaml/yaml/compare/v2.2.8...v2.3.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-05-13 11:45:36 +10:00
dependabot-preview[bot]
d814c48dce Bump github.com/stretchr/testify from 1.3.0 to 1.5.1 (#67)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.3.0 to 1.5.1.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.3.0...v1.5.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-05-06 11:56:10 +10:00
dependabot-preview[bot]
3af82e3e8e Bump golang.org/x/text from 0.3.0 to 0.3.2 (#56)
Bumps [golang.org/x/text](https://github.com/golang/text) from 0.3.0 to 0.3.2.
- [Release notes](https://github.com/golang/text/releases)
- [Commits](https://github.com/golang/text/compare/v0.3.0...v0.3.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
Co-authored-by: James Mills <prologic@shortcircuit.net.au>
2020-05-05 15:10:46 +10:00
dependabot-preview[bot]
8a8d7b1e97 Bump github.com/prometheus/client_golang from 0.9.2 to 0.9.4 (#58)
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 0.9.2 to 0.9.4.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v0.9.2...v0.9.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
Co-authored-by: James Mills <prologic@shortcircuit.net.au>
2020-05-05 15:09:36 +10:00
dependabot-preview[bot]
2ac33b7d2c Bump gopkg.in/yaml.v2 from 2.2.2 to 2.2.8 (#60)
Bumps [gopkg.in/yaml.v2](https://github.com/go-yaml/yaml) from 2.2.2 to 2.2.8.
- [Release notes](https://github.com/go-yaml/yaml/releases)
- [Commits](https://github.com/go-yaml/yaml/compare/v2.2.2...v2.2.8)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
Co-authored-by: James Mills <prologic@shortcircuit.net.au>
2020-05-05 15:08:30 +10:00
dependabot-preview[bot]
a54031de9e Bump github.com/stretchr/testify from 1.2.2 to 1.5.1 (#62)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.2.2 to 1.5.1.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.2.2...v1.5.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
Co-authored-by: James Mills <prologic@shortcircuit.net.au>
2020-05-05 15:08:14 +10:00
dependabot-preview[bot]
a49dea57d8 Bump github.com/imdario/mergo from 0.3.6 to 0.3.9 (#65)
Bumps [github.com/imdario/mergo](https://github.com/imdario/mergo) from 0.3.6 to 0.3.9.
- [Release notes](https://github.com/imdario/mergo/releases)
- [Commits](https://github.com/imdario/mergo/compare/v0.3.6...v0.3.9)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
Co-authored-by: James Mills <prologic@shortcircuit.net.au>
2020-05-05 15:07:20 +10:00
dependabot-preview[bot]
114c6aa80c Bump github.com/sirupsen/logrus from 1.2.0 to 1.6.0 (#66)
Bumps [github.com/sirupsen/logrus](https://github.com/sirupsen/logrus) from 1.2.0 to 1.6.0.
- [Release notes](https://github.com/sirupsen/logrus/releases)
- [Changelog](https://github.com/sirupsen/logrus/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sirupsen/logrus/compare/v1.2.0...v1.6.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-05-05 15:06:41 +10:00
James Mills
414c2fcf89 Added DependaBot config 2019-12-26 14:03:06 +10:00
James Mills
9d860692fa Update Drone CI config 2018-12-31 20:11:21 +10:00
James Mills
98cb66559a Migrate to Drone CI, Codecov and Go11Modules 2018-12-31 20:08:19 +10:00
James Mills
e14795818f Add related projects 2018-11-27 05:49:17 +10:00
James Mills
c394ea6735 Set theme jekyll-theme-architect 2018-11-27 05:41:33 +10:00
James Mills
c94884fb9f Add webhook for local CI 2018-05-19 14:00:00 -07:00
James Mills
784039998f Fixed local CI notify email settings 2018-05-19 13:35:25 -07:00
James Mills
6981b10763 Create PULL_REQUEST_TEMPLATE.md 2018-05-19 13:21:30 -07:00
James Mills
d170e01d38 Update issue templates 2018-05-19 13:20:24 -07:00
James Mills
5787059d11 Create CODE_OF_CONDUCT.md (#50) 2018-05-19 13:19:01 -07:00
James Mills
2e4ff30276 Add more recommended clients 2018-05-19 13:16:29 -07:00
James Mills
f18765a41a Add more recommended clients 2018-05-19 13:14:03 -07:00
James Mills
d23f7cf93d Reconfigure email notify rule for local CI 2018-05-19 12:51:15 -07:00
James Mills
6d64a46466 Updated 3rd-party vendored packages 2018-05-19 12:37:47 -07:00
James Mills
14ed3a6633 Fixed README 2018-05-19 10:25:44 -07:00
James Mills
cb46494733 Fixed build 2018-05-19 00:28:55 -07:00
James Mills
e3fea6c97b Fixed concurrent access to and data race on client.hasQuit 2018-05-19 00:01:14 -07:00
James Mills
9b70d25143 Add tests for metrics 2018-05-18 23:42:19 -07:00
James Mills
7a20037194 Disabled and commented out a recey test that still needs fixing 2018-05-18 23:31:03 -07:00
James Mills
5fa7214853 Fixed concurrent access to internl metrics maps 2018-05-18 23:30:43 -07:00
James Mills
e905b44fb4 Don't nilify the channel on closure (select will block indefinately) 2018-05-18 22:59:07 -07:00
James Mills
84a36a0095 Remove SIGQUIT and SIGHUP signals 2018-05-18 22:46:11 -07:00
James Mills
aa4907d8ae Add bench and profile targets 2018-05-18 22:34:09 -07:00
James Mills
facfcba232 Add grafana dashboard 2018-05-02 02:40:52 -07:00
James Mills
d3285748f9 Remove silly build token in version and add goreleaser config 2017-12-28 00:53:46 -08:00
Kevin Zita
283ef104a4 Aded support for HostMask user mode (+x) (#47) 2017-12-28 00:36:13 -08:00
James Mills
04d907d1e9 Fixed typo in README 2017-12-14 00:13:29 -08:00
James Mills
d74a6780fe Fixed writeloop goroutine leadk (#45) 2017-12-14 00:02:07 -08:00
James Mills
d7e9ef230a Refactored e2e integration tests 2017-12-06 20:59:04 -08:00
James Mills
75f224a7c0 Improves coverage of integreation tests to 40% (#42)
* Added TestChannel_PRIVMSG test

* Refactored integration testing framework for better timtouts and added TestChannel_InviteOnly test

* Try to fix TestChannel_PRIVMSG test

* Fuck it

* Added TestChannel_NoExternal test

* Added TestChannel_BadChannelKey and TestChannel_GoodChannelKey tests

* Bah humbut

* Update vendored 3rd-party deps

* Removed use of deadlock sync package

* Fix all tests :D yay

* Added some topic tests

* Add TestSASL
2017-12-06 02:19:27 -08:00
57 changed files with 2769 additions and 405 deletions

8
.dependabot/config.yml Normal file
View File

@@ -0,0 +1,8 @@
version: 1
update_configs:
- package_manager: "go:modules"
directory: "/"
update_schedule: "daily"
- package_manager: "docker"
directory: "/"
update_schedule: "weekly"

View File

@@ -1,36 +0,0 @@
workspace:
base: /go
path: src/github.com/prologic/eris
pipeline:
build:
image: golang
commands:
- go get -d ./...
- go build .
test:
image: golang
commands:
- go get -d ./...
- go test ./...
docker:
image: plugins/docker
repo: r.mills.io/prologic/eris
registry: r.mills.io
secrets: [ docker_username, docker_password ]
notify:
image: drillster/drone-email
host: mail.mills.io
from: drone@mills.io
skip_verify: true
when:
status: [ success, changed, failure ]
secrets:
registry_username:
external: true
registry_password:
external: true

35
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,35 @@
---
name: Bug report
about: Create a report to help us improve
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

View File

@@ -0,0 +1,17 @@
---
name: Feature request
about: Suggest an idea for this project
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

35
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,35 @@
---
name: Build
on:
push:
branches:
- master
pull_request:
jobs:
build:
name: Build
strategy:
matrix:
go-version:
- "1.12.x"
- "1.13.x"
- "1.14.x"
os:
- "ubuntu-latest"
- "macos-latest"
- "windows-latest"
runs-on: ${{ matrix.os }}
steps:
- name: Setup Go ${{ matrix.go-version }}
uses: actions/setup-go@v1
with:
go-version: ${{ matrix.go-version }}
id: go
- name: Checkout
uses: actions/checkout@v2
- name: Build
run: |
go build -v .
- name: Test
run: |
go test -v -race .

9
.gitignore vendored
View File

@@ -1,6 +1,9 @@
*~*
bin
*~
*.db
*.bak
*.pem
eris
/bin
/dist
/eris
/coverage.txt

57
.gitmodules vendored
View File

@@ -1,57 +0,0 @@
[submodule "vendor/github.com/sirupsen/logrus"]
path = vendor/github.com/sirupsen/logrus
url = https://github.com/sirupsen/logrus
[submodule "vendor/golang.org/x/crypto"]
path = vendor/golang.org/x/crypto
url = https://go.googlesource.com/crypto
[submodule "vendor/golang.org/x/sys"]
path = vendor/golang.org/x/sys
url = https://go.googlesource.com/sys
[submodule "vendor/github.com/DanielOaks/girc-go"]
path = vendor/github.com/DanielOaks/girc-go
url = https://github.com/DanielOaks/girc-go
[submodule "vendor/github.com/goshuirc/e-nfa"]
path = vendor/github.com/goshuirc/e-nfa
url = https://github.com/goshuirc/e-nfa
[submodule "vendor/github.com/imdario/mergo"]
path = vendor/github.com/imdario/mergo
url = https://github.com/imdario/mergo
[submodule "vendor/golang.org/x/text"]
path = vendor/golang.org/x/text
url = https://go.googlesource.com/text
[submodule "vendor/gopkg.in/yaml.v2"]
path = vendor/gopkg.in/yaml.v2
url = https://gopkg.in/yaml.v2
[submodule "vendor/github.com/prometheus/client_golang"]
path = vendor/github.com/prometheus/client_golang
url = https://github.com/prometheus/client_golang
[submodule "vendor/github.com/beorn7/perks"]
path = vendor/github.com/beorn7/perks
url = https://github.com/beorn7/perks
[submodule "vendor/github.com/golang/protobuf"]
path = vendor/github.com/golang/protobuf
url = https://github.com/golang/protobuf
[submodule "vendor/github.com/prometheus/client_model"]
path = vendor/github.com/prometheus/client_model
url = https://github.com/prometheus/client_model
[submodule "vendor/github.com/prometheus/common"]
path = vendor/github.com/prometheus/common
url = https://github.com/prometheus/common
[submodule "vendor/github.com/matttproud/golang_protobuf_extensions"]
path = vendor/github.com/matttproud/golang_protobuf_extensions
url = https://github.com/matttproud/golang_protobuf_extensions
[submodule "vendor/github.com/prometheus/procfs"]
path = vendor/github.com/prometheus/procfs
url = https://github.com/prometheus/procfs
[submodule "vendor/github.com/sasha-s/go-deadlock"]
path = vendor/github.com/sasha-s/go-deadlock
url = https://github.com/sasha-s/go-deadlock
[submodule "vendor/github.com/petermattis/goid"]
path = vendor/github.com/petermattis/goid
url = https://github.com/petermattis/goid
[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

31
.goreleaser.yml Normal file
View 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"

View File

@@ -1,8 +0,0 @@
language: go
sudo: false
go:
- tip
before_install:
- go get github.com/mattn/goveralls
script:
- $HOME/gopath/bin/goveralls -service=travis-ci

29
.yamllint.yml Normal file
View File

@@ -0,0 +1,29 @@
---
yaml-files:
- '*.yaml'
- '*.yml'
- '.yamllint'
rules:
braces: enable
brackets: enable
colons: enable
commas: enable
comments: disable
comments-indentation: disable
document-end: disable
document-start:
level: warning
empty-lines: enable
empty-values: disable
hyphens: enable
indentation: enable
key-duplicates: enable
key-ordering: disable
line-length: disable
new-line-at-end-of-file: enable
new-lines: enable
octal-values: enable
quoted-strings: disable
trailing-spaces: enable
truthy: disable

46
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,46 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at prologic@shortcircuit.net.au. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View File

@@ -2,7 +2,6 @@
FROM golang:alpine AS build
ARG TAG
ARG BUILD
ENV APP eris
ENV REPO prologic/$APP
@@ -12,7 +11,7 @@ RUN apk add --update git make build-base && \
WORKDIR /go/src/github.com/$REPO
COPY . /go/src/github.com/$REPO
RUN make TAG=$TAG BUILD=$BUILD build
RUN make TAG=$TAG build
# Runtime
FROM alpine

View File

@@ -6,7 +6,6 @@ APP=eris
PACKAGE=irc
REPO?=prologic/$(APP)
TAG?=latest
BUILD?=dev
all: dev
@@ -17,17 +16,24 @@ deps:
@go get ./...
build: clean deps
@echo " -> Building $(TAG)$(BUILD)"
@echo "github.com/$(REPO)/${PACKAGE}.GitCommit=$(COMMIT)"
@echo " -> Building $(REPO) $(TAG)@$(COMMIT)"
@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)"
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)"
profile:
@go test -cpuprofile cpu.prof -memprofile mem.prof -v -bench ./...
bench:
@go test -v -bench ./...
test:
@go test -v -cover -race $(TEST_ARGS)
@go test -v -cover -coverprofile=coverage.txt -covermode=atomic -coverpkg=./... -race ./...
clean:
@rm -rf $(APP)
@git clean -f -d -X

3
PULL_REQUEST_TEMPLATE.md Normal file
View File

@@ -0,0 +1,3 @@
<one line description here>
Fixes #xx

View File

@@ -1,10 +1,10 @@
# eris - IRC Server / Daemon written in Go
eris - IRC Server / Daemon written in Go
[![Build Status](https://travis-ci.org/prologic/eris.svg)](https://travis-ci.org/prologic/eris)
[![Go Report Card](https://goreportcard.com/badge/github.com/prologic/eris)](https://goreportcard.com/report/github.com/prologic/eris)
[![Coverage](https://coveralls.io/repos/prologic/eris/badge.svg)](https://coveralls.io/r/prologic/eris)
[![GoDoc](https://godoc.org/github.com/prologic/eris?status.svg)](https://godoc.org/github.com/prologic/eris)
[![Wiki](https://img.shields.io/badge/docs-wiki-blue.svg)](https://github.com/prologic/eris/wiki)
[![Build Status](https://cloud.drone.io/api/badges/prologic/eris/status.svg)](https://cloud.drone.io/prologic/eris)
[![CodeCov](https://codecov.io/gh/prologic/eris/branch/master/graph/badge.svg)](https://codecov.io/gh/prologic/eris)
[![Go Report Card](https://goreportcard.com/badge/prologic/eris)](https://goreportcard.com/report/prologic/eris)
[![GoDoc](https://godoc.org/github.com/prologic/eris?status.svg)](https://godoc.org/github.com/prologic/eris)
[![Sourcegraph](https://sourcegraph.com/github.com/prologic/eris/-/badge.svg)](https://sourcegraph.com/github.com/prologic/eris?badge)
> 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)
@@ -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
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!
Pull requests and issues are welcome.
@@ -145,10 +145,36 @@ 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.
* [Cadmus](https://github.com/prologic/cadmus) -- An IRC Bot that logs channels and provides an interface for viewing and searching logs
## Recommended Mobile clients
## Recommended Clients
### CLI / Terminal
* [irccat](https://github.com/prologic/irccat)
* [irssi](https://irssi.org/)
### Cloud
* [IRCCloud](https://www.irccloud.com/)
### Desktop
* [HexChat (Linux)](https://hexchat.github.io/)
* [Textual (OSX)](https://www.codeux.com/textual/)
* [mIRC (Windows)](https://www.mirc.com/)
### Mobile
* [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*)
### Web
* [Dispatch](https://github.com/khlieng/dispatch) -- TLS, Multiple Servers and Users, Client Certificates
## Related Projects
* [cadmus](https://github.com/prologic/cadmus) -- an IRC Bot written in Go that logs IRC Channels and provides an interface to view and search those logs
* [soter](https://github.com/prologic/soter) -- an IRC Bot written in Go that protects IRC Channels by persisting channel modes and topics
## License
eris is licensed under the MIT License.

1
_config.yml Normal file
View File

@@ -0,0 +1 @@
theme: jekyll-theme-architect

21
go.mod Normal file
View File

@@ -0,0 +1,21 @@
module github.com/prologic/eris
go 1.14
require (
github.com/DanielOaks/girc-go v0.0.0-20180430075055-8d136c4f9287
github.com/cretz/bine v0.1.0
github.com/eyedeekay/sam3 v0.32.31
github.com/google/uuid v1.1.0 // indirect
github.com/goshuirc/e-nfa v0.0.0-20160917075329-7071788e3940 // indirect
github.com/imdario/mergo v0.3.11
github.com/mmcloughlin/professor v0.0.0-20170922221822-6b97112ab8b3
github.com/prometheus/client_golang v0.9.4
github.com/renstrom/shortuuid v3.0.0+incompatible
github.com/sirupsen/logrus v1.7.0
github.com/stretchr/testify v1.6.0
github.com/thoj/go-ircevent v0.0.0-20180816043103-14f3614f28c3
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9
golang.org/x/text v0.3.4
gopkg.in/yaml.v2 v2.3.0
)

106
go.sum Normal file
View File

@@ -0,0 +1,106 @@
github.com/DanielOaks/girc-go v0.0.0-20180430075055-8d136c4f9287 h1:xOE8jDDulcwdPG+coLps6seNn6yERt5xgKSATNqWUM0=
github.com/DanielOaks/girc-go v0.0.0-20180430075055-8d136c4f9287/go.mod h1:nn+Gr++RLey8iGwfvI84UO5oZal6Muz7qPxDII0BsQ8=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/cretz/bine v0.1.0 h1:1/fvhLE+fk0bPzjdO5Ci+0ComYxEMuB1JhM4X5skT3g=
github.com/cretz/bine v0.1.0/go.mod h1:6PF6fWAvYtwjRGkAuDEJeWNOv3a2hUouSP/yRYXmvHw=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/eyedeekay/sam3 v0.32.31 h1:0fdDAupEQZSETHcyVQAsnFgpYArGJzU+lC2qN6f0GDk=
github.com/eyedeekay/sam3 v0.32.31/go.mod h1:qRA9KIIVxbrHlkj+ZB+OoxFGFgdKeGp1vSgPw26eOVU=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/uuid v1.1.0 h1:Jf4mxPC/ziBnoPIdpQdPJ9OeiomAUHLvxmPRSPH9m4s=
github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/goshuirc/e-nfa v0.0.0-20160917075329-7071788e3940 h1:KmRLPRstEJiE/9OjumKqI8Rccip8Qmyw2FwyTFxtVqs=
github.com/goshuirc/e-nfa v0.0.0-20160917075329-7071788e3940/go.mod h1:VOmrX6cmj7zwUeexC9HzznUdTIObHqIXUrWNYS+Ik7w=
github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mmcloughlin/professor v0.0.0-20170922221822-6b97112ab8b3 h1:2YMbJ6WbdQI9K73chxh9OWMDsZ2PNjAIRGTonp3T0l0=
github.com/mmcloughlin/professor v0.0.0-20170922221822-6b97112ab8b3/go.mod h1:LQkXsHRSPIEklPCq8OMQAzYNS2NGtYStdNE/ej1oJU8=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.4 h1:Y8E/JaaPbmFSW2V81Ab/d8yZFYQQGbni1b1jPcG9Y6A=
github.com/prometheus/client_golang v0.9.4/go.mod h1:oCXIBxdI62A4cR6aTRJCgetEjecSIYzOEaeAn4iYEpM=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/renstrom/shortuuid v3.0.0+incompatible h1:F6T1U7bWlI3FTV+JE8HyeR7bkTeYZJntqQLA9ST4HOQ=
github.com/renstrom/shortuuid v3.0.0+incompatible/go.mod h1:n18Ycpn8DijG+h/lLBQVnGKv1BCtTeXo8KKSbBOrQ8c=
github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.0 h1:jlIyCplCJFULU/01vCkhKuTyc3OorI3bJFuw6obfgho=
github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/thoj/go-ircevent v0.0.0-20180816043103-14f3614f28c3 h1:389FrrKIAlxqQMTscCQ7VH3JAVuxb/pe53v2LBiA7z8=
github.com/thoj/go-ircevent v0.0.0-20180816043103-14f3614f28c3/go.mod h1:QYOctLs5qEsaIrA/PKEc4YqAv2SozbxNEX0vMPs84p4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a h1:gOpx8G595UYyvj8UK4+OFyY4rx037g3fmfhe5SasG3U=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

File diff suppressed because it is too large Load Diff

View File

@@ -51,7 +51,7 @@ func (channel *Channel) Names(client *Client) {
}
func (channel *Channel) ClientIsOperator(client *Client) bool {
return client.flags[Operator] || channel.members.HasMode(client, ChannelOperator)
return client.modes.Has(Operator) || channel.members.HasMode(client, ChannelOperator)
}
func (channel *Channel) Nicks(target *Client) []string {
@@ -96,7 +96,7 @@ func (channel *Channel) String() string {
// <mode> <mode params>
func (channel *Channel) ModeString(client *Client) (str string) {
isMember := client.flags[Operator] || channel.members.Has(client)
isMember := client.modes.Has(Operator) || channel.members.Has(client)
showKey := isMember && (channel.key != "")
showUserLimit := channel.userLimit > 0
@@ -244,7 +244,7 @@ func (channel *Channel) CanSpeak(client *Client) bool {
channel.members.HasMode(client, ChannelOperator)) {
return false
}
if channel.flags.Has(SecureChan) && !client.flags[SecureConn] {
if channel.flags.Has(SecureChan) && !client.modes.Has(SecureConn) {
return false
}
return true
@@ -513,7 +513,7 @@ func (channel *Channel) Invite(invitee *Client, inviter *Client) {
inviter.RplInviting(invitee, channel.name)
invitee.Reply(RplInviteMsg(inviter, invitee, channel.name))
if invitee.flags[Away] {
if invitee.modes.Has(Away) {
inviter.RplAway(invitee)
}
}

View File

@@ -4,6 +4,7 @@ import (
"crypto/tls"
"fmt"
"net"
"sync"
"time"
log "github.com/sirupsen/logrus"
@@ -14,6 +15,30 @@ const (
QUIT_TIMEOUT = time.Minute // how long after idle before a client is kicked
)
type SyncBool struct {
sync.RWMutex
value bool
}
func NewSyncBool(value bool) *SyncBool {
return &SyncBool{value: value}
}
func (sb *SyncBool) Get() bool {
sb.RLock()
defer sb.RUnlock()
return sb.value
}
func (sb *SyncBool) Set(value bool) {
sb.Lock()
defer sb.Unlock()
sb.value = value
}
type Client struct {
atime time.Time
authorized bool
@@ -22,8 +47,8 @@ type Client struct {
capState CapState
channels *ChannelSet
ctime time.Time
flags map[UserMode]bool
hasQuit bool
modes *UserModeSet
hasQuit *SyncBool
hops uint
hostname Name
hostmask Name // Cloacked hostname (SHA256)
@@ -42,14 +67,15 @@ type Client struct {
func NewClient(server *Server, conn net.Conn) *Client {
now := time.Now()
client := &Client{
c := &Client{
atime: now,
authorized: len(server.password) == 0,
capState: CapNone,
capabilities: make(CapabilitySet),
channels: NewChannelSet(),
ctime: now,
flags: make(map[UserMode]bool),
modes: NewUserModeSet(),
hasQuit: NewSyncBool(false),
sasl: NewSaslState(),
server: server,
socket: NewSocket(conn),
@@ -57,45 +83,50 @@ func NewClient(server *Server, conn net.Conn) *Client {
}
if _, ok := conn.(*tls.Conn); ok {
client.flags[SecureConn] = true
c.modes.Set(SecureConn)
}
client.Touch()
go client.writeloop()
go client.readloop()
c.Touch()
go c.writeloop()
go c.readloop()
return client
return c
}
//
// command goroutine
//
func (client *Client) writeloop() {
for reply := range client.replies {
client.socket.Write(reply)
func (c *Client) writeloop() {
for {
select {
case reply, ok := <-c.replies:
if !ok || reply == "" || c.socket == nil {
return
}
c.socket.Write(reply)
}
}
}
func (client *Client) readloop() {
func (c *Client) readloop() {
var command Command
var err error
var line string
// Set the hostname for this client.
client.hostname = AddrLookupHostname(client.socket.conn.RemoteAddr())
client.hostmask = NewName(SHA256(client.hostname.String()))
c.hostname = AddrLookupHostname(c.socket.conn.RemoteAddr())
c.hostmask = NewName(SHA256(c.hostname.String()))
for err == nil {
if line, err = client.socket.Read(); err != nil {
if line, err = c.socket.Read(); err != nil {
command = NewQuitCommand("connection closed")
} else if command, err = ParseCommand(line); err != nil {
switch err {
case ErrParseCommand:
//TODO(dan): use the real failed numeric for this (400)
client.Reply(RplNotice(client.server, client,
NewText("failed to parse command")))
c.Reply(RplNotice(c.server, c, NewText("failed to parse command")))
case NotEnoughArgsError:
// TODO
@@ -105,7 +136,7 @@ func (client *Client) readloop() {
continue
} else if checkPass, ok := command.(checkPasswordCommand); ok {
checkPass.LoadPassword(client.server)
checkPass.LoadPassword(c.server)
// Block the client thread while handling a potentially expensive
// password bcrypt operation. Since the server is single-threaded
// for commands, we don't want the server to perform the bcrypt,
@@ -114,189 +145,182 @@ func (client *Client) readloop() {
checkPass.CheckPassword()
}
client.processCommand(command)
c.processCommand(command)
}
}
func (client *Client) processCommand(cmd Command) {
cmd.SetClient(client)
func (c *Client) processCommand(cmd Command) {
cmd.SetClient(c)
if !client.registered {
if !c.registered {
regCmd, ok := cmd.(RegServerCommand)
if !ok {
client.Quit("unexpected command")
c.Quit("unexpected command")
return
}
regCmd.HandleRegServer(client.server)
regCmd.HandleRegServer(c.server)
return
}
srvCmd, ok := cmd.(ServerCommand)
if !ok {
client.ErrUnknownCommand(cmd.Code())
c.ErrUnknownCommand(cmd.Code())
return
}
client.server.metrics.Counter("client", "commands").Inc()
c.server.metrics.Counter("client", "commands").Inc()
defer func(t time.Time) {
v := client.server.metrics.SummaryVec("client", "command_duration_seconds")
v := c.server.metrics.SummaryVec("client", "command_duration_seconds")
v.WithLabelValues(cmd.Code().String()).Observe(time.Now().Sub(t).Seconds())
}(time.Now())
switch srvCmd.(type) {
case *PingCommand, *PongCommand:
client.Touch()
c.Touch()
case *QuitCommand:
// no-op
default:
client.Active()
client.Touch()
c.Active()
c.Touch()
}
srvCmd.HandleServer(client.server)
srvCmd.HandleServer(c.server)
}
// quit timer goroutine
func (client *Client) connectionTimeout() {
client.processCommand(NewQuitCommand("connection timeout"))
func (c *Client) connectionTimeout() {
c.processCommand(NewQuitCommand("connection timeout"))
}
//
// idle timer goroutine
//
func (client *Client) connectionIdle() {
client.server.idle <- client
func (c *Client) connectionIdle() {
c.server.idle <- c
}
//
// server goroutine
//
func (client *Client) Active() {
client.atime = time.Now()
func (c *Client) Active() {
c.atime = time.Now()
}
func (client *Client) Touch() {
if client.quitTimer != nil {
client.quitTimer.Stop()
func (c *Client) Touch() {
if c.quitTimer != nil {
c.quitTimer.Stop()
}
if client.idleTimer == nil {
client.idleTimer = time.AfterFunc(IDLE_TIMEOUT, client.connectionIdle)
if c.idleTimer == nil {
c.idleTimer = time.AfterFunc(IDLE_TIMEOUT, c.connectionIdle)
} else {
client.idleTimer.Reset(IDLE_TIMEOUT)
c.idleTimer.Reset(IDLE_TIMEOUT)
}
}
func (client *Client) Idle() {
client.pingTime = time.Now()
client.Reply(RplPing(client.server))
func (c *Client) Idle() {
c.pingTime = time.Now()
c.Reply(RplPing(c.server))
if client.quitTimer == nil {
client.quitTimer = time.AfterFunc(QUIT_TIMEOUT, client.connectionTimeout)
if c.quitTimer == nil {
c.quitTimer = time.AfterFunc(QUIT_TIMEOUT, c.connectionTimeout)
} else {
client.quitTimer.Reset(QUIT_TIMEOUT)
c.quitTimer.Reset(QUIT_TIMEOUT)
}
}
func (client *Client) Register() {
if client.registered {
func (c *Client) Register() {
if c.registered {
return
}
client.registered = true
client.Touch()
c.registered = true
c.modes.Set(HostMask)
c.Touch()
}
func (client *Client) destroy() {
func (c *Client) destroy() {
// clean up channels
client.channels.Range(func(channel *Channel) bool {
channel.Quit(client)
c.channels.Range(func(channel *Channel) bool {
channel.Quit(c)
return true
})
// clean up server
if _, ok := client.socket.conn.(*tls.Conn); ok {
client.server.metrics.GaugeVec("server", "clients").WithLabelValues("secure").Dec()
if _, ok := c.socket.conn.(*tls.Conn); ok {
c.server.metrics.GaugeVec("server", "clients").WithLabelValues("secure").Dec()
} else {
client.server.metrics.GaugeVec("server", "clients").WithLabelValues("insecure").Dec()
c.server.metrics.GaugeVec("server", "clients").WithLabelValues("insecure").Dec()
}
client.server.connections.Dec()
client.server.clients.Remove(client)
c.server.connections.Dec()
c.server.clients.Remove(c)
// clean up self
if client.idleTimer != nil {
client.idleTimer.Stop()
if c.idleTimer != nil {
c.idleTimer.Stop()
}
if client.quitTimer != nil {
client.quitTimer.Stop()
if c.quitTimer != nil {
c.quitTimer.Stop()
}
close(client.replies)
client.replies = nil
close(c.replies)
client.socket.Close()
c.socket.Close()
log.Debugf("%s: destroyed", client)
log.Debugf("%s: destroyed", c)
}
func (client *Client) IdleTime() time.Duration {
return time.Since(client.atime)
func (c *Client) IdleTime() time.Duration {
return time.Since(c.atime)
}
func (client *Client) SignonTime() int64 {
return client.ctime.Unix()
func (c *Client) SignonTime() int64 {
return c.ctime.Unix()
}
func (client *Client) IdleSeconds() uint64 {
return uint64(client.IdleTime().Seconds())
func (c *Client) IdleSeconds() uint64 {
return uint64(c.IdleTime().Seconds())
}
func (client *Client) HasNick() bool {
return client.nick != ""
func (c *Client) HasNick() bool {
return c.nick != ""
}
func (client *Client) HasUsername() bool {
return client.username != ""
func (c *Client) HasUsername() bool {
return c.username != ""
}
func (client *Client) CanSpeak(target *Client) bool {
requiresSecure := client.flags[SecureOnly] || target.flags[SecureOnly]
isSecure := client.flags[SecureConn] && target.flags[SecureConn]
isOperator := client.flags[Operator]
func (c *Client) CanSpeak(target *Client) bool {
requiresSecure := c.modes.Has(SecureOnly) || target.modes.Has(SecureOnly)
isSecure := c.modes.Has(SecureConn) && target.modes.Has(SecureConn)
isOperator := c.modes.Has(Operator)
return !requiresSecure || (requiresSecure && (isOperator || isSecure))
}
// <mode>
func (c *Client) ModeString() (str string) {
for flag := range c.flags {
str += flag.String()
}
if len(str) > 0 {
str = "+" + str
}
return
return c.modes.String()
}
func (c *Client) UserHost(cloacked bool) Name {
username := "*"
if c.HasUsername() {
if c.username != "" {
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.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))
}
func (c *Client) Server() Name {
@@ -322,10 +346,10 @@ func (c *Client) String() string {
return c.Id().String()
}
func (client *Client) Friends() *ClientSet {
func (c *Client) Friends() *ClientSet {
friends := NewClientSet()
friends.Add(client)
client.channels.Range(func(channel *Channel) bool {
friends.Add(c)
c.channels.Range(func(channel *Channel) bool {
channel.members.Range(func(member *Client, _ *ChannelModeSet) bool {
friends.Add(member)
return true
@@ -335,48 +359,48 @@ func (client *Client) Friends() *ClientSet {
return friends
}
func (client *Client) SetNickname(nickname Name) {
if client.HasNick() {
log.Errorf("%s nickname already set!", client)
func (c *Client) SetNickname(nickname Name) {
if c.nick != "" {
log.Errorf("%s nickname already set!", c)
return
}
client.nick = nickname
client.server.clients.Add(client)
c.nick = nickname
c.server.clients.Add(c)
}
func (client *Client) ChangeNickname(nickname Name) {
func (c *Client) ChangeNickname(nickname Name) {
// Make reply before changing nick to capture original source id.
reply := RplNick(client, nickname)
client.server.clients.Remove(client)
client.server.whoWas.Append(client)
client.nick = nickname
client.server.clients.Add(client)
client.Friends().Range(func(friend *Client) bool {
reply := RplNick(c, nickname)
c.server.clients.Remove(c)
c.server.whoWas.Append(c)
c.nick = nickname
c.server.clients.Add(c)
c.Friends().Range(func(friend *Client) bool {
friend.Reply(reply)
return true
})
}
func (client *Client) Reply(reply string) {
if client.replies != nil {
client.replies <- reply
func (c *Client) Reply(reply string) {
if !c.hasQuit.Get() {
c.replies <- reply
}
}
func (client *Client) Quit(message Text) {
if client.hasQuit {
func (c *Client) Quit(message Text) {
if c.hasQuit.Get() {
return
}
client.hasQuit = true
client.Reply(RplError("quit"))
client.server.whoWas.Append(client)
friends := client.Friends()
friends.Remove(client)
client.destroy()
c.hasQuit.Set(true)
c.Reply(RplError("quit"))
c.server.whoWas.Append(c)
friends := c.Friends()
friends.Remove(c)
c.destroy()
if friends.Count() > 0 {
reply := RplQuit(client, message)
reply := RplQuit(c, message)
friends.Range(func(friend *Client) bool {
friend.Reply(reply)
return true

View File

@@ -4,9 +4,7 @@ import (
"errors"
"regexp"
"strings"
//"sync"
sync "github.com/sasha-s/go-deadlock"
"sync"
"github.com/DanielOaks/girc-go/ircmatch"
)

View File

@@ -4,9 +4,7 @@ import (
"errors"
"io/ioutil"
"log"
//"sync"
sync "github.com/sasha-s/go-deadlock"
"sync"
"github.com/imdario/mergo"
"gopkg.in/yaml.v2"
@@ -21,6 +19,18 @@ type TLSConfig struct {
Cert string
}
type I2PConfig struct {
I2Pkeys string
SAMaddr string
Base32 string
}
type TorConfig struct {
Torkeys string
ControlPort int
Onion string
}
func (conf *PassConfig) PasswordBytes() []byte {
bytes, err := DecodePassword(conf.Password)
if err != nil {
@@ -41,6 +51,8 @@ type Config struct {
PassConfig `yaml:",inline"`
Listen []string
TLSListen map[string]*TLSConfig
I2PListen map[string]*I2PConfig
TorListen map[string]*TorConfig
Log string
MOTD string
Name string
@@ -113,7 +125,7 @@ func LoadConfig(filename string) (config *Config, err error) {
return nil, errors.New("Server name must match the format of a hostname")
}
if len(config.Server.Listen)+len(config.Server.TLSListen) == 0 {
if len(config.Server.Listen)+len(config.Server.TLSListen)+len(config.Server.I2PListen) == 0 {
return nil, errors.New("Server listening addresses missing")
}

View File

@@ -3,6 +3,7 @@ package irc
import (
"fmt"
"net/http"
"sync"
log "github.com/sirupsen/logrus"
@@ -10,6 +11,7 @@ import (
"github.com/prometheus/client_golang/prometheus/promhttp"
)
// DefObjectives ...
var DefObjectives = map[float64]float64{
0.50: 0.05,
0.90: 0.01,
@@ -17,22 +19,27 @@ var DefObjectives = map[float64]float64{
0.99: 0.001,
}
// Metrics ...
type Metrics struct {
sync.RWMutex
namespace string
metrics map[string]prometheus.Metric
gaugevecs map[string]*prometheus.GaugeVec
guagevecs map[string]*prometheus.GaugeVec
sumvecs map[string]*prometheus.SummaryVec
}
// NewMetrics ...
func NewMetrics(namespace string) *Metrics {
return &Metrics{
namespace: namespace,
metrics: make(map[string]prometheus.Metric),
gaugevecs: make(map[string]*prometheus.GaugeVec),
guagevecs: make(map[string]*prometheus.GaugeVec),
sumvecs: make(map[string]*prometheus.SummaryVec),
}
}
// NewCounter ...
func (m *Metrics) NewCounter(subsystem, name, help string) prometheus.Counter {
counter := prometheus.NewCounter(
prometheus.CounterOpts{
@@ -44,12 +51,15 @@ func (m *Metrics) NewCounter(subsystem, name, help string) prometheus.Counter {
)
key := fmt.Sprintf("%s_%s", subsystem, name)
m.Lock()
m.metrics[key] = counter
m.Unlock()
prometheus.MustRegister(counter)
return counter
}
// NewCounterFunc ...
func (m *Metrics) NewCounterFunc(subsystem, name, help string, f func() float64) prometheus.CounterFunc {
counter := prometheus.NewCounterFunc(
prometheus.CounterOpts{
@@ -62,12 +72,15 @@ func (m *Metrics) NewCounterFunc(subsystem, name, help string, f func() float64)
)
key := fmt.Sprintf("%s_%s", subsystem, name)
m.Lock()
m.metrics[key] = counter
m.Unlock()
prometheus.MustRegister(counter)
return counter
}
// NewGauge ...
func (m *Metrics) NewGauge(subsystem, name, help string) prometheus.Gauge {
guage := prometheus.NewGauge(
prometheus.GaugeOpts{
@@ -79,12 +92,15 @@ func (m *Metrics) NewGauge(subsystem, name, help string) prometheus.Gauge {
)
key := fmt.Sprintf("%s_%s", subsystem, name)
m.Lock()
m.metrics[key] = guage
m.Unlock()
prometheus.MustRegister(guage)
return guage
}
// NewGaugeFunc ...
func (m *Metrics) NewGaugeFunc(subsystem, name, help string, f func() float64) prometheus.GaugeFunc {
guage := prometheus.NewGaugeFunc(
prometheus.GaugeOpts{
@@ -97,14 +113,17 @@ func (m *Metrics) NewGaugeFunc(subsystem, name, help string, f func() float64) p
)
key := fmt.Sprintf("%s_%s", subsystem, name)
m.Lock()
m.metrics[key] = guage
m.Unlock()
prometheus.MustRegister(guage)
return guage
}
// NewGaugeVec ...
func (m *Metrics) NewGaugeVec(subsystem, name, help string, labels []string) *prometheus.GaugeVec {
gauagevec := prometheus.NewGaugeVec(
guagevec := prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Namespace: m.namespace,
Subsystem: subsystem,
@@ -115,12 +134,15 @@ func (m *Metrics) NewGaugeVec(subsystem, name, help string, labels []string) *pr
)
key := fmt.Sprintf("%s_%s", subsystem, name)
m.gaugevecs[key] = gauagevec
prometheus.MustRegister(gauagevec)
m.Lock()
m.guagevecs[key] = guagevec
m.Unlock()
prometheus.MustRegister(guagevec)
return gauagevec
return guagevec
}
// NewSummary ...
func (m *Metrics) NewSummary(subsystem, name, help string) prometheus.Summary {
summary := prometheus.NewSummary(
prometheus.SummaryOpts{
@@ -133,12 +155,15 @@ func (m *Metrics) NewSummary(subsystem, name, help string) prometheus.Summary {
)
key := fmt.Sprintf("%s_%s", subsystem, name)
m.Lock()
m.metrics[key] = summary
m.Unlock()
prometheus.MustRegister(summary)
return summary
}
// NewSummaryVec ...
func (m *Metrics) NewSummaryVec(subsystem, name, help string, labels []string) *prometheus.SummaryVec {
sumvec := prometheus.NewSummaryVec(
prometheus.SummaryOpts{
@@ -152,41 +177,58 @@ func (m *Metrics) NewSummaryVec(subsystem, name, help string, labels []string) *
)
key := fmt.Sprintf("%s_%s", subsystem, name)
m.Lock()
m.sumvecs[key] = sumvec
m.Unlock()
prometheus.MustRegister(sumvec)
return sumvec
}
// Counter ...
func (m *Metrics) Counter(subsystem, name string) prometheus.Counter {
key := fmt.Sprintf("%s_%s", subsystem, name)
return m.metrics[key].(prometheus.Counter)
}
// Gauge ...
func (m *Metrics) Gauge(subsystem, name string) prometheus.Gauge {
key := fmt.Sprintf("%s_%s", subsystem, name)
m.RLock()
defer m.RUnlock()
return m.metrics[key].(prometheus.Gauge)
}
// GaugeVec ...
func (m *Metrics) GaugeVec(subsystem, name string) *prometheus.GaugeVec {
key := fmt.Sprintf("%s_%s", subsystem, name)
return m.gaugevecs[key]
m.RLock()
defer m.RUnlock()
return m.guagevecs[key]
}
// Summary ...
func (m *Metrics) Summary(subsystem, name string) prometheus.Summary {
key := fmt.Sprintf("%s_%s", subsystem, name)
m.RLock()
defer m.RUnlock()
return m.metrics[key].(prometheus.Summary)
}
// SummaryVec ...
func (m *Metrics) SummaryVec(subsystem, name string) *prometheus.SummaryVec {
key := fmt.Sprintf("%s_%s", subsystem, name)
m.RLock()
defer m.RUnlock()
return m.sumvecs[key]
}
// Handler ...
func (m *Metrics) Handler() http.Handler {
return promhttp.Handler()
}
// Run ...
func (m *Metrics) Run(addr string) {
http.Handle("/", m.Handler())
log.Infof("metrics endpoint listening on %s", addr)

51
irc/metrics_test.go Normal file
View File

@@ -0,0 +1,51 @@
package irc
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
)
func TestMetrics(t *testing.T) {
assert := assert.New(t)
m := NewMetrics("test")
m.NewCounter("foo", "counter", "help")
m.NewCounterFunc("foo", "counter_func", "help", func() float64 { return 1.0 })
m.NewGauge("foo", "gauge", "help")
m.NewGaugeFunc("foo", "gauge_func", "help", func() float64 { return 1.0 })
m.NewGaugeVec("foo", "gauge_vec", "help", []string{"test"})
m.Counter("foo", "counter").Inc()
m.Gauge("foo", "gauge").Add(1)
m.GaugeVec("foo", "gauge_vec").WithLabelValues("test").Add(1)
w := httptest.NewRecorder()
r, _ := http.NewRequest("GET", "/", nil)
m.Handler().ServeHTTP(w, r)
assert.Equal(w.Code, http.StatusOK)
assert.Regexp(
`
# HELP test_foo_counter help
# TYPE test_foo_counter counter
test_foo_counter 1
# HELP test_foo_counter_func help
# TYPE test_foo_counter_func counter
test_foo_counter_func 1
# HELP test_foo_gauge help
# TYPE test_foo_gauge gauge
test_foo_gauge 1
# HELP test_foo_gauge_func help
# TYPE test_foo_gauge_func gauge
test_foo_gauge_func 1
# HELP test_foo_gauge_vec help
# TYPE test_foo_gauge_vec gauge
test_foo_gauge_vec{test="test"} 1
`,
w.Body.String(),
)
}

View File

@@ -58,11 +58,12 @@ const (
Registered UserMode = 'r' // not a real user mode (flag)
SecureConn UserMode = 'z'
SecureOnly UserMode = 'Z'
HostMask UserMode = 'x'
)
var (
SupportedUserModes = UserModes{
Invisible, Operator,
Invisible, Operator, HostMask,
}
DefaultChannelModes = ChannelModes{
NoOutside, OpOnlyTopic,
@@ -107,7 +108,7 @@ func (m *ModeCommand) HandleServer(s *Server) {
return
}
if client != target && !client.flags[Operator] {
if client != target && !client.modes.Has(Operator) {
client.ErrUsersDontMatch()
return
}
@@ -116,29 +117,27 @@ func (m *ModeCommand) HandleServer(s *Server) {
for _, change := range m.changes {
switch change.mode {
case Invisible, WallOps, SecureOnly:
case Invisible, HostMask, WallOps, SecureOnly:
switch change.op {
case Add:
if target.flags[change.mode] {
if target.modes.Has(change.mode) {
continue
}
target.flags[change.mode] = true
target.modes.Set(change.mode)
changes = append(changes, change)
case Remove:
if !target.flags[change.mode] {
if !target.modes.Has(change.mode) {
continue
}
delete(target.flags, change.mode)
target.modes.Unset(change.mode)
changes = append(changes, change)
}
case Operator:
if change.op == Remove {
if !target.flags[change.mode] {
if !target.modes.Has(change.mode) {
continue
}
delete(target.flags, change.mode)
target.modes.Unset(change.mode)
changes = append(changes, change)
}
}

View File

@@ -67,7 +67,7 @@ type OperNickCommand struct {
func (msg *OperNickCommand) HandleServer(server *Server) {
client := msg.Client()
if !client.flags[Operator] {
if !client.modes.Has(Operator) {
client.ErrNoPrivileges()
return
}

View File

@@ -4,9 +4,8 @@ import (
"encoding/base64"
"fmt"
"golang.org/x/crypto/bcrypt"
//"sync"
"sync"
sync "github.com/sasha-s/go-deadlock"
log "github.com/sirupsen/logrus"
)

View File

@@ -5,9 +5,9 @@ func CanSeeChannel(client *Client, channel *Channel) bool {
isSecret := channel.flags.Has(Secret)
isMember := channel.members.Has(client)
isOperator := client.flags[Operator]
isRegistered := client.flags[Registered]
isSecure := client.flags[SecureConn]
isOperator := client.modes.Has(Operator)
isRegistered := client.modes.Has(Registered)
isSecure := client.modes.Has(SecureConn)
if !(isSecret || isPrivate) {
return true

View File

@@ -249,13 +249,13 @@ func (target *Client) RplRehashing() {
func (target *Client) RplWhois(client *Client) {
target.RplWhoisUser(client)
if client.flags[Operator] {
if client.modes.Has(Operator) {
target.RplWhoisOperator(client)
}
target.RplWhoisIdle(client)
target.RplWhoisChannels(client)
if client.flags[SecureConn] {
if client.modes.Has(SecureConn) {
target.RplWhoisSecure(client)
}
target.RplWhoisServer(client)
@@ -266,7 +266,7 @@ func (target *Client) RplWhois(client *Client) {
func (target *Client) RplWhoisUser(client *Client) {
var clientHost Name
if target.flags[Operator] {
if target.modes.Has(Operator) || !client.modes.Has(HostMask) {
clientHost = client.hostname
} else {
clientHost = client.hostmask
@@ -342,7 +342,7 @@ func (target *Client) RplChannelModeIs(channel *Channel) {
func (target *Client) RplWhoReply(channel *Channel, client *Client) {
var clientHost Name
if target.flags[Operator] {
if target.modes.Has(Operator) || !client.modes.Has(HostMask) {
clientHost = client.hostname
} else {
clientHost = client.hostmask
@@ -351,12 +351,12 @@ func (target *Client) RplWhoReply(channel *Channel, client *Client) {
channelName := "*"
flags := ""
if client.flags[Away] {
if client.modes.Has(Away) {
flags = "G"
} else {
flags = "H"
}
if client.flags[Operator] {
if client.modes.Has(Operator) {
flags += "*"
}
@@ -579,7 +579,7 @@ func (target *Client) RplLUserChannels() {
func (target *Client) RplLUserOp() {
nOperators := 0
target.server.clients.Range(func(_ Name, client *Client) bool {
if client.flags[Operator] {
if client.modes.Has(Operator) {
nOperators++
}
return true
@@ -610,7 +610,7 @@ func (target *Client) RplLUserMe() {
func (target *Client) RplWhoWasUser(whoWas *WhoWas) {
var whoWasHost Name
if target.flags[Operator] {
if target.modes.Has(Operator) {
whoWasHost = whoWas.hostname
} else {
whoWasHost = whoWas.hostmask

View File

@@ -2,9 +2,7 @@ package irc
import (
"bytes"
//"sync"
sync "github.com/sasha-s/go-deadlock"
"sync"
)
type SaslState struct {

View File

@@ -3,10 +3,12 @@ package irc
import (
"bufio"
"bytes"
"context"
"crypto/rand"
"crypto/tls"
"encoding/base64"
"fmt"
"io/ioutil"
"net"
"os"
"os/signal"
@@ -14,6 +16,10 @@ import (
"syscall"
"time"
"github.com/cretz/bine/tor"
"github.com/cretz/bine/torutil/ed25519"
"github.com/eyedeekay/sam3"
"github.com/eyedeekay/sam3/i2pkeys"
log "github.com/sirupsen/logrus"
)
@@ -51,8 +57,8 @@ type Server struct {
var (
SERVER_SIGNALS = []os.Signal{
syscall.SIGINT, syscall.SIGHUP,
syscall.SIGTERM, syscall.SIGQUIT,
syscall.SIGINT,
syscall.SIGTERM,
}
)
@@ -80,7 +86,7 @@ func NewServer(config *Config) *Server {
log.Debugf("accounts: %v", config.Accounts())
// TODO: Make this configurabel?
// TODO: Make this configureable?
server.ids["global"] = NewIdentity(config.Server.Name, "global")
if config.Server.Password != "" {
@@ -95,6 +101,22 @@ func NewServer(config *Config) *Server {
server.listentls(addr, tlsconfig)
}
for addr, i2pconfig := range config.Server.I2PListen {
server.listeni2p(addr, i2pconfig)
err := ioutil.WriteFile(i2pconfig.I2Pkeys+".i2p.public.txt", []byte(i2pconfig.Base32), 0644)
if err != nil {
log.Fatalf("error storing I2P base32 address in adjacent text file")
}
}
for addr, torconfig := range config.Server.TorListen {
server.listentor(addr, torconfig)
err := ioutil.WriteFile(torconfig.Torkeys+".tor.public.txt", []byte(torconfig.Onion), 0644)
if err != nil {
log.Fatalf("error storing Tor onion address in adjacent text file")
}
}
signal.Notify(server.signals, SERVER_SIGNALS...)
// server uptime counter
@@ -136,7 +158,7 @@ func NewServer(config *Config) *Server {
},
)
// server clients gauge (by secure/insecire)
// server clients gauge (by secure/insecure)
server.metrics.NewGaugeVec(
"server", "clients",
"Number of registered clients connected (by secure/insecure)",
@@ -173,7 +195,7 @@ func NewServer(config *Config) *Server {
func (server *Server) Wallops(message string) {
text := NewText(message)
server.clients.Range(func(_ Name, client *Client) bool {
if client.flags[WallOps] {
if client.modes.Has(WallOps) {
server.metrics.Counter("client", "messages").Inc()
client.replies <- RplNotice(server, client, text)
}
@@ -284,6 +306,107 @@ func (s *Server) listentls(addr string, tlsconfig *TLSConfig) {
go s.acceptor(listener)
}
//
// listen i2p goroutine
//
func (s *Server) listeni2p(addr string, i2pconfig *I2PConfig) {
sam, err := sam3.NewSAM(i2pconfig.SAMaddr)
if err != nil {
log.Fatalf("error connecting to SAM to %s: %s", addr, err)
}
var keys *i2pkeys.I2PKeys
if _, err := os.Stat(i2pconfig.I2Pkeys + ".i2p.private"); os.IsNotExist(err) {
f, err := os.Create(i2pconfig.I2Pkeys + ".i2p.private")
if err != nil {
log.Fatalf("unable to open I2P keyfile for writing: %s", err)
}
defer f.Close()
tkeys, err := sam.NewKeys()
if err != nil {
log.Fatalf("unable to generate I2P Keys, %s", err)
}
keys = &tkeys
err = i2pkeys.StoreKeysIncompat(*keys, f)
if err != nil {
log.Fatalf("unable to save newly generated I2P Keys, %s", err)
}
i2pconfig.Base32 = keys.Addr().Base32()
} else {
tkeys, err := i2pkeys.LoadKeys(i2pconfig.I2Pkeys + ".i2p.private")
if err != nil {
log.Fatalf("unable to load I2P Keys: %e", err)
}
keys = &tkeys
}
// If the keys and the base32 are different, keys win.
i2pconfig.Base32 = keys.Addr().Base32()
stream, err := sam.NewStreamSession(addr, *keys, sam3.Options_Medium)
if err != nil {
log.Fatalf("error creating I2P streaming connection %s: %s, %s.", addr, err, *keys)
}
listener, err := stream.Listen()
if err != nil {
log.Fatalf("error binding to %s: %s", keys.Addr().Base32(), err)
}
log.Infof("Listening on I2P address, %s", keys.Addr().Base32(), err)
go s.acceptor(listener)
}
//
// listen tor goroutine
//
func (s *Server) listentor(addr string, torconfig *TorConfig) {
log.Infof("Starting and registering onion service, please wait a couple of minutes...")
t, err := tor.Start(nil, &tor.StartConf{ControlPort: torconfig.ControlPort})
if err != nil {
log.Fatalf("Unable to start Tor: %v", err)
}
var keys *ed25519.KeyPair
if _, err := os.Stat(torconfig.Torkeys + ".tor.private"); os.IsNotExist(err) {
tkeys, err := ed25519.GenerateKey(nil)
if err != nil {
log.Fatalf("Unable to generate onion service key, %s", err)
}
keys = &tkeys
f, err := os.Create(torconfig.Torkeys + ".tor.private")
if err != nil {
log.Fatalf("Unable to create Tor keys file for writing, %s", err)
}
defer f.Close()
_, err = f.Write(tkeys.PrivateKey())
if err != nil {
log.Fatalf("Unable to write Tor keys to disk, %s", err)
}
} else if err == nil {
tkeys, err := ioutil.ReadFile(torconfig.Torkeys + ".tor.private")
if err != nil {
log.Fatalf("Unable to read Tor keys from disk")
}
k := ed25519.FromCryptoPrivateKey(tkeys)
keys = &k
} else {
log.Fatalf("Unable to set up Tor keys, %s", err)
}
listenCtx := context.Background()
// Create a v3 onion service to listen on any port but show as 6667
listener, err := t.Listen(
listenCtx,
&tor.ListenConf{
Version3: true,
RemotePorts: []int{6667},
Key: *keys,
},
)
if err != nil {
log.Fatalf("Unable to create onion service: %v", err)
}
torconfig.Onion = listener.ID + ".onion"
log.Infof("Listening on Onion address, %s", listener.ID, torconfig.Onion, err)
go s.acceptor(listener)
}
//
// server functionality
//
@@ -408,7 +531,7 @@ func (msg *RFC2812UserCommand) HandleRegServer(server *Server) {
flags := msg.Flags()
if len(flags) > 0 {
for _, mode := range flags {
client.flags[mode] = true
client.modes.Set(mode)
}
client.RplUModeIs(client)
}
@@ -495,7 +618,7 @@ func (msg *AuthenticateCommand) HandleRegServer(server *Server) {
client.RplLoggedIn(authcid)
client.RplSaslSuccess()
client.flags[Registered] = true
client.modes.Set(Registered)
client.Reply(
RplModeChanges(
client, client,
@@ -624,7 +747,7 @@ func (msg *PrivMsgCommand) HandleServer(server *Server) {
}
server.metrics.Counter("client", "messages").Inc()
target.Reply(RplPrivMsg(client, target, msg.message))
if target.flags[Away] {
if target.modes.Has(Away) {
client.RplAway(target)
}
}
@@ -673,7 +796,7 @@ func (m *WhoisCommand) HandleServer(server *Server) {
func whoChannel(client *Client, channel *Channel, friends *ClientSet) {
channel.members.Range(func(member *Client, _ *ChannelModeSet) bool {
if !client.flags[Invisible] || friends.Has(client) {
if !client.modes.Has(Invisible) || friends.Has(client) {
client.RplWhoReply(channel, member)
}
return true
@@ -715,8 +838,8 @@ func (msg *OperCommand) HandleServer(server *Server) {
return
}
client.flags[Operator] = true
client.flags[WallOps] = true
client.modes.Set(Operator)
client.modes.Set(WallOps)
client.RplYoureOper()
client.Reply(
RplModeChanges(
@@ -731,7 +854,7 @@ func (msg *OperCommand) HandleServer(server *Server) {
func (msg *RehashCommand) HandleServer(server *Server) {
client := msg.Client()
if !client.flags[Operator] {
if !client.modes.Has(Operator) {
client.ErrNoPrivileges()
return
}
@@ -756,9 +879,9 @@ func (msg *RehashCommand) HandleServer(server *Server) {
func (msg *AwayCommand) HandleServer(server *Server) {
client := msg.Client()
if len(msg.text) > 0 {
client.flags[Away] = true
client.modes.Set(Away)
} else {
delete(client.flags, Away)
client.modes.Unset(Away)
}
client.awayMessage = msg.text
}
@@ -783,7 +906,7 @@ func (msg *MOTDCommand) HandleServer(server *Server) {
func (msg *NoticeCommand) HandleServer(server *Server) {
client := msg.Client()
if msg.target == "*" && client.flags[Operator] {
if msg.target == "*" && client.modes.Has(Operator) {
server.Global(msg.message.String())
return
}
@@ -932,7 +1055,7 @@ func (msg *LUsersCommand) HandleServer(server *Server) {
func (msg *WallopsCommand) HandleServer(server *Server) {
client := msg.Client()
if !client.flags[Operator] {
if !client.modes.Has(Operator) {
client.ErrNoPrivileges()
return
}
@@ -942,7 +1065,7 @@ func (msg *WallopsCommand) HandleServer(server *Server) {
func (msg *KillCommand) HandleServer(server *Server) {
client := msg.Client()
if !client.flags[Operator] {
if !client.modes.Has(Operator) {
client.ErrNoPrivileges()
return
}

View File

@@ -3,9 +3,7 @@ package irc
import (
"fmt"
"strings"
//"sync"
sync "github.com/sasha-s/go-deadlock"
"sync"
)
//
@@ -96,6 +94,67 @@ func (c *ChannelNameMap) Remove(channel *Channel) error {
return nil
}
// UserModeSet holds a mapping of channel modes
type UserModeSet struct {
sync.RWMutex
modes map[UserMode]bool
}
// NewUserModeSet returns a new UserModeSet
func NewUserModeSet() *UserModeSet {
return &UserModeSet{modes: make(map[UserMode]bool)}
}
// Set sets mode
func (set *UserModeSet) Set(mode UserMode) {
set.Lock()
defer set.Unlock()
set.modes[mode] = true
}
// Unset unsets mode
func (set *UserModeSet) Unset(mode UserMode) {
set.Lock()
defer set.Unlock()
delete(set.modes, mode)
}
// Has returns true if the mode is set
func (set *UserModeSet) Has(mode UserMode) bool {
set.RLock()
defer set.RUnlock()
ok, _ := set.modes[mode]
return ok
}
// Range ranges of the modes calling f
func (set *UserModeSet) Range(f func(mode UserMode) bool) {
set.RLock()
defer set.RUnlock()
for mode := range set.modes {
if !f(mode) {
return
}
}
}
// String returns a string representing the channel modes
func (set *UserModeSet) String() string {
set.RLock()
defer set.RUnlock()
if len(set.modes) == 0 {
return ""
}
strs := make([]string, len(set.modes))
index := 0
for mode := range set.modes {
strs[index] = mode.String()
index++
}
return strings.Join(strs, "")
}
// ChannelModeSet holds a mapping of channel modes
type ChannelModeSet struct {
sync.RWMutex

View File

@@ -5,20 +5,17 @@ import (
)
var (
//PackageName package name
// Package package name
Package = "eris"
// Version release version
Version = "1.6.4"
// Build will be overwritten automatically by the build system
Build = "dev"
// GitCommit will be overwritten automatically by the build system
GitCommit = "HEAD"
)
// FullVersion display the full version and build
func FullVersion() string {
return fmt.Sprintf("%s-%s-%s@%s", Package, Version, Build, GitCommit)
return fmt.Sprintf("%s-%s@%s", Package, Version, GitCommit)
}

View File

@@ -1,9 +1,7 @@
package irc
import (
//"sync"
sync "github.com/sasha-s/go-deadlock"
"sync"
)
type WhoWasList struct {

View File

@@ -18,6 +18,24 @@ server:
":6697":
key: key.pem
cert: cert.pem
# Addresses to listen on for Invisible Internet
# note that if you choose this option, your ircd.yml will be
# rewritten to include the I2P address of your IRC server.
# You will lose any comments in your ircd.yml file
# i2plisten:
# "invisibleirc":
# i2pkeys: iirc
# samaddr: "127.0.0.1:7656"
# Addresses to listen on for Tor Onion Services.
# note that if you choose this option, your ircd.yml will be
# rewritten to include the Onion address of your IRC server.
# You will lose any comments in your ircd.yml
# torlisten:
# hiddenirc:
# torkeys: tirc
# controlport: 0
# password to login to the server
# generated using "mkpasswd" (from https://github.com/prologic/mkpasswd)

View File

@@ -7,6 +7,7 @@ import (
log "github.com/sirupsen/logrus"
"github.com/mmcloughlin/professor"
"github.com/prologic/eris/irc"
)
@@ -33,6 +34,10 @@ func main() {
log.SetLevel(log.WarnLevel)
}
if debug {
go professor.Launch(":6060")
}
config, err := irc.LoadConfig(configfile)
if err != nil {
log.Fatal("Config file did not load successfully:", err.Error())

View File

@@ -1,26 +1,31 @@
package main
import (
"crypto/sha256"
"flag"
"log"
"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 (
done chan bool
server *eris.Server
client *irc.Connection
clients map[string]*irc.Connection
tls = flag.Bool("tls", false, "run tests with TLS")
debug = flag.Bool("d", false, "enable debug logging")
)
func setupServer() *eris.Server {
@@ -31,6 +36,11 @@ func setupServer() *eris.Server {
config.Server.Description = "Test"
config.Server.Listen = []string{":6667"}
// SASL
config.Account = map[string]*eris.PassConfig{
"admin": {"JDJhJDA0JGtUU1JVc1JOUy9DbEh1WEdvYVlMdGVnclp6YnA3NDBOZGY1WUZhdTZtRzVmb1VKdXQ5ckZD"},
}
server := eris.NewServer(config)
go server.Run()
@@ -38,9 +48,21 @@ func setupServer() *eris.Server {
return server
}
func newClient(nick, user, name string, start bool) *irc.Connection {
client := irc.IRC(nick, user)
client.RealName = name
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 {
@@ -57,21 +79,16 @@ func newClient(nick, user, name string, start bool) *irc.Connection {
func TestMain(m *testing.M) {
flag.Parse()
done = make(chan bool)
if *debug {
log.SetLevel(log.DebugLevel)
} else {
log.SetLevel(log.WarnLevel)
}
server = setupServer()
client = newClient("test", "test", "Test", true)
clients = make(map[string]*irc.Connection)
clients["test1"] = newClient("test1", "test", "Test 1", true)
clients["test2"] = newClient("test2", "test", "Test 2", true)
result := m.Run()
for _, client := range clients {
client.Quit()
}
server.Stop()
os.Exit(result)
@@ -80,106 +97,481 @@ func TestMain(m *testing.M) {
func TestConnection(t *testing.T) {
assert := assert.New(t)
var (
expected bool
actual chan bool
)
expected := true
actual := make(chan bool)
expected = true
actual = make(chan bool)
client := newClient("connect", "connect", "Connect", false)
client := newClient(false)
client.AddCallback("001", func(e *irc.Event) {
defer func() { done <- true }()
actual <- true
})
time.AfterFunc(1*time.Second, func() { done <- true })
defer client.Quit()
go client.Loop()
<-done
assert.Equal(expected, <-actual)
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)
var (
expected string
actual chan string
)
expected := "Welcome to the .* Internet Relay Network .*!.*@.*"
actual := make(chan string)
expected = "Welcome to the .* Internet Relay Network .*!.*@.*"
actual = make(chan string)
client := newClient("connect", "connect", "Connect", false)
client := newClient(false)
client.AddCallback("001", func(e *irc.Event) {
defer func() { done <- true }()
actual <- e.Message()
})
time.AfterFunc(1*time.Second, func() { done <- true })
defer client.Quit()
go client.Loop()
<-done
assert.Regexp(expected, <-actual)
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)
var (
expected []string
actual chan string
)
client := newClient(false)
expected = []string{"test", "=", "#test", "@test"}
actual = make(chan string)
expected := []string{client.GetNick(), "=", "#join", fmt.Sprintf("@%s", client.GetNick())}
actual := make(chan string)
client.AddCallback("353", func(e *irc.Event) {
defer func() { done <- true }()
for i := range e.Arguments {
actual <- e.Arguments[i]
}
})
time.AfterFunc(1*time.Second, func() { done <- true })
client.Join("#test")
client.SendRaw("NAMES #test")
<-done
defer client.Quit()
go client.Loop()
client.Join("#join")
client.SendRaw("NAMES #join")
for i := range expected {
assert.Equal(expected[i], <-actual)
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")
}
}
/* FIXME: This test is racey :/
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)
var (
expected string
actual chan string
)
expected := "Hello World!"
actual := make(chan string)
expected = "Hello World!"
actual = make(chan string)
client1 := newClient(false)
client2 := newClient(false)
clients["test1"].AddCallback("PRIVMSG", func(e *irc.Event) {
defer func() { done <- true }()
client1.AddCallback("001", func(e *irc.Event) {
client1.Privmsg(client2.GetNick(), expected)
})
client1.AddCallback("PRIVMSG", func(e *irc.Event) {
actual <- e.Message()
})
time.AfterFunc(1*time.Second, func() { done <- true })
client.Privmsg("test1", expected)
<-done
client2.AddCallback("001", func(e *irc.Event) {
client2.Privmsg(client1.GetNick(), expected)
})
client2.AddCallback("PRIVMSG", func(e *irc.Event) {
actual <- e.Message()
})
assert.Equal(expected, <-actual)
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/beorn7/perks generated vendored

1
vendor/github.com/imdario/mergo generated vendored

1
vendor/golang.org/x/crypto generated vendored

Submodule vendor/golang.org/x/crypto deleted from 365904b0f3

1
vendor/golang.org/x/sys generated vendored

Submodule vendor/golang.org/x/sys deleted from a204229cd8

1
vendor/golang.org/x/text generated vendored

Submodule vendor/golang.org/x/text deleted from a352c5cd19

1
vendor/gopkg.in/yaml.v2 generated vendored

Submodule vendor/gopkg.in/yaml.v2 deleted from 287cf08546