3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-22 03:49:27 +01:00

Merge remote-tracking branch 'origin/master' into master+relaymsg

This commit is contained in:
Shivaram Lingamneni 2020-09-09 03:55:41 -04:00
commit 8102d1ddb6
328 changed files with 24635 additions and 16388 deletions

View File

@ -3,6 +3,10 @@
# exclude vendor/
SOURCES="./oragono.go ./irc"
if [ "$1" = "--fix" ]; then
exec gofmt -s -w $SOURCES
fi
if [ -n "$(gofmt -s -l $SOURCES)" ]; then
echo "Go code is not formatted correctly with \`gofmt -s\`:"
gofmt -s -d $SOURCES

View File

@ -45,7 +45,7 @@ archives:
- README
- CHANGELOG.md
- oragono.motd
- oragono.yaml
- default.yaml
- conventional.yaml
- docs/*
- languages/*.yaml

View File

@ -1,7 +1,7 @@
language: go
go:
- "1.14.x"
- "1.15.x"
before_install:
# https://github.com/travis-ci/travis-ci/issues/8361

View File

@ -1,6 +1,117 @@
# Changelog
All notable changes to Oragono will be documented in this file.
## [2.3.0] - 2020-09-06
We're pleased to announce Oragono 2.3.0, a new stable release.
This release contains primarily bug fixes, but includes one notable feature enhancement: a change contributed by [@hhirtz](https://github.com/hhirtz) that updates the `draft/rename` specification to correspond to the new (soon-to-be) published draft.
Many thanks to [@hhirtz](https://github.com/hhirtz) for contributing patches, to [@bogdomania](https://github.com/bogdomania), [@digitalcircuit](https://github.com/digitalcircuit), [@ivan-avalos](https://github.com/ivan-avalos), [@jesopo](https://github.com/jesopo), [@kylef](https://github.com/kylef), [@Mitaka8](https://github.com/Mitaka8), [@mogad0n](https://github.com/mogad0n), and [@ProgVal](https://github.com/ProgVal) for reporting issues and helping test, and to our translators for contributing translations.
This release includes no changes to the config file format or database changes.
### Config changes
* The recommended value of `lookup-hostnames` for configurations that cloak IPs (as has been the default since 2.1.0) is now `false` (#1228)
### Security
* Mitigated a potential DoS attack on websocket listeners (#1226)
### Removed
* Removed `/HOSTSERV OFFERLIST` and related commands; this functionality is superseded by IP cloaking (#1190)
### Fixed
* Fixed an edge case in handling no-op nick changes (#1242)
* Fixed edge cases with users transitioning in and out of always-on status (#1218, #1219, thanks [@bogdomania](https://github.com/bogdomania)!)
* Fixed a race condition related to the registration timeout (#1225, thanks [@hhirtz](https://github.com/hhirtz)!)
* Fixed incorrectly formatted account tags on some messages (#1254, thanks [@digitalcircuit](https://github.com/digitalcircuit)!)
* Improved checks for invalid config files (#1244, thanks [@ivan-avalos](https://github.com/ivan-avalos)!)
* Fixed messages to services and `*playback` not receiving echo-message when applicable (#1204, thanks [@kylef](https://github.com/kylef)!)
* Fixed a help string (#1237, thanks [@Mitaka8](https://github.com/Mitaka8)!)
### Changed
* Updated `draft/rename` implementation to the latest draft (#1223, thanks [@hhirtz](https://github.com/hhirtz)!)
### Internal
* Official release builds now use Go 1.15 (#1195)
* `/INFO` now includes the Go version (#1234)
## [2.2.0] - 2020-07-26
We're pleased to announce Oragono 2.2.0, a new stable release.
This release contains several notable enhancements, as well as bug fixes:
* Support for tracking seen/missed messages across multiple devices (#843)
* WHOX support contributed by @jesopo (#938)
* Authentication of users via external scripts (#1107)
Many thanks to [@clukawski](https://github.com/clukawski) and [@jesopo](https://github.com/jesopo) for contributing patches, to [@ajaspers](https://github.com/ajaspers), [@bogdomania](https://github.com/bogdomania), [@csmith](https://github.com/csmith), [@daurnimator](https://github.com/daurnimator), [@emersonveenstra](https://github.com/emersonveenstra), [@eskil](https://github.com/eskil), [@eskimo](https://github.com/eskimo), Geo-, [@happyhater](https://github.com/happyhater), [@jesopo](https://github.com/jesopo), [@jwheare](https://github.com/jwheare), [@k4bek4be](https://github.com/k4bek4be), [@KoraggKnightWolf](https://github.com/KoraggKnightWolf), [@kylef](https://github.com/kylef), [@LukeHoersten](https://github.com/LukeHoersten), [@mogad0n](https://github.com/mogad0n), r3m, [@RyanSquared](https://github.com/RyanSquared), savoyard, and [@wrmsr](https://github.com/wrmsr) for reporting issues and helping test, and to our translators for contributing translations.
This release includes changes to the config file format, including one breaking change: `timeout` is no longer an acceptable value of `accounts.nick-reservation.method`. (If you were using it, we suggest `strict` as a replacement.) All other changes to the config file format are backwards compatible and do not require updating before restart.
This release includes a database change. If you have `datastore.autoupgrade` set to `true` in your configuration, it will be automatically applied when you restart Oragono. Otherwise, you can update the database manually by running `oragono upgradedb` (see the manual for complete instructions).
### Removed
* Timeout-based nickname enforcement has been removed. We recommend `strict` as the default enforcement method. Users who configured `timeout` for their account will be upgraded to `strict`. With `accounts.login-via-pass-command` enabled, clients lacking support for SASL can authenticate via the `PASS` (server password command) by sending `account_name:account_password` as the server password. (#1027)
* Native support for LDAP has been removed. LDAP is now supported via the external [oragono-ldap](https://github.com/oragono/oragono-ldap) plugin; see its repository page for details. (#1142, #1107)
### Config changes
* Added `server.enforce-utf8`, controlling whether the server enforces that messages be valid UTF-8; a value of `true` for this is now the recommended default (#1151)
* Added `history.tagmsg-storage` for configuring which TAGMSG are stored in history; if this is not configured, TAGMSG will not be stored (#1172)
* All TLS certificate fingerprints in the config file are now named `certfp` instead of `fingerprint` (the old name of `fingerprint` is still accepted) (#1050, thanks [@RyanSquared](https://github.com/RyanSquared)!)
* Added `accounts.auth-script` section for configuring external authentication scripts (#1107, thanks [@daurnimator](https://github.com/daurnimator)!)
* Removed `accounts.ldap` section for configuring LDAP; LDAP is now available via the auth-script plugin interface (#1142)
* Added `defcon` operator capability, allowing use of the new `/DEFCON` command (#328)
* Default `awaylen`, `kicklen`, and `topiclen` limits now reflect the 512-character line limit (#1112, thanks [@k4bek4be](https://github.com/k4bek4be)!)
* Added `extjwt` section for configuring the EXTJWT extension (#948, #1136)
* `login-via-pass-command: true` is now a recommended default (#1186)
### Added
* Added support for [WHOX](https://github.com/ircv3/ircv3-specifications/issues/81), contributed by [@jesopo](https://github.com/jesopo) (#938, thanks!)
* Added support for tracking missed messages across multiple devices; see the "history" section of the manual for details (#843, thanks [@jwheare](https://github.com/jwheare) and [@wrmsr](https://github.com/wrmsr)!)
* Added `/NICKSERV SUSPEND` and `/NICKSERV UNSUSPEND` commands, allowing operators to suspend access to an abusive user account (#1135)
* Added support for external authentication systems, via subprocess ("auth-script") invocation (#1107, thanks [@daurnimator](https://github.com/daurnimator)!)
* Added the `/DEFCON` command, allowing operators to respond to spam or DoS attacks by disabling features at runtime without a rehash. (This feature requires that the operator have a newly defined capability, named `defcon`; this can be added to the appropriate oper blocks in the config file.) (#328, thanks [@bogdomania](https://github.com/bogdomania)!)
* Added support for the [EXTJWT](https://github.com/ircv3/ircv3-specifications/pull/341) draft extension, allowing Oragono to be integrated with other systems like Jitsi (#948, #1136)
* Services (NickServ, ChanServ, etc.) now respond to CTCP VERSION messages (#1055, thanks [@jesopo](https://github.com/jesopo)!)
* Added `BOT` ISUPPORT token, plus a `B` flag for bots in `352 RPL_WHOREPLY` (#1117)
* Added support for the `+T` no-CTCP user mode (#1007, thanks [@clukawski](https://github.com/clukawski)!)
* Added support for persisting the realname of always-on clients (#1065, thanks [@clukawski](https://github.com/clukawski)!)
* Added a warning on incorrect arguments to `/NICKSERV REGISTER` (#1179, thanks [@LukeHoersten](https://github.com/LukeHoersten)!)
* `/NICKSERV SET PASSWORD` now sends a warning (#1208)
### Fixed
* Fixed channels with only invisible users not being displayed in `/LIST` output (#1161, thanks [@bogdomania](https://github.com/bogdomania)!)
* Fixed `INVITE` not overriding a `+b` ban (#1168)
* Fixed incorrect `CHGHOST` lines during authentication with `/NICKSERV IDENTIFY` under some circumstances (#1108, thanks Geo-!)
* Fixed incorrect `CHGHOST` lines sent to users during connection registration (#1125, thanks [@jesopo](https://github.com/jesopo)!)
* Fixed a number of issues affecting the `znc.in/playback` capability, in particular restoring compatibility with Palaver (#1205, thanks [@kylef](https://github.com/kylef)!)
* Fixed interaction of auto-away with the regular `/AWAY` command (#1207)
* Fixed an incorrect interaction between always-on and `/NS SAREGISTER` (#1216)
* Fixed a race condition where nicknames of signed-out users could remain in the channel names list (#1166, thanks [@eskimo](https://github.com/eskimo)!)
* Fixed the last line of the MOTD being truncated in the absence of a terminating `\n` (#1167, thanks [@eskimo](https://github.com/eskimo)!)
* Fixed `away-notify` lines not being sent on channel JOIN (#1198, thanks savoyard!)
* Fixed incorrect source of some nickserv messages (#1185)
* Fixed idle time being updated on non-PRIVMSG commands (thanks r3m and [@happyhater](https://github.com/happyhater)!)
* Fixed `/NICKSERV UNREGISTER` and `/NICKSERV ERASE` not deleting stored user modes (#1157)
### Security
* Connections to an STS-only listener no longer reveal the exact server version or server creation time (#802, thanks [@csmith](https://github.com/csmith)!)
### Changed
* `/DLINE` now operates on individual client connections (#1135)
* When using the multiclient feature, each client now has its own independent `MONITOR` list (#1053, thanks [@ajaspers](https://github.com/ajaspers)!)
* `MONITOR L` now lists the nicknames in the form they were originally sent with `MONITOR +`, without casefolding (#1083)
* We now send the traditional `445 ERR_SUMMONDISABLED` and `446 ERR_USERSDISABLED` in response to the `SUMMON` and `USERS` commands (#1078, thanks [@KoraggKnightWolf](https://github.com/KoraggKnightWolf)!)
* RPL_ISUPPORT parameters with no values are now sent without an `=` (#1067, #1069, #1091, thanks [@KoraggKnightWolf](https://github.com/KoraggKnightWolf) and [@jesopo](https;//github.com/jesopo)!)
* TAGMSG storage is now controlled via the `history.tagmsg-storage` config block (#1172)
* `/NICKSERV CERT ADD` with no argument now adds the user's current TLS certificate fingerprint, when applicable (#1059, thanks [@emersonveenstra](https://github.com/emersonveenstra)!)
### Internal
* The config file containing recommended defaults is now named `default.yaml`, instead of `oragono.yaml` (#1130, thanks [@k4bek4be](https://github.com/k4bek4be)!)
* The output of the `/INFO` command now includes the full git hash, when applicable (#1105)
## [2.1.0] - 2020-06-01
We're pleased to announce Oragono 2.1.0, a new stable release.

View File

@ -12,7 +12,7 @@ Oragono vendors all its dependencies. Because of this, Oragono is self-contained
If you're upgrading the Go version used by Oragono, there are several places where it's hard-coded and must be changed:
1. `.travis.yml`, which controls the version that our CI test suite uses to build and test the code (e.g., for a PR)
2. `distrib/docker/Dockerfile`, which controls the version that the Oragono binaries in our Docker images are built with
2. `Dockerfile`, which controls the version that the Oragono binaries in our Docker images are built with
3. `go.mod`: this should be updated automatically by Go when you do module-related operations
@ -31,17 +31,26 @@ Develop branches are either used to work out implementation details in preperati
1. Run `irctest` over it to make sure nothing's severely broken. Talk to the maintainers to find out which version of irctest to run.
1. Run the `ircstress` chanflood benchmark to look for data races (enable race detection) and performance regressions (disable it).
1. Update the changelog with new changes and write release notes.
1. Update the version number `irc/constants.go` (either change `-unreleased` to `-rc1`, or remove `-rc1`, as appropriate).
1. Update the version number `irc/version.go` (either change `-unreleased` to `-rc1`, or remove `-rc1`, as appropriate).
1. Commit the new changelog and constants change.
1. Tag the release with `git tag v0.0.0 -m "Release v0.0.0"` (`0.0.0` replaced with the real ver number).
1. Tag the release with `git tag --sign v0.0.0 -m "Release v0.0.0"` (`0.0.0` replaced with the real ver number).
1. Build binaries using `make release`, upload release to Github including the changelog and binaries.
1. If it's a proper release (i.e. not an alpha/beta), merge the updates into the `stable` branch.
1. Make the appropriate announcements (Twitter, oragono.io/news)
1. Make the appropriate announcements:
* For a release candidate:
1. the channel topic
1. any operators who may be interested
* For a production release:
1. everything applicable to a release candidate
1. Twitter
1. oragono.io/news
1. ircv3.net support tables, if applicable
1. other social media?
Once it's built and released, you need to setup the new development version. To do so:
1. Ensure dependencies are up-to-date.
1. In `irc/constants.go`, update the version number to `0.0.1-unreleased`, where `0.0.1` is the previous release number with the minor field incremented by one (for instance, `0.9.2` -> `0.9.3-unreleased`).
1. Bump the version number in `irc/version.go`, typically by incrementing the second number in the 3-tuple, and add '-unreleased' (for instance, `2.2.0` -> `2.3.0-unreleased`).
1. Commit the new version number and changelog with the message `"Setup v0.0.1-unreleased devel ver"`.
**Unreleased changelog content**

View File

@ -1,5 +1,5 @@
## build Oragono
FROM golang:1.14-alpine AS build-env
FROM golang:1.15-alpine AS build-env
RUN apk add --no-cache git make curl sed
@ -10,8 +10,8 @@ ADD . /go/src/github.com/oragono/oragono/
# modify default config file so that it doesn't die on IPv6
# and so it can be exposed via 6667 by default
run sed -i 's/^\(\s*\)\"127.0.0.1:6667\":.*$/\1":6667":/' /go/src/github.com/oragono/oragono/oragono.yaml
run sed -i 's/^\s*\"\[::1\]:6667\":.*$//' /go/src/github.com/oragono/oragono/oragono.yaml
run sed -i 's/^\(\s*\)\"127.0.0.1:6667\":.*$/\1":6667":/' /go/src/github.com/oragono/oragono/default.yaml
run sed -i 's/^\s*\"\[::1\]:6667\":.*$//' /go/src/github.com/oragono/oragono/default.yaml
# make sure submodules are up-to-date
RUN git submodule update --init
@ -40,7 +40,7 @@ EXPOSE 6667/tcp 6697/tcp
RUN mkdir -p /ircd-bin
COPY --from=build-env /go/bin/oragono /ircd-bin
COPY --from=build-env /go/src/github.com/oragono/oragono/languages /ircd-bin/languages/
COPY --from=build-env /go/src/github.com/oragono/oragono/oragono.yaml /ircd-bin/oragono.yaml
COPY --from=build-env /go/src/github.com/oragono/oragono/default.yaml /ircd-bin/default.yaml
COPY distrib/docker/run.sh /ircd-bin/run.sh
RUN chmod +x /ircd-bin/run.sh

View File

@ -1,4 +1,4 @@
.PHONY: all install build release capdefs test smoke
.PHONY: all install build release capdefs test smoke gofmt
GIT_COMMIT := $(shell git rev-parse HEAD 2> /dev/null)
@ -34,5 +34,8 @@ test:
./.check-gofmt.sh
smoke:
oragono mkcerts --conf ./oragono.yaml || true
oragono run --conf ./oragono.yaml --smoke
oragono mkcerts --conf ./default.yaml || true
oragono run --conf ./default.yaml --smoke
gofmt:
./.check-gofmt.sh --fix

5
README
View File

@ -23,10 +23,9 @@ assorted IRCv3 support.
Copy the example config file to ircd.yaml with a command like:
$ cp oragono.yaml ircd.yaml
$ cp default.yaml ircd.yaml
Modify the config file as you like. In particular, the `connection-throttling` and
`connection-limits` sections are working looking over and tuning for your network's needs.
Modify the config file as you like.
To generate passwords for opers and connect passwords, you can use this command:

View File

@ -42,16 +42,17 @@ If you want to take a look at a running Oragono instance or test some client cod
* [IRCv3 support](https://ircv3.net/software/servers.html)
* a heavy focus on developing with [specifications](https://oragono.io/specs.html)
## Installation
## Quick start guide
To go through the standard installation, download the latest release from this page: https://github.com/oragono/oragono/releases/latest
Download the latest release from this page: https://github.com/oragono/oragono/releases/latest
Extract it into a folder, then run the following commands:
```sh
cp oragono.yaml ircd.yaml
vim ircd.yaml # modify the config file to your liking
cp default.yaml ircd.yaml
vim ircd.yaml # modify the config file to your liking
oragono mkcerts
oragono run # server should be ready to go!
```
**Note:** See the [productionizing guide in our manual](https://github.com/oragono/oragono/blob/master/docs/MANUAL.md#productionizing) for recommendations on how to run a production network, including obtaining valid TLS certificates.
@ -84,7 +85,7 @@ You'll need an [up-to-date distribution of the Go language for your OS and archi
## Configuration
The default config file [`oragono.yaml`](oragono.yaml) helps walk you through what each option means and changes. The configuration's intended to be sparse, so if there are options missing it's either because that feature isn't written/configurable yet or because we don't think it should be configurable.
The default config file [`default.yaml`](default.yaml) helps walk you through what each option means and changes.
You can use the `--conf` parameter when launching Oragono to control where it looks for the config file. For instance: `oragono run --conf /path/to/ircd.yaml`. The configuration file also stores where the log, database, certificate, and other files are opened. Normally, all these files use relative paths, but you can change them to be absolute (such as `/var/log/ircd.log`) when running Oragono as a service.
@ -102,14 +103,6 @@ oragono genpasswd
With this, you receive a blob of text which you can plug into your configuration file.
## Running
After this, running the server is easy! Simply run the below command and you should see the relevant startup information pop up.
```sh
oragono run
```
### How to register a channel
1. Register your account with `/NS REGISTER <password>`

View File

@ -18,7 +18,7 @@ server:
# This version of the config provides a public plaintext listener on
# port 6667 for testing and compatibility with legacy applications.
# We recommend disabling this listener in a production setting
# and replacing it with loopback-only listeners (see oragono.yaml):
# and replacing it with loopback-only listeners (see default.yaml):
":6667":
# The standard SSL/TLS port for IRC is 6697. This will listen on all interfaces:
@ -100,7 +100,7 @@ server:
# casemapping controls what kinds of strings are permitted as identifiers (nicknames,
# channel names, account names, etc.), and how they are normalized for case.
# with the recommended default of 'precis', utf-8 identifiers that are "sane"
# with the recommended default of 'precis', UTF8 identifiers that are "sane"
# (according to RFC 8265) are allowed, and the server additionally tries to protect
# against confusable characters ("homoglyph attacks").
# the other options are 'ascii' (traditional ASCII-only identifiers), and 'permissive',
@ -110,6 +110,11 @@ server:
# already up and running is problematic).
casemapping: "precis"
# enforce-utf8 controls whether the server allows non-UTF8 bytes in messages
# (as in traditional IRC) or preemptively discards non-UTF8 messages (since
# they cannot be relayed to websocket clients).
enforce-utf8: true
# whether to look up user hostnames with reverse DNS.
# (disabling this will expose user IPs instead of hostnames;
# to make IP/hostname information private, see the ip-cloaking section)
@ -122,6 +127,10 @@ server:
# use ident protocol to get usernames
check-ident: true
# ignore the supplied user/ident string from the USER command; always set the value to
# `~user` (literally) instead. this can potentially reduce confusion and simplify bans.
suppress-ident: false
# password to login to the server
# generated using "oragono genpasswd"
#password: ""
@ -162,7 +171,7 @@ server:
-
# SHA-256 fingerprint of the TLS certificate the gateway must use to connect
# (comment this out to use passwords only)
fingerprint: "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
certfp: "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
# password the gateway uses to connect, made with oragono genpasswd
password: "$2a$04$abcdef0123456789abcdef0123456789abcdef0123456789abcde"
@ -344,7 +353,7 @@ accounts:
skip-server-password: false
# enable login to accounts via the PASS command, e.g., PASS account:password
# this is sometimes useful for compatibility with old clients that don't support SASL
# this is useful for compatibility with old clients that don't support SASL
login-via-pass-command: false
# require-sasl controls whether clients are required to have accounts
@ -367,8 +376,6 @@ accounts:
additional-nick-limit: 2
# method describes how nickname reservation is handled
# timeout: let the user change to the registered nickname, give them X seconds
# to login and then rename them if they haven't done so
# strict: don't let the user change to the registered nickname unless they're
# already logged-in using SASL or NickServ
# optional: no enforcement by default, but allow users to opt in to
@ -382,9 +389,6 @@ accounts:
# to opt out of strict enforcement
allow-custom-enforcement: true
# rename-timeout - this is how long users have 'til they're renamed
rename-timeout: 30s
# format for guest nicknames:
# 1. these nicknames cannot be registered or reserved
# 2. if a client is automatically renamed by the server,
@ -459,51 +463,12 @@ accounts:
# before they can request a new one.
cooldown: 168h
# vhosts that users can take without approval, using `/HS TAKE`
offer-list:
#- "oragono.test"
# modes that are set by default when a user connects
# if unset, no user modes will be set by default
# +i is invisible (a user's channels are hidden from whois replies)
# see /QUOTE HELP umodes for more user modes
# default-user-modes: +i
# support for deferring password checking to an external LDAP server
# you should probably ignore this section! consult the grafana docs for details:
# https://grafana.com/docs/grafana/latest/auth/ldap/
# you will probably want to set require-sasl and disable accounts.registration.enabled
# ldap:
# enabled: true
# # should we automatically create users if their LDAP login succeeds?
# autocreate: true
# # example configuration that works with Forum Systems's testing server:
# # https://www.forumsys.com/tutorials/integration-how-to/ldap/online-ldap-test-server/
# host: "ldap.forumsys.com"
# port: 389
# timeout: 30s
# # example "single-bind" configuration, where we bind directly to the user's entry:
# bind-dn: "uid=%s,dc=example,dc=com"
# # example "admin bind" configuration, where we bind to an initial admin user,
# # then search for the user's entry with a search filter:
# #search-base-dns:
# # - "dc=example,dc=com"
# #bind-dn: "cn=read-only-admin,dc=example,dc=com"
# #bind-password: "password"
# #search-filter: "(uid=%s)"
# # example of requiring that users be in a particular group
# # (note that this is an OR over the listed groups, not an AND):
# #require-groups:
# # - "ou=mathematicians,dc=example,dc=com"
# #group-search-filter-user-attribute: "dn"
# #group-search-filter: "(uniqueMember=%s)"
# #group-search-base-dns:
# # - "dc=example,dc=com"
# # example of group membership testing via user attributes, as in AD
# # or with OpenLDAP's "memberOf overlay" (overrides group-search-filter):
# attributes:
# member-of: "memberOf"
# pluggable authentication mechanism, via subprocess invocation
# see the manual for details on how to write an authentication plugin script
auth-script:
@ -597,6 +562,7 @@ oper-classes:
- "chanreg"
- "history"
- "relaymsg-anywhere"
- "defcon"
# ircd operators
opers:
@ -623,7 +589,7 @@ opers:
# if a SHA-256 certificate fingerprint is configured here, then it will be
# required to /OPER. if you comment out the password hash above, then you can
# /OPER without a password.
#fingerprint: "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
#certfp: "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
# if 'auto' is set (and no password hash is set), operator permissions will be
# granted automatically as soon as you connect with the right fingerprint.
#auto: true
@ -793,6 +759,24 @@ roleplay:
# add the real nickname, in parentheses, to the end of every roleplay message?
add-suffix: true
# external services can integrate with the ircd using JSON Web Tokens (https://jwt.io).
# in effect, the server can sign a token attesting that the client is present on
# the server, is a member of a particular channel, etc.
extjwt:
# # default service config (for `EXTJWT #channel`).
# # expiration time for the token:
# expiration: 45s
# # you can configure tokens to be signed either with HMAC and a symmetric secret:
# secret: "65PHvk0K1_sM-raTsCEhatVkER_QD8a0zVV8gG2EWcI"
# # or with an RSA private key:
# #rsa-private-key-file: "extjwt.pem"
# # named services (for `EXTJWT #channel service_name`):
# services:
# "jitsi":
# expiration: 30s
# secret: "qmamLKDuOzIzlO8XqsGGewei_At11lewh6jtKfSTbkg"
# history message storage: this is used by CHATHISTORY, HISTORY, znc.in/playback,
# various autoreplay features, and the resume extension
history:
@ -844,7 +828,9 @@ history:
# users to do session resumption / query history after disconnections.
grace-period: 1h
# options to store history messages in a persistent database (currently only MySQL):
# options to store history messages in a persistent database (currently only MySQL).
# in order to enable any of this functionality, you must configure a MySQL server
# in the `datastore.mysql` section.
persistent:
enabled: false
@ -873,3 +859,18 @@ history:
# allowing deletion of JSON export of an account's messages. this
# may be needed for compliance with data privacy regulations.
enable-account-indexing: false
# options to control storage of TAGMSG
tagmsg-storage:
# by default, should TAGMSG be stored?
default: false
# if `default` is false, store TAGMSG containing any of these tags:
whitelist:
- "+draft/react"
- "react"
# if `default` is true, don't store TAGMSG containing any of these tags:
#blacklist:
# - "+draft/typing"
# - "typing"

View File

@ -126,7 +126,7 @@ server:
# casemapping controls what kinds of strings are permitted as identifiers (nicknames,
# channel names, account names, etc.), and how they are normalized for case.
# with the recommended default of 'precis', utf-8 identifiers that are "sane"
# with the recommended default of 'precis', UTF8 identifiers that are "sane"
# (according to RFC 8265) are allowed, and the server additionally tries to protect
# against confusable characters ("homoglyph attacks").
# the other options are 'ascii' (traditional ASCII-only identifiers), and 'permissive',
@ -136,10 +136,16 @@ server:
# already up and running is problematic).
casemapping: "precis"
# whether to look up user hostnames with reverse DNS.
# (disabling this will expose user IPs instead of hostnames;
# to make IP/hostname information private, see the ip-cloaking section)
lookup-hostnames: true
# enforce-utf8 controls whether the server allows non-UTF8 bytes in messages
# (as in traditional IRC) or preemptively discards non-UTF8 messages (since
# they cannot be relayed to websocket clients).
enforce-utf8: true
# whether to look up user hostnames with reverse DNS. there are 3 possibilities:
# 1. lookup-hostnames enabled, IP cloaking disabled; users will see each other's hostnames
# 2. lookup-hostnames disabled, IP cloaking disabled; users will see each other's numeric IPs
# 3. [the default] IP cloaking enabled; users will see cloaked hostnames
lookup-hostnames: false
# whether to confirm hostname lookups using "forward-confirmed reverse DNS", i.e., for
# any hostname returned from reverse DNS, resolve it back to an IP address and reject it
# unless it matches the connecting IP
@ -148,6 +154,10 @@ server:
# use ident protocol to get usernames
check-ident: false
# ignore the supplied user/ident string from the USER command; always set the value to
# `~user` (literally) instead. this can potentially reduce confusion and simplify bans.
suppress-ident: false
# password to login to the server
# generated using "oragono genpasswd"
#password: ""
@ -188,7 +198,7 @@ server:
-
# SHA-256 fingerprint of the TLS certificate the gateway must use to connect
# (comment this out to use passwords only)
fingerprint: "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
certfp: "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
# password the gateway uses to connect, made with oragono genpasswd
password: "$2a$04$abcdef0123456789abcdef0123456789abcdef0123456789abcde"
@ -266,6 +276,7 @@ server:
# DNS, users see fake domain names like pwbs2ui4377257x8.oragono. These names are
# generated deterministically from the underlying IP address, but if the underlying
# IP is not already known, it is infeasible to recover it from the cloaked name.
# If you disable this, you should probably enable lookup-hostnames in its place.
ip-cloaking:
# whether to enable IP cloaking
enabled: true
@ -370,8 +381,8 @@ accounts:
skip-server-password: false
# enable login to accounts via the PASS command, e.g., PASS account:password
# this is sometimes useful for compatibility with old clients that don't support SASL
login-via-pass-command: false
# this is useful for compatibility with old clients that don't support SASL
login-via-pass-command: true
# require-sasl controls whether clients are required to have accounts
# (and sign into them using SASL) to connect to the server
@ -393,8 +404,6 @@ accounts:
additional-nick-limit: 2
# method describes how nickname reservation is handled
# timeout: let the user change to the registered nickname, give them X seconds
# to login and then rename them if they haven't done so
# strict: don't let the user change to the registered nickname unless they're
# already logged-in using SASL or NickServ
# optional: no enforcement by default, but allow users to opt in to
@ -408,9 +417,6 @@ accounts:
# to opt out of strict enforcement
allow-custom-enforcement: false
# rename-timeout - this is how long users have 'til they're renamed
rename-timeout: 30s
# format for guest nicknames:
# 1. these nicknames cannot be registered or reserved
# 2. if a client is automatically renamed by the server,
@ -485,51 +491,12 @@ accounts:
# before they can request a new one.
cooldown: 168h
# vhosts that users can take without approval, using `/HS TAKE`
offer-list:
#- "oragono.test"
# modes that are set by default when a user connects
# if unset, no user modes will be set by default
# +i is invisible (a user's channels are hidden from whois replies)
# see /QUOTE HELP umodes for more user modes
default-user-modes: +i
# support for deferring password checking to an external LDAP server
# you should probably ignore this section! consult the grafana docs for details:
# https://grafana.com/docs/grafana/latest/auth/ldap/
# you will probably want to set require-sasl and disable accounts.registration.enabled
# ldap:
# enabled: true
# # should we automatically create users if their LDAP login succeeds?
# autocreate: true
# # example configuration that works with Forum Systems's testing server:
# # https://www.forumsys.com/tutorials/integration-how-to/ldap/online-ldap-test-server/
# host: "ldap.forumsys.com"
# port: 389
# timeout: 30s
# # example "single-bind" configuration, where we bind directly to the user's entry:
# bind-dn: "uid=%s,dc=example,dc=com"
# # example "admin bind" configuration, where we bind to an initial admin user,
# # then search for the user's entry with a search filter:
# #search-base-dns:
# # - "dc=example,dc=com"
# #bind-dn: "cn=read-only-admin,dc=example,dc=com"
# #bind-password: "password"
# #search-filter: "(uid=%s)"
# # example of requiring that users be in a particular group
# # (note that this is an OR over the listed groups, not an AND):
# #require-groups:
# # - "ou=mathematicians,dc=example,dc=com"
# #group-search-filter-user-attribute: "dn"
# #group-search-filter: "(uniqueMember=%s)"
# #group-search-base-dns:
# # - "dc=example,dc=com"
# # example of group membership testing via user attributes, as in AD
# # or with OpenLDAP's "memberOf overlay" (overrides group-search-filter):
# attributes:
# member-of: "memberOf"
# pluggable authentication mechanism, via subprocess invocation
# see the manual for details on how to write an authentication plugin script
auth-script:
@ -623,6 +590,7 @@ oper-classes:
- "chanreg"
- "history"
- "relaymsg-anywhere"
- "defcon"
# ircd operators
opers:
@ -649,7 +617,7 @@ opers:
# if a SHA-256 certificate fingerprint is configured here, then it will be
# required to /OPER. if you comment out the password hash above, then you can
# /OPER without a password.
#fingerprint: "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
#certfp: "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
# if 'auto' is set (and no password hash is set), operator permissions will be
# granted automatically as soon as you connect with the right fingerprint.
#auto: true
@ -819,6 +787,24 @@ roleplay:
# add the real nickname, in parentheses, to the end of every roleplay message?
add-suffix: true
# external services can integrate with the ircd using JSON Web Tokens (https://jwt.io).
# in effect, the server can sign a token attesting that the client is present on
# the server, is a member of a particular channel, etc.
extjwt:
# # default service config (for `EXTJWT #channel`).
# # expiration time for the token:
# expiration: 45s
# # you can configure tokens to be signed either with HMAC and a symmetric secret:
# secret: "65PHvk0K1_sM-raTsCEhatVkER_QD8a0zVV8gG2EWcI"
# # or with an RSA private key:
# #rsa-private-key-file: "extjwt.pem"
# # named services (for `EXTJWT #channel service_name`):
# services:
# "jitsi":
# expiration: 30s
# secret: "qmamLKDuOzIzlO8XqsGGewei_At11lewh6jtKfSTbkg"
# history message storage: this is used by CHATHISTORY, HISTORY, znc.in/playback,
# various autoreplay features, and the resume extension
history:
@ -870,7 +856,9 @@ history:
# users to do session resumption / query history after disconnections.
grace-period: 1h
# options to store history messages in a persistent database (currently only MySQL):
# options to store history messages in a persistent database (currently only MySQL).
# in order to enable any of this functionality, you must configure a MySQL server
# in the `datastore.mysql` section.
persistent:
enabled: false
@ -899,3 +887,18 @@ history:
# allowing deletion of JSON export of an account's messages. this
# may be needed for compliance with data privacy regulations.
enable-account-indexing: false
# options to control storage of TAGMSG
tagmsg-storage:
# by default, should TAGMSG be stored?
default: false
# if `default` is false, store TAGMSG containing any of these tags:
whitelist:
- "+draft/react"
- "react"
# if `default` is true, don't store TAGMSG containing any of these tags:
#blacklist:
# - "+draft/typing"
# - "typing"

View File

@ -8,9 +8,6 @@ The `latest` tag tracks the `stable` branch of Oragono, which contains
the latest stable release. The `dev` tag tracks the master branch, which
may by unstable and is not recommended for production.
You can see other tags [on Docker Hub](https://hub.docker.com/r/oragono/oragono/tags)
if you wish to run a specific version of Oragono.
## Quick start
The Oragono docker image is designed to work out of the box - it comes with a
@ -104,6 +101,6 @@ If you wish to manually build the docker image, you need to do so from
the root of the Oragono repository (not the `distrib/docker` directory):
```shell
docker build -f distrib/docker/Dockerfile .
docker build .
```

View File

@ -5,7 +5,7 @@ cd /ircd
# make config file
if [ ! -f "/ircd/ircd.yaml" ]; then
awk '{gsub(/path: languages/,"path: /ircd-bin/languages")}1' /ircd-bin/oragono.yaml > /tmp/ircd.yaml
awk '{gsub(/path: languages/,"path: /ircd-bin/languages")}1' /ircd-bin/default.yaml > /tmp/ircd.yaml
# change default oper passwd
OPERPASS=$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c20)

View File

@ -35,6 +35,7 @@ _Copyright © Daniel Oaks <daniel@danieloaks.net>, Shivaram Lingamneni <slingamn
- Multiclient ("Bouncer")
- History
- IP cloaking
- Moderation
- Frequently Asked Questions
- IRC over TLS
- Modes
@ -78,7 +79,7 @@ In addition to its unique features (integrated services and bouncer, comprehensi
We believe Oragono should scale comfortably to 10,000 clients and 2,000 clients per channel, making it suitable for small to medium-sized teams and communities. Oragono does not currently support server-to-server linking (federation), meaning that all clients must connect to the same instance. However, since Oragono is implemented in Go, it is reasonably effective at distributing work across multiple cores on a single server; in other words, it should "scale up" rather than "scaling out". (Federation is [planned](https://github.com/oragono/oragono/issues/26) but is not scheduled for development in the near term.)
Even though it runs as a single instance, Oragono can be deployed for high availability (i.e., with no single point of failure) using Kubernetes. This technique uses a k8s [LoadBalancer](https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/) to receive external traffic and a [Volume](https://kubernetes.io/docs/concepts/storage/volumes/) to store the embedded database file.
Even though it runs as a single instance, Oragono can be deployed for high availability (i.e., with no single point of failure) using Kubernetes. This technique uses a k8s [LoadBalancer](https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/) to receive external traffic and a [Volume](https://kubernetes.io/docs/concepts/storage/volumes/) to store the embedded database file. See [Hashbang's implementation](https://github.com/hashbang/gitops/tree/master/ircd) for a "worked example".
If you're interested in deploying Oragono at scale or for high availability, or want performance tuning advice, come find us on [`#oragono` on freenode](ircs://irc.freenode.net:6697/#oragono), we're very interested in what our software can do!
@ -97,7 +98,7 @@ To get started with Oragono on Windows:
1. Make sure you have the [latest release](https://github.com/oragono/oragono/releases/latest) downloaded.
1. Extract the zip file to a folder.
1. Copy and rename `oragono.yaml` to `ircd.yaml`.
1. Copy and rename `default.yaml` to `ircd.yaml`.
1. Open up `ircd.yaml` using any text editor, and then save it once you're happy.
1. Open up a `cmd.exe` window, then `cd` to where you have Oragono extracted.
1. Run `oragono.exe mkcerts` if you want to generate new self-signed SSL/TLS certificates (note that you can't enable STS if you use self-signed certs).
@ -111,7 +112,7 @@ To get started with Oragono on macOS, Linux, or on a Raspberry Pi:
1. Make sure you have the [latest release](https://github.com/oragono/oragono/releases/latest) for your OS/distro downloaded.
1. Extract the tar.gz file to a folder.
1. Copy and rename `oragono.yaml` to `ircd.yaml`.
1. Copy and rename `default.yaml` to `ircd.yaml`.
1. Open up `ircd.yaml` using any text editor, and then save it once you're happy.
1. Open up a Terminal window, then `cd` to where you have Oragono extracted.
1. Run `./oragono mkcerts` if you want to generate new self-signed SSL/TLS certificates (note that you can't enable STS if you use self-signed certs).
@ -347,7 +348,11 @@ Unfortunately, client support for history playback is still patchy. In descendin
1. The [IRCv3 chathistory specification](https://github.com/ircv3/ircv3-specifications/pull/393/) offers the most fine-grained control over history replay. It is supported by [Kiwi IRC](https://github.com/kiwiirc/kiwiirc), and hopefully other clients soon.
1. We emulate the [ZNC playback module](https://wiki.znc.in/Playback) for clients that support it. You may need to enable support for it explicitly in your client (see the "ZNC" section below).
1. If you are not using the multiclient functionality, but your client is set to be always-on (see the previous section for details), Oragono will remember the last time your client signed out. You can then set your account to replay only messages you missed with `/msg NickServ set autoreplay-missed on`. Unfortunately, this feature will only work reliably if you are *not* using the multiclient functionality described in the above section --- you must be connecting with at most one client at a time.
1. If you set your client to always-on (see the previous section for details), you can set a "device ID" for each device you use. Oragono will then remember the last time your device was present on the server, and each time you sign on, it will attempt to replay exactly those messages you missed. There are a few ways to set your device ID when connecting:
- You can add it to your SASL username with an `@`, e.g., if your SASL username is `alice` you can send `alice@phone`
- You can add it in a similar way to your IRC protocol username ("ident"), e.g., `alice@phone`
- If login to user accounts via the `PASS` command is enabled on the server, you can provide it there, e.g., by sending `alice@phone:hunter2` as the server password
1. If you only have one device, you can set your client to be always-on and furthermore `/msg NickServ set autoreplay-missed true`. This will replay missed messages, with the caveat that you must be connecting with at most one client at a time.
1. You can manually request history using `/history #channel 1h` (the parameter is either a message count or a time duration). (Depending on your client, you may need to use `/QUOTE history` instead.)
1. You can autoreplay a fixed number of lines (e.g., 25) each time you join a channel using `/msg NickServ set autoreplay-lines 25`.
@ -362,6 +367,26 @@ Oragono supports cloaking, which is enabled by default (via the `server.ip-cloak
Setting `server.ip-cloaking.num-bits` to 0 gives users cloaks that don't depend on their IP address information at all, which is an option for deployments where privacy is a more pressing concern than abuse. Holders of registered accounts can also use the vhost system (for details, `/msg HostServ HELP`.)
## Moderation
Oragono's multiclient and always-on features mean that moderation (at the server operator level) requires different techniques than a traditional IRC network. Server operators have three principal tools for moderation:
1. `/NICKSERV SUSPEND`, which disables a user account and disconnects all associated clients
2. `/DLINE ANDKILL`, which bans an IP or CIDR and disconnects clients
3. `/DEFCON`, which can impose emergency restrictions on user activity in response to attacks
See the `/HELP` (or `/HELPOP`) entries for these commands for more information, but here's a rough workflow for mitigating spam or other attacks:
1. Subscribe to the `a` snomask to monitor for abusive registration attempts (this is set automatically in the default operator config, but can be added manually with `/mode mynick +s u`)
2. Given abusive traffic from a nickname, identify whether they are using an account (this should be displayed in `/WHOIS` output)
3. If they are using an account, suspend the account with `/NICKSERV SUSPEND`, which will disconnect them
4. If they are not using an account, or if they're spamming new registrations from an IP, determine the IP (either from `/WHOIS` or from account registration notices) and temporarily `/DLINE` their IP
5. When facing a flood of abusive registrations that cannot be stemmed with `/DLINE`, use `/DEFCON 4` to temporarily restrict registrations. (At `/DEFCON 2`, all new connections to the server will require SASL, but this will likely be disruptive to legitimate users as well.)
For channel operators, as opposed to server operators, most traditional moderation tools should be effective. In particular, bans on cloaked hostnames (e.g., `/mode #chan +b *!*@98rgwnst3dahu.my.network`) should work as expected. With `force-nick-equals-account` enabled, channel operators can also ban nicknames (with `/mode #chan +b nick`, which Oragono automatically expands to `/mode #chan +b nick!*@*` as a way of banning an account.)
-------------------------------------------------------------------------------------------

12
go.mod
View File

@ -1,14 +1,14 @@
module github.com/oragono/oragono
go 1.14
go 1.15
require (
code.cloudfoundry.org/bytefmt v0.0.0-20200131002437-cf55d5288a48
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
github.com/go-ldap/ldap/v3 v3.1.10
github.com/go-sql-driver/mysql v1.5.0
github.com/go-test/deep v1.0.6 // indirect
github.com/gorilla/websocket v1.4.2
github.com/goshuirc/e-nfa v0.0.0-20160917075329-7071788e3940 // indirect
github.com/goshuirc/irc-go v0.0.0-20200311142257-57fd157327ac
github.com/onsi/ginkgo v1.12.0 // indirect
github.com/onsi/gomega v1.9.0 // indirect
@ -17,8 +17,8 @@ require (
github.com/stretchr/testify v1.4.0 // indirect
github.com/tidwall/buntdb v1.1.2
github.com/toorop/go-dkim v0.0.0-20200526084421-76378ae5207e
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073
golang.org/x/sys v0.0.0-20200523222454-059865788121 // indirect
golang.org/x/text v0.3.2
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f // indirect
golang.org/x/text v0.3.3
gopkg.in/yaml.v2 v2.3.0
)

45
go.sum
View File

@ -1,43 +1,28 @@
code.cloudfoundry.org/bytefmt v0.0.0-20190819182555-854d396b647c h1:2RuXx1+tSNWRjxhY0Bx52kjV2odJQ0a6MTbfTPhGAkg=
code.cloudfoundry.org/bytefmt v0.0.0-20190819182555-854d396b647c/go.mod h1:wN/zk7mhREp/oviagqUXY3EwuHhWyOvAdsn5Y4CzOrc=
code.cloudfoundry.org/bytefmt v0.0.0-20200131002437-cf55d5288a48 h1:/EMHruHCFXR9xClkGV/t0rmHrdhX4+trQUcBqjwc9xE=
code.cloudfoundry.org/bytefmt v0.0.0-20200131002437-cf55d5288a48/go.mod h1:wN/zk7mhREp/oviagqUXY3EwuHhWyOvAdsn5Y4CzOrc=
github.com/DanielOaks/go-idn v0.0.0-20160120021903-76db0e10dc65/go.mod h1:GYIaL2hleNQvfMUBTes1Zd/lDTyI/p2hv3kYB4jssyU=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/go-asn1-ber/asn1-ber v1.3.1 h1:gvPdv/Hr++TRFCl0UbPFHC54P9N9jgsRPnmnr419Uck=
github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-ldap/ldap/v3 v3.1.6 h1:VTihvB7egSAvU6KOagaiA/EvgJMR2jsjRAVIho2ydBo=
github.com/go-ldap/ldap/v3 v3.1.6/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q=
github.com/go-ldap/ldap/v3 v3.1.7 h1:aHjuWTgZsnxjMgqzx0JHwNqz4jBYZTcNarbPFkW1Oww=
github.com/go-ldap/ldap/v3 v3.1.7/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q=
github.com/go-ldap/ldap/v3 v3.1.10 h1:7WsKqasmPThNvdl0Q5GPpbTDD/ZD98CfuawrMIuh7qQ=
github.com/go-ldap/ldap/v3 v3.1.10/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-test/deep v1.0.6 h1:UHSEyLZUwX9Qoi99vVwvewiMC8mM2bf7XEM2nqvzEn8=
github.com/go-test/deep v1.0.6/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8=
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/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
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/goshuirc/irc-go v0.0.0-20190713001546-05ecc95249a0 h1:unxsR0de0MIS708eZI7lKa6HiP8FS0PhGCWwwEt9+vQ=
github.com/goshuirc/irc-go v0.0.0-20190713001546-05ecc95249a0/go.mod h1:rhIkxdehxNqK9iwJXWzQnxlGuuUR4cHu7PN64VryKXk=
github.com/goshuirc/irc-go v0.0.0-20200311142257-57fd157327ac h1:0JSojWrghcpK9/wx1RpV9Bv2d+3TbBWtHWubKjU2tao=
github.com/goshuirc/irc-go v0.0.0-20200311142257-57fd157327ac/go.mod h1:BRnLblzpqH2T5ANCODHBZLytz0NZN2KaMJ+di8oh3EM=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU=
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
@ -46,8 +31,6 @@ github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg=
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/oragono/confusables v0.0.0-20190624102032-fe1cf31a24b0 h1:4qw57EiWD2MhnmXoQus2ClSyPpGRd8/UxcAmmNe2FCg=
github.com/oragono/confusables v0.0.0-20190624102032-fe1cf31a24b0/go.mod h1:+uesPRay9e5tW6zhw4CJkRV3QOEbbZIJcsuo9ZnC+hE=
github.com/oragono/go-ident v0.0.0-20170110123031-337fed0fd21a h1:tZApUffT5QuX4XhJLz2KfBJT8JgdwjLUBWtvmRwgFu4=
github.com/oragono/go-ident v0.0.0-20170110123031-337fed0fd21a/go.mod h1:r5Fk840a4eu3ii1kxGDNSJupQu9Z1UC1nfJOZZXC24c=
github.com/oragono/go-ident v0.0.0-20200511222032-830550b1d775 h1:AMAsAn/i4AgsmWQYdMoze9omwtHpbxrKuT+AT1LmhtI=
github.com/oragono/go-ident v0.0.0-20200511222032-830550b1d775/go.mod h1:r5Fk840a4eu3ii1kxGDNSJupQu9Z1UC1nfJOZZXC24c=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@ -71,15 +54,15 @@ github.com/tidwall/rtree v0.0.0-20180113144539-6cd427091e0e h1:+NL1GDIUOKxVfbp2K
github.com/tidwall/rtree v0.0.0-20180113144539-6cd427091e0e/go.mod h1:/h+UnNGt0IhNNJLkGikcdcJqm66zGD/uJGMRxK/9+Ao=
github.com/tidwall/tinyqueue v0.0.0-20180302190814-1e39f5511563 h1:Otn9S136ELckZ3KKDyCkxapfufrqDqwmGjcHfAyXRrE=
github.com/tidwall/tinyqueue v0.0.0-20180302190814-1e39f5511563/go.mod h1:mLqSmt7Dv/CNneF2wfcChfN1rvapyQr01LGKnKex0DQ=
github.com/toorop/go-dkim v0.0.0-20191019073156-897ad64a2eeb h1:ilDZC+k9r67aJqSOalZLtEVLO7Cmmsq5ftfcvLirc24=
github.com/toorop/go-dkim v0.0.0-20191019073156-897ad64a2eeb/go.mod h1:BzWtXXrXzZUvMacR0oF/fbDDgUPO8L36tDMmRAf14ns=
github.com/toorop/go-dkim v0.0.0-20200526084421-76378ae5207e h1:uZTp+hhFm+PCH0t0Px5oE+QYlVTwVJ+XKNQr7ct4Q7w=
github.com/toorop/go-dkim v0.0.0-20200526084421-76378ae5207e/go.mod h1:BzWtXXrXzZUvMacR0oF/fbDDgUPO8L36tDMmRAf14ns=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708 h1:pXVtWnwHkrWD9ru3sDxY/qFK/bfc0egRovX91EjWjf4=
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 h1:DZhuSZLsGlFL4CmhA8BcRA0mnthyA/nZ00AqCUo7vHg=
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@ -87,16 +70,20 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6Zh
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e h1:N7DeIrjYszNmSW409R3frPPwglRwMkXSBzwVbkOjLLA=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121 h1:rITEj+UZHYC927n8GT97eC3zrpzXdb/voyeOuVKS46o=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c h1:UIcGWL6/wpCfyGuJnRFJRurA+yj8RrW7Q6x2YMCXt6c=
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f h1:Fqb3ao1hUmOR3GkUOg/Y+BadLwykBIzs5q8Ez2SbHyc=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -108,9 +95,5 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/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=

View File

@ -17,7 +17,6 @@ import (
"github.com/oragono/oragono/irc/connection_limits"
"github.com/oragono/oragono/irc/email"
"github.com/oragono/oragono/irc/ldap"
"github.com/oragono/oragono/irc/modes"
"github.com/oragono/oragono/irc/passwd"
"github.com/oragono/oragono/irc/utils"
@ -40,7 +39,8 @@ const (
keyAccountChannels = "account.channels %s" // channels registered to the account
keyAccountJoinedChannels = "account.joinedto %s" // channels a persistent client has joined
keyAccountLastSeen = "account.lastseen %s"
keyAccountModes = "account.modes %s" // user modes for the always-on client as a string
keyAccountModes = "account.modes %s" // user modes for the always-on client as a string
keyAccountRealname = "account.realname %s" // client realname stored as string
keyVHostQueueAcctToId = "vhostQueue %s"
vhostRequestIdx = "vhostQueue"
@ -128,7 +128,13 @@ func (am *AccountManager) createAlwaysOnClients(config *Config) {
account, err := am.LoadAccount(accountName)
if err == nil && account.Verified &&
persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, account.Settings.AlwaysOn) {
am.server.AddAlwaysOnClient(account, am.loadChannels(accountName), am.loadLastSeen(accountName), am.loadModes(accountName))
am.server.AddAlwaysOnClient(
account,
am.loadChannels(accountName),
am.loadLastSeen(accountName),
am.loadModes(accountName),
am.loadRealname(accountName),
)
}
}
}
@ -375,14 +381,14 @@ func (am *AccountManager) Register(client *Client, account string, callbackNames
return errAccountCreation
}
if restrictedCasefoldedNicks[casefoldedAccount] || restrictedSkeletons[skeleton] {
if restrictedCasefoldedNicks.Has(casefoldedAccount) || restrictedSkeletons.Has(skeleton) {
return errAccountAlreadyRegistered
}
config := am.server.Config()
// final "is registration allowed" check, probably redundant:
if !(config.Accounts.Registration.Enabled || callbackNamespace == "admin") {
// final "is registration allowed" check:
if !(config.Accounts.Registration.Enabled || callbackNamespace == "admin") || am.server.Defcon() <= 4 {
return errFeatureDisabled
}
@ -617,11 +623,12 @@ func (am *AccountManager) loadModes(account string) (uModes modes.Modes) {
return
}
func (am *AccountManager) saveLastSeen(account string, lastSeen time.Time) {
func (am *AccountManager) saveLastSeen(account string, lastSeen map[string]time.Time) {
key := fmt.Sprintf(keyAccountLastSeen, account)
var val string
if !lastSeen.IsZero() {
val = strconv.FormatInt(lastSeen.UnixNano(), 10)
if len(lastSeen) != 0 {
text, _ := json.Marshal(lastSeen)
val = string(text)
}
am.server.store.Update(func(tx *buntdb.Tx) error {
if val != "" {
@ -633,21 +640,41 @@ func (am *AccountManager) saveLastSeen(account string, lastSeen time.Time) {
})
}
func (am *AccountManager) loadLastSeen(account string) (lastSeen time.Time) {
func (am *AccountManager) loadLastSeen(account string) (lastSeen map[string]time.Time) {
key := fmt.Sprintf(keyAccountLastSeen, account)
var lsText string
am.server.store.Update(func(tx *buntdb.Tx) error {
lsText, _ = tx.Get(key)
// XXX clear this on startup, because it's not clear when it's
// going to be overwritten, and restarting the server twice in a row
// could result in a large amount of duplicated history replay
tx.Delete(key)
return nil
})
lsNum, err := strconv.ParseInt(lsText, 10, 64)
if err == nil {
return time.Unix(0, lsNum).UTC()
if lsText == "" {
return nil
}
err := json.Unmarshal([]byte(lsText), &lastSeen)
if err != nil {
return nil
}
return
}
func (am *AccountManager) saveRealname(account string, realname string) {
key := fmt.Sprintf(keyAccountRealname, account)
am.server.store.Update(func(tx *buntdb.Tx) error {
if realname != "" {
tx.Set(key, realname, nil)
} else {
tx.Delete(key)
}
return nil
})
}
func (am *AccountManager) loadRealname(account string) (realname string) {
key := fmt.Sprintf(keyAccountRealname, account)
am.server.store.Update(func(tx *buntdb.Tx) error {
realname, _ = tx.Get(key)
return nil
})
return
}
@ -864,14 +891,15 @@ func (am *AccountManager) Verify(client *Client, account string, code string) er
}
if client != nil {
am.Login(client, clientAccount)
}
_, method := am.EnforcementStatus(casefoldedAccount, skeleton)
if method != NickEnforcementNone {
currentClient := am.server.clients.Get(casefoldedAccount)
if currentClient == nil || currentClient == client || currentClient.Account() == casefoldedAccount {
return nil
if client.AlwaysOn() {
client.markDirty(IncludeRealname)
}
if method == NickEnforcementStrict {
}
// we may need to do nick enforcement here:
_, method := am.EnforcementStatus(casefoldedAccount, skeleton)
if method == NickEnforcementStrict {
currentClient := am.server.clients.Get(casefoldedAccount)
if currentClient != nil && currentClient != client && currentClient.Account() != casefoldedAccount {
am.server.RandomlyRename(currentClient)
}
}
@ -1052,6 +1080,10 @@ func (am *AccountManager) AuthenticateByPassphrase(client *Client, accountName s
}
}
if throttled, remainingTime := client.checkLoginThrottle(); throttled {
return &ThrottleError{remainingTime}
}
var account ClientAccount
defer func() {
@ -1061,24 +1093,13 @@ func (am *AccountManager) AuthenticateByPassphrase(client *Client, accountName s
}()
config := am.server.Config()
if config.Accounts.LDAP.Enabled {
ldapConf := am.server.Config().Accounts.LDAP
err = ldap.CheckLDAPPassphrase(ldapConf, accountName, passphrase, am.server.logger)
if err != nil {
account, err = am.loadWithAutocreation(accountName, ldapConf.Autocreate)
return
}
}
if config.Accounts.AuthScript.Enabled {
var output AuthScriptOutput
output, err = CheckAuthScript(config.Accounts.AuthScript,
AuthScriptInput{AccountName: accountName, Passphrase: passphrase, IP: client.IP().String()})
if err != nil {
am.server.logger.Error("internal", "failed shell auth invocation", err.Error())
return err
}
if output.Success {
} else if output.Success {
if output.AccountName != "" {
accountName = output.AccountName
}
@ -1231,6 +1252,69 @@ func (am *AccountManager) loadRawAccount(tx *buntdb.Tx, casefoldedAccount string
return
}
func (am *AccountManager) Suspend(accountName string) (err error) {
account, err := CasefoldName(accountName)
if err != nil {
return errAccountDoesNotExist
}
existsKey := fmt.Sprintf(keyAccountExists, account)
verifiedKey := fmt.Sprintf(keyAccountVerified, account)
err = am.server.store.Update(func(tx *buntdb.Tx) error {
_, err := tx.Get(existsKey)
if err != nil {
return errAccountDoesNotExist
}
_, err = tx.Delete(verifiedKey)
return err
})
if err == errAccountDoesNotExist {
return err
} else if err != nil {
am.server.logger.Error("internal", "couldn't persist suspension", account, err.Error())
} // keep going
am.Lock()
clients := am.accountToClients[account]
delete(am.accountToClients, account)
am.Unlock()
am.killClients(clients)
return nil
}
func (am *AccountManager) killClients(clients []*Client) {
for _, client := range clients {
client.Logout()
client.Quit(client.t("You are no longer authorized to be on this server"), nil)
client.destroy(nil)
}
}
func (am *AccountManager) Unsuspend(account string) (err error) {
cfaccount, err := CasefoldName(account)
if err != nil {
return errAccountDoesNotExist
}
existsKey := fmt.Sprintf(keyAccountExists, cfaccount)
verifiedKey := fmt.Sprintf(keyAccountVerified, cfaccount)
err = am.server.store.Update(func(tx *buntdb.Tx) error {
_, err := tx.Get(existsKey)
if err != nil {
return errAccountDoesNotExist
}
tx.Set(verifiedKey, "1", nil)
return nil
})
if err != nil {
return errAccountDoesNotExist
}
return nil
}
func (am *AccountManager) Unregister(account string, erase bool) error {
config := am.server.Config()
casefoldedAccount, err := CasefoldName(account)
@ -1253,8 +1337,12 @@ func (am *AccountManager) Unregister(account string, erase bool) error {
joinedChannelsKey := fmt.Sprintf(keyAccountJoinedChannels, casefoldedAccount)
lastSeenKey := fmt.Sprintf(keyAccountLastSeen, casefoldedAccount)
unregisteredKey := fmt.Sprintf(keyAccountUnregistered, casefoldedAccount)
modesKey := fmt.Sprintf(keyAccountModes, casefoldedAccount)
var clients []*Client
defer func() {
am.killClients(clients)
}()
var registeredChannels []string
// on our way out, unregister all the account's channels and delete them from the db
@ -1307,6 +1395,7 @@ func (am *AccountManager) Unregister(account string, erase bool) error {
tx.Delete(channelsKey)
tx.Delete(joinedChannelsKey)
tx.Delete(lastSeenKey)
tx.Delete(modesKey)
_, err := tx.Delete(vhostQueueKey)
am.decrementVHostQueueCount(casefoldedAccount, err)
@ -1347,12 +1436,6 @@ func (am *AccountManager) Unregister(account string, erase bool) error {
additionalSkel, _ := Skeleton(nick)
delete(am.skeletonToAccount, additionalSkel)
}
for _, client := range clients {
client.Logout()
client.Quit(client.t("You are no longer authorized to be on this server"), nil)
// destroy acquires a semaphore so we can't call it while holding a lock
go client.destroy(nil)
}
if err != nil && !erase {
return errAccountDoesNotExist
@ -1415,9 +1498,7 @@ func (am *AccountManager) AuthenticateByCertFP(client *Client, certfp, authzid s
AuthScriptInput{Certfp: certfp, IP: client.IP().String()})
if err != nil {
am.server.logger.Error("internal", "failed shell auth invocation", err.Error())
return err
}
if output.Success && output.AccountName != "" {
} else if output.Success && output.AccountName != "" {
clientAccount, err = am.loadWithAutocreation(output.AccountName, config.Accounts.AuthScript.Autocreate)
return
}
@ -1492,7 +1573,6 @@ func (am *AccountManager) ModifyAccountSettings(account string, munger settingsM
type VHostInfo struct {
ApprovedVHost string
Enabled bool
Forbidden bool
RequestedVHost string
RejectedVHost string
RejectionReason string
@ -1546,10 +1626,6 @@ func (am *AccountManager) VHostSet(account string, vhost string) (result VHostIn
func (am *AccountManager) VHostRequest(account string, vhost string, cooldown time.Duration) (result VHostInfo, err error) {
munger := func(input VHostInfo) (output VHostInfo, err error) {
output = input
if input.Forbidden {
err = errVhostsForbidden
return
}
// you can update your existing request, but if you were approved or rejected,
// you can't spam a new request
if output.RequestedVHost == "" {
@ -1568,32 +1644,6 @@ func (am *AccountManager) VHostRequest(account string, vhost string, cooldown ti
return am.performVHostChange(account, munger)
}
func (am *AccountManager) VHostTake(account string, vhost string, cooldown time.Duration) (result VHostInfo, err error) {
munger := func(input VHostInfo) (output VHostInfo, err error) {
output = input
if input.Forbidden {
err = errVhostsForbidden
return
}
// if you have a request pending, you can cancel it using take;
// otherwise, you're subject to the same throttling as if you were making a request
if output.RequestedVHost == "" {
err = output.checkThrottle(cooldown)
}
if err != nil {
return
}
output.ApprovedVHost = vhost
output.RequestedVHost = ""
output.RejectedVHost = ""
output.RejectionReason = ""
output.LastRequestTime = time.Now().UTC()
return
}
return am.performVHostChange(account, munger)
}
func (am *AccountManager) VHostApprove(account string) (result VHostInfo, err error) {
munger := func(input VHostInfo) (output VHostInfo, err error) {
output = input
@ -1633,16 +1683,6 @@ func (am *AccountManager) VHostSetEnabled(client *Client, enabled bool) (result
return am.performVHostChange(client.Account(), munger)
}
func (am *AccountManager) VHostForbid(account string, forbid bool) (result VHostInfo, err error) {
munger := func(input VHostInfo) (output VHostInfo, err error) {
output = input
output.Forbidden = forbid
return
}
return am.performVHostChange(account, munger)
}
func (am *AccountManager) performVHostChange(account string, munger vhostMunger) (result VHostInfo, err error) {
account, err = CasefoldName(account)
if err != nil || account == "" {
@ -1650,6 +1690,11 @@ func (am *AccountManager) performVHostChange(account string, munger vhostMunger)
return
}
if am.server.Defcon() <= 3 {
err = errFeatureDisabled
return
}
am.vHostUpdateMutex.Lock()
defer am.vHostUpdateMutex.Unlock()
@ -1758,12 +1803,12 @@ func (am *AccountManager) applyVHostInfo(client *Client, info VHostInfo) {
}
vhost := ""
if info.Enabled && !info.Forbidden {
if info.Enabled {
vhost = info.ApprovedVHost
}
oldNickmask := client.NickMaskString()
updated := client.SetVHost(vhost)
if updated {
if updated && client.Registered() {
// TODO: doing I/O here is kind of a kludge
client.sendChghost(oldNickmask, client.Hostname())
}

View File

@ -6,7 +6,6 @@
package irc
import (
"bytes"
"fmt"
"strconv"
"strings"
@ -447,7 +446,7 @@ func (channel *Channel) Names(client *Client, rb *ResponseBuffer) {
maxNamLen := 480 - len(client.server.name) - len(client.Nick())
var namesLines []string
var buffer bytes.Buffer
var buffer strings.Builder
if isJoined || !channel.flags.HasMode(modes.Secret) || isOper {
for _, target := range channel.Members() {
var nick string
@ -540,6 +539,13 @@ func (channel *Channel) ClientPrefixes(client *Client, isMultiPrefix bool) strin
}
}
func (channel *Channel) ClientStatus(client *Client) (present bool, cModes modes.Modes) {
channel.stateMutex.RLock()
defer channel.stateMutex.RUnlock()
modes, present := channel.members[client]
return present, modes.AllModes()
}
func (channel *Channel) ClientHasPrivsOver(client *Client, target *Client) bool {
channel.stateMutex.RLock()
founder := channel.registeredFounder
@ -640,7 +646,7 @@ func channelHistoryStatus(config *Config, registered bool, storedStatus HistoryS
}
func (channel *Channel) AddHistoryItem(item history.Item, account string) (err error) {
if !item.IsStorable() {
if !itemIsStorable(&item, channel.server.Config()) {
return
}
@ -654,7 +660,7 @@ func (channel *Channel) AddHistoryItem(item history.Item, account string) (err e
}
// Join joins the given client to this channel (if they can be joined).
func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *ResponseBuffer) {
func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *ResponseBuffer) error {
details := client.Details()
channel.stateMutex.RLock()
@ -670,39 +676,44 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
if alreadyJoined {
// no message needs to be sent
return
return nil
}
// the founder can always join (even if they disabled auto +q on join);
// anyone who automatically receives halfop or higher can always join
hasPrivs := isSajoin || (founder != "" && founder == details.account) || (persistentMode != 0 && persistentMode != modes.Voice)
// 0. SAJOIN always succeeds
// 1. the founder can always join (even if they disabled auto +q on join)
// 2. anyone who automatically receives halfop or higher can always join
// 3. people invited with INVITE can join
hasPrivs := isSajoin || (founder != "" && founder == details.account) ||
(persistentMode != 0 && persistentMode != modes.Voice) ||
client.CheckInvited(chcfname)
if !hasPrivs {
if limit != 0 && chcount >= limit {
return errLimitExceeded
}
if !hasPrivs && limit != 0 && chcount >= limit {
rb.Add(nil, client.server.name, ERR_CHANNELISFULL, details.nick, chname, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "l"))
return
if chkey != "" && !utils.SecretTokensMatch(chkey, key) {
return errWrongChannelKey
}
if channel.flags.HasMode(modes.InviteOnly) &&
!channel.lists[modes.InviteMask].Match(details.nickMaskCasefolded) {
return errInviteOnly
}
if channel.lists[modes.BanMask].Match(details.nickMaskCasefolded) &&
!channel.lists[modes.ExceptMask].Match(details.nickMaskCasefolded) &&
!channel.lists[modes.InviteMask].Match(details.nickMaskCasefolded) {
return errBanned
}
if details.account == "" &&
(channel.flags.HasMode(modes.RegisteredOnly) || channel.server.Defcon() <= 2) {
return errRegisteredOnly
}
}
if !hasPrivs && chkey != "" && !utils.SecretTokensMatch(chkey, key) {
rb.Add(nil, client.server.name, ERR_BADCHANNELKEY, details.nick, chname, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "k"))
return
}
isInvited := client.CheckInvited(chcfname) || channel.lists[modes.InviteMask].Match(details.nickMaskCasefolded)
if !hasPrivs && channel.flags.HasMode(modes.InviteOnly) && !isInvited {
rb.Add(nil, client.server.name, ERR_INVITEONLYCHAN, details.nick, chname, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "i"))
return
}
if !hasPrivs && channel.lists[modes.BanMask].Match(details.nickMaskCasefolded) &&
!isInvited &&
!channel.lists[modes.ExceptMask].Match(details.nickMaskCasefolded) {
rb.Add(nil, client.server.name, ERR_BANNEDFROMCHAN, details.nick, chname, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "b"))
return
}
if !hasPrivs && channel.flags.HasMode(modes.RegisteredOnly) && details.account == "" && !isInvited {
rb.Add(nil, client.server.name, ERR_NEEDREGGEDNICK, details.nick, chname, client.t("You must be registered to join that channel"))
return
if joinErr := client.addChannel(channel, rb == nil); joinErr != nil {
return joinErr
}
client.server.logger.Debug("join", fmt.Sprintf("%s joined channel %s", details.nick, chname))
@ -747,10 +758,8 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
channel.AddHistoryItem(histItem, details.account)
}
client.addChannel(channel, rb == nil)
if rb == nil {
return
return nil
}
var modestr string
@ -758,6 +767,7 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
modestr = fmt.Sprintf("+%v", givenMode)
}
isAway, awayMessage := client.Away()
for _, member := range channel.Members() {
for _, session := range member.Sessions() {
if session == rb.session {
@ -774,6 +784,9 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
if givenMode != 0 {
session.Send(nil, client.server.name, "MODE", chname, modestr, details.nick)
}
if isAway && session.capabilities.Has(caps.AwayNotify) {
session.sendFromClientInternal(false, time.Time{}, "", details.nickMask, details.accountName, nil, "AWAY", awayMessage)
}
}
}
@ -793,22 +806,26 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
rb.Flush(true)
channel.autoReplayHistory(client, rb, message.Msgid)
return nil
}
func (channel *Channel) autoReplayHistory(client *Client, rb *ResponseBuffer, skipMsgid string) {
// autoreplay any messages as necessary
var items []history.Item
hasAutoreplayTimestamps := false
var start, end time.Time
if rb.session.zncPlaybackTimes.ValidFor(channel.NameCasefolded()) {
hasAutoreplayTimestamps = true
start, end = rb.session.zncPlaybackTimes.start, rb.session.zncPlaybackTimes.end
} else if !rb.session.autoreplayMissedSince.IsZero() {
// we already checked for history caps in `playReattachMessages`
hasAutoreplayTimestamps = true
start = time.Now().UTC()
end = rb.session.autoreplayMissedSince
}
if !start.IsZero() || !end.IsZero() {
if hasAutoreplayTimestamps {
_, seq, _ := channel.server.GetHistorySequence(channel, client, "")
if seq != nil {
zncMax := channel.server.Config().History.ZNCMax
@ -1229,44 +1246,19 @@ func (channel *Channel) SendSplitMessage(command string, minPrefixMode modes.Mod
}
// send echo-message
if rb.session.capabilities.Has(caps.EchoMessage) {
var tagsToUse map[string]string
if rb.session.capabilities.Has(caps.MessageTags) {
tagsToUse = clientOnlyTags
}
if histType == history.Tagmsg && rb.session.capabilities.Has(caps.MessageTags) {
rb.AddFromClient(message.Time, message.Msgid, details.nickMask, details.accountName, tagsToUse, command, chname)
} else {
rb.AddSplitMessageFromClient(details.nickMask, details.accountName, tagsToUse, command, chname, message)
}
}
// send echo-message to other connected sessions
for _, session := range client.Sessions() {
if session == rb.session {
continue
}
var tagsToUse map[string]string
if session.capabilities.Has(caps.MessageTags) {
tagsToUse = clientOnlyTags
}
if histType == history.Tagmsg && session.capabilities.Has(caps.MessageTags) {
session.sendFromClientInternal(false, message.Time, message.Msgid, details.nickMask, details.accountName, tagsToUse, command, chname)
} else if histType != history.Tagmsg {
session.sendSplitMsgFromClientInternal(false, details.nickMask, details.accountName, tagsToUse, command, chname, message)
}
}
rb.addEchoMessage(clientOnlyTags, details.nickMask, details.accountName, command, chname, message)
for _, member := range channel.Members() {
// echo-message is handled above, so skip sending the msg to the user themselves as well
if member == client {
continue
}
if minPrefixMode != modes.Mode(0) && !channel.ClientIsAtLeast(member, minPrefixMode) {
// STATUSMSG
continue
}
for _, session := range member.Sessions() {
if session == rb.session {
continue // we already sent echo-message, if applicable
}
if isCTCP && session.isTor {
continue // #753
}
@ -1431,9 +1423,7 @@ func (channel *Channel) Invite(invitee *Client, inviter *Client, rb *ResponseBuf
return
}
if channel.flags.HasMode(modes.InviteOnly) {
invitee.Invite(channel.NameCasefolded())
}
invitee.Invite(channel.NameCasefolded())
for _, member := range channel.Members() {
if member == inviter || member == invitee || !channel.ClientIsAtLeast(member, modes.Halfop) {
@ -1450,7 +1440,14 @@ func (channel *Channel) Invite(invitee *Client, inviter *Client, rb *ResponseBuf
tnick := invitee.Nick()
rb.Add(nil, inviter.server.name, RPL_INVITING, cnick, tnick, chname)
invitee.Send(nil, inviter.NickMaskString(), "INVITE", tnick, chname)
if invitee.Away() {
rb.Add(nil, inviter.server.name, RPL_AWAY, cnick, tnick, invitee.AwayMessage())
if away, awayMessage := invitee.Away(); away {
rb.Add(nil, inviter.server.name, RPL_AWAY, cnick, tnick, awayMessage)
}
}
// data for RPL_LIST
func (channel *Channel) listData() (memberCount int, name, topic string) {
channel.stateMutex.RLock()
defer channel.stateMutex.RUnlock()
return len(channel.members), channel.name, channel.topic
}

View File

@ -5,6 +5,8 @@ package irc
import (
"sync"
"github.com/oragono/oragono/irc/utils"
)
type channelManagerEntry struct {
@ -23,17 +25,17 @@ type ChannelManager struct {
sync.RWMutex // tier 2
// chans is the main data structure, mapping casefolded name -> *Channel
chans map[string]*channelManagerEntry
chansSkeletons StringSet // skeletons of *unregistered* chans
registeredChannels StringSet // casefolds of registered chans
registeredSkeletons StringSet // skeletons of registered chans
purgedChannels StringSet // casefolds of purged chans
chansSkeletons utils.StringSet // skeletons of *unregistered* chans
registeredChannels utils.StringSet // casefolds of registered chans
registeredSkeletons utils.StringSet // skeletons of registered chans
purgedChannels utils.StringSet // casefolds of purged chans
server *Server
}
// NewChannelManager returns a new ChannelManager.
func (cm *ChannelManager) Initialize(server *Server) {
cm.chans = make(map[string]*channelManagerEntry)
cm.chansSkeletons = make(StringSet)
cm.chansSkeletons = make(utils.StringSet)
cm.server = server
cm.loadRegisteredChannels(server.Config())
@ -47,8 +49,8 @@ func (cm *ChannelManager) loadRegisteredChannels(config *Config) {
}
rawNames := cm.server.channelRegistry.AllChannels()
registeredChannels := make(StringSet, len(rawNames))
registeredSkeletons := make(StringSet, len(rawNames))
registeredChannels := make(utils.StringSet, len(rawNames))
registeredSkeletons := make(utils.StringSet, len(rawNames))
for _, name := range rawNames {
cfname, err := CasefoldChannel(name)
if err == nil {
@ -130,11 +132,11 @@ func (cm *ChannelManager) Join(client *Client, name string, key string, isSajoin
}
channel.EnsureLoaded()
channel.Join(client, key, isSajoin, rb)
err = channel.Join(client, key, isSajoin, rb)
cm.maybeCleanup(channel, true)
return nil
return err
}
func (cm *ChannelManager) maybeCleanup(channel *Channel, afterJoin bool) {
@ -187,6 +189,10 @@ func (cm *ChannelManager) Cleanup(channel *Channel) {
}
func (cm *ChannelManager) SetRegistered(channelName string, account string) (err error) {
if cm.server.Defcon() <= 4 {
return errFeatureDisabled
}
var channel *Channel
cfname, err := CasefoldChannel(channelName)
if err != nil {
@ -283,6 +289,14 @@ func (cm *ChannelManager) Rename(name string, newName string) (err error) {
cm.Lock()
defer cm.Unlock()
if newCfname == cfname {
entry := cm.chans[cfname]
if entry == nil || !entry.channel.IsLoaded() {
return errNoSuchChannel
}
entry.channel.Rename(newName, cfname)
return nil
}
if cm.chans[newCfname] != nil || cm.registeredChannels.Has(newCfname) {
return errChannelNameInUse
}

View File

@ -4,15 +4,16 @@
package irc
import (
"encoding/json"
"fmt"
"strconv"
"strings"
"time"
"encoding/json"
"github.com/tidwall/buntdb"
"github.com/oragono/oragono/irc/modes"
"github.com/tidwall/buntdb"
"github.com/oragono/oragono/irc/utils"
)
// this is exclusively the *persistence* layer for channel registration;
@ -140,8 +141,8 @@ func (reg *ChannelRegistry) AllChannels() (result []string) {
}
// PurgedChannels returns the set of all casefolded channel names that have been purged
func (reg *ChannelRegistry) PurgedChannels() (result map[string]empty) {
result = make(map[string]empty)
func (reg *ChannelRegistry) PurgedChannels() (result utils.StringSet) {
result = make(utils.StringSet)
prefix := fmt.Sprintf(keyChannelPurged, "")
reg.server.store.View(func(tx *buntdb.Tx) error {
@ -150,7 +151,7 @@ func (reg *ChannelRegistry) PurgedChannels() (result map[string]empty) {
return false
}
channel := strings.TrimPrefix(key, prefix)
result[channel] = empty{}
result.Add(channel)
return true
})
})

View File

@ -249,7 +249,7 @@ func csAmodeHandler(server *Server, client *Client, command string, params []str
}
case modes.Add, modes.Remove:
if len(affectedModes) > 0 {
csNotice(rb, fmt.Sprintf(client.t("Successfully set persistent mode %s%s on %s"), string(change.Op), string(change.Mode), change.Arg))
csNotice(rb, fmt.Sprintf(client.t("Successfully set persistent mode %[1]s on %[2]s"), strings.Join([]string{string(change.Op), string(change.Mode)}, ""), change.Arg))
// #729: apply change to current membership
for _, member := range channel.Members() {
if member.Account() == change.Arg {

View File

@ -27,9 +27,36 @@ import (
)
const (
// maximum line length not including tags; don't change this for a public server
MaxLineLen = 512
// IdentTimeout is how long before our ident (username) check times out.
IdentTimeout = time.Second + 500*time.Millisecond
IRCv3TimestampFormat = utils.IRCv3TimestampFormat
// limit the number of device IDs a client can use, as a DoS mitigation
maxDeviceIDsPerClient = 64
// controls how often often we write an autoreplay-missed client's
// deviceid->lastseentime mapping to the database
lastSeenWriteInterval = time.Hour
)
const (
// RegisterTimeout is how long clients have to register before we disconnect them
RegisterTimeout = time.Minute
// DefaultIdleTimeout is how long without traffic before we send the client a PING
DefaultIdleTimeout = time.Minute + 30*time.Second
// For Tor clients, we send a PING at least every 30 seconds, as a workaround for this bug
// (single-onion circuits will close unless the client sends data once every 60 seconds):
// https://bugs.torproject.org/29665
TorIdleTimeout = time.Second * 30
// This is how long a client gets without sending any message, including the PONG to our
// PING, before we disconnect them:
DefaultTotalTimeout = 2*time.Minute + 30*time.Second
// Resumeable clients (clients who have negotiated caps.Resume) get longer:
ResumeableTotalTimeout = 3*time.Minute + 30*time.Second
// round off the ping interval by this much, see below:
PingCoalesceThreshold = time.Second
)
// ResumeDetails is a place to stash data at various stages of
@ -54,14 +81,14 @@ type Client struct {
channels ChannelSet
ctime time.Time
destroyed bool
exitedSnomaskSent bool
modes modes.ModeSet
hostname string
invitedTo map[string]bool
invitedTo utils.StringSet
isSTSOnly bool
languages []string
lastActive time.Time // last time they sent a command that wasn't PONG or similar
lastSeen time.Time // last time they sent any kind of command
lastActive time.Time // last time they sent a command that wasn't PONG or similar
lastSeen map[string]time.Time // maps device ID (including "") to time of last received command
lastSeenLastWrite time.Time // last time `lastSeen` was written to the datastore
loginThrottle connection_limits.GenericThrottle
nick string
nickCasefolded string
@ -75,6 +102,7 @@ type Client struct {
realname string
realIP net.IP
registered bool
registrationTimer *time.Timer
resumeID string
server *Server
skeleton string
@ -112,8 +140,13 @@ const (
type Session struct {
client *Client
deviceID string
ctime time.Time
lastActive time.Time
lastActive time.Time // last non-CTCP PRIVMSG sent; updates publicly visible idle time
lastTouch time.Time // last line sent; updates timer for idle timeouts
idleTimer *time.Timer
pingSent bool // we sent PING to a putatively idle connection and we're waiting for PONG
socket *Socket
realIP net.IP
@ -121,7 +154,6 @@ type Session struct {
rawHostname string
isTor bool
idletimer IdleTimer
fakelag Fakelag
deferredFakelagCount int
destroyed uint32
@ -178,8 +210,8 @@ func (s *Session) EndMultilineBatch(label string) (batch MultilineBatch, err err
s.fakelag.Unsuspend()
// heuristics to estimate how much data they used while fakelag was suspended
fakelagBill := (batch.lenBytes / 512) + 1
fakelagBillLines := (batch.message.LenLines() * 60) / 512
fakelagBill := (batch.lenBytes / MaxLineLen) + 1
fakelagBillLines := (batch.message.LenLines() * 60) / MaxLineLen
if fakelagBill < fakelagBillLines {
fakelagBill = fakelagBillLines
}
@ -299,7 +331,6 @@ func (server *Server) RunClient(conn IRCConn) {
// give them 1k of grace over the limit:
socket := NewSocket(conn, config.Server.MaxSendQBytes)
client := &Client{
lastSeen: now,
lastActive: now,
channels: make(ChannelSet),
ctime: now,
@ -333,7 +364,6 @@ func (server *Server) RunClient(conn IRCConn) {
}
client.sessions = []*Session{session}
session.idletimer.Initialize(session)
session.resetFakelag()
if wConn.Secure {
@ -354,15 +384,16 @@ func (server *Server) RunClient(conn IRCConn) {
}
}
client.registrationTimer = time.AfterFunc(RegisterTimeout, client.handleRegisterTimeout)
server.stats.Add()
client.run(session)
}
func (server *Server) AddAlwaysOnClient(account ClientAccount, chnames []string, lastSeen time.Time, uModes modes.Modes) {
func (server *Server) AddAlwaysOnClient(account ClientAccount, chnames []string, lastSeen map[string]time.Time, uModes modes.Modes, realname string) {
now := time.Now().UTC()
config := server.Config()
if lastSeen.IsZero() {
lastSeen = now
if lastSeen == nil && account.Settings.AutoreplayMissed {
lastSeen = map[string]time.Time{"": now}
}
client := &Client{
@ -379,6 +410,7 @@ func (server *Server) AddAlwaysOnClient(account ClientAccount, chnames []string,
realIP: utils.IPv4LoopbackAddress,
alwaysOn: true,
realname: realname,
}
client.SetMode(modes.TLS, true)
@ -522,7 +554,7 @@ const (
authFailSaslRequired
)
func (client *Client) isAuthorized(config *Config, session *Session) AuthOutcome {
func (client *Client) isAuthorized(server *Server, config *Config, session *Session) AuthOutcome {
saslSent := client.account != ""
// PASS requirement
if (config.Server.passwordBytes != nil) && session.passStatus != serverPassSuccessful && !(config.Accounts.SkipServerPassword && saslSent) {
@ -533,7 +565,8 @@ func (client *Client) isAuthorized(config *Config, session *Session) AuthOutcome
return authFailTorSaslRequired
}
// finally, enforce require-sasl
if config.Accounts.RequireSasl.Enabled && !saslSent && !utils.IPInNets(session.IP(), config.Accounts.RequireSasl.exemptedNets) {
if !saslSent && (config.Accounts.RequireSasl.Enabled || server.Defcon() <= 2) &&
!utils.IPInNets(session.IP(), config.Accounts.RequireSasl.exemptedNets) {
return authFailSaslRequired
}
return authSuccess
@ -594,7 +627,7 @@ func (client *Client) run(session *Session) {
isReattach := client.Registered()
if isReattach {
session.idletimer.Touch()
client.Touch(session)
if session.resumeDetails != nil {
session.playResume()
session.resumeDetails = nil
@ -608,8 +641,11 @@ func (client *Client) run(session *Session) {
firstLine := !isReattach
for {
var invalidUtf8 bool
line, err := session.socket.Read()
if err != nil {
if err == errInvalidUtf8 {
invalidUtf8 = true // handle as normal, including labeling
} else if err != nil {
quitMessage := "connection closed"
if err == errReadQ {
quitMessage = "readQ exceeded"
@ -655,7 +691,7 @@ func (client *Client) run(session *Session) {
}
}
msg, err := ircmsg.ParseLineStrict(line, true, 512)
msg, err := ircmsg.ParseLineStrict(line, true, MaxLineLen)
if err == ircmsg.ErrorLineIsEmpty {
continue
} else if err == ircmsg.ErrorLineTooLong {
@ -669,6 +705,8 @@ func (client *Client) run(session *Session) {
cmd, exists := Commands[msg.Command]
if !exists {
cmd = unknownCommand
} else if invalidUtf8 {
cmd = invalidUtf8Command
}
isExiting := cmd.Run(client.server, client, session, msg)
@ -711,16 +749,109 @@ func (client *Client) playReattachMessages(session *Session) {
//
// Touch indicates that we received a line from the client (so the connection is healthy
// at this time, modulo network latency and fakelag). `active` means not a PING or suchlike
// (i.e. the user should be sitting in front of their client).
func (client *Client) Touch(active bool, session *Session) {
// at this time, modulo network latency and fakelag).
func (client *Client) Touch(session *Session) {
var markDirty bool
now := time.Now().UTC()
client.stateMutex.Lock()
defer client.stateMutex.Unlock()
client.lastSeen = now
if active {
client.lastActive = now
session.lastActive = now
if client.accountSettings.AutoreplayMissed || session.deviceID != "" {
client.setLastSeen(now, session.deviceID)
if now.Sub(client.lastSeenLastWrite) > lastSeenWriteInterval {
markDirty = true
client.lastSeenLastWrite = now
}
}
client.updateIdleTimer(session, now)
client.stateMutex.Unlock()
if markDirty {
client.markDirty(IncludeLastSeen)
}
}
func (client *Client) setLastSeen(now time.Time, deviceID string) {
if client.lastSeen == nil {
client.lastSeen = make(map[string]time.Time)
}
client.lastSeen[deviceID] = now
// evict the least-recently-used entry if necessary
if maxDeviceIDsPerClient < len(client.lastSeen) {
var minLastSeen time.Time
var minClientId string
for deviceID, lastSeen := range client.lastSeen {
if minLastSeen.IsZero() || lastSeen.Before(minLastSeen) {
minClientId, minLastSeen = deviceID, lastSeen
}
}
delete(client.lastSeen, minClientId)
}
}
func (client *Client) updateIdleTimer(session *Session, now time.Time) {
session.lastTouch = now
session.pingSent = false
if session.idleTimer == nil {
pingTimeout := DefaultIdleTimeout
if session.isTor {
pingTimeout = TorIdleTimeout
}
session.idleTimer = time.AfterFunc(pingTimeout, session.handleIdleTimeout)
}
}
func (session *Session) handleIdleTimeout() {
totalTimeout := DefaultTotalTimeout
if session.capabilities.Has(caps.Resume) {
totalTimeout = ResumeableTotalTimeout
}
pingTimeout := DefaultIdleTimeout
if session.isTor {
pingTimeout = TorIdleTimeout
}
session.client.stateMutex.Lock()
now := time.Now()
timeUntilDestroy := session.lastTouch.Add(totalTimeout).Sub(now)
timeUntilPing := session.lastTouch.Add(pingTimeout).Sub(now)
shouldDestroy := session.pingSent && timeUntilDestroy <= 0
// XXX this should really be time <= 0, but let's do some hacky timer coalescing:
// a typical idling client will do nothing other than respond immediately to our pings,
// so we'll PING at t=0, they'll respond at t=0.05, then we'll wake up at t=90 and find
// that we need to PING again at t=90.05. Rather than wake up again, just send it now:
shouldSendPing := !session.pingSent && timeUntilPing <= PingCoalesceThreshold
if !shouldDestroy {
if shouldSendPing {
session.pingSent = true
}
// check in again at the minimum of these 3 possible intervals:
// 1. the ping timeout (assuming we PING and they reply immediately with PONG)
// 2. the next time we would send PING (if they don't send any more lines)
// 3. the next time we would destroy (if they don't send any more lines)
nextTimeout := pingTimeout
if PingCoalesceThreshold < timeUntilPing && timeUntilPing < nextTimeout {
nextTimeout = timeUntilPing
}
if 0 < timeUntilDestroy && timeUntilDestroy < nextTimeout {
nextTimeout = timeUntilDestroy
}
session.idleTimer.Stop()
session.idleTimer.Reset(nextTimeout)
}
session.client.stateMutex.Unlock()
if shouldDestroy {
session.client.Quit(fmt.Sprintf("Ping timeout: %v", totalTimeout), session)
session.client.destroy(session)
} else if shouldSendPing {
session.Ping()
}
}
func (session *Session) stopIdleTimer() {
session.client.stateMutex.Lock()
defer session.client.stateMutex.Unlock()
if session.idleTimer != nil {
session.idleTimer.Stop()
}
}
@ -950,23 +1081,10 @@ func (client *Client) IdleSeconds() uint64 {
return uint64(client.IdleTime().Seconds())
}
// HasNick returns true if the client's nickname is set (used in registration).
func (client *Client) HasNick() bool {
client.stateMutex.RLock()
defer client.stateMutex.RUnlock()
return client.nick != "" && client.nick != "*"
}
// HasUsername returns true if the client's username is set (used in registration).
func (client *Client) HasUsername() bool {
client.stateMutex.RLock()
defer client.stateMutex.RUnlock()
return client.username != "" && client.username != "*"
}
// SetNames sets the client's ident and realname.
func (client *Client) SetNames(username, realname string, fromIdent bool) error {
limit := client.server.Config().Limits.IdentLen
config := client.server.Config()
limit := config.Limits.IdentLen
if !fromIdent {
limit -= 1 // leave room for the prepended ~
}
@ -978,7 +1096,9 @@ func (client *Client) SetNames(username, realname string, fromIdent bool) error
return errInvalidUsername
}
if !fromIdent {
if config.Server.SuppressIdent {
username = "~user"
} else if !fromIdent {
username = "~" + username
}
@ -1018,29 +1138,32 @@ func (client *Client) ModeString() (str string) {
}
// Friends refers to clients that share a channel with this client.
func (client *Client) Friends(capabs ...caps.Capability) (result map[*Session]bool) {
result = make(map[*Session]bool)
func (client *Client) Friends(capabs ...caps.Capability) (result map[*Session]empty) {
result = make(map[*Session]empty)
// look at the client's own sessions
for _, session := range client.Sessions() {
if session.capabilities.HasAll(capabs...) {
result[session] = true
}
}
addFriendsToSet(result, client, capabs...)
for _, channel := range client.Channels() {
for _, member := range channel.Members() {
for _, session := range member.Sessions() {
if session.capabilities.HasAll(capabs...) {
result[session] = true
}
}
addFriendsToSet(result, member, capabs...)
}
}
return
}
// helper for Friends
func addFriendsToSet(set map[*Session]empty, client *Client, capabs ...caps.Capability) {
client.stateMutex.RLock()
defer client.stateMutex.RUnlock()
for _, session := range client.sessions {
if session.capabilities.HasAll(capabs...) {
set[session] = empty{}
}
}
}
func (client *Client) SetOper(oper *Oper) {
client.stateMutex.Lock()
defer client.stateMutex.Unlock()
@ -1082,14 +1205,25 @@ func (client *Client) SetVHost(vhost string) (updated bool) {
return
}
// updateNick updates `nick` and `nickCasefolded`.
func (client *Client) updateNick(nick, nickCasefolded, skeleton string) {
// SetNick gives the client a nickname and marks it as registered, if necessary
func (client *Client) SetNick(nick, nickCasefolded, skeleton string) (success bool) {
client.stateMutex.Lock()
defer client.stateMutex.Unlock()
if client.destroyed {
return false
} else if !client.registered {
// XXX test this before setting it to avoid annoying the race detector
client.registered = true
if client.registrationTimer != nil {
client.registrationTimer.Stop()
client.registrationTimer = nil
}
}
client.nick = nick
client.nickCasefolded = nickCasefolded
client.skeleton = skeleton
client.updateNickMaskNoMutex()
return true
}
// updateNickMaskNoMutex updates the casefolded nickname and nickmask, not acquiring any mutexes.
@ -1159,11 +1293,11 @@ func (client *Client) Quit(message string, session *Session) {
// #364: don't send QUIT lines to unregistered clients
if client.registered {
quitMsg := ircmsg.MakeMessage(nil, client.nickMaskString, "QUIT", message)
finalData, _ = quitMsg.LineBytesStrict(false, 512)
finalData, _ = quitMsg.LineBytesStrict(false, MaxLineLen)
}
errorMsg := ircmsg.MakeMessage(nil, "", "ERROR", message)
errorMsgBytes, _ := errorMsg.LineBytesStrict(false, 512)
errorMsgBytes, _ := errorMsg.LineBytesStrict(false, MaxLineLen)
finalData = append(finalData, errorMsgBytes...)
sess.socket.SetFinalData(finalData)
@ -1193,16 +1327,20 @@ func (client *Client) Quit(message string, session *Session) {
func (client *Client) destroy(session *Session) {
config := client.server.Config()
var sessionsToDestroy []*Session
var saveLastSeen bool
client.stateMutex.Lock()
details := client.detailsNoMutex()
brbState := client.brbTimer.state
brbAt := client.brbTimer.brbAt
wasReattach := session != nil && session.client != client
sessionRemoved := false
registered := client.registered
alwaysOn := client.alwaysOn
saveLastSeen := alwaysOn && client.accountSettings.AutoreplayMissed
// XXX a temporary (reattaching) client can be marked alwaysOn when it logs in,
// but then the session attaches to another client and we need to clean it up here
alwaysOn := registered && client.alwaysOn
var remainingSessions int
if session == nil {
sessionsToDestroy = client.sessions
@ -1215,6 +1353,20 @@ func (client *Client) destroy(session *Session) {
}
}
// save last seen if applicable:
if alwaysOn {
if client.accountSettings.AutoreplayMissed {
saveLastSeen = true
} else {
for _, session := range sessionsToDestroy {
if session.deviceID != "" {
saveLastSeen = true
break
}
}
}
}
// should we destroy the whole client this time?
// BRB is not respected if this is a destroy of the whole client (i.e., session == nil)
brbEligible := session != nil && brbState == BrbEnabled
@ -1229,18 +1381,23 @@ func (client *Client) destroy(session *Session) {
if saveLastSeen {
client.dirtyBits |= IncludeLastSeen
}
exitedSnomaskSent := client.exitedSnomaskSent
autoAway := false
var awayMessage string
if alwaysOn && remainingSessions == 0 && persistenceEnabled(config.Accounts.Multiclient.AutoAway, client.accountSettings.AutoAway) {
if alwaysOn && !client.away && remainingSessions == 0 &&
persistenceEnabled(config.Accounts.Multiclient.AutoAway, client.accountSettings.AutoAway) {
autoAway = true
client.autoAway = true
client.away = true
awayMessage = config.languageManager.Translate(client.languages, `Disconnected from the server`)
awayMessage = config.languageManager.Translate(client.languages, `User is currently disconnected`)
client.awayMessage = awayMessage
}
if client.registrationTimer != nil {
// unconditionally stop; if the client is still unregistered it must be destroyed
client.registrationTimer.Stop()
}
client.stateMutex.Unlock()
// XXX there is no particular reason to persist this state here rather than
@ -1258,13 +1415,16 @@ func (client *Client) destroy(session *Session) {
// session has been attached to a new client; do not destroy it
continue
}
session.idletimer.Stop()
session.stopIdleTimer()
// send quit/error message to client if they haven't been sent already
client.Quit("", session)
quitMessage = session.quitMessage
session.SetDestroyed()
session.socket.Close()
// clean up monitor state
client.server.monitorManager.RemoveAll(session)
// remove from connection limits
var source string
if session.isTor {
@ -1330,8 +1490,6 @@ func (client *Client) destroy(session *Session) {
if registered {
client.server.monitorManager.AlertAbout(details.nick, details.nickCasefolded, false)
}
// clean up monitor state
client.server.monitorManager.RemoveAll(client)
// clean up channels
// (note that if this is a reattach, client has no channels and therefore no friends)
@ -1370,7 +1528,7 @@ func (client *Client) destroy(session *Session) {
friend.sendFromClientInternal(false, splitQuitMessage.Time, splitQuitMessage.Msgid, details.nickMask, details.accountName, nil, "QUIT", quitMessage)
}
if !exitedSnomaskSent && registered {
if registered {
client.server.snomasks.Send(sno.LocalQuits, fmt.Sprintf(ircfmt.Unescape("%s$r exited the network"), details.nick))
}
}
@ -1478,10 +1636,11 @@ func (session *Session) SendRawMessage(message ircmsg.IrcMessage, blocking bool)
}
// assemble message
line, err := message.LineBytesStrict(false, 512)
line, err := message.LineBytesStrict(false, MaxLineLen)
if err != nil {
logline := fmt.Sprintf("Error assembling message for sending: %v\n%s", err, debug.Stack())
session.client.server.logger.Error("internal", logline)
errorParams := []string{"Error assembling message for sending", err.Error(), message.Command}
errorParams = append(errorParams, message.Params...)
session.client.server.logger.Error("internal", errorParams...)
message = ircmsg.MakeMessage(nil, session.client.server.name, ERR_UNKNOWNERROR, "*", "Error assembling message for sending")
line, _ := message.LineBytesStrict(false, 0)
@ -1543,15 +1702,24 @@ func (session *Session) Notice(text string) {
// `simulated` is for the fake join of an always-on client
// (we just read the channel name from the database, there's no need to write it back)
func (client *Client) addChannel(channel *Channel, simulated bool) {
func (client *Client) addChannel(channel *Channel, simulated bool) (err error) {
config := client.server.Config()
client.stateMutex.Lock()
client.channels[channel] = true
alwaysOn := client.alwaysOn
if client.destroyed {
err = errClientDestroyed
} else if client.oper == nil && len(client.channels) >= config.Channels.MaxChannelsPerClient {
err = errTooManyChannels
} else {
client.channels[channel] = empty{} // success
}
client.stateMutex.Unlock()
if alwaysOn && !simulated {
if err == nil && alwaysOn && !simulated {
client.markDirty(IncludeChannels)
}
return
}
func (client *Client) removeChannel(channel *Channel) {
@ -1571,10 +1739,10 @@ func (client *Client) Invite(casefoldedChannel string) {
defer client.stateMutex.Unlock()
if client.invitedTo == nil {
client.invitedTo = make(map[string]bool)
client.invitedTo = make(utils.StringSet)
}
client.invitedTo[casefoldedChannel] = true
client.invitedTo.Add(casefoldedChannel)
}
// Checks that the client was invited to join a given channel
@ -1582,7 +1750,7 @@ func (client *Client) CheckInvited(casefoldedChannel string) (invited bool) {
client.stateMutex.Lock()
defer client.stateMutex.Unlock()
invited = client.invitedTo[casefoldedChannel]
invited = client.invitedTo.Has(casefoldedChannel)
// joining an invited channel "uses up" your invite, so you can't rejoin on kick
delete(client.invitedTo, casefoldedChannel)
return
@ -1595,7 +1763,7 @@ func (client *Client) attemptAutoOper(session *Session) {
return
}
for _, oper := range client.server.Config().operators {
if oper.Auto && oper.Pass == nil && oper.Fingerprint != "" && oper.Fingerprint == session.certfp {
if oper.Auto && oper.Pass == nil && oper.Certfp != "" && oper.Certfp == session.certfp {
rb := NewResponseBuffer(session)
applyOper(client, oper, rb)
rb.Send(true)
@ -1604,6 +1772,12 @@ func (client *Client) attemptAutoOper(session *Session) {
}
}
func (client *Client) checkLoginThrottle() (throttled bool, remainingTime time.Duration) {
client.stateMutex.Lock()
defer client.stateMutex.Unlock()
return client.loginThrottle.Touch()
}
func (client *Client) historyStatus(config *Config) (status HistoryStatus, target string) {
if !config.History.Enabled {
return HistoryDisabled, ""
@ -1624,12 +1798,28 @@ func (client *Client) historyStatus(config *Config) (status HistoryStatus, targe
return
}
func (client *Client) handleRegisterTimeout() {
client.Quit(fmt.Sprintf("Registration timeout: %v", RegisterTimeout), nil)
client.destroy(nil)
}
func (client *Client) copyLastSeen() (result map[string]time.Time) {
client.stateMutex.RLock()
defer client.stateMutex.RUnlock()
result = make(map[string]time.Time, len(client.lastSeen))
for id, lastSeen := range client.lastSeen {
result[id] = lastSeen
}
return
}
// these are bit flags indicating what part of the client status is "dirty"
// and needs to be read from memory and written to the db
const (
IncludeChannels uint = 1 << iota
IncludeLastSeen
IncludeUserModes
IncludeRealname
)
func (client *Client) markDirty(dirtyBits uint) {
@ -1651,7 +1841,7 @@ func (client *Client) wakeWriter() {
func (client *Client) writeLoop() {
for {
client.performWrite()
client.performWrite(0)
client.writerSemaphore.Release()
client.stateMutex.RLock()
@ -1664,12 +1854,11 @@ func (client *Client) writeLoop() {
}
}
func (client *Client) performWrite() {
func (client *Client) performWrite(additionalDirtyBits uint) {
client.stateMutex.Lock()
dirtyBits := client.dirtyBits
dirtyBits := client.dirtyBits | additionalDirtyBits
client.dirtyBits = 0
account := client.account
lastSeen := client.lastSeen
client.stateMutex.Unlock()
if account == "" {
@ -1686,7 +1875,7 @@ func (client *Client) performWrite() {
client.server.accounts.saveChannels(account, channelNames)
}
if (dirtyBits & IncludeLastSeen) != 0 {
client.server.accounts.saveLastSeen(account, lastSeen)
client.server.accounts.saveLastSeen(account, client.copyLastSeen())
}
if (dirtyBits & IncludeUserModes) != 0 {
uModes := make(modes.Modes, 0, len(modes.SupportedUserModes))
@ -1702,4 +1891,25 @@ func (client *Client) performWrite() {
}
client.server.accounts.saveModes(account, uModes)
}
if (dirtyBits & IncludeRealname) != 0 {
client.server.accounts.saveRealname(account, client.realname)
}
}
// Blocking store; see Channel.Store and Socket.BlockingWrite
func (client *Client) Store(dirtyBits uint) (err error) {
defer func() {
client.stateMutex.Lock()
isDirty := client.dirtyBits != 0
client.stateMutex.Unlock()
if isDirty {
client.wakeWriter()
}
}()
client.writerSemaphore.Acquire()
defer client.writerSemaphore.Release()
client.performWrite(dirtyBits)
return nil
}

View File

@ -28,14 +28,6 @@ func (clients *ClientManager) Initialize() {
clients.bySkeleton = make(map[string]*Client)
}
// Count returns how many clients are in the manager.
func (clients *ClientManager) Count() int {
clients.RLock()
defer clients.RUnlock()
count := len(clients.byNick)
return count
}
// Get retrieves a client from the manager, if they exist.
func (clients *ClientManager) Get(nick string) *Client {
casefoldedName, err := CasefoldName(nick)
@ -48,9 +40,8 @@ func (clients *ClientManager) Get(nick string) *Client {
return nil
}
func (clients *ClientManager) removeInternal(client *Client) (err error) {
func (clients *ClientManager) removeInternal(client *Client, oldcfnick, oldskeleton string) (err error) {
// requires holding the writable Lock()
oldcfnick, oldskeleton := client.uniqueIdentifiers()
if oldcfnick == "*" || oldcfnick == "" {
return errNickMissing
}
@ -88,7 +79,8 @@ func (clients *ClientManager) Remove(client *Client) error {
clients.Lock()
defer clients.Unlock()
return clients.removeInternal(client)
oldcfnick, oldskeleton := client.uniqueIdentifiers()
return clients.removeInternal(client, oldcfnick, oldskeleton)
}
// Handles a RESUME by attaching a session to a designated client. It is the
@ -173,7 +165,6 @@ func (clients *ClientManager) SetNick(client *Client, session *Session, newNick
return "", errNicknameInvalid, false
}
config := client.server.Config()
if config.Server.Relaying.Enabled {
for _, char := range config.Server.Relaying.Separators {
if strings.ContainsRune(newCfNick, char) {
@ -182,7 +173,7 @@ func (clients *ClientManager) SetNick(client *Client, session *Session, newNick
}
}
if restrictedCasefoldedNicks[newCfNick] || restrictedSkeletons[newSkeleton] {
if restrictedCasefoldedNicks.Has(newCfNick) || restrictedSkeletons.Has(newSkeleton) {
return "", errNicknameInvalid, false
}
@ -234,18 +225,13 @@ func (clients *ClientManager) SetNick(client *Client, session *Session, newNick
client.server.stats.AddRegistered(invisible, operator)
}
session.autoreplayMissedSince = lastSeen
// XXX SetNames only changes names if they are unset, so the realname change only
// takes effect on first attach to an always-on client (good), but the user/ident
// change is always a no-op (bad). we could make user/ident act the same way as
// realname, but then we'd have to send CHGHOST and i don't want to deal with that
// for performance reasons
currentClient.SetNames("user", realname, true)
// TODO: transition mechanism for #1065, clean this up eventually:
if currentClient.Realname() == "" {
currentClient.SetRealname(realname)
}
// successful reattach!
return newNick, nil, back
} else if currentClient == client && currentClient.Nick() == newNick {
// see #1019: normally no-op nick changes are caught earlier, by performNickChange,
// but they are not detected there when force-guest-format is enabled (because
// the proposed nickname is e.g. alice and the current nickname is Guest-alice)
return "", errNoop, false
}
// analogous checks for skeletons
@ -254,10 +240,13 @@ func (clients *ClientManager) SetNick(client *Client, session *Session, newNick
return "", errNicknameInUse, false
}
clients.removeInternal(client)
formercfnick, formerskeleton := client.uniqueIdentifiers()
if changeSuccess := client.SetNick(newNick, newCfNick, newSkeleton); !changeSuccess {
return "", errClientDestroyed, false
}
clients.removeInternal(client, formercfnick, formerskeleton)
clients.byNick[newCfNick] = client
clients.bySkeleton[newSkeleton] = client
client.updateNick(newNick, newCfNick, newSkeleton)
return newNick, nil, false
}
@ -273,21 +262,6 @@ func (clients *ClientManager) AllClients() (result []*Client) {
return
}
// AllWithCaps returns all clients with the given capabilities.
func (clients *ClientManager) AllWithCaps(capabs ...caps.Capability) (sessions []*Session) {
clients.RLock()
defer clients.RUnlock()
for _, client := range clients.byNick {
for _, session := range client.Sessions() {
if session.capabilities.HasAll(capabs...) {
sessions = append(sessions, session)
}
}
}
return
}
// AllWithCapsNotify returns all clients with the given capabilities, and that support cap-notify.
func (clients *ClientManager) AllWithCapsNotify(capabs ...caps.Capability) (sessions []*Session) {
capabs = append(capabs, caps.CapNotify)

View File

@ -5,11 +5,13 @@ package irc
import (
"testing"
"github.com/oragono/oragono/irc/utils"
)
func TestGenerateBatchID(t *testing.T) {
var session Session
s := make(StringSet)
s := make(utils.StringSet)
count := 100000
for i := 0; i < count; i++ {
@ -56,3 +58,33 @@ func TestUserMasks(t *testing.T) {
t.Error("failure to match")
}
}
func TestWhoFields(t *testing.T) {
var w whoxFields
if w.Has('a') {
t.Error("zero value of whoxFields must be empty")
}
w = w.Add('a')
if !w.Has('a') {
t.Error("failed to set and get")
}
if w.Has('A') {
t.Error("false positive")
}
if w.Has('o') {
t.Error("false positive")
}
w = w.Add('🐬')
if w.Has('🐬') {
t.Error("should not be able to set invalid who field")
}
w = w.Add('o')
if !w.Has('o') {
t.Error("failed to set and get")
}
w = w.Add('z')
if !w.Has('z') {
t.Error("failed to set and get")
}
}

View File

@ -12,13 +12,12 @@ import (
// Command represents a command accepted from a client.
type Command struct {
handler func(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool
oper bool
usablePreReg bool
leaveClientIdle bool // if true, leaves the client active time alone
allowedInBatch bool // allowed in client-to-server batches
minParams int
capabs []string
handler func(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool
oper bool
usablePreReg bool
allowedInBatch bool // allowed in client-to-server batches
minParams int
capabs []string
}
// Run runs this command with the given client/message.
@ -59,15 +58,8 @@ func (cmd *Command) Run(server *Server, client *Client, session *Session, msg ir
exiting = server.tryRegister(client, session)
}
// most servers do this only for PING/PONG, but we'll do it for any command:
if client.registered {
// touch even if `exiting`, so we record the time of a QUIT accurately
session.idletimer.Touch()
}
// TODO: eliminate idletimer entirely in favor of this measurement
if client.registered {
client.Touch(!cmd.leaveClientIdle, session)
client.Touch(session)
}
return exiting
@ -79,6 +71,11 @@ var unknownCommand = Command{
usablePreReg: true,
}
var invalidUtf8Command = Command{
handler: invalidUtf8Handler,
usablePreReg: true,
}
// Commands holds all commands executable by a client connected to us.
var Commands map[string]Command
@ -120,6 +117,10 @@ func init() {
minParams: 1,
oper: true,
},
"DEFCON": {
handler: defconHandler,
capabs: []string{"defcon"},
},
"DEOPER": {
handler: deoperHandler,
minParams: 0,
@ -130,6 +131,10 @@ func init() {
minParams: 1,
oper: true,
},
"EXTJWT": {
handler: extjwtHandler,
minParams: 1,
},
"HELP": {
handler: helpHandler,
minParams: 0,
@ -150,9 +155,8 @@ func init() {
minParams: 2,
},
"ISON": {
handler: isonHandler,
minParams: 1,
leaveClientIdle: true,
handler: isonHandler,
minParams: 1,
},
"JOIN": {
handler: joinHandler,
@ -234,16 +238,14 @@ func init() {
minParams: 1,
},
"PING": {
handler: pingHandler,
usablePreReg: true,
minParams: 1,
leaveClientIdle: true,
handler: pingHandler,
usablePreReg: true,
minParams: 1,
},
"PONG": {
handler: pongHandler,
usablePreReg: true,
minParams: 1,
leaveClientIdle: true,
handler: pongHandler,
usablePreReg: true,
minParams: 1,
},
"PRIVMSG": {
handler: messageHandler,
@ -344,9 +346,8 @@ func init() {
minParams: 4,
},
"WHO": {
handler: whoHandler,
minParams: 1,
leaveClientIdle: true,
handler: whoHandler,
minParams: 1,
},
"WHOIS": {
handler: whoisHandler,

View File

@ -6,6 +6,7 @@
package irc
import (
"bytes"
"crypto/tls"
"errors"
"fmt"
@ -21,20 +22,22 @@ import (
"time"
"code.cloudfoundry.org/bytefmt"
"github.com/goshuirc/irc-go/ircfmt"
"gopkg.in/yaml.v2"
"github.com/oragono/oragono/irc/caps"
"github.com/oragono/oragono/irc/cloaks"
"github.com/oragono/oragono/irc/connection_limits"
"github.com/oragono/oragono/irc/custime"
"github.com/oragono/oragono/irc/email"
"github.com/oragono/oragono/irc/isupport"
"github.com/oragono/oragono/irc/jwt"
"github.com/oragono/oragono/irc/languages"
"github.com/oragono/oragono/irc/ldap"
"github.com/oragono/oragono/irc/logger"
"github.com/oragono/oragono/irc/modes"
"github.com/oragono/oragono/irc/mysql"
"github.com/oragono/oragono/irc/passwd"
"github.com/oragono/oragono/irc/utils"
"gopkg.in/yaml.v2"
)
// here's how this works: exported (capitalized) members of the config structs
@ -257,7 +260,6 @@ type AccountConfig struct {
} `yaml:"require-sasl"`
DefaultUserModes *string `yaml:"default-user-modes"`
defaultUserModes modes.Modes
LDAP ldap.ServerConfig
LoginThrottling ThrottleConfig `yaml:"login-throttling"`
SkipServerPassword bool `yaml:"skip-server-password"`
LoginViaPassCommand bool `yaml:"login-via-pass-command"`
@ -265,8 +267,7 @@ type AccountConfig struct {
Enabled bool
AdditionalNickLimit int `yaml:"additional-nick-limit"`
Method NickEnforcementMethod
AllowCustomEnforcement bool `yaml:"allow-custom-enforcement"`
RenameTimeout time.Duration `yaml:"rename-timeout"`
AllowCustomEnforcement bool `yaml:"allow-custom-enforcement"`
// RenamePrefix is the legacy field, GuestFormat is the new version
RenamePrefix string `yaml:"rename-prefix"`
GuestFormat string `yaml:"guest-nickname-format"`
@ -313,7 +314,6 @@ type VHostConfig struct {
Channel string
Cooldown custime.Duration
} `yaml:"user-requests"`
OfferList []string `yaml:"offer-list"`
}
type NickEnforcementMethod int
@ -409,7 +409,8 @@ type OperConfig struct {
Vhost string
WhoisLine string `yaml:"whois-line"`
Password string
Fingerprint string
Fingerprint *string // legacy name for certfp, #1050
Certfp string
Auto bool
Modes string
}
@ -497,6 +498,7 @@ type Config struct {
lookupHostnames bool
ForwardConfirmHostnames bool `yaml:"forward-confirm-hostnames"`
CheckIdent bool `yaml:"check-ident"`
SuppressIdent bool `yaml:"suppress-ident"`
MOTD string
motdLines []string
MOTDFormatting bool `yaml:"motd-formatting"`
@ -524,6 +526,7 @@ type Config struct {
supportedCaps *caps.Set
capValues caps.Values
Casemapping Casemapping
EnforceUtf8 bool `yaml:"enforce-utf8"`
OutputPath string `yaml:"output-path"`
}
@ -536,6 +539,11 @@ type Config struct {
addSuffix bool
}
Extjwt struct {
Default jwt.JwtServiceConfig `yaml:",inline"`
Services map[string]jwt.JwtServiceConfig `yaml:"services"`
}
Languages struct {
Enabled bool
Path string
@ -608,6 +616,11 @@ type Config struct {
AllowIndividualDelete bool `yaml:"allow-individual-delete"`
EnableAccountIndexing bool `yaml:"enable-account-indexing"`
}
TagmsgStorage struct {
Default bool
Whitelist []string
Blacklist []string
} `yaml:"tagmsg-storage"`
}
Filename string
@ -616,8 +629,8 @@ type Config struct {
// OperClass defines an assembled operator class.
type OperClass struct {
Title string
WhoisLine string `yaml:"whois-line"`
Capabilities StringSet // map to make lookups much easier
WhoisLine string `yaml:"whois-line"`
Capabilities utils.StringSet // map to make lookups much easier
}
// OperatorClasses returns a map of assembled operator classes from the given config.
@ -632,7 +645,7 @@ func (conf *Config) OperatorClasses() (map[string]*OperClass, error) {
lenOfLastOcs := -1
for {
if lenOfLastOcs == len(ocs) {
return nil, ErrOperClassDependencies
return nil, errors.New("OperClasses contains a looping dependency, or a class extends from a class that doesn't exist")
}
lenOfLastOcs = len(ocs)
@ -655,7 +668,7 @@ func (conf *Config) OperatorClasses() (map[string]*OperClass, error) {
// create new operclass
var oc OperClass
oc.Capabilities = make(StringSet)
oc.Capabilities = make(utils.StringSet)
// get inhereted info from other operclasses
if len(info.Extends) > 0 {
@ -696,14 +709,14 @@ func (conf *Config) OperatorClasses() (map[string]*OperClass, error) {
// Oper represents a single assembled operator's config.
type Oper struct {
Name string
Class *OperClass
WhoisLine string
Vhost string
Pass []byte
Fingerprint string
Auto bool
Modes []modes.ModeChange
Name string
Class *OperClass
WhoisLine string
Vhost string
Pass []byte
Certfp string
Auto bool
Modes []modes.ModeChange
}
// Operators returns a map of operator configs from the given OperClass and config.
@ -725,15 +738,19 @@ func (conf *Config) Operators(oc map[string]*OperClass) (map[string]*Oper, error
return nil, fmt.Errorf("Oper %s has an invalid password hash: %s", oper.Name, err.Error())
}
}
if opConf.Fingerprint != "" {
oper.Fingerprint, err = utils.NormalizeCertfp(opConf.Fingerprint)
certfp := opConf.Certfp
if certfp == "" && opConf.Fingerprint != nil {
certfp = *opConf.Fingerprint
}
if certfp != "" {
oper.Certfp, err = utils.NormalizeCertfp(certfp)
if err != nil {
return nil, fmt.Errorf("Oper %s has an invalid fingerprint: %s", oper.Name, err.Error())
}
}
oper.Auto = opConf.Auto
if oper.Pass == nil && oper.Fingerprint == "" {
if oper.Pass == nil && oper.Certfp == "" {
return nil, fmt.Errorf("Oper %s has neither a password nor a fingerprint", name)
}
@ -810,6 +827,29 @@ func (conf *Config) prepareListeners() (err error) {
return nil
}
func (config *Config) processExtjwt() (err error) {
// first process the default service, which may be disabled
err = config.Extjwt.Default.Postprocess()
if err != nil {
return
}
// now process the named services. it is an error if any is disabled
// also, normalize the service names to lowercase
services := make(map[string]jwt.JwtServiceConfig, len(config.Extjwt.Services))
for service, sConf := range config.Extjwt.Services {
err := sConf.Postprocess()
if err != nil {
return err
}
if !sConf.Enabled() {
return fmt.Errorf("no keys enabled for extjwt service %s", service)
}
services[strings.ToLower(service)] = sConf
}
config.Extjwt.Services = services
return nil
}
// LoadRawConfig loads the config without doing any consistency checks or postprocessing
func LoadRawConfig(filename string) (config *Config, err error) {
data, err := ioutil.ReadFile(filename)
@ -834,24 +874,24 @@ func LoadConfig(filename string) (config *Config, err error) {
config.Filename = filename
if config.Network.Name == "" {
return nil, ErrNetworkNameMissing
return nil, errors.New("Network name missing")
}
if config.Server.Name == "" {
return nil, ErrServerNameMissing
return nil, errors.New("Server name missing")
}
if !utils.IsServerName(config.Server.Name) {
return nil, ErrServerNameNotHostname
return nil, errors.New("Server name must match the format of a hostname")
}
config.Server.nameCasefolded = strings.ToLower(config.Server.Name)
if config.Datastore.Path == "" {
return nil, ErrDatastorePathMissing
return nil, errors.New("Datastore path missing")
}
//dan: automagically fix identlen until a few releases in the future (from now, 0.12.0), being a newly-introduced limit
if config.Limits.IdentLen < 1 {
config.Limits.IdentLen = 20
}
if config.Limits.NickLen < 1 || config.Limits.ChannelLen < 2 || config.Limits.AwayLen < 1 || config.Limits.KickLen < 1 || config.Limits.TopicLen < 1 {
return nil, ErrLimitsAreInsane
return nil, errors.New("One or more limits values are too low")
}
if config.Limits.RegistrationMessages == 0 {
config.Limits.RegistrationMessages = 1024
@ -862,6 +902,10 @@ func LoadConfig(filename string) (config *Config, err error) {
}
}
if config.Server.CheckIdent && config.Server.SuppressIdent {
return nil, errors.New("Can't configure both check-ident and suppress-ident")
}
config.Server.supportedCaps = caps.NewCompleteSet()
config.Server.capValues = make(caps.Values)
@ -964,7 +1008,7 @@ func LoadConfig(filename string) (config *Config, err error) {
}
}
if methods["file"] && logConfig.Filename == "" {
return nil, ErrLoggerFilenameMissing
return nil, errors.New("Logging configuration specifies 'file' method but 'filename' is empty")
}
logConfig.MethodFile = methods["file"]
logConfig.MethodStdout = methods["stdout"]
@ -983,7 +1027,7 @@ func LoadConfig(filename string) (config *Config, err error) {
continue
}
if typeStr == "-" {
return nil, ErrLoggerExcludeEmpty
return nil, errors.New("Encountered logging type '-' with no type to exclude")
}
if typeStr[0] == '-' {
typeStr = typeStr[1:]
@ -993,7 +1037,7 @@ func LoadConfig(filename string) (config *Config, err error) {
}
}
if len(logConfig.Types) < 1 {
return nil, ErrLoggerHasNoTypes
return nil, errors.New("Logger has no types to log")
}
newLogConfigs = append(newLogConfigs, logConfig)
@ -1050,12 +1094,6 @@ func LoadConfig(filename string) (config *Config, err error) {
config.Accounts.VHosts.ValidRegexp = defaultValidVhostRegex
}
for _, vhost := range config.Accounts.VHosts.OfferList {
if !config.Accounts.VHosts.ValidRegexp.MatchString(vhost) {
return nil, fmt.Errorf("invalid offered vhost: %s", vhost)
}
}
config.Server.capValues[caps.SASL] = "PLAIN,EXTERNAL"
if !config.Accounts.AuthenticationEnabled {
config.Server.supportedCaps.Disable(caps.SASL)
@ -1134,11 +1172,16 @@ func LoadConfig(filename string) (config *Config, err error) {
}
if !config.History.Enabled || !config.History.Persistent.Enabled {
config.History.Persistent.Enabled = false
config.History.Persistent.UnregisteredChannels = false
config.History.Persistent.RegisteredChannels = PersistentDisabled
config.History.Persistent.DirectMessages = PersistentDisabled
}
if config.History.Persistent.Enabled && !config.Datastore.MySQL.Enabled {
return nil, fmt.Errorf("You must configure a MySQL server in order to enable persistent history")
}
if config.History.ZNCMax == 0 {
config.History.ZNCMax = config.History.ChathistoryMax
}
@ -1156,6 +1199,11 @@ func LoadConfig(filename string) (config *Config, err error) {
}
}
err = config.processExtjwt()
if err != nil {
return nil, err
}
// now that all postprocessing is complete, regenerate ISUPPORT:
err = config.generateISupport()
if err != nil {
@ -1193,6 +1241,9 @@ func (config *Config) generateISupport() (err error) {
isupport.Add("CHANTYPES", chanTypes)
isupport.Add("ELIST", "U")
isupport.Add("EXCEPTS", "")
if config.Extjwt.Default.Enabled() || len(config.Extjwt.Services) != 0 {
isupport.Add("EXTJWT", "1")
}
isupport.Add("INVEX", "")
isupport.Add("KICKLEN", strconv.Itoa(config.Limits.KickLen))
isupport.Add("MAXLIST", fmt.Sprintf("beI:%s", strconv.Itoa(config.Limits.ChanListModes)))
@ -1212,6 +1263,7 @@ func (config *Config) generateISupport() (err error) {
if config.Server.Casemapping == CasemappingPRECIS {
isupport.Add("UTF8MAPPING", precisUTF8MappingToken)
}
isupport.Add("WHOX", "")
err = isupport.RegenerateCachedReply()
return
@ -1255,6 +1307,16 @@ func (config *Config) Diff(oldConfig *Config) (addedCaps, removedCaps *caps.Set)
return
}
// determine whether we need to resize / create / destroy
// the in-memory history buffers:
func (config *Config) historyChangedFrom(oldConfig *Config) bool {
return config.History.Enabled != oldConfig.History.Enabled ||
config.History.ChannelLength != oldConfig.History.ChannelLength ||
config.History.ClientLength != oldConfig.History.ClientLength ||
config.History.AutoresizeWindow != oldConfig.History.AutoresizeWindow ||
config.History.Persistent != oldConfig.History.Persistent
}
func compileGuestRegexp(guestFormat string, casemapping Casemapping) (standard, folded *regexp.Regexp, err error) {
if strings.Count(guestFormat, "?") != 0 || strings.Count(guestFormat, "*") != 1 {
err = errors.New("guest format must contain 1 '*' and no '?'s")
@ -1280,3 +1342,34 @@ func compileGuestRegexp(guestFormat string, casemapping Casemapping) (standard,
folded, err = utils.CompileGlob(fmt.Sprintf("%s*%s", initialFolded, finalFolded), false)
return
}
func (config *Config) loadMOTD() error {
if config.Server.MOTD != "" {
file, err := os.Open(config.Server.MOTD)
if err != nil {
return err
}
defer file.Close()
contents, err := ioutil.ReadAll(file)
if err != nil {
return err
}
lines := bytes.Split(contents, []byte{'\n'})
for i, line := range lines {
lineToSend := string(bytes.TrimRight(line, "\r\n"))
if len(lineToSend) == 0 && i == len(lines)-1 {
// if the last line of the MOTD was properly terminated with \n,
// there's no need to send a blank line to clients
continue
}
if config.Server.MOTDFormatting {
lineToSend = ircfmt.Unescape(lineToSend)
}
// "- " is the required prefix for MOTD
lineToSend = fmt.Sprintf("- %s", lineToSend)
config.Server.motdLines = append(config.Server.motdLines, lineToSend)
}
}
return nil
}

View File

@ -8,6 +8,7 @@ package irc
import (
"errors"
"fmt"
"time"
"github.com/oragono/oragono/irc/utils"
)
@ -57,7 +58,6 @@ var (
errBanned = errors.New("IP or nickmask banned")
errInvalidParams = utils.ErrInvalidParams
errNoVhost = errors.New(`You do not have an approved vhost`)
errVhostsForbidden = errors.New(`An administrator has denied you the ability to use vhosts`)
errLimitExceeded = errors.New("Limit exceeded")
errNoop = errors.New("Action was a no-op")
errCASFailed = errors.New("Compare-and-swap update of database value failed")
@ -65,13 +65,12 @@ var (
errCredsExternallyManaged = errors.New("Credentials are externally managed and cannot be changed here")
errInvalidMultilineBatch = errors.New("Invalid multiline batch")
errTimedOut = errors.New("Operation timed out")
)
// Socket Errors
var (
errNoPeerCerts = errors.New("Client did not provide a certificate")
errNotTLS = errors.New("Not a TLS connection")
errReadQ = errors.New("ReadQ Exceeded")
errInvalidUtf8 = errors.New("Message rejected for invalid utf8")
errClientDestroyed = errors.New("Client was already destroyed")
errTooManyChannels = errors.New("You have joined too many channels")
errWrongChannelKey = errors.New("Cannot join password-protected channel without the password")
errInviteOnly = errors.New("Cannot join invite-only channel without an invite")
errRegisteredOnly = errors.New("Cannot join registered-only channel without an account")
)
// String Errors
@ -89,18 +88,10 @@ func (ck *CertKeyError) Error() string {
return fmt.Sprintf("Invalid TLS cert/key pair: %v", ck.Err)
}
// Config Errors
var (
ErrDatastorePathMissing = errors.New("Datastore path missing")
ErrLimitsAreInsane = errors.New("Limits aren't setup properly, check them and make them sane")
ErrLineLengthsTooSmall = errors.New("Line lengths must be 512 or greater (check the linelen section under server->limits)")
ErrLoggerExcludeEmpty = errors.New("Encountered logging type '-' with no type to exclude")
ErrLoggerFilenameMissing = errors.New("Logging configuration specifies 'file' method but 'filename' is empty")
ErrLoggerHasNoTypes = errors.New("Logger has no types to log")
ErrNetworkNameMissing = errors.New("Network name missing")
ErrNoFingerprintOrPassword = errors.New("Fingerprint or password needs to be specified")
ErrNoListenersDefined = errors.New("Server listening addresses missing")
ErrOperClassDependencies = errors.New("OperClasses contains a looping dependency, or a class extends from a class that doesn't exist")
ErrServerNameMissing = errors.New("Server name missing")
ErrServerNameNotHostname = errors.New("Server name must match the format of a hostname")
)
type ThrottleError struct {
time.Duration
}
func (te *ThrottleError) Error() string {
return fmt.Sprintf(`Please wait at least %v and try again`, te.Duration)
}

View File

@ -26,31 +26,39 @@ const (
)
type webircConfig struct {
PasswordString string `yaml:"password"`
Password []byte `yaml:"password-bytes"`
Fingerprint string
PasswordString string `yaml:"password"`
Password []byte `yaml:"password-bytes"`
Fingerprint *string // legacy name for certfp, #1050
Certfp string
Hosts []string
allowedNets []net.IPNet
}
// Populate fills out our password or fingerprint.
func (wc *webircConfig) Populate() (err error) {
if wc.Fingerprint == "" && wc.PasswordString == "" {
err = ErrNoFingerprintOrPassword
}
if err == nil && wc.PasswordString != "" {
if wc.PasswordString != "" {
wc.Password, err = decodeLegacyPasswordHash(wc.PasswordString)
if err != nil {
return
}
}
if err == nil && wc.Fingerprint != "" {
wc.Fingerprint, err = utils.NormalizeCertfp(wc.Fingerprint)
certfp := wc.Certfp
if certfp == "" && wc.Fingerprint != nil {
certfp = *wc.Fingerprint
}
if certfp != "" {
wc.Certfp, err = utils.NormalizeCertfp(certfp)
}
if err != nil {
return
}
if err == nil {
wc.allowedNets, err = utils.ParseNetList(wc.Hosts)
if wc.Certfp == "" && wc.PasswordString == "" {
return errors.New("webirc block has no certfp or password specified")
}
wc.allowedNets, err = utils.ParseNetList(wc.Hosts)
return err
}

View File

@ -37,6 +37,14 @@ func (server *Server) Languages() (lm *languages.Manager) {
return server.Config().languageManager
}
func (server *Server) Defcon() uint32 {
return atomic.LoadUint32(&server.defcon)
}
func (server *Server) SetDefcon(defcon uint32) {
atomic.StoreUint32(&server.defcon, defcon)
}
func (client *Client) Sessions() (sessions []*Session) {
client.stateMutex.RLock()
sessions = client.sessions
@ -62,6 +70,7 @@ type SessionData struct {
ip net.IP
hostname string
certfp string
deviceID string
}
func (client *Client) AllSessionData(currentSession *Session) (data []SessionData, currentIndex int) {
@ -79,6 +88,7 @@ func (client *Client) AllSessionData(currentSession *Session) (data []SessionDat
ctime: session.ctime,
hostname: session.rawHostname,
certfp: session.certfp,
deviceID: session.deviceID,
}
if session.proxiedIP != nil {
data[i].ip = session.proxiedIP
@ -102,8 +112,9 @@ func (client *Client) AddSession(session *Session) (success bool, numSessions in
newSessions := make([]*Session, len(client.sessions)+1)
copy(newSessions, client.sessions)
newSessions[len(newSessions)-1] = session
if client.accountSettings.AutoreplayMissed {
lastSeen = client.lastSeen
if client.accountSettings.AutoreplayMissed || session.deviceID != "" {
lastSeen = client.lastSeen[session.deviceID]
client.setLastSeen(time.Now().UTC(), session.deviceID)
}
client.sessions = newSessions
if client.autoAway {
@ -174,9 +185,9 @@ func (client *Client) Hostname() string {
return client.hostname
}
func (client *Client) Away() (result bool) {
func (client *Client) Away() (result bool, message string) {
client.stateMutex.Lock()
result = client.away
result, message = client.away, client.awayMessage
client.stateMutex.Unlock()
return
}
@ -190,15 +201,9 @@ func (client *Client) SetAway(away bool, awayMessage string) (changed bool) {
return
}
func (client *Client) SetExitedSnomaskSent() {
client.stateMutex.Lock()
client.exitedSnomaskSent = true
client.stateMutex.Unlock()
}
func (client *Client) AlwaysOn() (alwaysOn bool) {
client.stateMutex.Lock()
alwaysOn = client.alwaysOn
alwaysOn = client.registered && client.alwaysOn
client.stateMutex.Unlock()
return
}
@ -230,20 +235,15 @@ func (client *Client) Oper() *Oper {
return client.oper
}
func (client *Client) Registered() bool {
client.stateMutex.RLock()
defer client.stateMutex.RUnlock()
return client.registered
}
func (client *Client) SetRegistered() {
func (client *Client) Registered() (result bool) {
// `registered` is only written from the client's own goroutine, but may be
// read from other goroutines; therefore, the client's own goroutine may read
// the value without synchronization, but must write it with synchronization,
// and other goroutines must read it with synchronization
client.stateMutex.Lock()
client.registered = true
client.stateMutex.Unlock()
client.stateMutex.RLock()
result = client.registered
client.stateMutex.RUnlock()
return
}
func (client *Client) RawHostname() (result string) {
@ -285,11 +285,8 @@ func (client *Client) Login(account ClientAccount) {
client.account = account.NameCasefolded
client.accountName = account.Name
client.accountSettings = account.Settings
// check `registered` to avoid incorrectly marking a temporary (pre-reattach),
// SASL'ing client as always-on
if client.registered {
client.alwaysOn = alwaysOn
}
// mark always-on here: it will not be respected until the client is registered
client.alwaysOn = alwaysOn
client.accountRegDate = account.RegisteredAt
return
}
@ -324,17 +321,26 @@ func (client *Client) AccountSettings() (result AccountSettings) {
func (client *Client) SetAccountSettings(settings AccountSettings) {
// we mark dirty if the client is transitioning to always-on
markDirty := false
var becameAlwaysOn, autoreplayMissedDisabled bool
alwaysOn := persistenceEnabled(client.server.Config().Accounts.Multiclient.AlwaysOn, settings.AlwaysOn)
client.stateMutex.Lock()
client.accountSettings = settings
if client.registered {
markDirty = !client.alwaysOn && alwaysOn
// only allow the client to become always-on if their nick equals their account name
alwaysOn = alwaysOn && client.nick == client.accountName
autoreplayMissedDisabled = (client.accountSettings.AutoreplayMissed && !settings.AutoreplayMissed)
becameAlwaysOn = (!client.alwaysOn && alwaysOn)
client.alwaysOn = alwaysOn
if autoreplayMissedDisabled {
// clear the lastSeen entry for the default session, but not for device IDs
delete(client.lastSeen, "")
}
}
client.accountSettings = settings
client.stateMutex.Unlock()
if markDirty {
if becameAlwaysOn {
client.markDirty(IncludeAllAttrs)
} else if autoreplayMissedDisabled {
client.markDirty(IncludeLastSeen)
}
}
@ -363,7 +369,11 @@ func (client *Client) SetMode(mode modes.Mode, on bool) bool {
func (client *Client) SetRealname(realname string) {
client.stateMutex.Lock()
client.realname = realname
alwaysOn := client.registered && client.alwaysOn
client.stateMutex.Unlock()
if alwaysOn {
client.markDirty(IncludeRealname)
}
}
func (client *Client) Channels() (result []*Channel) {
@ -408,6 +418,21 @@ func (client *Client) detailsNoMutex() (result ClientDetails) {
return
}
func (client *Client) UpdateActive(session *Session) {
now := time.Now().UTC()
client.stateMutex.Lock()
defer client.stateMutex.Unlock()
client.lastActive = now
session.lastActive = now
}
func (client *Client) Realname() string {
client.stateMutex.RLock()
result := client.realname
client.stateMutex.RUnlock()
return result
}
func (channel *Channel) Name() string {
channel.stateMutex.RLock()
defer channel.stateMutex.RUnlock()
@ -423,9 +448,11 @@ func (channel *Channel) NameCasefolded() string {
func (channel *Channel) Rename(name, nameCasefolded string) {
channel.stateMutex.Lock()
channel.name = name
channel.nameCasefolded = nameCasefolded
if channel.registeredFounder != "" {
channel.registeredTime = time.Now().UTC()
if channel.nameCasefolded != nameCasefolded {
channel.nameCasefolded = nameCasefolded
if channel.registeredFounder != "" {
channel.registeredTime = time.Now().UTC()
}
}
channel.stateMutex.Unlock()
}

View File

@ -25,6 +25,7 @@ import (
"github.com/oragono/oragono/irc/caps"
"github.com/oragono/oragono/irc/custime"
"github.com/oragono/oragono/irc/history"
"github.com/oragono/oragono/irc/jwt"
"github.com/oragono/oragono/irc/modes"
"github.com/oragono/oragono/irc/sno"
"github.com/oragono/oragono/irc/utils"
@ -82,7 +83,7 @@ func sendSuccessfulRegResponse(client *Client, rb *ResponseBuffer, forNS bool) {
} else {
rb.Add(nil, client.server.name, RPL_REG_SUCCESS, details.nick, details.accountName, client.t("Account created"))
}
client.server.snomasks.Send(sno.LocalAccounts, fmt.Sprintf(ircfmt.Unescape("Client $c[grey][$r%s$c[grey]] registered account $c[grey][$r%s$c[grey]]"), details.nickMask, details.accountName))
client.server.snomasks.Send(sno.LocalAccounts, fmt.Sprintf(ircfmt.Unescape("Client $c[grey][$r%s$c[grey]] registered account $c[grey][$r%s$c[grey]] from IP %s"), details.nickMask, details.accountName, rb.session.IP().String()))
sendSuccessfulAccountAuth(client, rb, forNS, false)
}
@ -236,6 +237,15 @@ func authPlainHandler(server *Server, client *Client, mechanism string, value []
return false
}
// see #843: strip the device ID for the benefit of clients that don't
// distinguish user/ident from account name
if strudelIndex := strings.IndexByte(authcid, '@'); strudelIndex != -1 {
var deviceID string
authcid, deviceID = authcid[:strudelIndex], authcid[strudelIndex+1:]
if !client.registered {
rb.session.deviceID = deviceID
}
}
password := string(splitValue[2])
err := server.accounts.AuthenticateByPassphrase(client, authcid, password)
if err != nil {
@ -251,6 +261,10 @@ func authPlainHandler(server *Server, client *Client, mechanism string, value []
}
func authErrorToMessage(server *Server, err error) (msg string) {
if throttled, ok := err.(*ThrottleError); ok {
return throttled.Error()
}
switch err {
case errAccountDoesNotExist, errAccountUnverified, errAccountInvalidCredentials, errAuthzidAuthcidMismatch, errNickAccountMismatch:
return err.Error()
@ -280,6 +294,15 @@ func authExternalHandler(server *Server, client *Client, mechanism string, value
}
if err == nil {
// see #843: strip the device ID for the benefit of clients that don't
// distinguish user/ident from account name
if strudelIndex := strings.IndexByte(authzid, '@'); strudelIndex != -1 {
var deviceID string
authzid, deviceID = authzid[:strudelIndex], authzid[strudelIndex+1:]
if !client.registered {
rb.session.deviceID = deviceID
}
}
err = server.accounts.AuthenticateByCertFP(client, rb.session.certfp, authzid)
}
@ -325,9 +348,9 @@ func dispatchAwayNotify(client *Client, isAway bool, awayMessage string) {
details := client.Details()
for session := range client.Friends(caps.AwayNotify) {
if isAway {
session.sendFromClientInternal(false, time.Time{}, "", details.nickMask, details.account, nil, "AWAY", awayMessage)
session.sendFromClientInternal(false, time.Time{}, "", details.nickMask, details.accountName, nil, "AWAY", awayMessage)
} else {
session.sendFromClientInternal(false, time.Time{}, "", details.nickMask, details.account, nil, "AWAY")
session.sendFromClientInternal(false, time.Time{}, "", details.nickMask, details.accountName, nil, "AWAY")
}
}
}
@ -736,6 +759,21 @@ func debugHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res
return false
}
func defconHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
if len(msg.Params) > 0 {
level, err := strconv.Atoi(msg.Params[0])
if err == nil && 1 <= level && level <= 5 {
server.SetDefcon(uint32(level))
server.snomasks.Send(sno.LocalAnnouncements, fmt.Sprintf("%s [%s] set DEFCON level to %d", client.Nick(), client.Oper().Name, level))
} else {
rb.Add(nil, server.name, ERR_UNKNOWNERROR, client.Nick(), msg.Command, client.t("Invalid DEFCON parameter"))
return false
}
}
rb.Notice(fmt.Sprintf(client.t("Current DEFCON level is %d"), server.Defcon()))
return false
}
// helper for parsing the reason args to DLINE and KLINE
func getReasonsFromParams(params []string, currentArg int) (reason, operReason string) {
reason = "No reason given"
@ -829,7 +867,7 @@ func dlineHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res
return false
}
if !dlineMyself && hostNet.Contains(client.IP()) {
if !dlineMyself && hostNet.Contains(rb.session.IP()) {
rb.Add(nil, server.name, ERR_UNKNOWNERROR, client.nick, msg.Command, client.t("This ban matches you. To DLINE yourself, you must use the command: /DLINE MYSELF <arguments>"))
return false
}
@ -868,24 +906,30 @@ func dlineHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res
var killClient bool
if andKill {
var clientsToKill []*Client
var sessionsToKill []*Session
var killedClientNicks []string
for _, mcl := range server.clients.AllClients() {
if hostNet.Contains(mcl.IP()) {
clientsToKill = append(clientsToKill, mcl)
killedClientNicks = append(killedClientNicks, mcl.nick)
nickKilled := false
for _, session := range mcl.Sessions() {
if hostNet.Contains(session.IP()) {
sessionsToKill = append(sessionsToKill, session)
if !nickKilled {
killedClientNicks = append(killedClientNicks, mcl.Nick())
nickKilled = true
}
}
}
}
for _, mcl := range clientsToKill {
mcl.SetExitedSnomaskSent()
mcl.Quit(fmt.Sprintf(mcl.t("You have been banned from this server (%s)"), reason), nil)
if mcl == client {
for _, session := range sessionsToKill {
mcl := session.client
mcl.Quit(fmt.Sprintf(mcl.t("You have been banned from this server (%s)"), reason), session)
if session == rb.session {
killClient = true
} else {
// if mcl == client, we kill them below
mcl.destroy(nil)
mcl.destroy(session)
}
}
@ -897,6 +941,73 @@ func dlineHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res
return killClient
}
// EXTJWT <target> [service_name]
func extjwtHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
accountName := client.AccountName()
if accountName == "*" {
accountName = ""
}
claims := jwt.MapClaims{
"iss": server.name,
"sub": client.Nick(),
"account": accountName,
"umodes": []string{},
}
if msg.Params[0] != "*" {
channel := server.channels.Get(msg.Params[0])
if channel == nil {
rb.Add(nil, server.name, "FAIL", "EXTJWT", "NO_SUCH_CHANNEL", client.t("No such channel"))
return false
}
claims["channel"] = channel.Name()
claims["joined"] = 0
claims["cmodes"] = []string{}
if present, cModes := channel.ClientStatus(client); present {
claims["joined"] = 1
var modeStrings []string
for _, cMode := range cModes {
modeStrings = append(modeStrings, string(cMode))
}
claims["cmodes"] = modeStrings
}
}
config := server.Config()
var serviceName string
var sConfig jwt.JwtServiceConfig
if 1 < len(msg.Params) {
serviceName = strings.ToLower(msg.Params[1])
sConfig = config.Extjwt.Services[serviceName]
} else {
serviceName = "*"
sConfig = config.Extjwt.Default
}
if !sConfig.Enabled() {
rb.Add(nil, server.name, "FAIL", "EXTJWT", "NO_SUCH_SERVICE", client.t("No such service"))
return false
}
tokenString, err := sConfig.Sign(claims)
if err == nil {
maxTokenLength := 400
for maxTokenLength < len(tokenString) {
rb.Add(nil, server.name, "EXTJWT", msg.Params[0], serviceName, "*", tokenString[:maxTokenLength])
tokenString = tokenString[maxTokenLength:]
}
rb.Add(nil, server.name, "EXTJWT", msg.Params[0], serviceName, tokenString)
} else {
rb.Add(nil, server.name, "FAIL", "EXTJWT", "UNKNOWN_ERROR", client.t("Could not generate EXTJWT token"))
}
return false
}
// HELP [<query>]
func helpHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
argument := strings.ToLower(strings.TrimSpace(strings.Join(msg.Params, " ")))
@ -973,6 +1084,7 @@ func infoHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
if Commit != "" {
rb.Add(nil, server.name, RPL_INFO, nick, fmt.Sprintf(client.t("It was built from git hash %s."), Commit))
}
rb.Add(nil, server.name, RPL_INFO, nick, fmt.Sprintf(client.t("It was compiled using %s."), runtime.Version()))
rb.Add(nil, server.name, RPL_INFO, nick, "")
rb.Add(nil, server.name, RPL_INFO, nick, client.t("Oragono is released under the MIT license."))
rb.Add(nil, server.name, RPL_INFO, nick, "")
@ -1052,16 +1164,10 @@ func joinHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
keys = strings.Split(msg.Params[1], ",")
}
config := server.Config()
oper := client.Oper()
for i, name := range channels {
if name == "" {
continue // #679
}
if config.Channels.MaxChannelsPerClient <= client.NumChannels() && oper == nil {
rb.Add(nil, server.name, ERR_TOOMANYCHANNELS, client.Nick(), name, client.t("You have joined too many channels"))
return false
}
var key string
if len(keys) > i {
key = keys[i]
@ -1075,18 +1181,35 @@ func joinHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
}
func sendJoinError(client *Client, name string, rb *ResponseBuffer, err error) {
var errMsg string
var code, errMsg, forbiddingMode string
switch err {
case errInsufficientPrivs:
errMsg = `Only server operators can create new channels`
code, errMsg = ERR_NOSUCHCHANNEL, `Only server operators can create new channels`
case errConfusableIdentifier:
errMsg = `That channel name is too close to the name of another channel`
code, errMsg = ERR_NOSUCHCHANNEL, `That channel name is too close to the name of another channel`
case errChannelPurged:
errMsg = err.Error()
code, errMsg = ERR_NOSUCHCHANNEL, err.Error()
case errTooManyChannels:
code, errMsg = ERR_TOOMANYCHANNELS, `You have joined too many channels`
case errLimitExceeded:
code, forbiddingMode = ERR_CHANNELISFULL, "l"
case errWrongChannelKey:
code, forbiddingMode = ERR_BADCHANNELKEY, "k"
case errInviteOnly:
code, forbiddingMode = ERR_INVITEONLYCHAN, "i"
case errBanned:
code, forbiddingMode = ERR_BANNEDFROMCHAN, "b"
case errRegisteredOnly:
code, errMsg = ERR_NEEDREGGEDNICK, `You must be registered to join that channel`
default:
errMsg = `No such channel`
code, errMsg = ERR_NOSUCHCHANNEL, `No such channel`
}
rb.Add(nil, client.server.name, ERR_NOSUCHCHANNEL, client.Nick(), utils.SafeErrorParam(name), client.t(errMsg))
if forbiddingMode != "" {
errMsg = fmt.Sprintf(client.t("Cannot join channel (+%s)"), forbiddingMode)
} else {
errMsg = client.t(errMsg)
}
rb.Add(nil, client.server.name, code, client.Nick(), utils.SafeErrorParam(name), errMsg)
}
// SAJOIN [nick] #channel{,#channel}
@ -1180,14 +1303,15 @@ func killHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
target := server.clients.Get(nickname)
if target == nil {
rb.Add(nil, client.server.name, ERR_NOSUCHNICK, client.nick, utils.SafeErrorParam(nickname), client.t("No such nick"))
rb.Add(nil, client.server.name, ERR_NOSUCHNICK, client.Nick(), utils.SafeErrorParam(nickname), client.t("No such nick"))
return false
} else if target.AlwaysOn() {
rb.Add(nil, client.server.name, ERR_UNKNOWNERROR, client.Nick(), "KILL", fmt.Sprintf(client.t("Client %s is always-on and cannot be fully removed by /KILL; consider /NS SUSPEND instead"), target.Nick()))
}
quitMsg := fmt.Sprintf("Killed (%s (%s))", client.nick, comment)
server.snomasks.Send(sno.LocalKills, fmt.Sprintf(ircfmt.Unescape("%s$r was killed by %s $c[grey][$r%s$c[grey]]"), target.nick, client.nick, comment))
target.SetExitedSnomaskSent()
target.Quit(quitMsg, nil)
target.destroy(nil)
@ -1319,7 +1443,6 @@ func klineHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res
}
for _, mcl := range clientsToKill {
mcl.SetExitedSnomaskSent()
mcl.Quit(fmt.Sprintf(mcl.t("You have been banned from this server (%s)"), reason), nil)
if mcl == client {
killClient = true
@ -1440,6 +1563,13 @@ func listHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
}
}
nick := client.Nick()
rplList := func(channel *Channel) {
if members, name, topic := channel.listData(); members != 0 {
rb.Add(nil, client.server.name, RPL_LIST, nick, name, strconv.Itoa(members), topic)
}
}
clientIsOp := client.HasMode(modes.Operator)
if len(channels) == 0 {
for _, channel := range server.channels.Channels() {
@ -1447,7 +1577,7 @@ func listHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
continue
}
if matcher.Matches(channel) {
client.RplList(channel, rb)
rplList(channel)
}
}
} else {
@ -1465,7 +1595,7 @@ func listHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
continue
}
if matcher.Matches(channel) {
client.RplList(channel, rb)
rplList(channel)
}
}
}
@ -1632,11 +1762,7 @@ func monitorRemoveHandler(server *Server, client *Client, msg ircmsg.IrcMessage,
targets := strings.Split(msg.Params[1], ",")
for _, target := range targets {
cfnick, err := CasefoldName(target)
if err != nil {
continue
}
server.monitorManager.Remove(client, cfnick)
server.monitorManager.Remove(rb.session, target)
}
return false
@ -1662,12 +1788,7 @@ func monitorAddHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb
}
// add target
casefoldedTarget, err := CasefoldName(target)
if err != nil {
continue
}
err = server.monitorManager.Add(client, casefoldedTarget, limits.MonitorEntries)
err := server.monitorManager.Add(rb.session, target, limits.MonitorEntries)
if err == errMonitorLimitExceeded {
rb.Add(nil, server.name, ERR_MONLISTFULL, client.Nick(), strconv.Itoa(limits.MonitorEntries), strings.Join(targets, ","))
break
@ -1696,14 +1817,14 @@ func monitorAddHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb
// MONITOR C
func monitorClearHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
server.monitorManager.RemoveAll(client)
server.monitorManager.RemoveAll(rb.session)
return false
}
// MONITOR L
func monitorListHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
nick := client.Nick()
monitorList := server.monitorManager.List(client)
monitorList := server.monitorManager.List(rb.session)
var nickList []string
for _, cfnick := range monitorList {
@ -1730,7 +1851,7 @@ func monitorStatusHandler(server *Server, client *Client, msg ircmsg.IrcMessage,
var online []string
var offline []string
monitorList := server.monitorManager.List(client)
monitorList := server.monitorManager.List(rb.session)
for _, name := range monitorList {
currentNick := server.getCurrentNick(name)
@ -1874,7 +1995,12 @@ func messageHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *R
return false
}
if rb.session.isTor && utils.IsRestrictedCTCPMessage(message) {
isCTCP := utils.IsRestrictedCTCPMessage(message)
if histType == history.Privmsg && !isCTCP {
client.UpdateActive(rb.session)
}
if rb.session.isTor && isCTCP {
// note that error replies are never sent for NOTICE
if histType != history.Notice {
rb.Notice(client.t("CTCP messages are disabled over Tor"))
@ -1935,18 +2061,17 @@ func dispatchMessageToTarget(client *Client, tags map[string]string, histType hi
service, isService := OragonoServices[lowercaseTarget]
_, isZNC := zncHandlers[lowercaseTarget]
if histType == history.Privmsg {
if isService || isZNC {
details := client.Details()
rb.addEchoMessage(tags, details.nickMask, details.accountName, command, target, message)
if histType != history.Privmsg {
return // NOTICE and TAGMSG to services are ignored
}
if isService {
servicePrivmsgHandler(service, server, client, message.Message, rb)
return
} else if isZNC {
zncPrivmsgHandler(client, lowercaseTarget, message.Message, rb)
return
}
}
// NOTICE and TAGMSG to services are ignored
if isService || isZNC {
return
}
@ -1957,10 +2082,20 @@ func dispatchMessageToTarget(client *Client, tags map[string]string, histType hi
}
return
}
// Restrict CTCP message for target user with +T
if user.modes.HasMode(modes.UserNoCTCP) && message.IsRestrictedCTCPMessage() {
return
}
tDetails := user.Details()
tnick := tDetails.nick
details := client.Details()
if details.account == "" && server.Defcon() <= 3 {
rb.Add(nil, server.name, ERR_NEEDREGGEDNICK, client.Nick(), tnick, client.t("Direct messages from unregistered users are temporarily restricted"))
return
}
nickMaskString := details.nickMask
accountName := details.accountName
var deliverySessions []*Session
@ -1994,21 +2129,12 @@ func dispatchMessageToTarget(client *Client, tags map[string]string, histType hi
}
// the originating session may get an echo message:
if rb.session.capabilities.Has(caps.EchoMessage) {
hasTagsCap := rb.session.capabilities.Has(caps.MessageTags)
if histType == history.Tagmsg && hasTagsCap {
rb.AddFromClient(message.Time, message.Msgid, nickMaskString, accountName, tags, command, tnick)
} else {
tagsToSend := tags
if !hasTagsCap {
tagsToSend = nil
}
rb.AddSplitMessageFromClient(nickMaskString, accountName, tagsToSend, command, tnick, message)
}
}
if histType != history.Notice && user.Away() {
rb.addEchoMessage(tags, nickMaskString, accountName, command, tnick, message)
if histType != history.Notice {
//TODO(dan): possibly implement cooldown of away notifications to users
rb.Add(nil, server.name, RPL_AWAY, client.Nick(), tnick, user.AwayMessage())
if away, awayMessage := user.Away(); away {
rb.Add(nil, server.name, RPL_AWAY, client.Nick(), tnick, awayMessage)
}
}
config := server.Config()
@ -2022,7 +2148,7 @@ func dispatchMessageToTarget(client *Client, tags map[string]string, histType hi
AccountName: accountName,
Tags: tags,
}
if !item.IsStorable() || !allowedPlusR {
if !itemIsStorable(&item, config) || !allowedPlusR {
return
}
targetedItem := item
@ -2045,6 +2171,32 @@ func dispatchMessageToTarget(client *Client, tags map[string]string, histType hi
}
}
func itemIsStorable(item *history.Item, config *Config) bool {
switch item.Type {
case history.Tagmsg:
if config.History.TagmsgStorage.Default {
for _, blacklistedTag := range config.History.TagmsgStorage.Blacklist {
if _, ok := item.Tags[blacklistedTag]; ok {
return false
}
}
return true
} else {
for _, whitelistedTag := range config.History.TagmsgStorage.Whitelist {
if _, ok := item.Tags[whitelistedTag]; ok {
return true
}
}
return false
}
case history.Privmsg, history.Notice:
// don't store CTCP other than ACTION
return !item.Message.IsRestrictedCTCPMessage()
default:
return true
}
}
// NPC <target> <sourcenick> <message>
func npcHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
target := msg.Params[0]
@ -2093,8 +2245,8 @@ func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
var checkPassed, checkFailed, passwordFailed bool
oper := server.GetOperator(msg.Params[0])
if oper != nil {
if oper.Fingerprint != "" {
if oper.Fingerprint == rb.session.certfp {
if oper.Certfp != "" {
if oper.Certfp == rb.session.certfp {
checkPassed = true
} else {
checkFailed = true
@ -2199,8 +2351,8 @@ func passHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
rb.Add(nil, server.name, ERR_ALREADYREGISTRED, client.nick, client.t("You may not reregister"))
return false
}
// only give them one try to run the PASS command (all code paths end with this
// variable being set):
// only give them one try to run the PASS command (if a server password is set,
// then all code paths end with this variable being set):
if rb.session.passStatus != serverPassUnsent {
return false
}
@ -2211,18 +2363,17 @@ func passHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
if config.Accounts.LoginViaPassCommand {
colonIndex := strings.IndexByte(password, ':')
if colonIndex != -1 && client.Account() == "" {
// TODO consolidate all login throttle checks into AccountManager
throttled, _ := client.loginThrottle.Touch()
if !throttled {
account, accountPass := password[:colonIndex], password[colonIndex+1:]
err := server.accounts.AuthenticateByPassphrase(client, account, accountPass)
if err == nil {
sendSuccessfulAccountAuth(client, rb, false, true)
// login-via-pass-command entails that we do not need to check
// an actual server password (either no password or skip-server-password)
rb.session.passStatus = serverPassSuccessful
return false
}
account, accountPass := password[:colonIndex], password[colonIndex+1:]
if strudelIndex := strings.IndexByte(account, '@'); strudelIndex != -1 {
account, rb.session.deviceID = account[:strudelIndex], account[strudelIndex+1:]
}
err := server.accounts.AuthenticateByPassphrase(client, account, accountPass)
if err == nil {
sendSuccessfulAccountAuth(client, rb, false, true)
// login-via-pass-command entails that we do not need to check
// an actual server password (either no password or skip-server-password)
rb.session.passStatus = serverPassSuccessful
return false
}
}
}
@ -2251,7 +2402,7 @@ func passHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
// PING [params...]
func pingHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
rb.Add(nil, server.name, "PONG", msg.Params...)
rb.Add(nil, server.name, "PONG", server.name, msg.Params[0])
return false
}
@ -2351,8 +2502,7 @@ func relaymsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *
}
// RENAME <oldchan> <newchan> [<reason>]
func renameHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) (result bool) {
result = false
func renameHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
oldName, newName := msg.Params[0], msg.Params[1]
var reason string
if 2 < len(msg.Params) {
@ -2364,6 +2514,8 @@ func renameHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
rb.Add(nil, server.name, ERR_NOSUCHCHANNEL, client.Nick(), utils.SafeErrorParam(oldName), client.t("No such channel"))
return false
}
oldName = channel.Name()
if !(channel.ClientIsAtLeast(client, modes.ChannelOperator) || client.HasRoleCapabs("chanreg")) {
rb.Add(nil, server.name, ERR_CHANOPRIVSNEEDED, client.Nick(), oldName, client.t("You're not a channel operator"))
return false
@ -2371,14 +2523,14 @@ func renameHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
founder := channel.Founder()
if founder != "" && founder != client.Account() {
rb.Add(nil, server.name, ERR_CANNOTRENAME, client.Nick(), oldName, newName, client.t("Only channel founders can change registered channels"))
rb.Add(nil, server.name, "FAIL", "RENAME", "CANNOT_RENAME", oldName, utils.SafeErrorParam(newName), client.t("Only channel founders can change registered channels"))
return false
}
config := server.Config()
status, _ := channel.historyStatus(config)
if status == HistoryPersistent {
rb.Add(nil, server.name, ERR_CANNOTRENAME, client.Nick(), oldName, newName, client.t("Channels with persistent history cannot be renamed"))
rb.Add(nil, server.name, "FAIL", "RENAME", "CANNOT_RENAME", oldName, utils.SafeErrorParam(newName), client.t("Channels with persistent history cannot be renamed"))
return false
}
@ -2387,9 +2539,9 @@ func renameHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
if err == errInvalidChannelName {
rb.Add(nil, server.name, ERR_NOSUCHCHANNEL, client.Nick(), utils.SafeErrorParam(newName), client.t(err.Error()))
} else if err == errChannelNameInUse {
rb.Add(nil, server.name, ERR_CHANNAMEINUSE, client.Nick(), utils.SafeErrorParam(newName), client.t(err.Error()))
rb.Add(nil, server.name, "FAIL", "RENAME", "CHANNEL_NAME_IN_USE", oldName, utils.SafeErrorParam(newName), client.t(err.Error()))
} else if err != nil {
rb.Add(nil, server.name, ERR_CANNOTRENAME, client.Nick(), oldName, utils.SafeErrorParam(newName), client.t("Cannot rename channel"))
rb.Add(nil, server.name, "FAIL", "RENAME", "CANNOT_RENAME", oldName, utils.SafeErrorParam(newName), client.t("Cannot rename channel"))
}
if err != nil {
return false
@ -2485,13 +2637,18 @@ func sceneHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res
// SETNAME <realname>
func setnameHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
realname := msg.Params[0]
if realname == "" {
rb.Add(nil, server.name, "FAIL", "SETNAME", "INVALID_REALNAME", client.t("Realname is not valid"))
return false
}
client.SetRealname(realname)
details := client.Details()
// alert friends
now := time.Now().UTC()
for session := range client.Friends(caps.SetName) {
session.sendFromClientInternal(false, now, "", details.nickMask, details.account, nil, "SETNAME", details.realname)
session.sendFromClientInternal(false, now, "", details.nickMask, details.accountName, nil, "SETNAME", details.realname)
}
return false
@ -2601,6 +2758,22 @@ func userHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
return false
}
// #843: we accept either: `USER user:pass@clientid` or `USER user@clientid`
if strudelIndex := strings.IndexByte(username, '@'); strudelIndex != -1 {
username, rb.session.deviceID = username[:strudelIndex], username[strudelIndex+1:]
if colonIndex := strings.IndexByte(username, ':'); colonIndex != -1 {
var password string
username, password = username[:colonIndex], username[colonIndex+1:]
err := server.accounts.AuthenticateByPassphrase(client, username, password)
if err == nil {
sendSuccessfulAccountAuth(client, rb, false, true)
} else {
// this is wrong, but send something for debugging that will show up in a raw transcript
rb.Add(nil, server.name, ERR_SASLFAIL, client.Nick(), client.t("SASL authentication failed"))
}
}
}
err := client.SetNames(username, realname, false)
if err == errInvalidUsername {
// if client's using a unicode nick or something weird, let's just set 'em up with a stock username instead.
@ -2641,7 +2814,7 @@ func userhostHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *
if target.HasMode(modes.Operator) {
isOper = "*"
}
if target.Away() {
if away, _ := target.Away(); away {
isAway = "-"
} else {
isAway = "+"
@ -2712,7 +2885,7 @@ func webircHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
if 0 < len(info.Password) && bcrypt.CompareHashAndPassword(info.Password, givenPassword) != nil {
continue
}
if info.Fingerprint != "" && info.Fingerprint != rb.session.certfp {
if info.Certfp != "" && info.Certfp != rb.session.certfp {
continue
}
@ -2730,7 +2903,122 @@ func webircHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
return true
}
// WHO [<mask> [o]]
type whoxFields uint32 // bitset to hold the WHOX field values, 'a' through 'z'
func (fields whoxFields) Add(field rune) (result whoxFields) {
index := int(field) - int('a')
if 0 <= index && index < 26 {
return fields | (1 << index)
} else {
return fields
}
}
func (fields whoxFields) Has(field rune) bool {
index := int(field) - int('a')
if 0 <= index && index < 26 {
return (fields & (1 << index)) != 0
} else {
return false
}
}
// rplWhoReply returns the WHO(X) reply between one user and another channel/user.
// who format:
// <channel> <user> <host> <server> <nick> <H|G>[*][~|&|@|%|+][B] :<hopcount> <real name>
// whox format:
// <type> <channel> <user> <ip> <host> <server> <nick> <H|G>[*][~|&|@|%|+][B] <hops> <idle> <account> <rank> :<real name>
func (client *Client) rplWhoReply(channel *Channel, target *Client, rb *ResponseBuffer, isWhox bool, fields whoxFields, whoType string) {
params := []string{client.Nick()}
details := target.Details()
if fields.Has('t') {
params = append(params, whoType)
}
if fields.Has('c') {
fChannel := "*"
if channel != nil {
fChannel = channel.name
}
params = append(params, fChannel)
}
if fields.Has('u') {
params = append(params, details.username)
}
if fields.Has('i') {
fIP := "255.255.255.255"
if client.HasMode(modes.Operator) || client == target {
// you can only see a target's IP if they're you or you're an oper
fIP = target.IPString()
}
params = append(params, fIP)
}
if fields.Has('h') {
params = append(params, details.hostname)
}
if fields.Has('s') {
params = append(params, target.server.name)
}
if fields.Has('n') {
params = append(params, details.nick)
}
if fields.Has('f') { // "flags" (away + oper state + channel status prefix + bot)
var flags strings.Builder
if away, _ := target.Away(); away {
flags.WriteRune('G') // Gone
} else {
flags.WriteRune('H') // Here
}
if target.HasMode(modes.Operator) {
flags.WriteRune('*')
}
if channel != nil {
flags.WriteString(channel.ClientPrefixes(target, rb.session.capabilities.Has(caps.MultiPrefix)))
}
if target.HasMode(modes.Bot) {
flags.WriteRune('B')
}
params = append(params, flags.String())
}
if fields.Has('d') { // server hops from us to target
params = append(params, "0")
}
if fields.Has('l') {
params = append(params, fmt.Sprintf("%d", target.IdleSeconds()))
}
if fields.Has('a') {
fAccount := "0"
if details.accountName != "*" {
// WHOX uses "0" to mean "no account"
fAccount = details.accountName
}
params = append(params, fAccount)
}
if fields.Has('o') { // target's channel power level
//TODO: implement this
params = append(params, "0")
}
if fields.Has('r') {
params = append(params, details.realname)
}
numeric := RPL_WHOSPCRPL
if !isWhox {
numeric = RPL_WHOREPLY
// if this isn't WHOX, stick hops + realname at the end
params = append(params, "0 "+details.realname)
}
rb.Add(nil, client.server.name, numeric, params...)
}
// WHO <mask> [<filter>%<fields>,<type>]
func whoHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
mask := msg.Params[0]
var err error
@ -2748,6 +3036,26 @@ func whoHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Respo
return false
}
sFields := "cuhsnf"
whoType := "0"
isWhox := false
if len(msg.Params) > 1 && strings.Contains(msg.Params[1], "%") {
isWhox = true
whoxData := msg.Params[1]
fieldStart := strings.Index(whoxData, "%")
sFields = whoxData[fieldStart+1:]
typeIndex := strings.Index(sFields, ",")
if typeIndex > -1 && typeIndex < (len(sFields)-1) { // make sure there's , and a value after it
whoType = sFields[typeIndex+1:]
sFields = strings.ToLower(sFields[:typeIndex])
}
}
var fields whoxFields
for _, field := range sFields {
fields = fields.Add(field)
}
//TODO(dan): is this used and would I put this param in the Modern doc?
// if not, can we remove it?
//var operatorOnly bool
@ -2765,16 +3073,16 @@ func whoHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Respo
if !channel.flags.HasMode(modes.Secret) || isJoined || isOper {
for _, member := range channel.Members() {
if !member.HasMode(modes.Invisible) || isJoined || isOper {
client.rplWhoReply(channel, member, rb)
client.rplWhoReply(channel, member, rb, isWhox, fields, whoType)
}
}
}
}
} else {
// Construct set of channels the client is in.
userChannels := make(map[*Channel]bool)
userChannels := make(ChannelSet)
for _, channel := range client.Channels() {
userChannels[channel] = true
userChannels[channel] = empty{}
}
// Another client is a friend if they share at least one channel, or they are the same client.
@ -2784,7 +3092,7 @@ func whoHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Respo
}
for _, channel := range otherClient.Channels() {
if userChannels[channel] {
if _, present := userChannels[channel]; present {
return true
}
}
@ -2793,7 +3101,7 @@ func whoHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Respo
for mclient := range server.clients.FindAll(mask) {
if isOper || !mclient.HasMode(modes.Invisible) || isFriend(mclient) {
client.rplWhoReply(nil, mclient, rb)
client.rplWhoReply(nil, mclient, rb, isWhox, fields, whoType)
}
}
}
@ -2893,7 +3201,12 @@ func whowasHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
// ZNC <module> [params]
func zncHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
zncModuleHandler(client, msg.Params[0], msg.Params[1:], rb)
params := msg.Params[1:]
// #1205: compatibility with Palaver, which sends `ZNC *playback :play ...`
if len(params) == 1 && strings.IndexByte(params[0], ' ') != -1 {
params = strings.Fields(params[0])
}
zncModuleHandler(client, msg.Params[0], params, rb)
return false
}
@ -2902,3 +3215,9 @@ func unknownCommandHandler(server *Server, client *Client, msg ircmsg.IrcMessage
rb.Add(nil, server.name, ERR_UNKNOWNCOMMAND, client.Nick(), utils.SafeErrorParam(msg.Command), client.t("Unknown command"))
return false
}
// fake handler for invalid utf8
func invalidUtf8Handler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
rb.Add(nil, server.name, "FAIL", utils.SafeErrorParam(msg.Command), "INVALID_UTF8", client.t("Message rejected for containing invalid UTF-8"))
return false
}

View File

@ -51,6 +51,8 @@ Oragono supports the following channel modes:
+R | Only registered users can join the channel.
+s | Secret mode, channel won't show up in /LIST or whois replies.
+t | Only channel opers can modify the topic.
+E | Roleplaying commands are enabled in the channel.
+C | Clients are blocked from sending CTCP messages in the channel.
= Prefixes =
@ -66,9 +68,12 @@ Oragono supports the following user modes:
+a | User is marked as being away. This mode is set with the /AWAY command.
+i | User is marked as invisible (their channels are hidden from whois replies).
+o | User is an IRC operator.
+R | User only accepts messages from other registered users.
+R | User only accepts messages from other registered users.
+s | Server Notice Masks (see help with /HELPOP snomasks).
+Z | User is connected via TLS.`
+Z | User is connected via TLS.
+B | User is a bot.
+E | User can receive roleplaying commands.
+T | User is blocked from sending CTCP messages.`
snomaskHelpText = `== Server Notice Masks ==
Oragono supports the following server notice masks for operators:
@ -162,6 +167,20 @@ Provides various debugging commands for the IRCd. <option> can be one of:
* STOPCPUPROFILE: Stops the CPU profiler.
* PROFILEHEAP: Writes a memory profile.
* CRASHSERVER: Crashes the server (for use in failover testing)`,
},
"defcon": {
oper: true,
text: `DEFCON [level]
The DEFCON system can disable server features at runtime, to mitigate
spam or other hostile activity. It has five levels, which are cumulative
(i.e., level 3 includes all restrictions from level 4 and so on):
5: Normal operation
4: No new account or channel registrations
3: All users are +R; no changes to vhosts
2: No new unauthenticated connections; all channels are +R
1: No new connections except from localhost or other trusted IPs`,
},
"deoper": {
oper: true,
@ -198,6 +217,11 @@ ON <server> specifies that the ban is to be set on that specific server.
[reason] and [oper reason], if they exist, are separated by a vertical bar (|).
If "DLINE LIST" is sent, the server sends back a list of our current DLINEs.`,
},
"extjwt": {
text: `EXTJWT <target> [service_name]
Get a JSON Web Token for target (either * or a channel name).`,
},
"help": {
text: `HELP <argument>

View File

@ -29,12 +29,6 @@ const (
initialAutoSize = 32
)
// a Tagmsg that consists entirely of transient tags is not stored
var transientTags = map[string]bool{
"+draft/typing": true,
"+typing": true, // future-proofing
}
// Item represents an event (e.g., a PRIVMSG or a JOIN) and its associated data
type Item struct {
Type ItemType
@ -57,23 +51,6 @@ func (item *Item) HasMsgid(msgid string) bool {
return item.Message.Msgid == msgid
}
func (item *Item) IsStorable() bool {
switch item.Type {
case Tagmsg:
for name := range item.Tags {
if !transientTags[name] {
return true
}
}
return false // all tags were blacklisted
case Privmsg, Notice:
// don't store CTCP other than ACTION
return !item.Message.IsRestrictedCTCPMessage()
default:
return true
}
}
type Predicate func(item *Item) (matches bool)
func Reverse(results []Item) {

View File

@ -7,8 +7,7 @@ import (
"time"
)
// Selector represents a parameter to a CHATHISTORY command;
// at most one of Msgid or Time may be nonzero
// Selector represents a parameter to a CHATHISTORY command
type Selector struct {
Msgid string
Time time.Time

View File

@ -129,51 +129,6 @@ for the rejection.`,
maxParams: 2,
unsplitFinalParam: true,
},
"forbid": {
handler: hsForbidHandler,
help: `Syntax: $bFORBID <user>$b
FORBID prevents a user from using any vhost, including ones on the offer list.`,
helpShort: `$bFORBID$b prevents a user from using vhosts.`,
capabs: []string{"vhosts"},
enabled: hostservEnabled,
minParams: 1,
maxParams: 1,
},
"permit": {
handler: hsForbidHandler,
help: `Syntax: $bPERMIT <user>$b
PERMIT undoes FORBID, allowing the user to TAKE vhosts again.`,
helpShort: `$bPERMIT$b allows a user to use vhosts again.`,
capabs: []string{"vhosts"},
enabled: hostservEnabled,
minParams: 1,
maxParams: 1,
},
"offerlist": {
handler: hsOfferListHandler,
help: `Syntax: $bOFFERLIST$b
OFFERLIST lists vhosts that can be chosen without requiring operator approval;
to use one of the listed vhosts, take it with /HOSTSERV TAKE.`,
helpShort: `$bOFFERLIST$b lists vhosts that can be taken without operator approval.`,
enabled: hostservEnabled,
minParams: 0,
maxParams: 0,
},
"take": {
handler: hsTakeHandler,
help: `Syntax: $bTAKE$b <vhost>
TAKE sets your vhost to one of the vhosts in the server's offer list; to see
the offered vhosts, use /HOSTSERV OFFERLIST.`,
helpShort: `$bTAKE$b sets your vhost to one of the options from the offer list.`,
enabled: hostservEnabled,
authRequired: true,
minParams: 1,
maxParams: 1,
},
"setcloaksecret": {
handler: hsSetCloakSecretHandler,
help: `Syntax: $bSETCLOAKSECRET$b <secret> [code]
@ -238,8 +193,6 @@ func hsRequestHandler(server *Server, client *Client, command string, params []s
if err != nil {
if throttled, ok := err.(*vhostThrottleExceeded); ok {
hsNotice(rb, fmt.Sprintf(client.t("You must wait an additional %v before making another request"), throttled.timeRemaining))
} else if err == errVhostsForbidden {
hsNotice(rb, client.t("An administrator has denied you the ability to use vhosts"))
} else {
hsNotice(rb, client.t("An error occurred"))
}
@ -276,11 +229,6 @@ func hsStatusHandler(server *Server, client *Client, command string, params []st
return
}
if account.VHost.Forbidden {
hsNotice(rb, client.t("An administrator has denied you the ability to use vhosts"))
return
}
if account.VHost.ApprovedVHost != "" {
hsNotice(rb, fmt.Sprintf(client.t("Account %[1]s has vhost: %[2]s"), accountName, account.VHost.ApprovedVHost))
if !account.VHost.Enabled {
@ -382,70 +330,6 @@ func hsRejectHandler(server *Server, client *Client, command string, params []st
}
}
func hsForbidHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
user := params[0]
forbidden := command == "forbid"
_, err := server.accounts.VHostForbid(user, forbidden)
if err == errAccountDoesNotExist {
hsNotice(rb, client.t("No such account"))
} else if err != nil {
hsNotice(rb, client.t("An error occurred"))
} else {
if forbidden {
hsNotice(rb, fmt.Sprintf(client.t("User %s is no longer allowed to use vhosts"), user))
} else {
hsNotice(rb, fmt.Sprintf(client.t("User %s is now allowed to use vhosts"), user))
}
}
}
func hsOfferListHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
vhostConfig := server.Config().Accounts.VHosts
if len(vhostConfig.OfferList) == 0 {
if vhostConfig.UserRequests.Enabled {
hsNotice(rb, client.t("The server does not offer any vhosts, but you can request one with /HOSTSERV REQUEST"))
} else {
hsNotice(rb, client.t("The server does not offer any vhosts"))
}
} else {
hsNotice(rb, client.t("The following vhosts are available and can be chosen with /HOSTSERV TAKE:"))
for _, vhost := range vhostConfig.OfferList {
hsNotice(rb, vhost)
}
}
}
func hsTakeHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
config := server.Config()
vhost := params[0]
found := false
for _, offered := range config.Accounts.VHosts.OfferList {
if offered == vhost {
found = true
}
}
if !found {
hsNotice(rb, client.t("That vhost isn't being offered by the server"))
return
}
account := client.Account()
_, err := server.accounts.VHostTake(account, vhost, time.Duration(config.Accounts.VHosts.UserRequests.Cooldown))
if err != nil {
if throttled, ok := err.(*vhostThrottleExceeded); ok {
hsNotice(rb, fmt.Sprintf(client.t("You must wait an additional %v before taking a vhost"), throttled.timeRemaining))
} else if err == errVhostsForbidden {
hsNotice(rb, client.t("An administrator has denied you the ability to use vhosts"))
} else {
hsNotice(rb, client.t("An error occurred"))
}
} else {
hsNotice(rb, client.t("Successfully set vhost"))
server.snomasks.Send(sno.LocalVhosts, fmt.Sprintf("Client %s (account %s) took vhost %s", client.Nick(), account, vhost))
}
}
func hsSetCloakSecretHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
secret := params[0]
expectedCode := utils.ConfirmationCode(secret, server.ctime)

View File

@ -4,179 +4,9 @@
package irc
import (
"fmt"
"sync"
"time"
"github.com/oragono/oragono/irc/caps"
)
const (
// RegisterTimeout is how long clients have to register before we disconnect them
RegisterTimeout = time.Minute
// DefaultIdleTimeout is how long without traffic before we send the client a PING
DefaultIdleTimeout = time.Minute + 30*time.Second
// For Tor clients, we send a PING at least every 30 seconds, as a workaround for this bug
// (single-onion circuits will close unless the client sends data once every 60 seconds):
// https://bugs.torproject.org/29665
TorIdleTimeout = time.Second * 30
// This is how long a client gets without sending any message, including the PONG to our
// PING, before we disconnect them:
DefaultTotalTimeout = 2*time.Minute + 30*time.Second
// Resumeable clients (clients who have negotiated caps.Resume) get longer:
ResumeableTotalTimeout = 3*time.Minute + 30*time.Second
)
// client idleness state machine
type TimerState uint
const (
TimerUnregistered TimerState = iota // client is unregistered
TimerActive // client is actively sending commands
TimerIdle // client is idle, we sent PING and are waiting for PONG
TimerDead // client was terminated
)
type IdleTimer struct {
sync.Mutex // tier 1
// immutable after construction
registerTimeout time.Duration
session *Session
// mutable
idleTimeout time.Duration
quitTimeout time.Duration
state TimerState
timer *time.Timer
}
// Initialize sets up an IdleTimer and starts counting idle time;
// if there is no activity from the client, it will eventually be stopped.
func (it *IdleTimer) Initialize(session *Session) {
it.session = session
it.registerTimeout = RegisterTimeout
it.idleTimeout, it.quitTimeout = it.recomputeDurations()
registered := session.client.Registered()
it.Lock()
defer it.Unlock()
if registered {
it.state = TimerActive
} else {
it.state = TimerUnregistered
}
it.resetTimeout()
}
// recomputeDurations recomputes the idle and quit durations, given the client's caps.
func (it *IdleTimer) recomputeDurations() (idleTimeout, quitTimeout time.Duration) {
totalTimeout := DefaultTotalTimeout
// if they have the resume cap, wait longer before pinging them out
// to give them a chance to resume their connection
if it.session.capabilities.Has(caps.Resume) {
totalTimeout = ResumeableTotalTimeout
}
idleTimeout = DefaultIdleTimeout
if it.session.isTor {
idleTimeout = TorIdleTimeout
}
quitTimeout = totalTimeout - idleTimeout
return
}
func (it *IdleTimer) Touch() {
idleTimeout, quitTimeout := it.recomputeDurations()
it.Lock()
defer it.Unlock()
it.idleTimeout, it.quitTimeout = idleTimeout, quitTimeout
// a touch transitions TimerUnregistered or TimerIdle into TimerActive
if it.state != TimerDead {
it.state = TimerActive
it.resetTimeout()
}
}
func (it *IdleTimer) processTimeout() {
idleTimeout, quitTimeout := it.recomputeDurations()
var previousState TimerState
func() {
it.Lock()
defer it.Unlock()
it.idleTimeout, it.quitTimeout = idleTimeout, quitTimeout
previousState = it.state
// TimerActive transitions to TimerIdle, all others to TimerDead
if it.state == TimerActive {
// send them a ping, give them time to respond
it.state = TimerIdle
it.resetTimeout()
} else {
it.state = TimerDead
}
}()
if previousState == TimerActive {
it.session.Ping()
} else {
it.session.client.Quit(it.quitMessage(previousState), it.session)
it.session.client.destroy(it.session)
}
}
// Stop stops counting idle time.
func (it *IdleTimer) Stop() {
if it == nil {
return
}
it.Lock()
defer it.Unlock()
it.state = TimerDead
it.resetTimeout()
}
func (it *IdleTimer) resetTimeout() {
if it.timer != nil {
it.timer.Stop()
}
var nextTimeout time.Duration
switch it.state {
case TimerUnregistered:
nextTimeout = it.registerTimeout
case TimerActive:
nextTimeout = it.idleTimeout
case TimerIdle:
nextTimeout = it.quitTimeout
case TimerDead:
return
}
if it.timer != nil {
it.timer.Reset(nextTimeout)
} else {
it.timer = time.AfterFunc(nextTimeout, it.processTimeout)
}
}
func (it *IdleTimer) quitMessage(state TimerState) string {
switch state {
case TimerUnregistered:
return fmt.Sprintf("Registration timeout: %v", it.registerTimeout)
case TimerIdle:
// how many seconds before registered clients are timed out (IdleTimeout plus QuitTimeout).
it.Lock()
defer it.Unlock()
return fmt.Sprintf("Ping timeout: %v", (it.idleTimeout + it.quitTimeout))
default:
// shouldn't happen
return ""
}
}
// BrbTimer is a timer on the client as a whole (not an individual session) for implementing
// the BRB command and related functionality (where a client can remain online without
// having any connected sessions).

View File

@ -3,6 +3,7 @@ package irc
import (
"bufio"
"bytes"
"errors"
"net"
"unicode/utf8"
@ -13,11 +14,12 @@ import (
)
const (
maxReadQBytes = ircmsg.MaxlenTagsFromClient + 512 + 1024
maxReadQBytes = ircmsg.MaxlenTagsFromClient + MaxLineLen + 1024
)
var (
crlf = []byte{'\r', '\n'}
crlf = []byte{'\r', '\n'}
errReadQ = errors.New("ReadQ Exceeded")
)
// IRCConn abstracts away the distinction between a regular
@ -31,7 +33,7 @@ type IRCConn interface {
// these take an IRC line or lines, correctly terminated with CRLF:
WriteLine([]byte) error
WriteLines([][]byte) error
// this returns an IRC line without the terminating CRLF:
// this returns an IRC line, possibly terminated with CRLF, LF, or nothing:
ReadLine() (line []byte, err error)
Close() error
@ -76,7 +78,9 @@ func (cc *IRCStreamConn) ReadLine() (line []byte, err error) {
if isPrefix {
return nil, errReadQ
}
line = bytes.TrimSuffix(line, crlf)
if globalUtf8EnforcementSetting && !utf8.Valid(line) {
err = errInvalidUtf8
}
return
}
@ -101,9 +105,9 @@ func (wc IRCWSConn) UnderlyingConn() *utils.WrappedConn {
func (wc IRCWSConn) WriteLine(buf []byte) (err error) {
buf = bytes.TrimSuffix(buf, crlf)
// there's not much we can do about this;
// silently drop the message
if !utf8.Valid(buf) {
if !globalUtf8EnforcementSetting && !utf8.Valid(buf) {
// there's not much we can do about this;
// silently drop the message
return nil
}
return wc.conn.WriteMessage(websocket.TextMessage, buf)
@ -120,13 +124,18 @@ func (wc IRCWSConn) WriteLines(buffers [][]byte) (err error) {
}
func (wc IRCWSConn) ReadLine() (line []byte, err error) {
for {
var messageType int
messageType, line, err = wc.conn.ReadMessage()
// on empty message or non-text message, try again, block if necessary
if err != nil || (messageType == websocket.TextMessage && len(line) != 0) {
return
messageType, line, err := wc.conn.ReadMessage()
if err == nil {
if messageType == websocket.TextMessage {
return line, nil
} else {
// for purposes of fakelag, treat non-text message as an empty line
return nil, nil
}
} else if err == websocket.ErrReadLimit {
return line, errReadQ
} else {
return line, err
}
}

77
irc/jwt/extjwt.go Normal file
View File

@ -0,0 +1,77 @@
// Copyright (c) 2020 Daniel Oaks <daniel@danieloaks.net>
// Copyright (c) 2020 Shivaram Lingamneni <slingamn@cs.stanford.edu>
// released under the MIT license
package jwt
import (
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
"time"
"github.com/dgrijalva/jwt-go"
)
var (
ErrNoKeys = errors.New("No signing keys are enabled")
)
type MapClaims jwt.MapClaims
type JwtServiceConfig struct {
Expiration time.Duration
Secret string
secretBytes []byte
RSAPrivateKeyFile string `yaml:"rsa-private-key-file"`
rsaPrivateKey *rsa.PrivateKey
}
func (t *JwtServiceConfig) Postprocess() (err error) {
t.secretBytes = []byte(t.Secret)
t.Secret = ""
if t.RSAPrivateKeyFile != "" {
keyBytes, err := ioutil.ReadFile(t.RSAPrivateKeyFile)
if err != nil {
return err
}
d, _ := pem.Decode(keyBytes)
if err != nil {
return err
}
t.rsaPrivateKey, err = x509.ParsePKCS1PrivateKey(d.Bytes)
if err != nil {
privateKey, err := x509.ParsePKCS8PrivateKey(d.Bytes)
if err != nil {
return err
}
if rsaPrivateKey, ok := privateKey.(*rsa.PrivateKey); ok {
t.rsaPrivateKey = rsaPrivateKey
} else {
return fmt.Errorf("Non-RSA key type for extjwt: %T", privateKey)
}
}
}
return nil
}
func (t *JwtServiceConfig) Enabled() bool {
return t.Expiration != 0 && (len(t.secretBytes) != 0 || t.rsaPrivateKey != nil)
}
func (t *JwtServiceConfig) Sign(claims MapClaims) (result string, err error) {
claims["exp"] = time.Now().Unix() + int64(t.Expiration/time.Second)
if t.rsaPrivateKey != nil {
token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims(claims))
return token.SignedString(t.rsaPrivateKey)
} else if len(t.secretBytes) != 0 {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims(claims))
return token.SignedString(t.secretBytes)
} else {
return "", ErrNoKeys
}
}

View File

@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2015 Grafana Labs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,62 +0,0 @@
// Copyright 2014-2018 Grafana Labs
// Released under the Apache 2.0 license
// Modification notice:
// 1. All field names were changed from toml and snake case to yaml and kebab case,
// matching the Oragono project conventions
// 2. Four fields were added:
// 2.1 `Enabled`
// 2.2 `Autocreate`
// 2.3 `Timeout`
// 2.4 `RequireGroups`
// XXX: none of AttributeMap does anything in oragono, except MemberOf,
// which can be used to retrieve group memberships
package ldap
import (
"time"
)
type ServerConfig struct {
Enabled bool
Autocreate bool
Host string
Port int
Timeout time.Duration
UseSSL bool `yaml:"use-ssl"`
StartTLS bool `yaml:"start-tls"`
SkipVerifySSL bool `yaml:"ssl-skip-verify"`
RootCACert string `yaml:"root-ca-cert"`
ClientCert string `yaml:"client-cert"`
ClientKey string `yaml:"client-key"`
BindDN string `yaml:"bind-dn"`
BindPassword string `yaml:"bind-password"`
SearchFilter string `yaml:"search-filter"`
SearchBaseDNs []string `yaml:"search-base-dns"`
// user validation: require them to be in any one of these groups
RequireGroups []string `yaml:"require-groups"`
// two ways of testing group membership:
// either by searching for groups that match the user's DN
// and testing their names:
GroupSearchFilter string `yaml:"group-search-filter"`
GroupSearchFilterUserAttribute string `yaml:"group-search-filter-user-attribute"`
GroupSearchBaseDNs []string `yaml:"group-search-base-dns"`
// or by an attribute on the user's DN, typically named 'memberOf', but customizable:
Attr AttributeMap `yaml:"attributes"`
}
// AttributeMap is a struct representation for LDAP "attributes" setting
type AttributeMap struct {
Username string
Name string
Surname string
Email string
MemberOf string `yaml:"member-of"`
}

View File

@ -1,267 +0,0 @@
// Copyright 2014-2018 Grafana Labs
// Released under the Apache 2.0 license
// Modification notice:
// 1. `serverConn` was substituted for `Server` as the type of the server object
// 2. Debug loglines were altered to work with Oragono's logging system
package ldap
import (
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"io/ioutil"
"strings"
ldap "github.com/go-ldap/ldap/v3"
)
var (
// ErrInvalidCredentials is returned if username and password do not match
ErrInvalidCredentials = errors.New("Invalid Username or Password")
// ErrCouldNotFindUser is returned when username hasn't been found (not username+password)
ErrCouldNotFindUser = errors.New("Can't find user in LDAP")
)
// shouldAdminBind checks if we should use
// admin username & password for LDAP bind
func (server *serverConn) shouldAdminBind() bool {
return server.Config.BindPassword != ""
}
// singleBindDN combines the bind with the username
// in order to get the proper path
func (server *serverConn) singleBindDN(username string) string {
return fmt.Sprintf(server.Config.BindDN, username)
}
// shouldSingleBind checks if we can use "single bind" approach
func (server *serverConn) shouldSingleBind() bool {
return strings.Contains(server.Config.BindDN, "%s")
}
// Dial dials in the LDAP
// TODO: decrease cyclomatic complexity
func (server *serverConn) Dial() error {
var err error
var certPool *x509.CertPool
if server.Config.RootCACert != "" {
certPool = x509.NewCertPool()
for _, caCertFile := range strings.Split(server.Config.RootCACert, " ") {
pem, err := ioutil.ReadFile(caCertFile)
if err != nil {
return err
}
if !certPool.AppendCertsFromPEM(pem) {
return errors.New("Failed to append CA certificate " + caCertFile)
}
}
}
var clientCert tls.Certificate
if server.Config.ClientCert != "" && server.Config.ClientKey != "" {
clientCert, err = tls.LoadX509KeyPair(server.Config.ClientCert, server.Config.ClientKey)
if err != nil {
return err
}
}
for _, host := range strings.Split(server.Config.Host, " ") {
address := fmt.Sprintf("%s:%d", host, server.Config.Port)
if server.Config.UseSSL {
tlsCfg := &tls.Config{
InsecureSkipVerify: server.Config.SkipVerifySSL,
ServerName: host,
RootCAs: certPool,
}
if len(clientCert.Certificate) > 0 {
tlsCfg.Certificates = append(tlsCfg.Certificates, clientCert)
}
if server.Config.StartTLS {
server.Connection, err = ldap.Dial("tcp", address)
if err == nil {
if err = server.Connection.StartTLS(tlsCfg); err == nil {
return nil
}
}
} else {
server.Connection, err = ldap.DialTLS("tcp", address, tlsCfg)
}
} else {
server.Connection, err = ldap.Dial("tcp", address)
}
if err == nil {
return nil
}
}
return err
}
// Close closes the LDAP connection
// Dial() sets the connection with the server for this Struct. Therefore, we require a
// call to Dial() before being able to execute this function.
func (server *serverConn) Close() {
server.Connection.Close()
}
// userBind binds the user with the LDAP server
func (server *serverConn) userBind(path, password string) error {
err := server.Connection.Bind(path, password)
if err != nil {
if ldapErr, ok := err.(*ldap.Error); ok {
if ldapErr.ResultCode == 49 {
return ErrInvalidCredentials
}
}
return err
}
return nil
}
// users is helper method for the Users()
func (server *serverConn) users(logins []string) (
[]*ldap.Entry,
error,
) {
var result *ldap.SearchResult
var Config = server.Config
var err error
for _, base := range Config.SearchBaseDNs {
result, err = server.Connection.Search(
server.getSearchRequest(base, logins),
)
if err != nil {
return nil, err
}
if len(result.Entries) > 0 {
break
}
}
return result.Entries, nil
}
// getSearchRequest returns LDAP search request for users
func (server *serverConn) getSearchRequest(
base string,
logins []string,
) *ldap.SearchRequest {
attributes := []string{}
inputs := server.Config.Attr
attributes = appendIfNotEmpty(
attributes,
inputs.Username,
inputs.Surname,
inputs.Email,
inputs.Name,
inputs.MemberOf,
// In case for the POSIX LDAP schema server
server.Config.GroupSearchFilterUserAttribute,
)
search := ""
for _, login := range logins {
query := strings.Replace(
server.Config.SearchFilter,
"%s", ldap.EscapeFilter(login),
-1,
)
search = search + query
}
filter := fmt.Sprintf("(|%s)", search)
return &ldap.SearchRequest{
BaseDN: base,
Scope: ldap.ScopeWholeSubtree,
DerefAliases: ldap.NeverDerefAliases,
Attributes: attributes,
Filter: filter,
}
}
// requestMemberOf use this function when POSIX LDAP
// schema does not support memberOf, so it manually search the groups
func (server *serverConn) requestMemberOf(entry *ldap.Entry) ([]string, error) {
var memberOf []string
var config = server.Config
for _, groupSearchBase := range config.GroupSearchBaseDNs {
var filterReplace string
if config.GroupSearchFilterUserAttribute == "" {
filterReplace = getAttribute(config.Attr.Username, entry)
} else {
filterReplace = getAttribute(
config.GroupSearchFilterUserAttribute,
entry,
)
}
filter := strings.Replace(
config.GroupSearchFilter, "%s",
ldap.EscapeFilter(filterReplace),
-1,
)
server.logger.Debug("ldap", "Searching for groups with filter", filter)
// support old way of reading settings
groupIDAttribute := config.Attr.MemberOf
// but prefer dn attribute if default settings are used
if groupIDAttribute == "" || groupIDAttribute == "memberOf" {
groupIDAttribute = "dn"
}
groupSearchReq := ldap.SearchRequest{
BaseDN: groupSearchBase,
Scope: ldap.ScopeWholeSubtree,
DerefAliases: ldap.NeverDerefAliases,
Attributes: []string{groupIDAttribute},
Filter: filter,
}
groupSearchResult, err := server.Connection.Search(&groupSearchReq)
if err != nil {
return nil, err
}
if len(groupSearchResult.Entries) > 0 {
for _, group := range groupSearchResult.Entries {
memberOf = append(
memberOf,
getAttribute(groupIDAttribute, group),
)
}
break
}
}
return memberOf, nil
}
// getMemberOf finds memberOf property or request it
func (server *serverConn) getMemberOf(result *ldap.Entry) (
[]string, error,
) {
if server.Config.GroupSearchFilter == "" {
memberOf := getArrayAttribute(server.Config.Attr.MemberOf, result)
return memberOf, nil
}
memberOf, err := server.requestMemberOf(result)
if err != nil {
return nil, err
}
return memberOf, nil
}

View File

@ -1,60 +0,0 @@
// Copyright 2014-2018 Grafana Labs
// Released under the Apache 2.0 license
package ldap
import (
"strings"
ldap "github.com/go-ldap/ldap/v3"
)
func isMemberOf(memberOf []string, group string) bool {
if group == "*" {
return true
}
for _, member := range memberOf {
if strings.EqualFold(member, group) {
return true
}
}
return false
}
func getArrayAttribute(name string, entry *ldap.Entry) []string {
if strings.ToLower(name) == "dn" {
return []string{entry.DN}
}
for _, attr := range entry.Attributes {
if attr.Name == name && len(attr.Values) > 0 {
return attr.Values
}
}
return []string{}
}
func getAttribute(name string, entry *ldap.Entry) string {
if strings.ToLower(name) == "dn" {
return entry.DN
}
for _, attr := range entry.Attributes {
if attr.Name == name {
if len(attr.Values) > 0 {
return attr.Values[0]
}
}
}
return ""
}
func appendIfNotEmpty(slice []string, values ...string) []string {
for _, v := range values {
if v != "" {
slice = append(slice, v)
}
}
return slice
}

View File

@ -1,152 +0,0 @@
// Copyright (c) 2020 Matt Ouille
// Copyright (c) 2020 Shivaram Lingamneni
// released under the MIT license
// Portions of this code copyright Grafana Labs and contributors
// and released under the Apache 2.0 license
// Copying Grafana's original comment on the different cases for LDAP:
// There are several cases -
// 1. "admin" user
// Bind the "admin" user (defined in Grafana config file) which has the search privileges
// in LDAP server, then we search the targeted user through that bind, then the second
// perform the bind via passed login/password.
// 2. Single bind
// // If all the users meant to be used with Grafana have the ability to search in LDAP server
// then we bind with LDAP server with targeted login/password
// and then search for the said user in order to retrive all the information about them
// 3. Unauthenticated bind
// For some LDAP configurations it is allowed to search the
// user without login/password binding with LDAP server, in such case
// we will perform "unauthenticated bind", then search for the
// targeted user and then perform the bind with passed login/password.
// Note: the only validation we do on users is to check RequiredGroups.
// If RequiredGroups is not set and we can do a single bind, we don't
// even need to search. So our case 2 is not restricted
// to setups where all the users have search privileges: we only need to
// be able to do DN resolution via pure string substitution.
package ldap
import (
"errors"
"fmt"
ldap "github.com/go-ldap/ldap/v3"
"github.com/oragono/oragono/irc/logger"
)
var (
ErrUserNotInRequiredGroup = errors.New("User is not a member of any required groups")
)
// equivalent of Grafana's `Server`, but unexported
// also, `log` was renamed to `logger`, since the APIs are slightly different
// and this way the compiler will catch any unchanged references to Grafana's `Server.log`
type serverConn struct {
Config *ServerConfig
Connection *ldap.Conn
logger *logger.Manager
}
func CheckLDAPPassphrase(config ServerConfig, accountName, passphrase string, log *logger.Manager) (err error) {
defer func() {
if err != nil {
log.Debug("ldap", "failed passphrase check", err.Error())
}
}()
server := serverConn{
Config: &config,
logger: log,
}
err = server.Dial()
if err != nil {
return
}
defer server.Close()
server.Connection.SetTimeout(config.Timeout)
passphraseChecked := false
if server.shouldSingleBind() {
log.Debug("ldap", "attempting single bind to", accountName)
err = server.userBind(server.singleBindDN(accountName), passphrase)
passphraseChecked = (err == nil)
} else if server.shouldAdminBind() {
log.Debug("ldap", "attempting admin bind to", config.BindDN)
err = server.userBind(config.BindDN, config.BindPassword)
} else {
log.Debug("ldap", "attempting unauthenticated bind")
err = server.Connection.UnauthenticatedBind(config.BindDN)
}
if err != nil {
return
}
if passphraseChecked && len(config.RequireGroups) == 0 {
return nil
}
users, err := server.users([]string{accountName})
if err != nil {
log.Debug("ldap", "failed user lookup")
return err
}
if len(users) == 0 {
return ErrCouldNotFindUser
}
user := users[0]
log.Debug("ldap", "looked up user", user.DN)
err = server.validateGroupMembership(user)
if err != nil {
return err
}
if !passphraseChecked {
log.Debug("ldap", "rebinding", user.DN)
err = server.userBind(user.DN, passphrase)
}
return err
}
func (server *serverConn) validateGroupMembership(user *ldap.Entry) (err error) {
if len(server.Config.RequireGroups) == 0 {
return
}
var memberOf []string
memberOf, err = server.getMemberOf(user)
if err != nil {
server.logger.Debug("ldap", "could not retrieve group memberships", err.Error())
return
}
server.logger.Debug("ldap", fmt.Sprintf("found group memberships: %v", memberOf))
foundGroup := false
for _, inGroup := range memberOf {
for _, acceptableGroup := range server.Config.RequireGroups {
if inGroup == acceptableGroup {
foundGroup = true
break
}
}
if foundGroup {
break
}
}
if foundGroup {
return nil
} else {
return ErrUserNotInRequiredGroup
}
}

View File

@ -9,7 +9,6 @@ import (
"net/http"
"os"
"strings"
"sync"
"time"
"github.com/gorilla/websocket"
@ -112,12 +111,10 @@ func (nl *NetListener) serve() {
// WSListener is a listener for IRC-over-websockets (initially HTTP, then upgraded to a
// different application protocol that provides a message-based API, possibly with TLS)
type WSListener struct {
sync.Mutex // tier 1
listener *utils.ReloadableListener
httpServer *http.Server
server *Server
addr string
config utils.ListenerConfig
}
func NewWSListener(server *Server, addr string, listener *utils.ReloadableListener, config utils.ListenerConfig) (result *WSListener, err error) {
@ -125,7 +122,6 @@ func NewWSListener(server *Server, addr string, listener *utils.ReloadableListen
listener: listener,
server: server,
addr: addr,
config: config,
}
result.httpServer = &http.Server{
Handler: http.HandlerFunc(result.handle),

View File

@ -34,7 +34,7 @@ func ApplyUserModeChanges(client *Client, changes modes.ModeChanges, force bool,
for _, change := range changes {
switch change.Mode {
case modes.Bot, modes.Invisible, modes.WallOps, modes.UserRoleplaying, modes.Operator, modes.LocalOperator, modes.RegisteredOnly:
case modes.Bot, modes.Invisible, modes.WallOps, modes.UserRoleplaying, modes.Operator, modes.LocalOperator, modes.RegisteredOnly, modes.UserNoCTCP:
switch change.Op {
case modes.Add:
if (change.Mode == modes.Operator || change.Mode == modes.LocalOperator) && !(force && oper != nil) {

View File

@ -105,6 +105,7 @@ const (
RegisteredOnly Mode = 'R'
ServerNotice Mode = 's'
TLS Mode = 'Z'
UserNoCTCP Mode = 'T'
UserRoleplaying Mode = 'E'
WallOps Mode = 'w'
)

View File

@ -12,25 +12,24 @@ import (
// MonitorManager keeps track of who's monitoring which nicks.
type MonitorManager struct {
sync.RWMutex // tier 2
// client -> nicks it's watching
watching map[*Client]map[string]bool
// nick -> clients watching it
watchedby map[string]map[*Client]bool
// (all nicks must be normalized externally by casefolding)
// client -> (casefolded nick it's watching -> uncasefolded nick)
watching map[*Session]map[string]string
// casefolded nick -> clients watching it
watchedby map[string]map[*Session]empty
}
func (mm *MonitorManager) Initialize() {
mm.watching = make(map[*Client]map[string]bool)
mm.watchedby = make(map[string]map[*Client]bool)
mm.watching = make(map[*Session]map[string]string)
mm.watchedby = make(map[string]map[*Session]empty)
}
// AlertAbout alerts everyone monitoring `client`'s nick that `client` is now {on,off}line.
func (manager *MonitorManager) AlertAbout(nick, cfnick string, online bool) {
var watchers []*Client
var watchers []*Session
// safely copy the list of clients watching our nick
manager.RLock()
for client := range manager.watchedby[cfnick] {
watchers = append(watchers, client)
for session := range manager.watchedby[cfnick] {
watchers = append(watchers, session)
}
manager.RUnlock()
@ -39,58 +38,67 @@ func (manager *MonitorManager) AlertAbout(nick, cfnick string, online bool) {
command = RPL_MONONLINE
}
for _, mClient := range watchers {
mClient.Send(nil, mClient.server.name, command, mClient.Nick(), nick)
for _, session := range watchers {
session.Send(nil, session.client.server.name, command, session.client.Nick(), nick)
}
}
// Add registers `client` to receive notifications about `nick`.
func (manager *MonitorManager) Add(client *Client, nick string, limit int) error {
func (manager *MonitorManager) Add(session *Session, nick string, limit int) error {
cfnick, err := CasefoldName(nick)
if err != nil {
return err
}
manager.Lock()
defer manager.Unlock()
if manager.watching[client] == nil {
manager.watching[client] = make(map[string]bool)
if manager.watching[session] == nil {
manager.watching[session] = make(map[string]string)
}
if manager.watchedby[nick] == nil {
manager.watchedby[nick] = make(map[*Client]bool)
if manager.watchedby[cfnick] == nil {
manager.watchedby[cfnick] = make(map[*Session]empty)
}
if len(manager.watching[client]) >= limit {
if len(manager.watching[session]) >= limit {
return errMonitorLimitExceeded
}
manager.watching[client][nick] = true
manager.watchedby[nick][client] = true
manager.watching[session][cfnick] = nick
manager.watchedby[cfnick][session] = empty{}
return nil
}
// Remove unregisters `client` from receiving notifications about `nick`.
func (manager *MonitorManager) Remove(client *Client, nick string) error {
func (manager *MonitorManager) Remove(session *Session, nick string) (err error) {
cfnick, err := CasefoldName(nick)
if err != nil {
return
}
manager.Lock()
defer manager.Unlock()
// deleting from nil maps is fine
delete(manager.watching[client], nick)
delete(manager.watchedby[nick], client)
delete(manager.watching[session], cfnick)
delete(manager.watchedby[cfnick], session)
return nil
}
// RemoveAll unregisters `client` from receiving notifications about *all* nicks.
func (manager *MonitorManager) RemoveAll(client *Client) {
func (manager *MonitorManager) RemoveAll(session *Session) {
manager.Lock()
defer manager.Unlock()
for nick := range manager.watching[client] {
delete(manager.watchedby[nick], client)
for cfnick := range manager.watching[session] {
delete(manager.watchedby[cfnick], session)
}
delete(manager.watching, client)
delete(manager.watching, session)
}
// List lists all nicks that `client` is registered to receive notifications about.
func (manager *MonitorManager) List(client *Client) (nicks []string) {
func (manager *MonitorManager) List(session *Session) (nicks []string) {
manager.RLock()
defer manager.RUnlock()
for nick := range manager.watching[client] {
for _, nick := range manager.watching[session] {
nicks = append(nicks, nick)
}
return nicks

View File

@ -23,16 +23,13 @@ var (
"MemoServ", "BotServ", "OperServ",
}
restrictedCasefoldedNicks = make(map[string]bool)
restrictedSkeletons = make(map[string]bool)
restrictedCasefoldedNicks = make(utils.StringSet)
restrictedSkeletons = make(utils.StringSet)
)
func performNickChange(server *Server, client *Client, target *Client, session *Session, nickname string, rb *ResponseBuffer) error {
currentNick := client.Nick()
details := target.Details()
if details.nick == nickname {
return nil
}
hadNick := details.nick != "*"
origNickMask := details.nickMask
@ -91,13 +88,11 @@ func performNickChange(server *Server, client *Client, target *Client, session *
channel.AddHistoryItem(histItem, details.account)
}
if target.Registered() {
newCfnick := target.NickCasefolded()
if newCfnick != details.nickCasefolded {
client.server.monitorManager.AlertAbout(details.nick, details.nickCasefolded, false)
client.server.monitorManager.AlertAbout(assignedNickname, newCfnick, true)
}
} // else: these will be deferred to the end of registration (see #572)
newCfnick := target.NickCasefolded()
if newCfnick != details.nickCasefolded {
client.server.monitorManager.AlertAbout(details.nick, details.nickCasefolded, false)
client.server.monitorManager.AlertAbout(assignedNickname, newCfnick, true)
}
return nil
}
@ -134,7 +129,8 @@ func fixupNickEqualsAccount(client *Client, rb *ResponseBuffer, config *Config)
if !client.registered {
return true
}
if performNickChange(client.server, client, client, rb.session, client.AccountName(), rb) != nil {
err := performNickChange(client.server, client, client, rb.session, client.AccountName(), rb)
if err != nil && err != errNoop {
client.server.accounts.Logout(client)
nsNotice(rb, client.t("A client is already using that account; try logging out and logging back in with SASL"))
return false

View File

@ -244,7 +244,6 @@ SET modifies your account settings. The following settings are available:`,
'enforce' lets you specify a custom enforcement mechanism for your registered
nicknames. Your options are:
1. 'none' [no enforcement, overriding the server default]
or else they will be renamed]
2. 'strict' [you must already be authenticated to use the nick]
3. 'default' [use the server default]`,
@ -317,6 +316,24 @@ example with $bCERT ADD <account> <fingerprint>$b.`,
enabled: servCmdRequiresAuthEnabled,
minParams: 1,
},
"suspend": {
handler: nsSuspendHandler,
help: `Syntax: $bSUSPEND <nickname>$b
SUSPEND disables an account and disconnects the associated clients.`,
helpShort: `$bSUSPEND$b disables an account and disconnects the clients`,
minParams: 1,
capabs: []string{"accreg"},
},
"unsuspend": {
handler: nsUnsuspendHandler,
help: `Syntax: $bUNSUSPEND <nickname>$b
UNSUSPEND reverses a previous SUSPEND, restoring access to the account.`,
helpShort: `$bUNSUSPEND$b restores access to a suspended account`,
minParams: 1,
capabs: []string{"accreg"},
},
}
)
@ -424,8 +441,8 @@ func displaySetting(settingName string, settings AccountSettings, client *Client
}
case "dm-history":
effectiveValue := historyEnabled(config.History.Persistent.DirectMessages, settings.DMHistory)
csNotice(rb, fmt.Sprintf(client.t("Your stored direct message history setting is: %s"), historyStatusToString(settings.DMHistory)))
csNotice(rb, fmt.Sprintf(client.t("Given current server settings, your direct message history setting is: %s"), historyStatusToString(effectiveValue)))
nsNotice(rb, fmt.Sprintf(client.t("Your stored direct message history setting is: %s"), historyStatusToString(settings.DMHistory)))
nsNotice(rb, fmt.Sprintf(client.t("Given current server settings, your direct message history setting is: %s"), historyStatusToString(effectiveValue)))
default:
nsNotice(rb, client.t("No such setting"))
@ -445,7 +462,7 @@ func nsSetHandler(server *Server, client *Client, command string, params []strin
var finalSettings AccountSettings
var err error
switch strings.ToLower(params[0]) {
case "pass":
case "pass", "password":
nsNotice(rb, client.t("To change a password, use the PASSWD command. For details, /msg NickServ HELP PASSWD"))
return
case "enforce":
@ -649,14 +666,11 @@ func nsGroupHandler(server *Server, client *Client, command string, params []str
}
func nsLoginThrottleCheck(client *Client, rb *ResponseBuffer) (success bool) {
client.stateMutex.Lock()
throttled, remainingTime := client.loginThrottle.Touch()
client.stateMutex.Unlock()
throttled, remainingTime := client.checkLoginThrottle()
if throttled {
nsNotice(rb, fmt.Sprintf(client.t("Please wait at least %v and try again"), remainingTime))
return false
}
return true
return !throttled
}
func nsIdentifyHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
@ -685,9 +699,6 @@ func nsIdentifyHandler(server *Server, client *Client, command string, params []
// try passphrase
if passphrase != "" {
if !nsLoginThrottleCheck(client, rb) {
return
}
err = server.accounts.AuthenticateByPassphrase(client, username, passphrase)
loginSuccessful = (err == nil)
}
@ -804,6 +815,14 @@ func nsRegisterHandler(server *Server, client *Client, command string, params []
}
}
if passphrase != "" {
cfPassphrase, err := Casefold(passphrase)
if err == nil && cfPassphrase == details.nickCasefolded {
nsNotice(rb, client.t("Usage: REGISTER <passphrase> [email]")) // #1179
return
}
}
if !nsLoginThrottleCheck(client, rb) {
return
}
@ -1070,6 +1089,9 @@ func nsSessionsHandler(server *Server, client *Client, command string, params []
} else {
nsNotice(rb, fmt.Sprintf(client.t("Session %d:"), i+1))
}
if session.deviceID != "" {
nsNotice(rb, fmt.Sprintf(client.t("Device ID: %s"), session.deviceID))
}
nsNotice(rb, fmt.Sprintf(client.t("IP address: %s"), session.ip.String()))
nsNotice(rb, fmt.Sprintf(client.t("Hostname: %s"), session.hostname))
nsNotice(rb, fmt.Sprintf(client.t("Created at: %s"), session.ctime.Format(time.RFC1123)))
@ -1095,6 +1117,8 @@ func nsCertHandler(server *Server, client *Client, command string, params []stri
target, certfp = params[0], params[1]
} else if len(params) == 1 {
certfp = params[0]
} else if len(params) == 0 && verb == "add" && rb.session.certfp != "" {
certfp = rb.session.certfp // #1059
} else {
nsNotice(rb, client.t("Invalid parameters"))
return
@ -1171,3 +1195,27 @@ func nsCertHandler(server *Server, client *Client, command string, params []stri
nsNotice(rb, client.t("An error occurred"))
}
}
func nsSuspendHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
err := server.accounts.Suspend(params[0])
switch err {
case nil:
nsNotice(rb, fmt.Sprintf(client.t("Successfully suspended account %s"), params[0]))
case errAccountDoesNotExist:
nsNotice(rb, client.t("No such account"))
default:
nsNotice(rb, client.t("An error occurred"))
}
}
func nsUnsuspendHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
err := server.accounts.Unsuspend(params[0])
switch err {
case nil:
nsNotice(rb, fmt.Sprintf(client.t("Successfully un-suspended account %s"), params[0]))
case errAccountDoesNotExist:
nsNotice(rb, client.t("No such account"))
default:
nsNotice(rb, client.t("An error occurred"))
}
}

View File

@ -86,6 +86,7 @@ const (
RPL_VERSION = "351"
RPL_WHOREPLY = "352"
RPL_NAMREPLY = "353"
RPL_WHOSPCRPL = "354"
RPL_LINKS = "364"
RPL_ENDOFLINKS = "365"
RPL_ENDOFNAMES = "366"
@ -167,8 +168,6 @@ const (
ERR_CANNOTSENDRP = "573"
RPL_WHOISSECURE = "671"
RPL_YOURLANGUAGESARE = "687"
ERR_CHANNAMEINUSE = "692"
ERR_CANNOTRENAME = "693"
ERR_INVALIDMODEPARAM = "696"
ERR_LISTMODEALREADYSET = "697"
ERR_LISTMODENOTSET = "698"

View File

@ -25,7 +25,7 @@ const (
type ResponseBuffer struct {
Label string // label if this is a labeled response batch
batchID string // ID of the labeled response batch, if one has been initiated
batchType string // type of the labeled response batch (possibly `history` or `chathistory`)
batchType string // type of the labeled response batch (currently either `labeled-response` or `chathistory`)
// stack of batch IDs of nested batches, which are handled separately
// from the underlying labeled-response batch. starting a new nested batch
@ -143,6 +143,23 @@ func (rb *ResponseBuffer) AddSplitMessageFromClient(fromNickMask string, fromAcc
}
}
func (rb *ResponseBuffer) addEchoMessage(tags map[string]string, nickMask, accountName, command, target string, message utils.SplitMessage) {
if rb.session.capabilities.Has(caps.EchoMessage) {
hasTagsCap := rb.session.capabilities.Has(caps.MessageTags)
if command == "TAGMSG" {
if hasTagsCap {
rb.AddFromClient(message.Time, message.Msgid, nickMask, accountName, tags, command, target)
}
} else {
tagsToSend := tags
if !hasTagsCap {
tagsToSend = nil
}
rb.AddSplitMessageFromClient(nickMask, accountName, tagsToSend, command, target, message)
}
}
}
func (rb *ResponseBuffer) sendBatchStart(blocking bool) {
if rb.batchID != "" {
// batch already initialized
@ -200,9 +217,7 @@ func (rb *ResponseBuffer) EndNestedBatch(batchID string) {
// supported by the client (`history`, `chathistory`, or no batch, in descending order).
func (rb *ResponseBuffer) StartNestedHistoryBatch(params ...string) (batchID string) {
var batchType string
if rb.session.capabilities.Has(caps.EventPlayback) {
batchType = "history"
} else if rb.session.capabilities.Has(caps.Batch) {
if rb.session.capabilities.Has(caps.Batch) {
batchType = "chathistory"
}
if batchType != "" {

View File

@ -110,9 +110,9 @@ func sendRoleplayMessage(server *Server, client *Client, source string, targetSt
for _, session := range user.Sessions() {
session.sendSplitMsgFromClientInternal(false, source, "", nil, "PRIVMSG", tnick, splitMessage)
}
if user.Away() {
if away, awayMessage := user.Away(); away {
//TODO(dan): possibly implement cooldown of away notifications to users
rb.Add(nil, server.name, RPL_AWAY, cnick, tnick, user.AwayMessage())
rb.Add(nil, server.name, RPL_AWAY, cnick, tnick, awayMessage)
}
}
}

View File

@ -6,7 +6,6 @@
package irc
import (
"bufio"
"fmt"
"net"
"net/http"
@ -21,6 +20,7 @@ import (
"unsafe"
"github.com/goshuirc/irc-go/ircfmt"
"github.com/oragono/oragono/irc/caps"
"github.com/oragono/oragono/irc/connection_limits"
"github.com/oragono/oragono/irc/history"
@ -41,7 +41,7 @@ var (
// whitelist of caps to serve on the STS-only listener. In particular,
// never advertise SASL, to discourage people from sending their passwords:
stsOnlyCaps = caps.NewSet(caps.STS, caps.MessageTags, caps.ServerTime, caps.LabeledResponse, caps.Nope)
stsOnlyCaps = caps.NewSet(caps.STS, caps.MessageTags, caps.ServerTime, caps.Batch, caps.LabeledResponse, caps.EchoMessage, caps.Nope)
// we only have standard channels for now. TODO: any updates to this
// will also need to be reflected in CasefoldChannel
@ -80,6 +80,7 @@ type Server struct {
whoWas WhoWasList
stats Stats
semaphores ServerSemaphores
defcon uint32
}
// NewServer returns a new Oragono server.
@ -91,6 +92,7 @@ func NewServer(config *Config, logger *logger.Manager) (*Server, error) {
logger: logger,
rehashSignal: make(chan os.Signal, 1),
signals: make(chan os.Signal, len(ServerExitSignals)),
defcon: 5,
}
server.clients.Initialize()
@ -116,6 +118,9 @@ func (server *Server) Shutdown() {
//TODO(dan): Make sure we disallow new nicks
for _, client := range server.clients.AllClients() {
client.Notice("Server is shutting down")
if client.AlwaysOn() {
client.Store(IncludeLastSeen)
}
}
if err := server.store.Close(); err != nil {
@ -146,6 +151,12 @@ func (server *Server) Run() {
}
func (server *Server) checkBans(ipaddr net.IP) (banned bool, message string) {
if server.Defcon() == 1 {
if !(ipaddr.IsLoopback() || utils.IPInNets(ipaddr, server.Config().Server.secureNets)) {
return true, "New connections to this server are temporarily restricted"
}
}
// check DLINEs
isBanned, info := server.dlines.CheckIP(ipaddr)
if isBanned {
@ -211,13 +222,14 @@ func (server *Server) tryRegister(c *Client, session *Session) (exiting bool) {
}
if c.isSTSOnly {
server.playRegistrationBurst(session)
server.playSTSBurst(session)
return true
}
// client MUST send PASS if necessary, or authenticate with SASL if necessary,
// before completing the other registration commands
authOutcome := c.isAuthorized(server.Config(), session)
config := server.Config()
authOutcome := c.isAuthorized(server, config, session)
var quitMessage string
switch authOutcome {
case authFailPass:
@ -267,21 +279,30 @@ func (server *Server) tryRegister(c *Client, session *Session) (exiting bool) {
// Apply default user modes (without updating the invisible counter)
// The number of invisible users will be updated by server.stats.Register
// if we're using default user mode +i.
for _, defaultMode := range server.Config().Accounts.defaultUserModes {
for _, defaultMode := range config.Accounts.defaultUserModes {
c.SetMode(defaultMode, true)
}
// registration has succeeded:
c.SetRegistered()
// count new user in statistics
server.stats.Register(c.HasMode(modes.Invisible))
server.monitorManager.AlertAbout(c.Nick(), c.NickCasefolded(), true)
server.playRegistrationBurst(session)
return false
}
func (server *Server) playSTSBurst(session *Session) {
nick := utils.SafeErrorParam(session.client.preregNick)
session.Send(nil, server.name, RPL_WELCOME, nick, fmt.Sprintf("Welcome to the Internet Relay Network %s", nick))
session.Send(nil, server.name, RPL_YOURHOST, nick, fmt.Sprintf("Your host is %[1]s, running version %[2]s", server.name, "oragono"))
session.Send(nil, server.name, RPL_CREATED, nick, fmt.Sprintf("This server was created %s", time.Time{}.Format(time.RFC1123)))
session.Send(nil, server.name, RPL_MYINFO, nick, server.name, "oragono", "o", "o", "o")
session.Send(nil, server.name, RPL_ISUPPORT, nick, "CASEMAPPING=ascii", "are supported by this server")
session.Send(nil, server.name, ERR_NOMOTD, nick, "MOTD is unavailable")
for _, line := range server.Config().Server.STS.bannerLines {
session.Send(nil, server.name, "NOTICE", nick, line)
}
}
func (server *Server) playRegistrationBurst(session *Session) {
c := session.client
// continue registration
@ -297,13 +318,6 @@ func (server *Server) playRegistrationBurst(session *Session) {
session.Send(nil, server.name, RPL_CREATED, d.nick, fmt.Sprintf(c.t("This server was created %s"), server.ctime.Format(time.RFC1123)))
session.Send(nil, server.name, RPL_MYINFO, d.nick, server.name, Ver, rplMyInfo1, rplMyInfo2, rplMyInfo3)
if c.isSTSOnly {
for _, line := range server.Config().Server.STS.bannerLines {
c.Notice(line)
}
return
}
rb := NewResponseBuffer(session)
server.RplISupport(c, rb)
server.Lusers(c, rb)
@ -418,40 +432,11 @@ func (client *Client) getWhoisOf(target *Client, rb *ResponseBuffer) {
}
}
rb.Add(nil, client.server.name, RPL_WHOISIDLE, cnick, tnick, strconv.FormatUint(target.IdleSeconds(), 10), strconv.FormatInt(target.SignonTime(), 10), client.t("seconds idle, signon time"))
if target.Away() {
rb.Add(nil, client.server.name, RPL_AWAY, cnick, tnick, target.AwayMessage())
if away, awayMessage := target.Away(); away {
rb.Add(nil, client.server.name, RPL_AWAY, cnick, tnick, awayMessage)
}
}
// rplWhoReply returns the WHO reply between one user and another channel/user.
// <channel> <user> <host> <server> <nick> ( "H" / "G" ) ["*"] [ ( "@" / "+" ) ]
// :<hopcount> <real name>
func (client *Client) rplWhoReply(channel *Channel, target *Client, rb *ResponseBuffer) {
channelName := "*"
flags := ""
if target.Away() {
flags = "G"
} else {
flags = "H"
}
if target.HasMode(modes.Operator) {
flags += "*"
}
if channel != nil {
// TODO is this right?
flags += channel.ClientPrefixes(target, rb.session.capabilities.Has(caps.MultiPrefix))
channelName = channel.name
}
if target.HasMode(modes.Bot) {
flags += "B"
}
details := target.Details()
// hardcode a hopcount of 0 for now
rb.Add(nil, client.server.name, RPL_WHOREPLY, client.Nick(), channelName, details.username, details.hostname, client.server.name, details.nick, flags, "0 "+details.realname)
}
// rehash reloads the config and applies the changes from the config file.
func (server *Server) rehash() error {
server.logger.Info("server", "Attempting rehash")
@ -487,6 +472,7 @@ func (server *Server) applyConfig(config *Config) (err error) {
server.name = config.Server.Name
server.nameCasefolded = config.Server.nameCasefolded
globalCasemappingSetting = config.Server.Casemapping
globalUtf8EnforcementSetting = config.Server.EnforceUtf8
} else {
// enforce configs that can't be changed after launch:
if server.name != config.Server.Name {
@ -495,6 +481,8 @@ func (server *Server) applyConfig(config *Config) (err error) {
return fmt.Errorf("Datastore path cannot be changed after launching the server, rehash aborted")
} else if globalCasemappingSetting != config.Server.Casemapping {
return fmt.Errorf("Casemapping cannot be changed after launching the server, rehash aborted")
} else if globalUtf8EnforcementSetting != config.Server.EnforceUtf8 {
return fmt.Errorf("UTF-8 enforcement cannot be changed after launching the server, rehash aborted")
} else if oldConfig.Accounts.Multiclient.AlwaysOn != config.Accounts.Multiclient.AlwaysOn {
return fmt.Errorf("Default always-on setting cannot be changed after launching the server, rehash aborted")
} else if oldConfig.Server.Relaying.Enabled != config.Server.Relaying.Enabled {
@ -538,7 +526,7 @@ func (server *Server) applyConfig(config *Config) (err error) {
server.channels.loadRegisteredChannels(config)
}
// resize history buffers as needed
if oldConfig.History != config.History {
if config.historyChangedFrom(oldConfig) {
for _, channel := range server.channels.Channels() {
channel.resizeHistory(config)
}
@ -665,35 +653,6 @@ func (server *Server) setupPprofListener(config *Config) {
}
}
func (config *Config) loadMOTD() (err error) {
if config.Server.MOTD != "" {
file, err := os.Open(config.Server.MOTD)
if err == nil {
defer file.Close()
reader := bufio.NewReader(file)
for {
line, err := reader.ReadString('\n')
if err != nil {
break
}
line = strings.TrimRight(line, "\r\n")
if config.Server.MOTDFormatting {
line = ircfmt.Unescape(line)
}
// "- " is the required prefix for MOTD, we just add it here to make
// bursting it out to clients easier
line = fmt.Sprintf("- %s", line)
config.Server.motdLines = append(config.Server.motdLines, line)
}
}
}
return
}
func (server *Server) loadDatastore(config *Config) error {
// open the datastore and load server state for which it (rather than config)
// is the source of truth
@ -968,26 +927,6 @@ func (matcher *elistMatcher) Matches(channel *Channel) bool {
return true
}
// RplList returns the RPL_LIST numeric for the given channel.
func (target *Client) RplList(channel *Channel, rb *ResponseBuffer) {
// get the correct number of channel members
var memberCount int
if target.HasMode(modes.Operator) || channel.hasClient(target) {
memberCount = len(channel.Members())
} else {
for _, member := range channel.Members() {
if !member.HasMode(modes.Invisible) {
memberCount++
}
}
}
// #704: some channels are kept around even with no members
if memberCount != 0 {
rb.Add(nil, target.server.name, RPL_LIST, target.nick, channel.name, strconv.Itoa(memberCount), channel.topic)
}
}
var (
infoString1 = strings.Split(` ·
·

View File

@ -350,11 +350,11 @@ func initializeServices() {
if err != nil {
panic(err)
}
restrictedCasefoldedNicks[cfName] = true
restrictedCasefoldedNicks.Add(cfName)
skeleton, err := Skeleton(restrictedNickname)
if err != nil {
panic(err)
}
restrictedSkeletons[skeleton] = true
restrictedSkeletons.Add(skeleton)
}
}

View File

@ -7,7 +7,6 @@ package irc
import (
"errors"
"io"
"strings"
"sync"
"github.com/oragono/oragono/irc/utils"
@ -59,27 +58,24 @@ func (socket *Socket) Close() {
// Read returns a single IRC line from a Socket.
func (socket *Socket) Read() (string, error) {
// immediately fail if Close() has been called, even if there's
// still data in a bufio.Reader or websocket buffer:
if socket.IsClosed() {
return "", io.EOF
}
lineBytes, err := socket.conn.ReadLine()
// convert bytes to string
line := string(lineBytes)
// read last message properly (such as ERROR/QUIT/etc), just fail next reads/writes
if err == io.EOF {
socket.Close()
// process last message properly (such as ERROR/QUIT/etc), just fail next reads/writes
if line != "" {
err = nil
}
}
if err == io.EOF && strings.TrimSpace(line) != "" {
// don't do anything
} else if err != nil {
return "", err
}
return line, nil
return line, err
}
// Write sends the given string out of Socket. Requirements:

View File

@ -60,6 +60,12 @@ const (
// this happens-before all IRC connections and all casefolding operations.
var globalCasemappingSetting Casemapping = CasemappingPRECIS
// XXX analogous unsynchronized global variable controlling utf8 validation
// if this is off, you get the traditional IRC behavior (relaying any valid RFC1459
// octets) and invalid utf8 messages are silently dropped for websocket clients only.
// if this is on, invalid utf8 inputs get a FAIL reply.
var globalUtf8EnforcementSetting bool
// Each pass of PRECIS casefolding is a composition of idempotent operations,
// but not idempotent itself. Therefore, the spec says "do it four times and hope
// it converges" (lolwtf). Golang's PRECIS implementation has a "repeat" option,

View File

@ -28,17 +28,6 @@ func (clients ClientSet) Has(client *Client) bool {
return ok
}
type StringSet map[string]empty
func (s StringSet) Has(str string) bool {
_, ok := s[str]
return ok
}
func (s StringSet) Add(str string) {
s[str] = empty{}
}
// MemberSet is a set of members with modes.
type MemberSet map[*Client]*modes.ModeSet
@ -69,4 +58,4 @@ func (members MemberSet) AnyHasMode(mode modes.Mode) bool {
}
// ChannelSet is a set of channels.
type ChannelSet map[*Channel]bool
type ChannelSet map[*Channel]empty

View File

@ -4,14 +4,14 @@
package utils
import (
"bytes"
"regexp"
"regexp/syntax"
"strings"
)
// yet another glob implementation in Go
func addRegexp(buf *bytes.Buffer, glob string, submatch bool) (err error) {
func addRegexp(buf *strings.Builder, glob string, submatch bool) (err error) {
for _, r := range glob {
switch r {
case '*':
@ -36,7 +36,7 @@ func addRegexp(buf *bytes.Buffer, glob string, submatch bool) (err error) {
}
func CompileGlob(glob string, submatch bool) (result *regexp.Regexp, err error) {
var buf bytes.Buffer
var buf strings.Builder
buf.WriteByte('^')
err = addRegexp(&buf, glob, submatch)
if err != nil {
@ -50,7 +50,7 @@ func CompileGlob(glob string, submatch bool) (result *regexp.Regexp, err error)
// This is used for channel ban/invite/exception lists. It's applicable to k-lines
// but we're not using it there yet.
func CompileMasks(masks []string) (result *regexp.Regexp, err error) {
var buf bytes.Buffer
var buf strings.Builder
buf.WriteString("^(")
for i, mask := range masks {
err = addRegexp(&buf, mask, false)

View File

@ -10,27 +10,25 @@ import (
"time"
)
type e struct{}
// Semaphore is a counting semaphore.
// A semaphore of capacity 1 can be used as a trylock.
type Semaphore (chan e)
type Semaphore (chan empty)
// Initialize initializes a semaphore to a given capacity.
func (semaphore *Semaphore) Initialize(capacity int) {
*semaphore = make(chan e, capacity)
*semaphore = make(chan empty, capacity)
}
// Acquire acquires a semaphore, blocking if necessary.
func (semaphore *Semaphore) Acquire() {
(*semaphore) <- e{}
(*semaphore) <- empty{}
}
// TryAcquire tries to acquire a semaphore, returning whether the acquire was
// successful. It never blocks.
func (semaphore *Semaphore) TryAcquire() (acquired bool) {
select {
case (*semaphore) <- e{}:
case (*semaphore) <- empty{}:
return true
default:
return false
@ -47,7 +45,7 @@ func (semaphore *Semaphore) AcquireWithTimeout(timeout time.Duration) (acquired
timer := time.NewTimer(timeout)
select {
case (*semaphore) <- e{}:
case (*semaphore) <- empty{}:
acquired = true
case <-timer.C:
acquired = false
@ -61,7 +59,7 @@ func (semaphore *Semaphore) AcquireWithTimeout(timeout time.Duration) (acquired
// Note that if the context is already expired, the acquire may succeed anyway.
func (semaphore *Semaphore) AcquireWithContext(ctx context.Context) (acquired bool) {
select {
case (*semaphore) <- e{}:
case (*semaphore) <- empty{}:
acquired = true
case <-ctx.Done():
acquired = false

View File

@ -4,7 +4,6 @@
package utils
import (
"bytes"
"strings"
"time"
)
@ -98,7 +97,7 @@ func (sm *SplitMessage) Is512() bool {
type TokenLineBuilder struct {
lineLen int
delim string
buf bytes.Buffer
buf strings.Builder
result []string
}

17
irc/utils/types.go Normal file
View File

@ -0,0 +1,17 @@
// Copyright (c) 2020 Shivaram Lingamneni
// released under the MIT license
package utils
type empty struct{}
type StringSet map[string]empty
func (s StringSet) Has(str string) bool {
_, ok := s[str]
return ok
}
func (s StringSet) Add(str string) {
s[str] = empty{}
}

View File

@ -7,7 +7,7 @@ import "fmt"
const (
// SemVer is the semantic version of Oragono.
SemVer = "2.2.0-unreleased"
SemVer = "2.4.0-unreleased"
)
var (

View File

@ -10,6 +10,7 @@ import (
"time"
"github.com/oragono/oragono/irc/history"
"github.com/oragono/oragono/irc/utils"
)
const (
@ -53,10 +54,16 @@ func zncWireTimeToTime(str string) (result time.Time) {
return time.Unix(seconds, int64(fraction*1000000000)).UTC()
}
func timeToZncWireTime(t time.Time) (result string) {
secs := t.Unix()
nano := t.UnixNano() - (secs * 1000000000)
return fmt.Sprintf("%d.%d", secs, nano)
}
type zncPlaybackTimes struct {
start time.Time
end time.Time
targets StringSet // nil for "*" (everything), otherwise the channel names
targets utils.StringSet // nil for "*" (everything), otherwise the channel names
setAt time.Time
}
@ -77,19 +84,33 @@ func (z *zncPlaybackTimes) ValidFor(target string) bool {
}
// https://wiki.znc.in/Playback
func zncPlaybackHandler(client *Client, command string, params []string, rb *ResponseBuffer) {
if len(params) == 0 {
return
}
switch strings.ToLower(params[0]) {
case "play":
zncPlaybackPlayHandler(client, command, params, rb)
case "list":
zncPlaybackListHandler(client, command, params, rb)
default:
return
}
}
// PRIVMSG *playback :play <target> [lower_bound] [upper_bound]
// e.g., PRIVMSG *playback :play * 1558374442
func zncPlaybackHandler(client *Client, command string, params []string, rb *ResponseBuffer) {
func zncPlaybackPlayHandler(client *Client, command string, params []string, rb *ResponseBuffer) {
if len(params) < 2 || len(params) > 4 {
return
} else if strings.ToLower(params[0]) != "play" {
return
}
targetString := params[1]
now := time.Now().UTC()
var start, end time.Time
switch len(params) {
case 2:
// #1205: this should have the same semantics as `LATEST *`
case 3:
// #831: this should have the same semantics as `LATEST timestamp=qux`,
// or equivalently `BETWEEN timestamp=$now timestamp=qux`, as opposed to
@ -102,7 +123,7 @@ func zncPlaybackHandler(client *Client, command string, params []string, rb *Res
end = zncWireTimeToTime(params[3])
}
var targets StringSet
var targets utils.StringSet
var nickTargets []string
// three cases:
@ -121,12 +142,15 @@ func zncPlaybackHandler(client *Client, command string, params []string, rb *Res
// 3.3 When the client sends a subsequent redundant JOIN line for those
// channels; redundant JOIN is a complete no-op so we won't replay twice
playPrivmsgs := false
if params[1] == "*" {
zncPlayPrivmsgs(client, rb, "*", start, end)
playPrivmsgs = true // XXX nil `targets` means "every channel"
} else {
targets = make(StringSet)
targets = make(utils.StringSet)
for _, targetName := range strings.Split(targetString, ",") {
if strings.HasPrefix(targetName, "#") {
if targetName == "*self" {
playPrivmsgs = true
} else if strings.HasPrefix(targetName, "#") {
if cfTarget, err := CasefoldChannel(targetName); err == nil {
targets.Add(cfTarget)
}
@ -138,6 +162,10 @@ func zncPlaybackHandler(client *Client, command string, params []string, rb *Res
}
}
if playPrivmsgs {
zncPlayPrivmsgs(client, rb, "*", start, end)
}
rb.session.zncPlaybackTimes = &zncPlaybackTimes{
start: start,
end: end,
@ -169,3 +197,22 @@ func zncPlayPrivmsgs(client *Client, rb *ResponseBuffer, target string, after, b
client.replayPrivmsgHistory(rb, items, "", true)
}
}
// PRIVMSG *playback :list
func zncPlaybackListHandler(client *Client, command string, params []string, rb *ResponseBuffer) {
nick := client.Nick()
for _, channel := range client.Channels() {
_, sequence, err := client.server.GetHistorySequence(channel, client, "")
if err != nil {
client.server.logger.Error("internal", "couldn't get history sequence for ZNC list", err.Error())
continue
}
items, _, err := sequence.Between(history.Selector{}, history.Selector{}, 1) // i.e., LATEST * 1
if err != nil {
client.server.logger.Error("internal", "couldn't query history for ZNC list", err.Error())
} else if len(items) != 0 {
stamp := timeToZncWireTime(items[0].Message.Time)
rb.Add(nil, "*playback!znc@znc.in", "PRIVMSG", nick, fmt.Sprintf("%s 0 %s", channel.Name(), stamp))
}
}
}

View File

@ -1,8 +1,8 @@
{
"= Help Topics =\n\nCommands:\n%[1]s\n\nRPL_ISUPPORT Tokens:\n%[2]s\n\nInformation:\n%[3]s": "",
"== Channel Modes ==\n\nOragono supports the following channel modes:\n\n +b | Client masks that are banned from the channel (e.g. *!*@127.0.0.1)\n +e | Client masks that are exempted from bans.\n +I | Client masks that are exempted from the invite-only flag.\n +i | Invite-only mode, only invited clients can join the channel.\n +k | Key required when joining the channel.\n +l | Client join limit for the channel.\n +m | Moderated mode, only privileged clients can talk on the channel.\n +n | No-outside-messages mode, only users that are on the channel can send\n | messages to it.\n +R | Only registered users can join the channel.\n +s | Secret mode, channel won't show up in /LIST or whois replies.\n +t | Only channel opers can modify the topic.\n\n= Prefixes =\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.": "",
"== Channel Modes ==\n\nOragono supports the following channel modes:\n\n +b | Client masks that are banned from the channel (e.g. *!*@127.0.0.1)\n +e | Client masks that are exempted from bans.\n +I | Client masks that are exempted from the invite-only flag.\n +i | Invite-only mode, only invited clients can join the channel.\n +k | Key required when joining the channel.\n +l | Client join limit for the channel.\n +m | Moderated mode, only privileged clients can talk on the channel.\n +n | No-outside-messages mode, only users that are on the channel can send\n | messages to it.\n +R | Only registered users can join the channel.\n +s | Secret mode, channel won't show up in /LIST or whois replies.\n +t | Only channel opers can modify the topic.\n +E | Roleplaying commands are enabled in the channel.\n +C | Clients are blocked from sending CTCP messages in the channel.\n\n= Prefixes =\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.": "",
"== Server Notice Masks ==\n\nOragono supports the following server notice masks for operators:\n\n a | Local announcements.\n c | Local client connections.\n j | Local channel actions.\n k | Local kills.\n n | Local nick changes.\n o | Local oper actions.\n q | Local quits.\n t | Local /STATS usage.\n u | Local client account actions.\n x | Local X-lines (DLINE/KLINE/etc).\n v | Local vhost changes.\n\nTo set a snomask, do this with your nickname:\n\n /MODE <nick> +s <chars>\n\nFor instance, this would set the kill, oper, account and xline snomasks on dan:\n\n /MODE dan +s koux": "",
"== User Modes ==\n\nOragono supports the following user modes:\n\n +a | User is marked as being away. This mode is set with the /AWAY command.\n +i | User is marked as invisible (their channels are hidden from whois replies).\n +o | User is an IRC operator.\n +R | User only accepts messages from other registered users. \n +s | Server Notice Masks (see help with /HELPOP snomasks).\n +Z | User is connected via TLS.": "",
"== User Modes ==\n\nOragono supports the following user modes:\n\n +a | User is marked as being away. This mode is set with the /AWAY command.\n +i | User is marked as invisible (their channels are hidden from whois replies).\n +o | User is an IRC operator.\n +R | User only accepts messages from other registered users. \n +s | Server Notice Masks (see help with /HELPOP snomasks).\n +Z | User is connected via TLS.\n +B | User is a bot.\n +E | User can receive roleplaying commands.\n +T | User is blocked from sending CTCP messages.": "",
"@+client-only-tags TAGMSG <target>{,<target>}\n\nSends the given client-only tags to the given targets as a TAGMSG. See the IRCv3\nspecs for more info: http://ircv3.net/specs/core/message-tags-3.3.html": "",
"ACC LS\nACC REGISTER <accountname> [callback_namespace:]<callback> [cred_type] :<credential>\nACC VERIFY <accountname> <auth_code>\n\nUsed in account registration. See the relevant specs for more info:\nhttps://oragono.io/specs.html": "",
"AMBIANCE <target> <text to be sent>\n\nThe AMBIANCE command is used to send a scene notification to the given target.": "",
@ -13,8 +13,10 @@
"CAP <subcommand> [:<capabilities>]\n\nUsed in capability negotiation. See the IRCv3 specs for more info:\nhttp://ircv3.net/specs/core/capability-negotiation-3.1.html\nhttp://ircv3.net/specs/core/capability-negotiation-3.2.html": "",
"CHATHISTORY [params]\n\nCHATHISTORY is a history replay command associated with the IRCv3\nspecification draft/chathistory. See this document:\nhttps://github.com/ircv3/ircv3-specifications/pull/393": "",
"DEBUG <option>\n\nProvides various debugging commands for the IRCd. <option> can be one of:\n\n* GCSTATS: Garbage control statistics.\n* NUMGOROUTINE: Number of goroutines in use.\n* STARTCPUPROFILE: Starts the CPU profiler.\n* STOPCPUPROFILE: Stops the CPU profiler.\n* PROFILEHEAP: Writes a memory profile.\n* CRASHSERVER: Crashes the server (for use in failover testing)": "",
"DEFCON [level]\n\nThe DEFCON system can disable server features at runtime, to mitigate\nspam or other hostile activity. It has five levels, which are cumulative\n(i.e., level 3 includes all restrictions from level 4 and so on):\n\n5: Normal operation\n4: No new account or channel registrations\n3: All users are +R; no changes to vhosts\n2: No new unauthenticated connections; all channels are +R\n1: No new connections except from localhost or other trusted IPs": "",
"DEOPER\n\nDEOPER removes the IRCop privileges granted to you by a successful /OPER.": "",
"DLINE [ANDKILL] [MYSELF] [duration] <ip>/<net> [ON <server>] [reason [| oper reason]]\nDLINE LIST\n\nBans an IP address or network from connecting to the server. If the duration is\ngiven then only for that long. The reason is shown to the user themselves, but\neveryone else will see a standard message. The oper reason is shown to\noperators getting info about the DLINEs that exist.\n\nBans are saved across subsequent launches of the server.\n\n\"ANDKILL\" means that all matching clients are also removed from the server.\n\n\"MYSELF\" is required when the DLINE matches the address the person applying it is connected\nfrom. If \"MYSELF\" is not given, trying to DLINE yourself will result in an error.\n\n[duration] can be of the following forms:\n\t1y 12mo 31d 10h 8m 13s\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24\n\nON <server> specifies that the ban is to be set on that specific server.\n\n[reason] and [oper reason], if they exist, are separated by a vertical bar (|).\n\nIf \"DLINE LIST\" is sent, the server sends back a list of our current DLINEs.": "",
"EXTJWT <target> [service_name]\n\nGet a JSON Web Token for target (either * or a channel name).": "",
"HELP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.": "",
"HELPOP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.": "",
"HISTORY <target> [limit]\n\nReplay message history. <target> can be a channel name, \"me\" to replay direct\nmessage history, or a nickname to replay another client's direct message\nhistory (they must be logged into the same account as you). [limit] can be\neither an integer (the maximum number of messages to replay), or a time\nduration like 10m or 1h (the time window within which to replay messages).": "",
@ -53,12 +55,14 @@
"SANICK <currentnick> <newnick>\n\nGives the given user a new nickname.": "",
"SCENE <target> <text to be sent>\n\nThe SCENE command is used to send a scene notification to the given target.": "",
"SETNAME <realname>\n\nThe SETNAME command updates the realname to be the newly-given one.": "",
"SUMMON [parameters]\n\nThe SUMMON command is not implemented.": "",
"TIME [server]\n\nShows the time of the current, or the given, server.": "",
"TOPIC <channel> [topic]\n\nIf [topic] is given, sets the topic in the channel to that. If [topic] is not\ngiven, views the current topic on the channel.": "",
"UNDLINE <ip>/<net>\n\nRemoves an existing ban on an IP address or a network.\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24": "",
"UNKLINE <mask>\n\nRemoves an existing ban on a mask.\n\nFor example:\n\tdan\n\tdan!5*@127.*": "",
"USER <username> 0 * <realname>\n\nUsed in connection registration, sets your username and realname to the given\nvalues (though your username may also be looked up with Ident).": "",
"USERHOST <nickname>{ <nickname>}\n\t\t\nShows information about the given users. Takes up to 10 nicknames.": "",
"USERS [parameters]\n\nThe USERS command is not implemented.": "",
"VERSION [server]\n\nViews the version of software and the RPL_ISUPPORT tokens for the given server.": "",
"WEBIRC <password> <gateway> <hostname> <ip> [:<flags>]\n\nUsed by web<->IRC gateways and bouncers, the WEBIRC command allows gateways to\npass-through the real IP addresses of clients:\nircv3.net/specs/extensions/webirc.html\n\n<flags> is a list of space-separated strings indicating various details about\nthe connection from the client to the gateway, such as:\n\n- tls: this flag indicates that the client->gateway connection is secure": "",
"WHO <name> [o]\n\nReturns information for the given user.": "",

View File

@ -86,6 +86,7 @@
"Channel renamed": "",
"Channel renamed: %s": "",
"Channels with persistent history cannot be renamed": "",
"Client %s is always-on and cannot be fully removed by /KILL; consider /NS SUSPEND instead": "",
"Client reconnected": "",
"Client reconnected (message history may have been lost)": "",
"Client reconnected (up to %d seconds of message history lost)": "",
@ -95,6 +96,7 @@
"Could not accept ownership of channel %s": "",
"Could not delete message": "",
"Could not find given client": "",
"Could not generate EXTJWT token": "",
"Could not look up account name, proceeding anyway": "",
"Could not parse IP address or CIDR network": "",
"Could not register": "",
@ -106,9 +108,12 @@
"Could not transfer channel": "",
"Could not ungroup nick": "",
"Created at: %s": "",
"Current DEFCON level is %d": "",
"Current global users %[1]s, max %[2]s": "",
"Current local users %[1]s, max %[2]s": "",
"Data export for %[1]s completed and written to %[2]s": "",
"Device ID: %s": "",
"Direct messages from unregistered users are temporarily restricted": "",
"End of /HELPOP": "",
"End of /INFO": "",
"End of /WHOIS list": "",
@ -154,6 +159,7 @@
"Insufficient privileges": "",
"Internal error": "",
"Invalid CAP subcommand": "",
"Invalid DEFCON parameter": "",
"Invalid account name": "",
"Invalid certificate fingerprint": "",
"Invalid channel name": "",
@ -165,6 +171,7 @@
"Invalid params": "",
"Invalid regex": "",
"Invalid vhost": "",
"It was built from git hash %s.": "",
"It was rejected for reason: %s": "",
"JOIN 0 is not allowed": "",
"Language %s is not supported by this server": "",
@ -173,6 +180,7 @@
"MOTD File is missing": "",
"Malformed username": "",
"Mask isn't valid": "",
"Message rejected for containing invalid UTF-8": "",
"Messages could not be retrieved": "",
"Multiclient functionality is currently disabled for your account": "",
"Multiclient functionality is currently disabled for your account, but you can opt in": "",
@ -189,6 +197,7 @@
"No such channel": "",
"No such module [%s]": "",
"No such nick": "",
"No such service": "",
"No such setting": "",
"No text to send": "",
"No topic is set": "",
@ -207,6 +216,7 @@
"Purge reason: %s": "",
"Purged at: %s": "",
"Purged by operator: %s": "",
"Realname is not valid": "",
"Received malformed line": "",
"Registered at: %s": "",
"Registered channel: %s": "",
@ -228,6 +238,7 @@
"SASL authentication failed: Passphrase too long": "",
"SASL authentication failed: authcid and authzid should be the same": "",
"SASL message too long": "",
"SUMMON has been disabled": "",
"Server notice masks": "",
"Session %d (currently attached session):": "",
"Session %d:": "",
@ -247,9 +258,11 @@
"Successfully registered account %s": "",
"Successfully rejected vhost request for %s": "",
"Successfully reset channel access": "",
"Successfully set persistent mode %s%s on %s": "",
"Successfully set persistent mode %[1]s on %[2]s": "",
"Successfully set vhost": "",
"Successfully suspended account %s": "",
"Successfully transferred channel %[1]s to account %[2]s": "",
"Successfully un-suspended account %s": "",
"Successfully ungrouped nick %s with your account": "",
"Successfully unpurged channel %s from the server": "",
"Successfully unregistered account %s": "",
@ -272,6 +285,7 @@
"This ban matches you. To KLINE yourself, you must use the command: /KLINE MYSELF <arguments>": "",
"This command has been disabled by the server administrators": "",
"This feature has been disabled by the server administrators": "",
"This is Oragono version %s.": "",
"This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.": "",
"This server requires that you wait %v after connecting before you can use /LIST. You have %v left.": "",
"This server was created %s": "",
@ -285,10 +299,12 @@
"Transfer of channel %[1]s to account %[2]s succeeded, pending acceptance": "",
"Translators:": "",
"Try again later": "",
"USERS has been disabled": "",
"Unknown command": "",
"Unknown command. To see available commands, run: /%s HELP": "",
"Unknown subcommand": "",
"Unrecognized DEBUG subcommand": "",
"Usage: REGISTER <passphrase> [email]": "",
"User %s is no longer allowed to use vhosts": "",
"User %s is now allowed to use vhosts": "",
"User doesn't have roleplaying mode enabled": "",
@ -317,13 +333,11 @@
"You have been marked as being away": "",
"You have been offered ownership of channel %[1]s. To accept, /CS TRANSFER ACCEPT %[1]s": "",
"You have enabled autoreplay of missed messages, but you can't receive them because your client isn't set to always-on": "",
"You have joined too many channels": "",
"You have sent too many registration messages": "",
"You have too many nicks reserved already (you can remove some with /NS DROP)": "",
"You may not reregister": "",
"You must be an oper on the channel to register it": "",
"You must be connected with TLS and a client certificate to do this": "",
"You must be registered to join that channel": "",
"You must have rehash permissions in order to execute DEBUG CRASHSERVER": "",
"You must log in with SASL to join this server": "",
"You must specify an account": "",

View File

@ -1,16 +1,59 @@
{
"NickServ lets you register and login to an account.\n\nTo see in-depth help for a specific NickServ command, try:\n $b/NS HELP <command>$b\n\nHere are the commands you can use:\n%s": "",
"$bALWAYS-ON$b\n'always-on' controls whether your nickname/identity will remain active\neven while you are disconnected from the server. Your options are 'true',\n'false', and 'default' (use the server default value).": "",
"$bAUTO-AWAY$b\n'auto-away' is only effective for always-on clients. If enabled, you will\nautomatically be marked away when all your sessions are disconnected, and\nautomatically return from away when you connect again.": "",
"$bAUTOREPLAY-LINES$b\n'autoreplay-lines' controls the number of lines of channel history that will\nbe replayed to you automatically when joining a channel. Your options are any\npositive number, 0 to disable the feature, and 'default' to use the server\ndefault.": "",
"$bAUTOREPLAY-MISSED$b\n'autoreplay-missed' is only effective for always-on clients. If enabled,\nif you have at most one active session, the server will remember the time\nyou disconnect and then replay missed messages to you when you reconnect.\nYour options are 'on' and 'off'.": "",
"$bCERT$b controls a user account's certificate fingerprints": "",
"$bDM-HISTORY$b\n'dm-history' is only effective for always-on clients. It lets you control\nhow the history of your direct messages is stored. Your options are:\n1. 'off' [no history]\n2. 'ephemeral' [a limited amount of temporary history, not stored on disk]\n3. 'on' [history stored in a permanent database, if available]\n4. 'default' [use the server default]": "",
"$bDROP$b de-links your current (or the given) nickname from your user account.": "",
"$bENFORCE$b\n'enforce' lets you specify a custom enforcement mechanism for your registered\nnicknames. Your options are:\n1. 'none' [no enforcement, overriding the server default]\n2. 'strict' [you must already be authenticated to use the nick]\n3. 'default' [use the server default]": "",
"$bERASE$b erases all records of an account, allowing reuse.": "",
"$bGET$b queries the current values of your account settings": "",
"$bGHOST$b reclaims your nickname.": "",
"$bGROUP$b links your current nickname to your user account.": "",
"$bIDENTIFY$b lets you login to your account.": "",
"$bINFO$b gives you information on a user account.": "",
"$bLIST$b searches the list of registered nicknames.": "",
"$bMULTICLIENT$b\nIf 'multiclient' is enabled and you are already logged in and using a nick, a\nsecond client of yours that authenticates with SASL and requests the same nick\nis allowed to attach to the nick as well (this is comparable to the behavior\nof IRC \"bouncers\" like ZNC). Your options are 'on' (allow this behavior),\n'off' (disallow it), and 'default' (use the server default value).": "",
"$bPASSWD$b lets you change your password.": "",
"$bREGISTER$b lets you register a user account.": "",
"$bREPLAY-JOINS$b\n'replay-joins' controls whether replayed channel history will include\nlines for join and part. This provides more information about the context of\nmessages, but may be spammy. Your options are 'always', 'never', and the default\nof 'commands-only' (the messages will be replayed in /HISTORY output, but not\nduring autoreplay).": "",
"$bSADROP$b forcibly de-links the given nickname from its user account.": "",
"$bSAGET$b queries the current values of another user's account settings": "",
"$bSAREGISTER$b registers an account on someone else's behalf.": "",
"$bSASET$b modifies another user's account settings": "",
"$bSESSIONS$b lists the sessions attached to a nickname.": "",
"$bSET$b modifies your account settings": "",
"$bSUSPEND$b disables an account and disconnects the clients": "",
"$bUNREGISTER$b lets you delete your user account.": "",
"$bUNSUSPEND$b restores access to a suspended account": "",
"$bVERIFY$b lets you complete account registration.": "",
"Insufficient privileges": "",
"Invalid parameters": "",
"NickServ lets you register, log in to, and manage an account.": "",
"Password incorrect": "",
"Passwords do not match": "",
"Syntax $bSET <setting> <value>$b\n\nSET modifies your account settings. The following settings are available:": "",
"Syntax: $bCERT <LIST | ADD | DEL> [account] [certfp]$b\n\nCERT examines or modifies the TLS certificate fingerprints that can be used to\nlog into an account. Specifically, $bCERT LIST$b lists the authorized\nfingerprints, $bCERT ADD <fingerprint>$b adds a new fingerprint, and\n$bCERT DEL <fingerprint>$b removes a fingerprint. If you're an IRC operator\nwith the correct permissions, you can act on another user's account, for\nexample with $bCERT ADD <account> <fingerprint>$b.": "",
"Syntax: $bDROP [nickname]$b\n\nDROP de-links the given (or your current) nickname from your user account.": "",
"Syntax: $bENFORCE [method]$b\n\nENFORCE lets you specify a custom enforcement mechanism for your registered\nnicknames. Your options are:\n1. 'none' [no enforcement, overriding the server default]\n2. 'timeout' [anyone using the nick must authenticate before a deadline,\n or else they will be renamed]\n3. 'strict' [you must already be authenticated to use the nick]\n4. 'default' [use the server default]\nWith no arguments, queries your current enforcement status.": "",
"Syntax: $bENFORCE [method]$b\n\nENFORCE is an alias for $bGET enforce$b and $bSET enforce$b. See the help\nentry for $bSET$b for more information.": "",
"Syntax: $bERASE <username> [code]$b\n\nERASE deletes all records of an account, allowing it to be re-registered.\nThis should be used with caution, because it violates an expectation that\naccount names are permanent identifiers. Typically, UNREGISTER should be\nused instead. A confirmation code is required; invoking the command\nwithout a code will display the necessary code.": "",
"Syntax: $bGET <setting>$b\n\nGET queries the current values of your account settings. For more information\non the settings and their possible values, see HELP SET.": "",
"Syntax: $bGHOST <nickname>$b\n\nGHOST disconnects the given user from the network if they're logged in with the\nsame user account, letting you reclaim your nickname.": "",
"Syntax: $bGROUP$b\n\nGROUP links your current nickname with your logged-in account, so other people\nwill not be able to use it.": "",
"Syntax: $bIDENTIFY <username> [password]$b\n\nIDENTIFY lets you login to the given username using either password auth, or\ncertfp (your client certificate) if a password is not given.": "",
"Syntax: $bINFO [username]$b\n\nINFO gives you information about the given (or your own) user account.": "",
"Syntax: $bPASSWD <current> <new> <new_again>$b\nOr: $bPASSWD <username> <new>$b\n\nPASSWD lets you change your account password. You must supply your current\npassword and confirm the new one by typing it twice. If you're an IRC operator\nwith the correct permissions, you can use PASSWD to reset someone else's\npassword by supplying their username and then the desired password.": "",
"Syntax: $bREGISTER <username> <email> [password]$b\n\nREGISTER lets you register a user account. If the server allows anonymous\nregistration, you can send an asterisk (*) as the email address.\n\nIf the password is left out, your account will be registered to your TLS client\ncertificate (and you will need to use that certificate to login in future).": "",
"Syntax: $bLIST [regex]$b\n\nLIST returns the list of registered nicknames, which match the given regex.\nIf no regex is provided, all registered nicknames are returned.": "",
"Syntax: $bPASSWD <current> <new> <new_again>$b\nOr: $bPASSWD <username> <new>$b\n\nPASSWD lets you change your account password. You must supply your current\npassword and confirm the new one by typing it twice. If you're an IRC operator\nwith the correct permissions, you can use PASSWD to reset someone else's\npassword by supplying their username and then the desired password. To\nindicate an empty password, use * instead.": "",
"Syntax: $bREGISTER <password> [email]$b\n\nREGISTER lets you register your current nickname as a user account. If the\nserver allows anonymous registration, you can omit the e-mail address.\n\nIf you are currently logged in with a TLS client certificate and wish to use\nit instead of a password to log in, send * as the password.": "",
"Syntax: $bSADROP <nickname>$b\n\nSADROP forcibly de-links the given nickname from the attached user account.": "",
"Syntax: $bSAREGISTER <username> <password>$b\n\nSAREGISTER registers an account on someone else's behalf.\nThis is for use in configurations that require SASL for all connections;\nan administrator can set use this command to set up user accounts.": "",
"Syntax: $bSESSIONS [nickname]$b\n\nSESSIONS lists information about the sessions currently attached, via\nthe server's bouncer functionality, to your nickname. An administrator\ncan use this command to list another user's sessions.": "",
"Syntax: $bSAGET <account> <setting>$b\n\nSAGET queries the values of someone else's account settings. For more\ninformation on the settings and their possible values, see HELP SET.": "",
"Syntax: $bSAREGISTER <username> [password]$b\n\nSAREGISTER registers an account on someone else's behalf.\nThis is for use in configurations that require SASL for all connections;\nan administrator can set use this command to set up user accounts.": "",
"Syntax: $bSASET <account> <setting> <value>$b\n\nSASET modifies the values of someone else's account settings. For more\ninformation on the settings and their possible values, see HELP SET.": "",
"Syntax: $bSESSIONS [nickname]$b\n\nSESSIONS lists information about the sessions currently attached, via\nthe server's multiclient functionality, to your nickname. An administrator\ncan use this command to list another user's sessions.": "",
"Syntax: $bSUSPEND <nickname>$b\n\nSUSPEND disables an account and disconnects the associated clients.": "",
"Syntax: $bUNREGISTER <username> [code]$b\n\nUNREGISTER lets you delete your user account (or someone else's, if you're an\nIRC operator with the correct permissions). To prevent accidental\nunregistrations, a verification code is required; invoking the command without\na code will display the necessary code.": "",
"Syntax: $bVERIFY <username> <code>$b\n\nVERIFY lets you complete an account registration, if the server requires email\nor other verification.": ""
"Syntax: $bUNSUSPEND <nickname>$b\n\nUNSUSPEND reverses a previous SUSPEND, restoring access to the account.": "",
"Syntax: $bVERIFY <username> <code>$b\n\nVERIFY lets you complete an account registration, if the server requires email\nor other verification.": "",
"You're not logged into an account": ""
}

View File

@ -1,8 +1,8 @@
{
"= Help Topics =\n\nCommands:\n%[1]s\n\nRPL_ISUPPORT Tokens:\n%[2]s\n\nInformation:\n%[3]s": "",
"== Channel Modes ==\n\nOragono supports the following channel modes:\n\n +b | Client masks that are banned from the channel (e.g. *!*@127.0.0.1)\n +e | Client masks that are exempted from bans.\n +I | Client masks that are exempted from the invite-only flag.\n +i | Invite-only mode, only invited clients can join the channel.\n +k | Key required when joining the channel.\n +l | Client join limit for the channel.\n +m | Moderated mode, only privileged clients can talk on the channel.\n +n | No-outside-messages mode, only users that are on the channel can send\n | messages to it.\n +R | Only registered users can join the channel.\n +s | Secret mode, channel won't show up in /LIST or whois replies.\n +t | Only channel opers can modify the topic.\n\n= Prefixes =\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.": "",
"== Channel Modes ==\n\nOragono supports the following channel modes:\n\n +b | Client masks that are banned from the channel (e.g. *!*@127.0.0.1)\n +e | Client masks that are exempted from bans.\n +I | Client masks that are exempted from the invite-only flag.\n +i | Invite-only mode, only invited clients can join the channel.\n +k | Key required when joining the channel.\n +l | Client join limit for the channel.\n +m | Moderated mode, only privileged clients can talk on the channel.\n +n | No-outside-messages mode, only users that are on the channel can send\n | messages to it.\n +R | Only registered users can join the channel.\n +s | Secret mode, channel won't show up in /LIST or whois replies.\n +t | Only channel opers can modify the topic.\n +E | Roleplaying commands are enabled in the channel.\n +C | Clients are blocked from sending CTCP messages in the channel.\n\n= Prefixes =\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.": "",
"== Server Notice Masks ==\n\nOragono supports the following server notice masks for operators:\n\n a | Local announcements.\n c | Local client connections.\n j | Local channel actions.\n k | Local kills.\n n | Local nick changes.\n o | Local oper actions.\n q | Local quits.\n t | Local /STATS usage.\n u | Local client account actions.\n x | Local X-lines (DLINE/KLINE/etc).\n v | Local vhost changes.\n\nTo set a snomask, do this with your nickname:\n\n /MODE <nick> +s <chars>\n\nFor instance, this would set the kill, oper, account and xline snomasks on dan:\n\n /MODE dan +s koux": "",
"== User Modes ==\n\nOragono supports the following user modes:\n\n +a | User is marked as being away. This mode is set with the /AWAY command.\n +i | User is marked as invisible (their channels are hidden from whois replies).\n +o | User is an IRC operator.\n +R | User only accepts messages from other registered users. \n +s | Server Notice Masks (see help with /HELPOP snomasks).\n +Z | User is connected via TLS.": "",
"== User Modes ==\n\nOragono supports the following user modes:\n\n +a | User is marked as being away. This mode is set with the /AWAY command.\n +i | User is marked as invisible (their channels are hidden from whois replies).\n +o | User is an IRC operator.\n +R | User only accepts messages from other registered users. \n +s | Server Notice Masks (see help with /HELPOP snomasks).\n +Z | User is connected via TLS.\n +B | User is a bot.\n +E | User can receive roleplaying commands.\n +T | User is blocked from sending CTCP messages.": "",
"@+client-only-tags TAGMSG <target>{,<target>}\n\nSends the given client-only tags to the given targets as a TAGMSG. See the IRCv3\nspecs for more info: http://ircv3.net/specs/core/message-tags-3.3.html": "",
"ACC LS\nACC REGISTER <accountname> [callback_namespace:]<callback> [cred_type] :<credential>\nACC VERIFY <accountname> <auth_code>\n\nUsed in account registration. See the relevant specs for more info:\nhttps://oragono.io/specs.html": "",
"AMBIANCE <target> <text to be sent>\n\nThe AMBIANCE command is used to send a scene notification to the given target.": "",
@ -13,8 +13,10 @@
"CAP <subcommand> [:<capabilities>]\n\nUsed in capability negotiation. See the IRCv3 specs for more info:\nhttp://ircv3.net/specs/core/capability-negotiation-3.1.html\nhttp://ircv3.net/specs/core/capability-negotiation-3.2.html": "",
"CHATHISTORY [params]\n\nCHATHISTORY is a history replay command associated with the IRCv3\nspecification draft/chathistory. See this document:\nhttps://github.com/ircv3/ircv3-specifications/pull/393": "",
"DEBUG <option>\n\nProvides various debugging commands for the IRCd. <option> can be one of:\n\n* GCSTATS: Garbage control statistics.\n* NUMGOROUTINE: Number of goroutines in use.\n* STARTCPUPROFILE: Starts the CPU profiler.\n* STOPCPUPROFILE: Stops the CPU profiler.\n* PROFILEHEAP: Writes a memory profile.\n* CRASHSERVER: Crashes the server (for use in failover testing)": "",
"DEFCON [level]\n\nThe DEFCON system can disable server features at runtime, to mitigate\nspam or other hostile activity. It has five levels, which are cumulative\n(i.e., level 3 includes all restrictions from level 4 and so on):\n\n5: Normal operation\n4: No new account or channel registrations\n3: All users are +R; no changes to vhosts\n2: No new unauthenticated connections; all channels are +R\n1: No new connections except from localhost or other trusted IPs": "",
"DEOPER\n\nDEOPER removes the IRCop privileges granted to you by a successful /OPER.": "",
"DLINE [ANDKILL] [MYSELF] [duration] <ip>/<net> [ON <server>] [reason [| oper reason]]\nDLINE LIST\n\nBans an IP address or network from connecting to the server. If the duration is\ngiven then only for that long. The reason is shown to the user themselves, but\neveryone else will see a standard message. The oper reason is shown to\noperators getting info about the DLINEs that exist.\n\nBans are saved across subsequent launches of the server.\n\n\"ANDKILL\" means that all matching clients are also removed from the server.\n\n\"MYSELF\" is required when the DLINE matches the address the person applying it is connected\nfrom. If \"MYSELF\" is not given, trying to DLINE yourself will result in an error.\n\n[duration] can be of the following forms:\n\t1y 12mo 31d 10h 8m 13s\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24\n\nON <server> specifies that the ban is to be set on that specific server.\n\n[reason] and [oper reason], if they exist, are separated by a vertical bar (|).\n\nIf \"DLINE LIST\" is sent, the server sends back a list of our current DLINEs.": "",
"EXTJWT <target> [service_name]\n\nGet a JSON Web Token for target (either * or a channel name).": "",
"HELP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.": "",
"HELPOP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.": "",
"HISTORY <target> [limit]\n\nReplay message history. <target> can be a channel name, \"me\" to replay direct\nmessage history, or a nickname to replay another client's direct message\nhistory (they must be logged into the same account as you). [limit] can be\neither an integer (the maximum number of messages to replay), or a time\nduration like 10m or 1h (the time window within which to replay messages).": "",
@ -53,12 +55,14 @@
"SANICK <currentnick> <newnick>\n\nGives the given user a new nickname.": "",
"SCENE <target> <text to be sent>\n\nThe SCENE command is used to send a scene notification to the given target.": "",
"SETNAME <realname>\n\nThe SETNAME command updates the realname to be the newly-given one.": "",
"SUMMON [parameters]\n\nThe SUMMON command is not implemented.": "",
"TIME [server]\n\nShows the time of the current, or the given, server.": "",
"TOPIC <channel> [topic]\n\nIf [topic] is given, sets the topic in the channel to that. If [topic] is not\ngiven, views the current topic on the channel.": "",
"UNDLINE <ip>/<net>\n\nRemoves an existing ban on an IP address or a network.\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24": "",
"UNKLINE <mask>\n\nRemoves an existing ban on a mask.\n\nFor example:\n\tdan\n\tdan!5*@127.*": "",
"USER <username> 0 * <realname>\n\nUsed in connection registration, sets your username and realname to the given\nvalues (though your username may also be looked up with Ident).": "",
"USERHOST <nickname>{ <nickname>}\n\t\t\nShows information about the given users. Takes up to 10 nicknames.": "",
"USERS [parameters]\n\nThe USERS command is not implemented.": "",
"VERSION [server]\n\nViews the version of software and the RPL_ISUPPORT tokens for the given server.": "",
"WEBIRC <password> <gateway> <hostname> <ip> [:<flags>]\n\nUsed by web<->IRC gateways and bouncers, the WEBIRC command allows gateways to\npass-through the real IP addresses of clients:\nircv3.net/specs/extensions/webirc.html\n\n<flags> is a list of space-separated strings indicating various details about\nthe connection from the client to the gateway, such as:\n\n- tls: this flag indicates that the client->gateway connection is secure": "",
"WHO <name> [o]\n\nReturns information for the given user.": "",

View File

@ -86,6 +86,7 @@
"Channel renamed": "",
"Channel renamed: %s": "",
"Channels with persistent history cannot be renamed": "",
"Client %s is always-on and cannot be fully removed by /KILL; consider /NS SUSPEND instead": "",
"Client reconnected": "",
"Client reconnected (message history may have been lost)": "",
"Client reconnected (up to %d seconds of message history lost)": "",
@ -95,6 +96,7 @@
"Could not accept ownership of channel %s": "",
"Could not delete message": "",
"Could not find given client": "",
"Could not generate EXTJWT token": "",
"Could not look up account name, proceeding anyway": "",
"Could not parse IP address or CIDR network": "",
"Could not register": "",
@ -106,9 +108,12 @@
"Could not transfer channel": "",
"Could not ungroup nick": "",
"Created at: %s": "",
"Current DEFCON level is %d": "",
"Current global users %[1]s, max %[2]s": "",
"Current local users %[1]s, max %[2]s": "",
"Data export for %[1]s completed and written to %[2]s": "",
"Device ID: %s": "",
"Direct messages from unregistered users are temporarily restricted": "",
"End of /HELPOP": "",
"End of /INFO": "",
"End of /WHOIS list": "",
@ -154,6 +159,7 @@
"Insufficient privileges": "",
"Internal error": "",
"Invalid CAP subcommand": "",
"Invalid DEFCON parameter": "",
"Invalid account name": "",
"Invalid certificate fingerprint": "",
"Invalid channel name": "",
@ -165,6 +171,7 @@
"Invalid params": "",
"Invalid regex": "",
"Invalid vhost": "",
"It was built from git hash %s.": "",
"It was rejected for reason: %s": "",
"JOIN 0 is not allowed": "",
"Language %s is not supported by this server": "",
@ -173,6 +180,7 @@
"MOTD File is missing": "",
"Malformed username": "",
"Mask isn't valid": "",
"Message rejected for containing invalid UTF-8": "",
"Messages could not be retrieved": "",
"Multiclient functionality is currently disabled for your account": "",
"Multiclient functionality is currently disabled for your account, but you can opt in": "",
@ -189,6 +197,7 @@
"No such channel": "",
"No such module [%s]": "",
"No such nick": "",
"No such service": "",
"No such setting": "",
"No text to send": "",
"No topic is set": "",
@ -207,6 +216,7 @@
"Purge reason: %s": "",
"Purged at: %s": "",
"Purged by operator: %s": "",
"Realname is not valid": "",
"Received malformed line": "",
"Registered at: %s": "",
"Registered channel: %s": "",
@ -228,6 +238,7 @@
"SASL authentication failed: Passphrase too long": "",
"SASL authentication failed: authcid and authzid should be the same": "",
"SASL message too long": "",
"SUMMON has been disabled": "",
"Server notice masks": "",
"Session %d (currently attached session):": "",
"Session %d:": "",
@ -247,9 +258,11 @@
"Successfully registered account %s": "",
"Successfully rejected vhost request for %s": "",
"Successfully reset channel access": "",
"Successfully set persistent mode %s%s on %s": "",
"Successfully set persistent mode %[1]s on %[2]s": "",
"Successfully set vhost": "",
"Successfully suspended account %s": "",
"Successfully transferred channel %[1]s to account %[2]s": "",
"Successfully un-suspended account %s": "",
"Successfully ungrouped nick %s with your account": "",
"Successfully unpurged channel %s from the server": "",
"Successfully unregistered account %s": "",
@ -272,6 +285,7 @@
"This ban matches you. To KLINE yourself, you must use the command: /KLINE MYSELF <arguments>": "",
"This command has been disabled by the server administrators": "",
"This feature has been disabled by the server administrators": "",
"This is Oragono version %s.": "",
"This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.": "",
"This server requires that you wait %v after connecting before you can use /LIST. You have %v left.": "",
"This server was created %s": "",
@ -285,10 +299,12 @@
"Transfer of channel %[1]s to account %[2]s succeeded, pending acceptance": "",
"Translators:": "",
"Try again later": "",
"USERS has been disabled": "",
"Unknown command": "",
"Unknown command. To see available commands, run: /%s HELP": "",
"Unknown subcommand": "",
"Unrecognized DEBUG subcommand": "",
"Usage: REGISTER <passphrase> [email]": "",
"User %s is no longer allowed to use vhosts": "",
"User %s is now allowed to use vhosts": "",
"User doesn't have roleplaying mode enabled": "",
@ -317,13 +333,11 @@
"You have been marked as being away": "",
"You have been offered ownership of channel %[1]s. To accept, /CS TRANSFER ACCEPT %[1]s": "",
"You have enabled autoreplay of missed messages, but you can't receive them because your client isn't set to always-on": "",
"You have joined too many channels": "",
"You have sent too many registration messages": "",
"You have too many nicks reserved already (you can remove some with /NS DROP)": "",
"You may not reregister": "",
"You must be an oper on the channel to register it": "",
"You must be connected with TLS and a client certificate to do this": "",
"You must be registered to join that channel": "",
"You must have rehash permissions in order to execute DEBUG CRASHSERVER": "",
"You must log in with SASL to join this server": "",
"You must specify an account": "",

View File

@ -1,16 +1,59 @@
{
"NickServ lets you register and login to an account.\n\nTo see in-depth help for a specific NickServ command, try:\n $b/NS HELP <command>$b\n\nHere are the commands you can use:\n%s": "",
"$bALWAYS-ON$b\n'always-on' controls whether your nickname/identity will remain active\neven while you are disconnected from the server. Your options are 'true',\n'false', and 'default' (use the server default value).": "",
"$bAUTO-AWAY$b\n'auto-away' is only effective for always-on clients. If enabled, you will\nautomatically be marked away when all your sessions are disconnected, and\nautomatically return from away when you connect again.": "",
"$bAUTOREPLAY-LINES$b\n'autoreplay-lines' controls the number of lines of channel history that will\nbe replayed to you automatically when joining a channel. Your options are any\npositive number, 0 to disable the feature, and 'default' to use the server\ndefault.": "",
"$bAUTOREPLAY-MISSED$b\n'autoreplay-missed' is only effective for always-on clients. If enabled,\nif you have at most one active session, the server will remember the time\nyou disconnect and then replay missed messages to you when you reconnect.\nYour options are 'on' and 'off'.": "",
"$bCERT$b controls a user account's certificate fingerprints": "",
"$bDM-HISTORY$b\n'dm-history' is only effective for always-on clients. It lets you control\nhow the history of your direct messages is stored. Your options are:\n1. 'off' [no history]\n2. 'ephemeral' [a limited amount of temporary history, not stored on disk]\n3. 'on' [history stored in a permanent database, if available]\n4. 'default' [use the server default]": "",
"$bDROP$b de-links your current (or the given) nickname from your user account.": "",
"$bENFORCE$b\n'enforce' lets you specify a custom enforcement mechanism for your registered\nnicknames. Your options are:\n1. 'none' [no enforcement, overriding the server default]\n2. 'strict' [you must already be authenticated to use the nick]\n3. 'default' [use the server default]": "",
"$bERASE$b erases all records of an account, allowing reuse.": "",
"$bGET$b queries the current values of your account settings": "",
"$bGHOST$b reclaims your nickname.": "",
"$bGROUP$b links your current nickname to your user account.": "",
"$bIDENTIFY$b lets you login to your account.": "",
"$bINFO$b gives you information on a user account.": "",
"$bLIST$b searches the list of registered nicknames.": "",
"$bMULTICLIENT$b\nIf 'multiclient' is enabled and you are already logged in and using a nick, a\nsecond client of yours that authenticates with SASL and requests the same nick\nis allowed to attach to the nick as well (this is comparable to the behavior\nof IRC \"bouncers\" like ZNC). Your options are 'on' (allow this behavior),\n'off' (disallow it), and 'default' (use the server default value).": "",
"$bPASSWD$b lets you change your password.": "",
"$bREGISTER$b lets you register a user account.": "",
"$bREPLAY-JOINS$b\n'replay-joins' controls whether replayed channel history will include\nlines for join and part. This provides more information about the context of\nmessages, but may be spammy. Your options are 'always', 'never', and the default\nof 'commands-only' (the messages will be replayed in /HISTORY output, but not\nduring autoreplay).": "",
"$bSADROP$b forcibly de-links the given nickname from its user account.": "",
"$bSAGET$b queries the current values of another user's account settings": "",
"$bSAREGISTER$b registers an account on someone else's behalf.": "",
"$bSASET$b modifies another user's account settings": "",
"$bSESSIONS$b lists the sessions attached to a nickname.": "",
"$bSET$b modifies your account settings": "",
"$bSUSPEND$b disables an account and disconnects the clients": "",
"$bUNREGISTER$b lets you delete your user account.": "",
"$bUNSUSPEND$b restores access to a suspended account": "",
"$bVERIFY$b lets you complete account registration.": "",
"Insufficient privileges": "",
"Invalid parameters": "",
"NickServ lets you register, log in to, and manage an account.": "",
"Password incorrect": "",
"Passwords do not match": "",
"Syntax $bSET <setting> <value>$b\n\nSET modifies your account settings. The following settings are available:": "",
"Syntax: $bCERT <LIST | ADD | DEL> [account] [certfp]$b\n\nCERT examines or modifies the TLS certificate fingerprints that can be used to\nlog into an account. Specifically, $bCERT LIST$b lists the authorized\nfingerprints, $bCERT ADD <fingerprint>$b adds a new fingerprint, and\n$bCERT DEL <fingerprint>$b removes a fingerprint. If you're an IRC operator\nwith the correct permissions, you can act on another user's account, for\nexample with $bCERT ADD <account> <fingerprint>$b.": "",
"Syntax: $bDROP [nickname]$b\n\nDROP de-links the given (or your current) nickname from your user account.": "",
"Syntax: $bENFORCE [method]$b\n\nENFORCE lets you specify a custom enforcement mechanism for your registered\nnicknames. Your options are:\n1. 'none' [no enforcement, overriding the server default]\n2. 'timeout' [anyone using the nick must authenticate before a deadline,\n or else they will be renamed]\n3. 'strict' [you must already be authenticated to use the nick]\n4. 'default' [use the server default]\nWith no arguments, queries your current enforcement status.": "",
"Syntax: $bENFORCE [method]$b\n\nENFORCE is an alias for $bGET enforce$b and $bSET enforce$b. See the help\nentry for $bSET$b for more information.": "",
"Syntax: $bERASE <username> [code]$b\n\nERASE deletes all records of an account, allowing it to be re-registered.\nThis should be used with caution, because it violates an expectation that\naccount names are permanent identifiers. Typically, UNREGISTER should be\nused instead. A confirmation code is required; invoking the command\nwithout a code will display the necessary code.": "",
"Syntax: $bGET <setting>$b\n\nGET queries the current values of your account settings. For more information\non the settings and their possible values, see HELP SET.": "",
"Syntax: $bGHOST <nickname>$b\n\nGHOST disconnects the given user from the network if they're logged in with the\nsame user account, letting you reclaim your nickname.": "",
"Syntax: $bGROUP$b\n\nGROUP links your current nickname with your logged-in account, so other people\nwill not be able to use it.": "",
"Syntax: $bIDENTIFY <username> [password]$b\n\nIDENTIFY lets you login to the given username using either password auth, or\ncertfp (your client certificate) if a password is not given.": "",
"Syntax: $bINFO [username]$b\n\nINFO gives you information about the given (or your own) user account.": "",
"Syntax: $bPASSWD <current> <new> <new_again>$b\nOr: $bPASSWD <username> <new>$b\n\nPASSWD lets you change your account password. You must supply your current\npassword and confirm the new one by typing it twice. If you're an IRC operator\nwith the correct permissions, you can use PASSWD to reset someone else's\npassword by supplying their username and then the desired password.": "",
"Syntax: $bREGISTER <username> <email> [password]$b\n\nREGISTER lets you register a user account. If the server allows anonymous\nregistration, you can send an asterisk (*) as the email address.\n\nIf the password is left out, your account will be registered to your TLS client\ncertificate (and you will need to use that certificate to login in future).": "",
"Syntax: $bLIST [regex]$b\n\nLIST returns the list of registered nicknames, which match the given regex.\nIf no regex is provided, all registered nicknames are returned.": "",
"Syntax: $bPASSWD <current> <new> <new_again>$b\nOr: $bPASSWD <username> <new>$b\n\nPASSWD lets you change your account password. You must supply your current\npassword and confirm the new one by typing it twice. If you're an IRC operator\nwith the correct permissions, you can use PASSWD to reset someone else's\npassword by supplying their username and then the desired password. To\nindicate an empty password, use * instead.": "",
"Syntax: $bREGISTER <password> [email]$b\n\nREGISTER lets you register your current nickname as a user account. If the\nserver allows anonymous registration, you can omit the e-mail address.\n\nIf you are currently logged in with a TLS client certificate and wish to use\nit instead of a password to log in, send * as the password.": "",
"Syntax: $bSADROP <nickname>$b\n\nSADROP forcibly de-links the given nickname from the attached user account.": "",
"Syntax: $bSAREGISTER <username> <password>$b\n\nSAREGISTER registers an account on someone else's behalf.\nThis is for use in configurations that require SASL for all connections;\nan administrator can set use this command to set up user accounts.": "",
"Syntax: $bSESSIONS [nickname]$b\n\nSESSIONS lists information about the sessions currently attached, via\nthe server's bouncer functionality, to your nickname. An administrator\ncan use this command to list another user's sessions.": "",
"Syntax: $bSAGET <account> <setting>$b\n\nSAGET queries the values of someone else's account settings. For more\ninformation on the settings and their possible values, see HELP SET.": "",
"Syntax: $bSAREGISTER <username> [password]$b\n\nSAREGISTER registers an account on someone else's behalf.\nThis is for use in configurations that require SASL for all connections;\nan administrator can set use this command to set up user accounts.": "",
"Syntax: $bSASET <account> <setting> <value>$b\n\nSASET modifies the values of someone else's account settings. For more\ninformation on the settings and their possible values, see HELP SET.": "",
"Syntax: $bSESSIONS [nickname]$b\n\nSESSIONS lists information about the sessions currently attached, via\nthe server's multiclient functionality, to your nickname. An administrator\ncan use this command to list another user's sessions.": "",
"Syntax: $bSUSPEND <nickname>$b\n\nSUSPEND disables an account and disconnects the associated clients.": "",
"Syntax: $bUNREGISTER <username> [code]$b\n\nUNREGISTER lets you delete your user account (or someone else's, if you're an\nIRC operator with the correct permissions). To prevent accidental\nunregistrations, a verification code is required; invoking the command without\na code will display the necessary code.": "",
"Syntax: $bVERIFY <username> <code>$b\n\nVERIFY lets you complete an account registration, if the server requires email\nor other verification.": ""
"Syntax: $bUNSUSPEND <nickname>$b\n\nUNSUSPEND reverses a previous SUSPEND, restoring access to the account.": "",
"Syntax: $bVERIFY <username> <code>$b\n\nVERIFY lets you complete an account registration, if the server requires email\nor other verification.": "",
"You're not logged into an account": ""
}

View File

@ -1,8 +1,8 @@
{
"= Help Topics =\n\nCommands:\n%[1]s\n\nRPL_ISUPPORT Tokens:\n%[2]s\n\nInformation:\n%[3]s": "= Pomoć po temama =\n\nNaredbe:\n%[1]s\n\nRPL_ISUPPORT žetoni (Token-i):\n%[2]s\n\nInformacije:\n%[3]s",
"== Channel Modes ==\n\nOragono supports the following channel modes:\n\n +b | Client masks that are banned from the channel (e.g. *!*@127.0.0.1)\n +e | Client masks that are exempted from bans.\n +I | Client masks that are exempted from the invite-only flag.\n +i | Invite-only mode, only invited clients can join the channel.\n +k | Key required when joining the channel.\n +l | Client join limit for the channel.\n +m | Moderated mode, only privileged clients can talk on the channel.\n +n | No-outside-messages mode, only users that are on the channel can send\n | messages to it.\n +R | Only registered users can join the channel.\n +s | Secret mode, channel won't show up in /LIST or whois replies.\n +t | Only channel opers can modify the topic.\n\n= Prefixes =\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.": "",
"== Channel Modes ==\n\nOragono supports the following channel modes:\n\n +b | Client masks that are banned from the channel (e.g. *!*@127.0.0.1)\n +e | Client masks that are exempted from bans.\n +I | Client masks that are exempted from the invite-only flag.\n +i | Invite-only mode, only invited clients can join the channel.\n +k | Key required when joining the channel.\n +l | Client join limit for the channel.\n +m | Moderated mode, only privileged clients can talk on the channel.\n +n | No-outside-messages mode, only users that are on the channel can send\n | messages to it.\n +R | Only registered users can join the channel.\n +s | Secret mode, channel won't show up in /LIST or whois replies.\n +t | Only channel opers can modify the topic.\n +E | Roleplaying commands are enabled in the channel.\n +C | Clients are blocked from sending CTCP messages in the channel.\n\n= Prefixes =\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.": "",
"== Server Notice Masks ==\n\nOragono supports the following server notice masks for operators:\n\n a | Local announcements.\n c | Local client connections.\n j | Local channel actions.\n k | Local kills.\n n | Local nick changes.\n o | Local oper actions.\n q | Local quits.\n t | Local /STATS usage.\n u | Local client account actions.\n x | Local X-lines (DLINE/KLINE/etc).\n v | Local vhost changes.\n\nTo set a snomask, do this with your nickname:\n\n /MODE <nick> +s <chars>\n\nFor instance, this would set the kill, oper, account and xline snomasks on dan:\n\n /MODE dan +s koux": "",
"== User Modes ==\n\nOragono supports the following user modes:\n\n +a | User is marked as being away. This mode is set with the /AWAY command.\n +i | User is marked as invisible (their channels are hidden from whois replies).\n +o | User is an IRC operator.\n +R | User only accepts messages from other registered users. \n +s | Server Notice Masks (see help with /HELPOP snomasks).\n +Z | User is connected via TLS.": "== Korisnički režimi (modovi) ==\n\nOragono podržava slijedeće korisničke režime:\n\n+a | Korisnik je označen kao odsutan. Ovaj način rada se postavlja pomoću naredbe / AWAY.\n+i | Korisnik je označen kao nevidljiv (njegovi kanali su skriveni u whois odgvoru).\n+o | Korisnik je IRC operator.\n+R | Korisnik prihvata poruke samo od registrovanih korisnika.\n+s | Maske za obavijesti poslužitelja (potraži pomoć upotrebom naredbe / HELPOP snomasks).\n+Z | Korisnik je povezan koristeći TLS.",
"== User Modes ==\n\nOragono supports the following user modes:\n\n +a | User is marked as being away. This mode is set with the /AWAY command.\n +i | User is marked as invisible (their channels are hidden from whois replies).\n +o | User is an IRC operator.\n +R | User only accepts messages from other registered users. \n +s | Server Notice Masks (see help with /HELPOP snomasks).\n +Z | User is connected via TLS.\n +B | User is a bot.\n +E | User can receive roleplaying commands.\n +T | User is blocked from sending CTCP messages.": "",
"@+client-only-tags TAGMSG <target>{,<target>}\n\nSends the given client-only tags to the given targets as a TAGMSG. See the IRCv3\nspecs for more info: http://ircv3.net/specs/core/message-tags-3.3.html": "@+oznake-specifične-samo-za-klijent TAGMSG <cilj>{,<cilj>}\n\nDaje zadane oznake specifične samo za klijente danim ciljevima kao TAGMSG. Za više informacija pogledajte IRCv3 specifikacije: http://ircv3.net/specs/core/message-tags-3.3.html",
"ACC LS\nACC REGISTER <accountname> [callback_namespace:]<callback> [cred_type] :<credential>\nACC VERIFY <accountname> <auth_code>\n\nUsed in account registration. See the relevant specs for more info:\nhttps://oragono.io/specs.html": "-> ACC LS\nACC REGISTER <accountname> [callback_namespace:]<callback> [cred_type] :<credential>\nACC VERIFY <accountname> <auth_code>\n\nKoristi se pri registraciji korisničkog računa. Za više informacija pogledajte relevantne specifikacije:\nhttps://oragono.io/specs.html",
"AMBIANCE <target> <text to be sent>\n\nThe AMBIANCE command is used to send a scene notification to the given target.": "AMBIANCE <cilj> <poruka koju se šalje>\n\nNaredba AMBIANCE se koristi za slanje obavijesti o sceni/ambijentu zadanom cilju.",
@ -13,8 +13,10 @@
"CAP <subcommand> [:<capabilities>]\n\nUsed in capability negotiation. See the IRCv3 specs for more info:\nhttp://ircv3.net/specs/core/capability-negotiation-3.1.html\nhttp://ircv3.net/specs/core/capability-negotiation-3.2.html": "CAP <podnaredba> [:<sposobnosti>]\n\nKoristi se u pregovaranju o sposobnostima. Za više informacija pogledajte IRCv3 specifikacije:\nhttp://ircv3.net/specs/core/capability-negotiation-3.1.html\nhttp://ircv3.net/specs/core/capability-negotiation-3.2.html",
"CHATHISTORY [params]\n\nCHATHISTORY is a history replay command associated with the IRCv3\nspecification draft/chathistory. See this document:\nhttps://github.com/ircv3/ircv3-specifications/pull/393": "",
"DEBUG <option>\n\nProvides various debugging commands for the IRCd. <option> can be one of:\n\n* GCSTATS: Garbage control statistics.\n* NUMGOROUTINE: Number of goroutines in use.\n* STARTCPUPROFILE: Starts the CPU profiler.\n* STOPCPUPROFILE: Stops the CPU profiler.\n* PROFILEHEAP: Writes a memory profile.\n* CRASHSERVER: Crashes the server (for use in failover testing)": "",
"DEFCON [level]\n\nThe DEFCON system can disable server features at runtime, to mitigate\nspam or other hostile activity. It has five levels, which are cumulative\n(i.e., level 3 includes all restrictions from level 4 and so on):\n\n5: Normal operation\n4: No new account or channel registrations\n3: All users are +R; no changes to vhosts\n2: No new unauthenticated connections; all channels are +R\n1: No new connections except from localhost or other trusted IPs": "",
"DEOPER\n\nDEOPER removes the IRCop privileges granted to you by a successful /OPER.": "",
"DLINE [ANDKILL] [MYSELF] [duration] <ip>/<net> [ON <server>] [reason [| oper reason]]\nDLINE LIST\n\nBans an IP address or network from connecting to the server. If the duration is\ngiven then only for that long. The reason is shown to the user themselves, but\neveryone else will see a standard message. The oper reason is shown to\noperators getting info about the DLINEs that exist.\n\nBans are saved across subsequent launches of the server.\n\n\"ANDKILL\" means that all matching clients are also removed from the server.\n\n\"MYSELF\" is required when the DLINE matches the address the person applying it is connected\nfrom. If \"MYSELF\" is not given, trying to DLINE yourself will result in an error.\n\n[duration] can be of the following forms:\n\t1y 12mo 31d 10h 8m 13s\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24\n\nON <server> specifies that the ban is to be set on that specific server.\n\n[reason] and [oper reason], if they exist, are separated by a vertical bar (|).\n\nIf \"DLINE LIST\" is sent, the server sends back a list of our current DLINEs.": "DLINE [ANDKILL] [MYSELF] [duration] <ip>/<net> [ON <poslužitelj>] [razlog [| oper razlog]]\nDLINE LIST\n\nZabranjuje IP adresi ili mreži (<net>) povezivanje s poslužiteljem (serverom). Ako je trajanje zadano onda zabrana vrijedi samo u tom periodu. Razlog je prikazan samom korisniku, ali svi će vidjeti standardnu poruku. Oper razlog se prikazuje operaterima koji dobivaju informacije o postojećim DLINE-ovima.\n\nZabrane se spremaju tijekom sljedećih pokretanja poslužitelja.\n\n\"ANDKILL\" znači da su svi odgovarajući klijenti također uklonjeni s poslužitelja.\n\n\"MYSELF\" je obavezno kada DLINE odgovara adresi \n na koju je osoba koja ga primjenjuje povezana. Ako \"MYSELF\" nije zadano, pokušaj da DLINE izvršite na samima sebi rezultirače greškom.\n\n[duration] trajanje može biti izraženo u slijedećim oblicima:\n 1y 12mo 31d 10h 8m 13s\n\n<net> je specificirano u uobičajenoj CIDR notaciji. Na primjer:\n 127.0.0.1/8\n\t8.8.8.8/24\n\nON <poslužitelj> nalaže da će zabrana (ban) biti postavljena za taj određeni poslužitelj (server).\n\n[reason] i [oper razlog], ako su navedeni, razdvojeni su vertikalnom crtom (|).\n\nAko je \"DLINE LIST\" poslan, server šalje nazad listu naših trenutačnih DLINE-a.",
"EXTJWT <target> [service_name]\n\nGet a JSON Web Token for target (either * or a channel name).": "",
"HELP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.": "HELP <argument>\n\nZatražite objašnjenje <argumenta> ili \"index\" popisa tema pomoći.",
"HELPOP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.": "HELPOP <argument>\n\nPogledaj objašnjenje <argumenta>, ili \"index\" za popis tema pomoći.",
"HISTORY <target> [limit]\n\nReplay message history. <target> can be a channel name, \"me\" to replay direct\nmessage history, or a nickname to replay another client's direct message\nhistory (they must be logged into the same account as you). [limit] can be\neither an integer (the maximum number of messages to replay), or a time\nduration like 10m or 1h (the time window within which to replay messages).": "",
@ -53,12 +55,14 @@
"SANICK <currentnick> <newnick>\n\nGives the given user a new nickname.": "SANICK <trenutninadimak> <novinadimak>\n\nDaje zadanom korisniku novi nadimak.",
"SCENE <target> <text to be sent>\n\nThe SCENE command is used to send a scene notification to the given target.": "SCENE <cilj> <tekst koji se šalje>\n\nNaredba SCENE se koristi kako bi se datom cilju poslala notifikacija o \"sceni\".",
"SETNAME <realname>\n\nThe SETNAME command updates the realname to be the newly-given one.": "SETNAME <realname>\n\nSETNAME naredba ažurira pravoime, kako bi ono bilo novo-dano ime.",
"SUMMON [parameters]\n\nThe SUMMON command is not implemented.": "",
"TIME [server]\n\nShows the time of the current, or the given, server.": "TIME [server]\n\nPrikazuje vrijeme poslužitelja (servera) na kojem se nalazite ili zadanog poslužitelja.",
"TOPIC <channel> [topic]\n\nIf [topic] is given, sets the topic in the channel to that. If [topic] is not\ngiven, views the current topic on the channel.": "TOPIC <kanal> [topic]\n\nAko je [topic] unešen, postavlja temu (topic) kanala u zadanu vrijednost. Ako [topic] nije zadan, pregleda trenutnu temu kanala.",
"UNDLINE <ip>/<net>\n\nRemoves an existing ban on an IP address or a network.\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24": "UNDLINE <ip>/<net>\n\nUklanja potojeću zabranu za datu IP adresu ili mrežu.\n\n<net> je specificirana uobičajenom CIDR notacijom. Na primjer:\n27.0.0.1/8\n\t8.8.8.8/24",
"UNKLINE <mask>\n\nRemoves an existing ban on a mask.\n\nFor example:\n\tdan\n\tdan!5*@127.*": "UNKLINE <maska>\n\nUklanja potojeću zabranu za datu masku.\n\nNa primjer:\n dan\n dan!5*@127.*",
"USER <username> 0 * <realname>\n\nUsed in connection registration, sets your username and realname to the given\nvalues (though your username may also be looked up with Ident).": "USER <korisničkoime> 0 * <pravoime>\n\nUpotrijebava se pri registraciji konekcije, postavlja vaše korisničko ime i stvarno ime na zadane vrijednosti (iako vaše korisničko ime također može biti traženo pomoću Ident-a).",
"USERHOST <nickname>{ <nickname>}\n\t\t\nShows information about the given users. Takes up to 10 nicknames.": "USERHOST <nadimak>{ <nadimak>}\n\nPrikazuje informacije o danim korisnicima. Prihvata do 10 nadimaka.",
"USERS [parameters]\n\nThe USERS command is not implemented.": "",
"VERSION [server]\n\nViews the version of software and the RPL_ISUPPORT tokens for the given server.": "VERSION [poslužitelj]\n\nPrikazuje verziju softvera i RPL_ISUPPORT oznake za dati poslužitelj.",
"WEBIRC <password> <gateway> <hostname> <ip> [:<flags>]\n\nUsed by web<->IRC gateways and bouncers, the WEBIRC command allows gateways to\npass-through the real IP addresses of clients:\nircv3.net/specs/extensions/webirc.html\n\n<flags> is a list of space-separated strings indicating various details about\nthe connection from the client to the gateway, such as:\n\n- tls: this flag indicates that the client->gateway connection is secure": "WEBIRC <lozinka> <gateway> <hostname> <ip> [:<flags>]\n\nKoristi se od strane web<->IRC pristupnika (gateways) i bouncer-a, WEBIRC naredbba omogućava pristupnicima (gateways) da propuste stvarne IP adrese klijenata:\nircv3.net/specs/extensions/webirc.html\n\n<flags> je lista praznim prostorom odvojenih nizova koji upućuju na različite detalje o vezi sa klijenta prema pristupniku (gateway), kao npr.:\n\n-tls: ova oznaka (flag) indicira da je veza klijent->pristupnik sigurna",
"WHO <name> [o]\n\nReturns information for the given user.": "WHO <ime> [o]\n\nUzvraća informacijama o datom korisniku.",

View File

@ -86,6 +86,7 @@
"Channel renamed": "Kanal je preimenovan",
"Channel renamed: %s": "Kanal preimenovan: %s",
"Channels with persistent history cannot be renamed": "",
"Client %s is always-on and cannot be fully removed by /KILL; consider /NS SUSPEND instead": "",
"Client reconnected": "Klijent je ponovo povezan",
"Client reconnected (message history may have been lost)": "",
"Client reconnected (up to %d seconds of message history lost)": "",
@ -95,6 +96,7 @@
"Could not accept ownership of channel %s": "",
"Could not delete message": "",
"Could not find given client": "Zadani klijent nije mogao biti pronađen",
"Could not generate EXTJWT token": "",
"Could not look up account name, proceeding anyway": "",
"Could not parse IP address or CIDR network": "Nije moguće rasčlaniti IP adresu ili CIDR mrežu",
"Could not register": "Ne može se registrirati",
@ -106,9 +108,12 @@
"Could not transfer channel": "",
"Could not ungroup nick": "Nick nije moguće izdvojiti iz grupe",
"Created at: %s": "Kreirano u: %s",
"Current DEFCON level is %d": "",
"Current global users %[1]s, max %[2]s": "",
"Current local users %[1]s, max %[2]s": "",
"Data export for %[1]s completed and written to %[2]s": "",
"Device ID: %s": "",
"Direct messages from unregistered users are temporarily restricted": "",
"End of /HELPOP": "Kraj /HELPOP",
"End of /INFO": "Kraj /INFO",
"End of /WHOIS list": "Kraj /WHOIS liste",
@ -154,6 +159,7 @@
"Insufficient privileges": "Nedovoljne privilegije",
"Internal error": "Interna greška",
"Invalid CAP subcommand": "Netačna CAP podnaredba",
"Invalid DEFCON parameter": "",
"Invalid account name": "Pogrešan naziv računa",
"Invalid certificate fingerprint": "",
"Invalid channel name": "",
@ -165,6 +171,7 @@
"Invalid params": "",
"Invalid regex": "",
"Invalid vhost": "Nevažeći vhost",
"It was built from git hash %s.": "",
"It was rejected for reason: %s": "Razlog odbijanja: %s",
"JOIN 0 is not allowed": "JOIN 0 nije dopušteno",
"Language %s is not supported by this server": "Jezik %s nije podržan od strane servera",
@ -173,6 +180,7 @@
"MOTD File is missing": "MOTD datoteka nedostaje",
"Malformed username": "Pogrešno formirano korisničko ime",
"Mask isn't valid": "Maska nije validna",
"Message rejected for containing invalid UTF-8": "",
"Messages could not be retrieved": "",
"Multiclient functionality is currently disabled for your account": "",
"Multiclient functionality is currently disabled for your account, but you can opt in": "",
@ -189,6 +197,7 @@
"No such channel": "Nepostojeći kanal",
"No such module [%s]": "",
"No such nick": "Takav nadimak nije prisutan",
"No such service": "",
"No such setting": "",
"No text to send": "",
"No topic is set": "Tema (topic) nije postavljena",
@ -207,6 +216,7 @@
"Purge reason: %s": "",
"Purged at: %s": "",
"Purged by operator: %s": "",
"Realname is not valid": "",
"Received malformed line": "Primljena neispravna linija",
"Registered at: %s": "Registrirano u: %s",
"Registered channel: %s": "Registriran kanal: %s",
@ -228,6 +238,7 @@
"SASL authentication failed: Passphrase too long": "SASL provjera autentičnosti nije uspjela: fraza preduga",
"SASL authentication failed: authcid and authzid should be the same": "SASL provjera autentičnosti nije uspjela: authcid i authzid trebali bi biti isti",
"SASL message too long": "SASL poruka preduga",
"SUMMON has been disabled": "",
"Server notice masks": "Maske za obavijesti poslužitelja",
"Session %d (currently attached session):": "Sesija %d (trenutno pridružena sesija):",
"Session %d:": "Sesija %d:",
@ -247,9 +258,11 @@
"Successfully registered account %s": "Uspješno registrovan korisnički račun %s",
"Successfully rejected vhost request for %s": "Uspješno odbijen vhost zahtjev za: %s",
"Successfully reset channel access": "",
"Successfully set persistent mode %s%s on %s": "",
"Successfully set persistent mode %[1]s on %[2]s": "",
"Successfully set vhost": "Uspješno postavljen vhost",
"Successfully suspended account %s": "",
"Successfully transferred channel %[1]s to account %[2]s": "",
"Successfully un-suspended account %s": "",
"Successfully ungrouped nick %s with your account": "Uspješno od-grupisan nadimak %s od korisničkog računa",
"Successfully unpurged channel %s from the server": "",
"Successfully unregistered account %s": "Uspješno poništena registracija računa %s",
@ -272,6 +285,7 @@
"This ban matches you. To KLINE yourself, you must use the command: /KLINE MYSELF <arguments>": "Ova zabrana odgovara Vama. Da biste izveli KLINE sami na sebi, a za otkazivanje ove zabrane, morate koristiti naredbu: /KLINE MYSELF <arguments>",
"This command has been disabled by the server administrators": "Ova naredba je onemogućen od strane administratora poslužitelja",
"This feature has been disabled by the server administrators": "",
"This is Oragono version %s.": "",
"This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.": "Ovaj poslužitelj je u načinu za otklanjanje grešaka i bilježi sve korisničke I/O. Ako ne želite da sve što pošaljete bude vidljivo vlasniku/cima poslužitelja, diskonektujte se.",
"This server requires that you wait %v after connecting before you can use /LIST. You have %v left.": "",
"This server was created %s": "Ovaj server je kreiran %s",
@ -285,10 +299,12 @@
"Transfer of channel %[1]s to account %[2]s succeeded, pending acceptance": "",
"Translators:": "Prevodioci:",
"Try again later": "",
"USERS has been disabled": "",
"Unknown command": "Nepoznata naredba",
"Unknown command. To see available commands, run: /%s HELP": "Nepoznata naredba. Za pregled raspoloživih naredbi upotrijebi: /%s HELP",
"Unknown subcommand": "Nepoznata podnaredba",
"Unrecognized DEBUG subcommand": "",
"Usage: REGISTER <passphrase> [email]": "",
"User %s is no longer allowed to use vhosts": "",
"User %s is now allowed to use vhosts": "",
"User doesn't have roleplaying mode enabled": "Korisnik nema omogućen režim (mode) za igranja uloga",
@ -317,13 +333,11 @@
"You have been marked as being away": "Označeni ste kao Odsutni",
"You have been offered ownership of channel %[1]s. To accept, /CS TRANSFER ACCEPT %[1]s": "",
"You have enabled autoreplay of missed messages, but you can't receive them because your client isn't set to always-on": "",
"You have joined too many channels": "Pristupili ste prevelikom broju kanala",
"You have sent too many registration messages": "",
"You have too many nicks reserved already (you can remove some with /NS DROP)": "Već imate previše rezerviranih nadimaka (neke možete ukloniti pomoću / NS DROP)",
"You may not reregister": "Ne možete se ponovno registrirati",
"You must be an oper on the channel to register it": "Morate biti operater na kanalu da biste ga registrirali",
"You must be connected with TLS and a client certificate to do this": "",
"You must be registered to join that channel": "Morate biti registrovani kako biste pristupili ovom kanalu",
"You must have rehash permissions in order to execute DEBUG CRASHSERVER": "",
"You must log in with SASL to join this server": "",
"You must specify an account": "Morate navesti korisnički račun",

View File

@ -1,16 +1,59 @@
{
"NickServ lets you register and login to an account.\n\nTo see in-depth help for a specific NickServ command, try:\n $b/NS HELP <command>$b\n\nHere are the commands you can use:\n%s": "NickServ vam omogućava da registrujete i da se prijavite na korisnički račun.\n\nDa biste dobili detaljnu pomoć za određenu NickServ naredbu, pokušajte:\n$b/NS HELP <naredba>$b\n\nOvo su raspoložive naredbe koje možete koristiti:\n%s",
"$bALWAYS-ON$b\n'always-on' controls whether your nickname/identity will remain active\neven while you are disconnected from the server. Your options are 'true',\n'false', and 'default' (use the server default value).": "",
"$bAUTO-AWAY$b\n'auto-away' is only effective for always-on clients. If enabled, you will\nautomatically be marked away when all your sessions are disconnected, and\nautomatically return from away when you connect again.": "",
"$bAUTOREPLAY-LINES$b\n'autoreplay-lines' controls the number of lines of channel history that will\nbe replayed to you automatically when joining a channel. Your options are any\npositive number, 0 to disable the feature, and 'default' to use the server\ndefault.": "",
"$bAUTOREPLAY-MISSED$b\n'autoreplay-missed' is only effective for always-on clients. If enabled,\nif you have at most one active session, the server will remember the time\nyou disconnect and then replay missed messages to you when you reconnect.\nYour options are 'on' and 'off'.": "",
"$bCERT$b controls a user account's certificate fingerprints": "",
"$bDM-HISTORY$b\n'dm-history' is only effective for always-on clients. It lets you control\nhow the history of your direct messages is stored. Your options are:\n1. 'off' [no history]\n2. 'ephemeral' [a limited amount of temporary history, not stored on disk]\n3. 'on' [history stored in a permanent database, if available]\n4. 'default' [use the server default]": "",
"$bDROP$b de-links your current (or the given) nickname from your user account.": "",
"$bENFORCE$b\n'enforce' lets you specify a custom enforcement mechanism for your registered\nnicknames. Your options are:\n1. 'none' [no enforcement, overriding the server default]\n2. 'strict' [you must already be authenticated to use the nick]\n3. 'default' [use the server default]": "",
"$bERASE$b erases all records of an account, allowing reuse.": "",
"$bGET$b queries the current values of your account settings": "",
"$bGHOST$b reclaims your nickname.": "",
"$bGROUP$b links your current nickname to your user account.": "",
"$bIDENTIFY$b lets you login to your account.": "",
"$bINFO$b gives you information on a user account.": "",
"$bLIST$b searches the list of registered nicknames.": "",
"$bMULTICLIENT$b\nIf 'multiclient' is enabled and you are already logged in and using a nick, a\nsecond client of yours that authenticates with SASL and requests the same nick\nis allowed to attach to the nick as well (this is comparable to the behavior\nof IRC \"bouncers\" like ZNC). Your options are 'on' (allow this behavior),\n'off' (disallow it), and 'default' (use the server default value).": "",
"$bPASSWD$b lets you change your password.": "",
"$bREGISTER$b lets you register a user account.": "",
"$bREPLAY-JOINS$b\n'replay-joins' controls whether replayed channel history will include\nlines for join and part. This provides more information about the context of\nmessages, but may be spammy. Your options are 'always', 'never', and the default\nof 'commands-only' (the messages will be replayed in /HISTORY output, but not\nduring autoreplay).": "",
"$bSADROP$b forcibly de-links the given nickname from its user account.": "",
"$bSAGET$b queries the current values of another user's account settings": "",
"$bSAREGISTER$b registers an account on someone else's behalf.": "",
"$bSASET$b modifies another user's account settings": "",
"$bSESSIONS$b lists the sessions attached to a nickname.": "",
"$bSET$b modifies your account settings": "",
"$bSUSPEND$b disables an account and disconnects the clients": "",
"$bUNREGISTER$b lets you delete your user account.": "",
"$bUNSUSPEND$b restores access to a suspended account": "",
"$bVERIFY$b lets you complete account registration.": "",
"Insufficient privileges": "Nedovoljne privilegije",
"Invalid parameters": "",
"NickServ lets you register, log in to, and manage an account.": "",
"Password incorrect": "Netačna lozinka",
"Passwords do not match": "",
"Syntax $bSET <setting> <value>$b\n\nSET modifies your account settings. The following settings are available:": "",
"Syntax: $bCERT <LIST | ADD | DEL> [account] [certfp]$b\n\nCERT examines or modifies the TLS certificate fingerprints that can be used to\nlog into an account. Specifically, $bCERT LIST$b lists the authorized\nfingerprints, $bCERT ADD <fingerprint>$b adds a new fingerprint, and\n$bCERT DEL <fingerprint>$b removes a fingerprint. If you're an IRC operator\nwith the correct permissions, you can act on another user's account, for\nexample with $bCERT ADD <account> <fingerprint>$b.": "",
"Syntax: $bDROP [nickname]$b\n\nDROP de-links the given (or your current) nickname from your user account.": "Sintaksa: $bDROP [nickname]$b\n\nDROP prekida povezanost datog (ili trenutnog) nadimka sa korisničkim računom.",
"Syntax: $bENFORCE [method]$b\n\nENFORCE lets you specify a custom enforcement mechanism for your registered\nnicknames. Your options are:\n1. 'none' [no enforcement, overriding the server default]\n2. 'timeout' [anyone using the nick must authenticate before a deadline,\n or else they will be renamed]\n3. 'strict' [you must already be authenticated to use the nick]\n4. 'default' [use the server default]\nWith no arguments, queries your current enforcement status.": "Sintaksa: $bENFORCE [method]$b\n\nENFORCE omogućuje određivanje prilagođenog mehanizma za provođenje registriranih nadimaka. Vaše opcije su:\n\n1. 'none' [nema provedbe, nadjačava zadani metod servera]\n2. \"timeout\" [korisnik koji koristi nadimak (nick) mora izvršiti autentifikaciju prije isteka zadanog roka, u protivnom će njihov nick biti promijenjen]\n3. 'strickt' [autentifikacija mora biti unaprijed urađena kako biste mogli koristiti taj nadimak]\n4. 'default' [koristi postavke zadane serverom]\nBez zadanog argumenta, ispituje vaš trenutni status izvršenja.",
"Syntax: $bENFORCE [method]$b\n\nENFORCE is an alias for $bGET enforce$b and $bSET enforce$b. See the help\nentry for $bSET$b for more information.": "",
"Syntax: $bERASE <username> [code]$b\n\nERASE deletes all records of an account, allowing it to be re-registered.\nThis should be used with caution, because it violates an expectation that\naccount names are permanent identifiers. Typically, UNREGISTER should be\nused instead. A confirmation code is required; invoking the command\nwithout a code will display the necessary code.": "",
"Syntax: $bGET <setting>$b\n\nGET queries the current values of your account settings. For more information\non the settings and their possible values, see HELP SET.": "",
"Syntax: $bGHOST <nickname>$b\n\nGHOST disconnects the given user from the network if they're logged in with the\nsame user account, letting you reclaim your nickname.": "Sintaksa: $bGHOST <nickname>$b\n\nGHOST diskonektuje korisnika s mreže ako su prijavljeni s istim korisničkim računom, što vam omogućuje povrat vašeg nadimka.",
"Syntax: $bGROUP$b\n\nGROUP links your current nickname with your logged-in account, so other people\nwill not be able to use it.": "Sintaksa: $bGROUP$b\n\nGROUP povezuje tvoj trenutni nadimak (nickname) sa računom na koji si prijavljen, kako drugi nebi mogli da ga koriste.",
"Syntax: $bIDENTIFY <username> [password]$b\n\nIDENTIFY lets you login to the given username using either password auth, or\ncertfp (your client certificate) if a password is not given.": "Sintaksa: $bIDENTIFY <username> [password]$b\n\nIDENTIFY omogućuje prijavu na zadano korisničko ime pomoću lozinke ili certfp (certifikat vašeg klijenta) ako lozinka nije dana.",
"Syntax: $bINFO [username]$b\n\nINFO gives you information about the given (or your own) user account.": "Sintaksa: $bINFO [username]$b\n\nINFO daje informacije o danom (ili vlastitom) korisničkom računu.",
"Syntax: $bPASSWD <current> <new> <new_again>$b\nOr: $bPASSWD <username> <new>$b\n\nPASSWD lets you change your account password. You must supply your current\npassword and confirm the new one by typing it twice. If you're an IRC operator\nwith the correct permissions, you can use PASSWD to reset someone else's\npassword by supplying their username and then the desired password.": "Sintaksa: $bPASSWD <current> <new> <new_again>$b\nIli: $bPASSWD <username> <new>$b\n\nPASSWD omogućuje promjenu lozinke računa. Morate unijeti svoju trenutnu lozinku i potvrditi novu tako da je upišete dvaput. Ako ste IRC operator s ispravnim dozvolama, možete koristiti PASSWD za ponovno postavljanje tuđe lozinke tako da unesete korisničko ime, a zatim željenu zaporku.",
"Syntax: $bREGISTER <username> <email> [password]$b\n\nREGISTER lets you register a user account. If the server allows anonymous\nregistration, you can send an asterisk (*) as the email address.\n\nIf the password is left out, your account will be registered to your TLS client\ncertificate (and you will need to use that certificate to login in future).": "Sintaksa: $bREGISTER <username> <email> [password]$b\n\nREGISTER omogućuje registriranje korisničkog računa. Ako poslužitelj dopušta anonimnu registraciju, možete poslati zvjezdicu (*) kao adresu e-pošte.\n\nAko je lozinka izostavljena, vaš će račun biti registriran na vaš TLS klijentski certifikat (i morat ćete ga upotrijebiti za prijavu u budućnosti).",
"Syntax: $bLIST [regex]$b\n\nLIST returns the list of registered nicknames, which match the given regex.\nIf no regex is provided, all registered nicknames are returned.": "",
"Syntax: $bPASSWD <current> <new> <new_again>$b\nOr: $bPASSWD <username> <new>$b\n\nPASSWD lets you change your account password. You must supply your current\npassword and confirm the new one by typing it twice. If you're an IRC operator\nwith the correct permissions, you can use PASSWD to reset someone else's\npassword by supplying their username and then the desired password. To\nindicate an empty password, use * instead.": "",
"Syntax: $bREGISTER <password> [email]$b\n\nREGISTER lets you register your current nickname as a user account. If the\nserver allows anonymous registration, you can omit the e-mail address.\n\nIf you are currently logged in with a TLS client certificate and wish to use\nit instead of a password to log in, send * as the password.": "",
"Syntax: $bSADROP <nickname>$b\n\nSADROP forcibly de-links the given nickname from the attached user account.": "Sintaksa: $bSADROP <nickname>$b\n\nSADROP prisilno odvaja dati nadimak (nickname) od korisničkog računa za koji je trenutno vezan.",
"Syntax: $bSAREGISTER <username> <password>$b\n\nSAREGISTER registers an account on someone else's behalf.\nThis is for use in configurations that require SASL for all connections;\nan administrator can set use this command to set up user accounts.": "Syntax: $bSAREGISTER <korisničkoime> <lozinka>$b\n\nSAREGISTER registruje korisnički račun na tuđe ime, za dugo lice.\nKoristi se u konfiguraciji koja zahtjeva SASL za sve konekcije;\nadministrator može koristiti ovu naredbu za postavljanje korisničkih računa.",
"Syntax: $bSESSIONS [nickname]$b\n\nSESSIONS lists information about the sessions currently attached, via\nthe server's bouncer functionality, to your nickname. An administrator\ncan use this command to list another user's sessions.": "Sintaksa: $bSESSIONS [nickname]$b\n\nSESSIONS ispisuje informacije o trenutačnim sesijama vezanim za tvoj nadimak (nickname), koristeći se pri tome bouncer funkcijom servera. Administrator može da koristi ovu naredbu kako bi isčitao sesije drugih korisnika.",
"Syntax: $bSAGET <account> <setting>$b\n\nSAGET queries the values of someone else's account settings. For more\ninformation on the settings and their possible values, see HELP SET.": "",
"Syntax: $bSAREGISTER <username> [password]$b\n\nSAREGISTER registers an account on someone else's behalf.\nThis is for use in configurations that require SASL for all connections;\nan administrator can set use this command to set up user accounts.": "",
"Syntax: $bSASET <account> <setting> <value>$b\n\nSASET modifies the values of someone else's account settings. For more\ninformation on the settings and their possible values, see HELP SET.": "",
"Syntax: $bSESSIONS [nickname]$b\n\nSESSIONS lists information about the sessions currently attached, via\nthe server's multiclient functionality, to your nickname. An administrator\ncan use this command to list another user's sessions.": "",
"Syntax: $bSUSPEND <nickname>$b\n\nSUSPEND disables an account and disconnects the associated clients.": "",
"Syntax: $bUNREGISTER <username> [code]$b\n\nUNREGISTER lets you delete your user account (or someone else's, if you're an\nIRC operator with the correct permissions). To prevent accidental\nunregistrations, a verification code is required; invoking the command without\na code will display the necessary code.": "Sintaksa: $bUNREGISTER <username> [code]$b\n\nUNREGISTER vam omogućuje da izbrišete svoj korisnički račun (ili tuđi, ako ste IRC operator s ispravnim ovlastima). Da bi se spriječilo slučajno poništavanje registracije, potreban je kontrolni kod; pozivanjem naredbe bez koda prikazat će se potreban kod.",
"Syntax: $bVERIFY <username> <code>$b\n\nVERIFY lets you complete an account registration, if the server requires email\nor other verification.": "Sintaksa: $bVERIFY <username> <code>$b\n\nVERIFY omogućuje dovršetak registracije računa, ako poslužitelj (server) zahtijeva e-mail ili drugu provjeru."
"Syntax: $bUNSUSPEND <nickname>$b\n\nUNSUSPEND reverses a previous SUSPEND, restoring access to the account.": "",
"Syntax: $bVERIFY <username> <code>$b\n\nVERIFY lets you complete an account registration, if the server requires email\nor other verification.": "Sintaksa: $bVERIFY <username> <code>$b\n\nVERIFY omogućuje dovršetak registracije računa, ako poslužitelj (server) zahtijeva e-mail ili drugu provjeru.",
"You're not logged into an account": "Niste prijavljeni na korisnički račun"
}

View File

@ -1,8 +1,8 @@
{
"= Help Topics =\n\nCommands:\n%[1]s\n\nRPL_ISUPPORT Tokens:\n%[2]s\n\nInformation:\n%[3]s": "",
"== Channel Modes ==\n\nOragono supports the following channel modes:\n\n +b | Client masks that are banned from the channel (e.g. *!*@127.0.0.1)\n +e | Client masks that are exempted from bans.\n +I | Client masks that are exempted from the invite-only flag.\n +i | Invite-only mode, only invited clients can join the channel.\n +k | Key required when joining the channel.\n +l | Client join limit for the channel.\n +m | Moderated mode, only privileged clients can talk on the channel.\n +n | No-outside-messages mode, only users that are on the channel can send\n | messages to it.\n +R | Only registered users can join the channel.\n +s | Secret mode, channel won't show up in /LIST or whois replies.\n +t | Only channel opers can modify the topic.\n\n= Prefixes =\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.": "",
"== Channel Modes ==\n\nOragono supports the following channel modes:\n\n +b | Client masks that are banned from the channel (e.g. *!*@127.0.0.1)\n +e | Client masks that are exempted from bans.\n +I | Client masks that are exempted from the invite-only flag.\n +i | Invite-only mode, only invited clients can join the channel.\n +k | Key required when joining the channel.\n +l | Client join limit for the channel.\n +m | Moderated mode, only privileged clients can talk on the channel.\n +n | No-outside-messages mode, only users that are on the channel can send\n | messages to it.\n +R | Only registered users can join the channel.\n +s | Secret mode, channel won't show up in /LIST or whois replies.\n +t | Only channel opers can modify the topic.\n +E | Roleplaying commands are enabled in the channel.\n +C | Clients are blocked from sending CTCP messages in the channel.\n\n= Prefixes =\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.": "",
"== Server Notice Masks ==\n\nOragono supports the following server notice masks for operators:\n\n a | Local announcements.\n c | Local client connections.\n j | Local channel actions.\n k | Local kills.\n n | Local nick changes.\n o | Local oper actions.\n q | Local quits.\n t | Local /STATS usage.\n u | Local client account actions.\n x | Local X-lines (DLINE/KLINE/etc).\n v | Local vhost changes.\n\nTo set a snomask, do this with your nickname:\n\n /MODE <nick> +s <chars>\n\nFor instance, this would set the kill, oper, account and xline snomasks on dan:\n\n /MODE dan +s koux": "",
"== User Modes ==\n\nOragono supports the following user modes:\n\n +a | User is marked as being away. This mode is set with the /AWAY command.\n +i | User is marked as invisible (their channels are hidden from whois replies).\n +o | User is an IRC operator.\n +R | User only accepts messages from other registered users. \n +s | Server Notice Masks (see help with /HELPOP snomasks).\n +Z | User is connected via TLS.": "",
"== User Modes ==\n\nOragono supports the following user modes:\n\n +a | User is marked as being away. This mode is set with the /AWAY command.\n +i | User is marked as invisible (their channels are hidden from whois replies).\n +o | User is an IRC operator.\n +R | User only accepts messages from other registered users. \n +s | Server Notice Masks (see help with /HELPOP snomasks).\n +Z | User is connected via TLS.\n +B | User is a bot.\n +E | User can receive roleplaying commands.\n +T | User is blocked from sending CTCP messages.": "",
"@+client-only-tags TAGMSG <target>{,<target>}\n\nSends the given client-only tags to the given targets as a TAGMSG. See the IRCv3\nspecs for more info: http://ircv3.net/specs/core/message-tags-3.3.html": "",
"ACC LS\nACC REGISTER <accountname> [callback_namespace:]<callback> [cred_type] :<credential>\nACC VERIFY <accountname> <auth_code>\n\nUsed in account registration. See the relevant specs for more info:\nhttps://oragono.io/specs.html": "",
"AMBIANCE <target> <text to be sent>\n\nThe AMBIANCE command is used to send a scene notification to the given target.": "",
@ -13,8 +13,10 @@
"CAP <subcommand> [:<capabilities>]\n\nUsed in capability negotiation. See the IRCv3 specs for more info:\nhttp://ircv3.net/specs/core/capability-negotiation-3.1.html\nhttp://ircv3.net/specs/core/capability-negotiation-3.2.html": "",
"CHATHISTORY [params]\n\nCHATHISTORY is a history replay command associated with the IRCv3\nspecification draft/chathistory. See this document:\nhttps://github.com/ircv3/ircv3-specifications/pull/393": "",
"DEBUG <option>\n\nProvides various debugging commands for the IRCd. <option> can be one of:\n\n* GCSTATS: Garbage control statistics.\n* NUMGOROUTINE: Number of goroutines in use.\n* STARTCPUPROFILE: Starts the CPU profiler.\n* STOPCPUPROFILE: Stops the CPU profiler.\n* PROFILEHEAP: Writes a memory profile.\n* CRASHSERVER: Crashes the server (for use in failover testing)": "",
"DEFCON [level]\n\nThe DEFCON system can disable server features at runtime, to mitigate\nspam or other hostile activity. It has five levels, which are cumulative\n(i.e., level 3 includes all restrictions from level 4 and so on):\n\n5: Normal operation\n4: No new account or channel registrations\n3: All users are +R; no changes to vhosts\n2: No new unauthenticated connections; all channels are +R\n1: No new connections except from localhost or other trusted IPs": "",
"DEOPER\n\nDEOPER removes the IRCop privileges granted to you by a successful /OPER.": "",
"DLINE [ANDKILL] [MYSELF] [duration] <ip>/<net> [ON <server>] [reason [| oper reason]]\nDLINE LIST\n\nBans an IP address or network from connecting to the server. If the duration is\ngiven then only for that long. The reason is shown to the user themselves, but\neveryone else will see a standard message. The oper reason is shown to\noperators getting info about the DLINEs that exist.\n\nBans are saved across subsequent launches of the server.\n\n\"ANDKILL\" means that all matching clients are also removed from the server.\n\n\"MYSELF\" is required when the DLINE matches the address the person applying it is connected\nfrom. If \"MYSELF\" is not given, trying to DLINE yourself will result in an error.\n\n[duration] can be of the following forms:\n\t1y 12mo 31d 10h 8m 13s\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24\n\nON <server> specifies that the ban is to be set on that specific server.\n\n[reason] and [oper reason], if they exist, are separated by a vertical bar (|).\n\nIf \"DLINE LIST\" is sent, the server sends back a list of our current DLINEs.": "",
"EXTJWT <target> [service_name]\n\nGet a JSON Web Token for target (either * or a channel name).": "",
"HELP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.": "",
"HELPOP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.": "",
"HISTORY <target> [limit]\n\nReplay message history. <target> can be a channel name, \"me\" to replay direct\nmessage history, or a nickname to replay another client's direct message\nhistory (they must be logged into the same account as you). [limit] can be\neither an integer (the maximum number of messages to replay), or a time\nduration like 10m or 1h (the time window within which to replay messages).": "",
@ -53,12 +55,14 @@
"SANICK <currentnick> <newnick>\n\nGives the given user a new nickname.": "",
"SCENE <target> <text to be sent>\n\nThe SCENE command is used to send a scene notification to the given target.": "",
"SETNAME <realname>\n\nThe SETNAME command updates the realname to be the newly-given one.": "",
"SUMMON [parameters]\n\nThe SUMMON command is not implemented.": "",
"TIME [server]\n\nShows the time of the current, or the given, server.": "",
"TOPIC <channel> [topic]\n\nIf [topic] is given, sets the topic in the channel to that. If [topic] is not\ngiven, views the current topic on the channel.": "",
"UNDLINE <ip>/<net>\n\nRemoves an existing ban on an IP address or a network.\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24": "",
"UNKLINE <mask>\n\nRemoves an existing ban on a mask.\n\nFor example:\n\tdan\n\tdan!5*@127.*": "",
"USER <username> 0 * <realname>\n\nUsed in connection registration, sets your username and realname to the given\nvalues (though your username may also be looked up with Ident).": "",
"USERHOST <nickname>{ <nickname>}\n\t\t\nShows information about the given users. Takes up to 10 nicknames.": "",
"USERS [parameters]\n\nThe USERS command is not implemented.": "",
"VERSION [server]\n\nViews the version of software and the RPL_ISUPPORT tokens for the given server.": "",
"WEBIRC <password> <gateway> <hostname> <ip> [:<flags>]\n\nUsed by web<->IRC gateways and bouncers, the WEBIRC command allows gateways to\npass-through the real IP addresses of clients:\nircv3.net/specs/extensions/webirc.html\n\n<flags> is a list of space-separated strings indicating various details about\nthe connection from the client to the gateway, such as:\n\n- tls: this flag indicates that the client->gateway connection is secure": "",
"WHO <name> [o]\n\nReturns information for the given user.": "",

View File

@ -86,6 +86,7 @@
"Channel renamed": "",
"Channel renamed: %s": "",
"Channels with persistent history cannot be renamed": "",
"Client %s is always-on and cannot be fully removed by /KILL; consider /NS SUSPEND instead": "",
"Client reconnected": "",
"Client reconnected (message history may have been lost)": "",
"Client reconnected (up to %d seconds of message history lost)": "",
@ -95,6 +96,7 @@
"Could not accept ownership of channel %s": "",
"Could not delete message": "",
"Could not find given client": "",
"Could not generate EXTJWT token": "",
"Could not look up account name, proceeding anyway": "",
"Could not parse IP address or CIDR network": "",
"Could not register": "",
@ -106,9 +108,12 @@
"Could not transfer channel": "",
"Could not ungroup nick": "",
"Created at: %s": "",
"Current DEFCON level is %d": "",
"Current global users %[1]s, max %[2]s": "",
"Current local users %[1]s, max %[2]s": "",
"Data export for %[1]s completed and written to %[2]s": "",
"Device ID: %s": "",
"Direct messages from unregistered users are temporarily restricted": "",
"End of /HELPOP": "",
"End of /INFO": "",
"End of /WHOIS list": "",
@ -154,6 +159,7 @@
"Insufficient privileges": "",
"Internal error": "",
"Invalid CAP subcommand": "",
"Invalid DEFCON parameter": "",
"Invalid account name": "",
"Invalid certificate fingerprint": "",
"Invalid channel name": "",
@ -165,6 +171,7 @@
"Invalid params": "",
"Invalid regex": "",
"Invalid vhost": "",
"It was built from git hash %s.": "",
"It was rejected for reason: %s": "",
"JOIN 0 is not allowed": "",
"Language %s is not supported by this server": "",
@ -173,6 +180,7 @@
"MOTD File is missing": "",
"Malformed username": "",
"Mask isn't valid": "",
"Message rejected for containing invalid UTF-8": "",
"Messages could not be retrieved": "",
"Multiclient functionality is currently disabled for your account": "",
"Multiclient functionality is currently disabled for your account, but you can opt in": "",
@ -189,6 +197,7 @@
"No such channel": "",
"No such module [%s]": "",
"No such nick": "",
"No such service": "",
"No such setting": "",
"No text to send": "",
"No topic is set": "",
@ -207,6 +216,7 @@
"Purge reason: %s": "",
"Purged at: %s": "",
"Purged by operator: %s": "",
"Realname is not valid": "",
"Received malformed line": "",
"Registered at: %s": "",
"Registered channel: %s": "",
@ -228,6 +238,7 @@
"SASL authentication failed: Passphrase too long": "",
"SASL authentication failed: authcid and authzid should be the same": "",
"SASL message too long": "",
"SUMMON has been disabled": "",
"Server notice masks": "",
"Session %d (currently attached session):": "",
"Session %d:": "",
@ -247,9 +258,11 @@
"Successfully registered account %s": "",
"Successfully rejected vhost request for %s": "",
"Successfully reset channel access": "",
"Successfully set persistent mode %s%s on %s": "",
"Successfully set persistent mode %[1]s on %[2]s": "",
"Successfully set vhost": "",
"Successfully suspended account %s": "",
"Successfully transferred channel %[1]s to account %[2]s": "",
"Successfully un-suspended account %s": "",
"Successfully ungrouped nick %s with your account": "",
"Successfully unpurged channel %s from the server": "",
"Successfully unregistered account %s": "",
@ -272,6 +285,7 @@
"This ban matches you. To KLINE yourself, you must use the command: /KLINE MYSELF <arguments>": "",
"This command has been disabled by the server administrators": "",
"This feature has been disabled by the server administrators": "",
"This is Oragono version %s.": "",
"This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.": "",
"This server requires that you wait %v after connecting before you can use /LIST. You have %v left.": "",
"This server was created %s": "",
@ -285,10 +299,12 @@
"Transfer of channel %[1]s to account %[2]s succeeded, pending acceptance": "",
"Translators:": "",
"Try again later": "",
"USERS has been disabled": "",
"Unknown command": "",
"Unknown command. To see available commands, run: /%s HELP": "",
"Unknown subcommand": "",
"Unrecognized DEBUG subcommand": "",
"Usage: REGISTER <passphrase> [email]": "",
"User %s is no longer allowed to use vhosts": "",
"User %s is now allowed to use vhosts": "",
"User doesn't have roleplaying mode enabled": "",
@ -317,13 +333,11 @@
"You have been marked as being away": "",
"You have been offered ownership of channel %[1]s. To accept, /CS TRANSFER ACCEPT %[1]s": "",
"You have enabled autoreplay of missed messages, but you can't receive them because your client isn't set to always-on": "",
"You have joined too many channels": "",
"You have sent too many registration messages": "",
"You have too many nicks reserved already (you can remove some with /NS DROP)": "",
"You may not reregister": "",
"You must be an oper on the channel to register it": "",
"You must be connected with TLS and a client certificate to do this": "",
"You must be registered to join that channel": "",
"You must have rehash permissions in order to execute DEBUG CRASHSERVER": "",
"You must log in with SASL to join this server": "",
"You must specify an account": "",

View File

@ -1,16 +1,59 @@
{
"NickServ lets you register and login to an account.\n\nTo see in-depth help for a specific NickServ command, try:\n $b/NS HELP <command>$b\n\nHere are the commands you can use:\n%s": "",
"$bALWAYS-ON$b\n'always-on' controls whether your nickname/identity will remain active\neven while you are disconnected from the server. Your options are 'true',\n'false', and 'default' (use the server default value).": "",
"$bAUTO-AWAY$b\n'auto-away' is only effective for always-on clients. If enabled, you will\nautomatically be marked away when all your sessions are disconnected, and\nautomatically return from away when you connect again.": "",
"$bAUTOREPLAY-LINES$b\n'autoreplay-lines' controls the number of lines of channel history that will\nbe replayed to you automatically when joining a channel. Your options are any\npositive number, 0 to disable the feature, and 'default' to use the server\ndefault.": "",
"$bAUTOREPLAY-MISSED$b\n'autoreplay-missed' is only effective for always-on clients. If enabled,\nif you have at most one active session, the server will remember the time\nyou disconnect and then replay missed messages to you when you reconnect.\nYour options are 'on' and 'off'.": "",
"$bCERT$b controls a user account's certificate fingerprints": "",
"$bDM-HISTORY$b\n'dm-history' is only effective for always-on clients. It lets you control\nhow the history of your direct messages is stored. Your options are:\n1. 'off' [no history]\n2. 'ephemeral' [a limited amount of temporary history, not stored on disk]\n3. 'on' [history stored in a permanent database, if available]\n4. 'default' [use the server default]": "",
"$bDROP$b de-links your current (or the given) nickname from your user account.": "",
"$bENFORCE$b\n'enforce' lets you specify a custom enforcement mechanism for your registered\nnicknames. Your options are:\n1. 'none' [no enforcement, overriding the server default]\n2. 'strict' [you must already be authenticated to use the nick]\n3. 'default' [use the server default]": "",
"$bERASE$b erases all records of an account, allowing reuse.": "",
"$bGET$b queries the current values of your account settings": "",
"$bGHOST$b reclaims your nickname.": "",
"$bGROUP$b links your current nickname to your user account.": "",
"$bIDENTIFY$b lets you login to your account.": "",
"$bINFO$b gives you information on a user account.": "",
"$bLIST$b searches the list of registered nicknames.": "",
"$bMULTICLIENT$b\nIf 'multiclient' is enabled and you are already logged in and using a nick, a\nsecond client of yours that authenticates with SASL and requests the same nick\nis allowed to attach to the nick as well (this is comparable to the behavior\nof IRC \"bouncers\" like ZNC). Your options are 'on' (allow this behavior),\n'off' (disallow it), and 'default' (use the server default value).": "",
"$bPASSWD$b lets you change your password.": "",
"$bREGISTER$b lets you register a user account.": "",
"$bREPLAY-JOINS$b\n'replay-joins' controls whether replayed channel history will include\nlines for join and part. This provides more information about the context of\nmessages, but may be spammy. Your options are 'always', 'never', and the default\nof 'commands-only' (the messages will be replayed in /HISTORY output, but not\nduring autoreplay).": "",
"$bSADROP$b forcibly de-links the given nickname from its user account.": "",
"$bSAGET$b queries the current values of another user's account settings": "",
"$bSAREGISTER$b registers an account on someone else's behalf.": "",
"$bSASET$b modifies another user's account settings": "",
"$bSESSIONS$b lists the sessions attached to a nickname.": "",
"$bSET$b modifies your account settings": "",
"$bSUSPEND$b disables an account and disconnects the clients": "",
"$bUNREGISTER$b lets you delete your user account.": "",
"$bUNSUSPEND$b restores access to a suspended account": "",
"$bVERIFY$b lets you complete account registration.": "",
"Insufficient privileges": "",
"Invalid parameters": "",
"NickServ lets you register, log in to, and manage an account.": "",
"Password incorrect": "",
"Passwords do not match": "",
"Syntax $bSET <setting> <value>$b\n\nSET modifies your account settings. The following settings are available:": "",
"Syntax: $bCERT <LIST | ADD | DEL> [account] [certfp]$b\n\nCERT examines or modifies the TLS certificate fingerprints that can be used to\nlog into an account. Specifically, $bCERT LIST$b lists the authorized\nfingerprints, $bCERT ADD <fingerprint>$b adds a new fingerprint, and\n$bCERT DEL <fingerprint>$b removes a fingerprint. If you're an IRC operator\nwith the correct permissions, you can act on another user's account, for\nexample with $bCERT ADD <account> <fingerprint>$b.": "",
"Syntax: $bDROP [nickname]$b\n\nDROP de-links the given (or your current) nickname from your user account.": "",
"Syntax: $bENFORCE [method]$b\n\nENFORCE lets you specify a custom enforcement mechanism for your registered\nnicknames. Your options are:\n1. 'none' [no enforcement, overriding the server default]\n2. 'timeout' [anyone using the nick must authenticate before a deadline,\n or else they will be renamed]\n3. 'strict' [you must already be authenticated to use the nick]\n4. 'default' [use the server default]\nWith no arguments, queries your current enforcement status.": "",
"Syntax: $bENFORCE [method]$b\n\nENFORCE is an alias for $bGET enforce$b and $bSET enforce$b. See the help\nentry for $bSET$b for more information.": "",
"Syntax: $bERASE <username> [code]$b\n\nERASE deletes all records of an account, allowing it to be re-registered.\nThis should be used with caution, because it violates an expectation that\naccount names are permanent identifiers. Typically, UNREGISTER should be\nused instead. A confirmation code is required; invoking the command\nwithout a code will display the necessary code.": "",
"Syntax: $bGET <setting>$b\n\nGET queries the current values of your account settings. For more information\non the settings and their possible values, see HELP SET.": "",
"Syntax: $bGHOST <nickname>$b\n\nGHOST disconnects the given user from the network if they're logged in with the\nsame user account, letting you reclaim your nickname.": "",
"Syntax: $bGROUP$b\n\nGROUP links your current nickname with your logged-in account, so other people\nwill not be able to use it.": "",
"Syntax: $bIDENTIFY <username> [password]$b\n\nIDENTIFY lets you login to the given username using either password auth, or\ncertfp (your client certificate) if a password is not given.": "",
"Syntax: $bINFO [username]$b\n\nINFO gives you information about the given (or your own) user account.": "",
"Syntax: $bPASSWD <current> <new> <new_again>$b\nOr: $bPASSWD <username> <new>$b\n\nPASSWD lets you change your account password. You must supply your current\npassword and confirm the new one by typing it twice. If you're an IRC operator\nwith the correct permissions, you can use PASSWD to reset someone else's\npassword by supplying their username and then the desired password.": "",
"Syntax: $bREGISTER <username> <email> [password]$b\n\nREGISTER lets you register a user account. If the server allows anonymous\nregistration, you can send an asterisk (*) as the email address.\n\nIf the password is left out, your account will be registered to your TLS client\ncertificate (and you will need to use that certificate to login in future).": "",
"Syntax: $bLIST [regex]$b\n\nLIST returns the list of registered nicknames, which match the given regex.\nIf no regex is provided, all registered nicknames are returned.": "",
"Syntax: $bPASSWD <current> <new> <new_again>$b\nOr: $bPASSWD <username> <new>$b\n\nPASSWD lets you change your account password. You must supply your current\npassword and confirm the new one by typing it twice. If you're an IRC operator\nwith the correct permissions, you can use PASSWD to reset someone else's\npassword by supplying their username and then the desired password. To\nindicate an empty password, use * instead.": "",
"Syntax: $bREGISTER <password> [email]$b\n\nREGISTER lets you register your current nickname as a user account. If the\nserver allows anonymous registration, you can omit the e-mail address.\n\nIf you are currently logged in with a TLS client certificate and wish to use\nit instead of a password to log in, send * as the password.": "",
"Syntax: $bSADROP <nickname>$b\n\nSADROP forcibly de-links the given nickname from the attached user account.": "",
"Syntax: $bSAREGISTER <username> <password>$b\n\nSAREGISTER registers an account on someone else's behalf.\nThis is for use in configurations that require SASL for all connections;\nan administrator can set use this command to set up user accounts.": "",
"Syntax: $bSESSIONS [nickname]$b\n\nSESSIONS lists information about the sessions currently attached, via\nthe server's bouncer functionality, to your nickname. An administrator\ncan use this command to list another user's sessions.": "",
"Syntax: $bSAGET <account> <setting>$b\n\nSAGET queries the values of someone else's account settings. For more\ninformation on the settings and their possible values, see HELP SET.": "",
"Syntax: $bSAREGISTER <username> [password]$b\n\nSAREGISTER registers an account on someone else's behalf.\nThis is for use in configurations that require SASL for all connections;\nan administrator can set use this command to set up user accounts.": "",
"Syntax: $bSASET <account> <setting> <value>$b\n\nSASET modifies the values of someone else's account settings. For more\ninformation on the settings and their possible values, see HELP SET.": "",
"Syntax: $bSESSIONS [nickname]$b\n\nSESSIONS lists information about the sessions currently attached, via\nthe server's multiclient functionality, to your nickname. An administrator\ncan use this command to list another user's sessions.": "",
"Syntax: $bSUSPEND <nickname>$b\n\nSUSPEND disables an account and disconnects the associated clients.": "",
"Syntax: $bUNREGISTER <username> [code]$b\n\nUNREGISTER lets you delete your user account (or someone else's, if you're an\nIRC operator with the correct permissions). To prevent accidental\nunregistrations, a verification code is required; invoking the command without\na code will display the necessary code.": "",
"Syntax: $bVERIFY <username> <code>$b\n\nVERIFY lets you complete an account registration, if the server requires email\nor other verification.": ""
"Syntax: $bUNSUSPEND <nickname>$b\n\nUNSUSPEND reverses a previous SUSPEND, restoring access to the account.": "",
"Syntax: $bVERIFY <username> <code>$b\n\nVERIFY lets you complete an account registration, if the server requires email\nor other verification.": "",
"You're not logged into an account": ""
}

View File

@ -1,8 +1,8 @@
{
"= Help Topics =\n\nCommands:\n%[1]s\n\nRPL_ISUPPORT Tokens:\n%[2]s\n\nInformation:\n%[3]s": "",
"== Channel Modes ==\n\nOragono supports the following channel modes:\n\n +b | Client masks that are banned from the channel (e.g. *!*@127.0.0.1)\n +e | Client masks that are exempted from bans.\n +I | Client masks that are exempted from the invite-only flag.\n +i | Invite-only mode, only invited clients can join the channel.\n +k | Key required when joining the channel.\n +l | Client join limit for the channel.\n +m | Moderated mode, only privileged clients can talk on the channel.\n +n | No-outside-messages mode, only users that are on the channel can send\n | messages to it.\n +R | Only registered users can join the channel.\n +s | Secret mode, channel won't show up in /LIST or whois replies.\n +t | Only channel opers can modify the topic.\n\n= Prefixes =\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.": "",
"== Channel Modes ==\n\nOragono supports the following channel modes:\n\n +b | Client masks that are banned from the channel (e.g. *!*@127.0.0.1)\n +e | Client masks that are exempted from bans.\n +I | Client masks that are exempted from the invite-only flag.\n +i | Invite-only mode, only invited clients can join the channel.\n +k | Key required when joining the channel.\n +l | Client join limit for the channel.\n +m | Moderated mode, only privileged clients can talk on the channel.\n +n | No-outside-messages mode, only users that are on the channel can send\n | messages to it.\n +R | Only registered users can join the channel.\n +s | Secret mode, channel won't show up in /LIST or whois replies.\n +t | Only channel opers can modify the topic.\n +E | Roleplaying commands are enabled in the channel.\n +C | Clients are blocked from sending CTCP messages in the channel.\n\n= Prefixes =\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.": "",
"== Server Notice Masks ==\n\nOragono supports the following server notice masks for operators:\n\n a | Local announcements.\n c | Local client connections.\n j | Local channel actions.\n k | Local kills.\n n | Local nick changes.\n o | Local oper actions.\n q | Local quits.\n t | Local /STATS usage.\n u | Local client account actions.\n x | Local X-lines (DLINE/KLINE/etc).\n v | Local vhost changes.\n\nTo set a snomask, do this with your nickname:\n\n /MODE <nick> +s <chars>\n\nFor instance, this would set the kill, oper, account and xline snomasks on dan:\n\n /MODE dan +s koux": "",
"== User Modes ==\n\nOragono supports the following user modes:\n\n +a | User is marked as being away. This mode is set with the /AWAY command.\n +i | User is marked as invisible (their channels are hidden from whois replies).\n +o | User is an IRC operator.\n +R | User only accepts messages from other registered users. \n +s | Server Notice Masks (see help with /HELPOP snomasks).\n +Z | User is connected via TLS.": "",
"== User Modes ==\n\nOragono supports the following user modes:\n\n +a | User is marked as being away. This mode is set with the /AWAY command.\n +i | User is marked as invisible (their channels are hidden from whois replies).\n +o | User is an IRC operator.\n +R | User only accepts messages from other registered users. \n +s | Server Notice Masks (see help with /HELPOP snomasks).\n +Z | User is connected via TLS.\n +B | User is a bot.\n +E | User can receive roleplaying commands.\n +T | User is blocked from sending CTCP messages.": "",
"@+client-only-tags TAGMSG <target>{,<target>}\n\nSends the given client-only tags to the given targets as a TAGMSG. See the IRCv3\nspecs for more info: http://ircv3.net/specs/core/message-tags-3.3.html": "",
"ACC LS\nACC REGISTER <accountname> [callback_namespace:]<callback> [cred_type] :<credential>\nACC VERIFY <accountname> <auth_code>\n\nUsed in account registration. See the relevant specs for more info:\nhttps://oragono.io/specs.html": "",
"AMBIANCE <target> <text to be sent>\n\nThe AMBIANCE command is used to send a scene notification to the given target.": "",
@ -13,8 +13,10 @@
"CAP <subcommand> [:<capabilities>]\n\nUsed in capability negotiation. See the IRCv3 specs for more info:\nhttp://ircv3.net/specs/core/capability-negotiation-3.1.html\nhttp://ircv3.net/specs/core/capability-negotiation-3.2.html": "",
"CHATHISTORY [params]\n\nCHATHISTORY is a history replay command associated with the IRCv3\nspecification draft/chathistory. See this document:\nhttps://github.com/ircv3/ircv3-specifications/pull/393": "",
"DEBUG <option>\n\nProvides various debugging commands for the IRCd. <option> can be one of:\n\n* GCSTATS: Garbage control statistics.\n* NUMGOROUTINE: Number of goroutines in use.\n* STARTCPUPROFILE: Starts the CPU profiler.\n* STOPCPUPROFILE: Stops the CPU profiler.\n* PROFILEHEAP: Writes a memory profile.\n* CRASHSERVER: Crashes the server (for use in failover testing)": "",
"DEFCON [level]\n\nThe DEFCON system can disable server features at runtime, to mitigate\nspam or other hostile activity. It has five levels, which are cumulative\n(i.e., level 3 includes all restrictions from level 4 and so on):\n\n5: Normal operation\n4: No new account or channel registrations\n3: All users are +R; no changes to vhosts\n2: No new unauthenticated connections; all channels are +R\n1: No new connections except from localhost or other trusted IPs": "",
"DEOPER\n\nDEOPER removes the IRCop privileges granted to you by a successful /OPER.": "",
"DLINE [ANDKILL] [MYSELF] [duration] <ip>/<net> [ON <server>] [reason [| oper reason]]\nDLINE LIST\n\nBans an IP address or network from connecting to the server. If the duration is\ngiven then only for that long. The reason is shown to the user themselves, but\neveryone else will see a standard message. The oper reason is shown to\noperators getting info about the DLINEs that exist.\n\nBans are saved across subsequent launches of the server.\n\n\"ANDKILL\" means that all matching clients are also removed from the server.\n\n\"MYSELF\" is required when the DLINE matches the address the person applying it is connected\nfrom. If \"MYSELF\" is not given, trying to DLINE yourself will result in an error.\n\n[duration] can be of the following forms:\n\t1y 12mo 31d 10h 8m 13s\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24\n\nON <server> specifies that the ban is to be set on that specific server.\n\n[reason] and [oper reason], if they exist, are separated by a vertical bar (|).\n\nIf \"DLINE LIST\" is sent, the server sends back a list of our current DLINEs.": "",
"EXTJWT <target> [service_name]\n\nGet a JSON Web Token for target (either * or a channel name).": "",
"HELP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.": "",
"HELPOP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.": "",
"HISTORY <target> [limit]\n\nReplay message history. <target> can be a channel name, \"me\" to replay direct\nmessage history, or a nickname to replay another client's direct message\nhistory (they must be logged into the same account as you). [limit] can be\neither an integer (the maximum number of messages to replay), or a time\nduration like 10m or 1h (the time window within which to replay messages).": "",
@ -53,12 +55,14 @@
"SANICK <currentnick> <newnick>\n\nGives the given user a new nickname.": "",
"SCENE <target> <text to be sent>\n\nThe SCENE command is used to send a scene notification to the given target.": "",
"SETNAME <realname>\n\nThe SETNAME command updates the realname to be the newly-given one.": "",
"SUMMON [parameters]\n\nThe SUMMON command is not implemented.": "",
"TIME [server]\n\nShows the time of the current, or the given, server.": "",
"TOPIC <channel> [topic]\n\nIf [topic] is given, sets the topic in the channel to that. If [topic] is not\ngiven, views the current topic on the channel.": "",
"UNDLINE <ip>/<net>\n\nRemoves an existing ban on an IP address or a network.\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24": "",
"UNKLINE <mask>\n\nRemoves an existing ban on a mask.\n\nFor example:\n\tdan\n\tdan!5*@127.*": "",
"USER <username> 0 * <realname>\n\nUsed in connection registration, sets your username and realname to the given\nvalues (though your username may also be looked up with Ident).": "",
"USERHOST <nickname>{ <nickname>}\n\t\t\nShows information about the given users. Takes up to 10 nicknames.": "",
"USERS [parameters]\n\nThe USERS command is not implemented.": "",
"VERSION [server]\n\nViews the version of software and the RPL_ISUPPORT tokens for the given server.": "",
"WEBIRC <password> <gateway> <hostname> <ip> [:<flags>]\n\nUsed by web<->IRC gateways and bouncers, the WEBIRC command allows gateways to\npass-through the real IP addresses of clients:\nircv3.net/specs/extensions/webirc.html\n\n<flags> is a list of space-separated strings indicating various details about\nthe connection from the client to the gateway, such as:\n\n- tls: this flag indicates that the client->gateway connection is secure": "",
"WHO <name> [o]\n\nReturns information for the given user.": "",

View File

@ -86,6 +86,7 @@
"Channel renamed": "",
"Channel renamed: %s": "",
"Channels with persistent history cannot be renamed": "",
"Client %s is always-on and cannot be fully removed by /KILL; consider /NS SUSPEND instead": "",
"Client reconnected": "",
"Client reconnected (message history may have been lost)": "",
"Client reconnected (up to %d seconds of message history lost)": "",
@ -95,6 +96,7 @@
"Could not accept ownership of channel %s": "",
"Could not delete message": "",
"Could not find given client": "",
"Could not generate EXTJWT token": "",
"Could not look up account name, proceeding anyway": "",
"Could not parse IP address or CIDR network": "",
"Could not register": "",
@ -106,9 +108,12 @@
"Could not transfer channel": "",
"Could not ungroup nick": "",
"Created at: %s": "",
"Current DEFCON level is %d": "",
"Current global users %[1]s, max %[2]s": "",
"Current local users %[1]s, max %[2]s": "",
"Data export for %[1]s completed and written to %[2]s": "",
"Device ID: %s": "",
"Direct messages from unregistered users are temporarily restricted": "",
"End of /HELPOP": "",
"End of /INFO": "",
"End of /WHOIS list": "",
@ -154,6 +159,7 @@
"Insufficient privileges": "",
"Internal error": "",
"Invalid CAP subcommand": "",
"Invalid DEFCON parameter": "",
"Invalid account name": "",
"Invalid certificate fingerprint": "",
"Invalid channel name": "",
@ -165,6 +171,7 @@
"Invalid params": "",
"Invalid regex": "",
"Invalid vhost": "",
"It was built from git hash %s.": "",
"It was rejected for reason: %s": "",
"JOIN 0 is not allowed": "",
"Language %s is not supported by this server": "",
@ -173,6 +180,7 @@
"MOTD File is missing": "",
"Malformed username": "",
"Mask isn't valid": "",
"Message rejected for containing invalid UTF-8": "",
"Messages could not be retrieved": "",
"Multiclient functionality is currently disabled for your account": "",
"Multiclient functionality is currently disabled for your account, but you can opt in": "",
@ -189,6 +197,7 @@
"No such channel": "",
"No such module [%s]": "",
"No such nick": "",
"No such service": "",
"No such setting": "",
"No text to send": "",
"No topic is set": "",
@ -207,6 +216,7 @@
"Purge reason: %s": "",
"Purged at: %s": "",
"Purged by operator: %s": "",
"Realname is not valid": "",
"Received malformed line": "",
"Registered at: %s": "",
"Registered channel: %s": "",
@ -228,6 +238,7 @@
"SASL authentication failed: Passphrase too long": "",
"SASL authentication failed: authcid and authzid should be the same": "",
"SASL message too long": "",
"SUMMON has been disabled": "",
"Server notice masks": "",
"Session %d (currently attached session):": "",
"Session %d:": "",
@ -247,9 +258,11 @@
"Successfully registered account %s": "",
"Successfully rejected vhost request for %s": "",
"Successfully reset channel access": "",
"Successfully set persistent mode %s%s on %s": "",
"Successfully set persistent mode %[1]s on %[2]s": "",
"Successfully set vhost": "",
"Successfully suspended account %s": "",
"Successfully transferred channel %[1]s to account %[2]s": "",
"Successfully un-suspended account %s": "",
"Successfully ungrouped nick %s with your account": "",
"Successfully unpurged channel %s from the server": "",
"Successfully unregistered account %s": "",
@ -272,6 +285,7 @@
"This ban matches you. To KLINE yourself, you must use the command: /KLINE MYSELF <arguments>": "",
"This command has been disabled by the server administrators": "",
"This feature has been disabled by the server administrators": "",
"This is Oragono version %s.": "",
"This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.": "",
"This server requires that you wait %v after connecting before you can use /LIST. You have %v left.": "",
"This server was created %s": "",
@ -285,10 +299,12 @@
"Transfer of channel %[1]s to account %[2]s succeeded, pending acceptance": "",
"Translators:": "",
"Try again later": "",
"USERS has been disabled": "",
"Unknown command": "",
"Unknown command. To see available commands, run: /%s HELP": "",
"Unknown subcommand": "",
"Unrecognized DEBUG subcommand": "",
"Usage: REGISTER <passphrase> [email]": "",
"User %s is no longer allowed to use vhosts": "",
"User %s is now allowed to use vhosts": "",
"User doesn't have roleplaying mode enabled": "",
@ -317,13 +333,11 @@
"You have been marked as being away": "",
"You have been offered ownership of channel %[1]s. To accept, /CS TRANSFER ACCEPT %[1]s": "",
"You have enabled autoreplay of missed messages, but you can't receive them because your client isn't set to always-on": "",
"You have joined too many channels": "",
"You have sent too many registration messages": "",
"You have too many nicks reserved already (you can remove some with /NS DROP)": "",
"You may not reregister": "",
"You must be an oper on the channel to register it": "",
"You must be connected with TLS and a client certificate to do this": "",
"You must be registered to join that channel": "",
"You must have rehash permissions in order to execute DEBUG CRASHSERVER": "",
"You must log in with SASL to join this server": "",
"You must specify an account": "",

View File

@ -1,16 +1,59 @@
{
"NickServ lets you register and login to an account.\n\nTo see in-depth help for a specific NickServ command, try:\n $b/NS HELP <command>$b\n\nHere are the commands you can use:\n%s": "",
"$bALWAYS-ON$b\n'always-on' controls whether your nickname/identity will remain active\neven while you are disconnected from the server. Your options are 'true',\n'false', and 'default' (use the server default value).": "",
"$bAUTO-AWAY$b\n'auto-away' is only effective for always-on clients. If enabled, you will\nautomatically be marked away when all your sessions are disconnected, and\nautomatically return from away when you connect again.": "",
"$bAUTOREPLAY-LINES$b\n'autoreplay-lines' controls the number of lines of channel history that will\nbe replayed to you automatically when joining a channel. Your options are any\npositive number, 0 to disable the feature, and 'default' to use the server\ndefault.": "",
"$bAUTOREPLAY-MISSED$b\n'autoreplay-missed' is only effective for always-on clients. If enabled,\nif you have at most one active session, the server will remember the time\nyou disconnect and then replay missed messages to you when you reconnect.\nYour options are 'on' and 'off'.": "",
"$bCERT$b controls a user account's certificate fingerprints": "",
"$bDM-HISTORY$b\n'dm-history' is only effective for always-on clients. It lets you control\nhow the history of your direct messages is stored. Your options are:\n1. 'off' [no history]\n2. 'ephemeral' [a limited amount of temporary history, not stored on disk]\n3. 'on' [history stored in a permanent database, if available]\n4. 'default' [use the server default]": "",
"$bDROP$b de-links your current (or the given) nickname from your user account.": "",
"$bENFORCE$b\n'enforce' lets you specify a custom enforcement mechanism for your registered\nnicknames. Your options are:\n1. 'none' [no enforcement, overriding the server default]\n2. 'strict' [you must already be authenticated to use the nick]\n3. 'default' [use the server default]": "",
"$bERASE$b erases all records of an account, allowing reuse.": "",
"$bGET$b queries the current values of your account settings": "",
"$bGHOST$b reclaims your nickname.": "",
"$bGROUP$b links your current nickname to your user account.": "",
"$bIDENTIFY$b lets you login to your account.": "",
"$bINFO$b gives you information on a user account.": "",
"$bLIST$b searches the list of registered nicknames.": "",
"$bMULTICLIENT$b\nIf 'multiclient' is enabled and you are already logged in and using a nick, a\nsecond client of yours that authenticates with SASL and requests the same nick\nis allowed to attach to the nick as well (this is comparable to the behavior\nof IRC \"bouncers\" like ZNC). Your options are 'on' (allow this behavior),\n'off' (disallow it), and 'default' (use the server default value).": "",
"$bPASSWD$b lets you change your password.": "",
"$bREGISTER$b lets you register a user account.": "",
"$bREPLAY-JOINS$b\n'replay-joins' controls whether replayed channel history will include\nlines for join and part. This provides more information about the context of\nmessages, but may be spammy. Your options are 'always', 'never', and the default\nof 'commands-only' (the messages will be replayed in /HISTORY output, but not\nduring autoreplay).": "",
"$bSADROP$b forcibly de-links the given nickname from its user account.": "",
"$bSAGET$b queries the current values of another user's account settings": "",
"$bSAREGISTER$b registers an account on someone else's behalf.": "",
"$bSASET$b modifies another user's account settings": "",
"$bSESSIONS$b lists the sessions attached to a nickname.": "",
"$bSET$b modifies your account settings": "",
"$bSUSPEND$b disables an account and disconnects the clients": "",
"$bUNREGISTER$b lets you delete your user account.": "",
"$bUNSUSPEND$b restores access to a suspended account": "",
"$bVERIFY$b lets you complete account registration.": "",
"Insufficient privileges": "",
"Invalid parameters": "",
"NickServ lets you register, log in to, and manage an account.": "",
"Password incorrect": "",
"Passwords do not match": "",
"Syntax $bSET <setting> <value>$b\n\nSET modifies your account settings. The following settings are available:": "",
"Syntax: $bCERT <LIST | ADD | DEL> [account] [certfp]$b\n\nCERT examines or modifies the TLS certificate fingerprints that can be used to\nlog into an account. Specifically, $bCERT LIST$b lists the authorized\nfingerprints, $bCERT ADD <fingerprint>$b adds a new fingerprint, and\n$bCERT DEL <fingerprint>$b removes a fingerprint. If you're an IRC operator\nwith the correct permissions, you can act on another user's account, for\nexample with $bCERT ADD <account> <fingerprint>$b.": "",
"Syntax: $bDROP [nickname]$b\n\nDROP de-links the given (or your current) nickname from your user account.": "",
"Syntax: $bENFORCE [method]$b\n\nENFORCE lets you specify a custom enforcement mechanism for your registered\nnicknames. Your options are:\n1. 'none' [no enforcement, overriding the server default]\n2. 'timeout' [anyone using the nick must authenticate before a deadline,\n or else they will be renamed]\n3. 'strict' [you must already be authenticated to use the nick]\n4. 'default' [use the server default]\nWith no arguments, queries your current enforcement status.": "",
"Syntax: $bENFORCE [method]$b\n\nENFORCE is an alias for $bGET enforce$b and $bSET enforce$b. See the help\nentry for $bSET$b for more information.": "",
"Syntax: $bERASE <username> [code]$b\n\nERASE deletes all records of an account, allowing it to be re-registered.\nThis should be used with caution, because it violates an expectation that\naccount names are permanent identifiers. Typically, UNREGISTER should be\nused instead. A confirmation code is required; invoking the command\nwithout a code will display the necessary code.": "",
"Syntax: $bGET <setting>$b\n\nGET queries the current values of your account settings. For more information\non the settings and their possible values, see HELP SET.": "",
"Syntax: $bGHOST <nickname>$b\n\nGHOST disconnects the given user from the network if they're logged in with the\nsame user account, letting you reclaim your nickname.": "",
"Syntax: $bGROUP$b\n\nGROUP links your current nickname with your logged-in account, so other people\nwill not be able to use it.": "",
"Syntax: $bIDENTIFY <username> [password]$b\n\nIDENTIFY lets you login to the given username using either password auth, or\ncertfp (your client certificate) if a password is not given.": "",
"Syntax: $bINFO [username]$b\n\nINFO gives you information about the given (or your own) user account.": "",
"Syntax: $bPASSWD <current> <new> <new_again>$b\nOr: $bPASSWD <username> <new>$b\n\nPASSWD lets you change your account password. You must supply your current\npassword and confirm the new one by typing it twice. If you're an IRC operator\nwith the correct permissions, you can use PASSWD to reset someone else's\npassword by supplying their username and then the desired password.": "",
"Syntax: $bREGISTER <username> <email> [password]$b\n\nREGISTER lets you register a user account. If the server allows anonymous\nregistration, you can send an asterisk (*) as the email address.\n\nIf the password is left out, your account will be registered to your TLS client\ncertificate (and you will need to use that certificate to login in future).": "",
"Syntax: $bLIST [regex]$b\n\nLIST returns the list of registered nicknames, which match the given regex.\nIf no regex is provided, all registered nicknames are returned.": "",
"Syntax: $bPASSWD <current> <new> <new_again>$b\nOr: $bPASSWD <username> <new>$b\n\nPASSWD lets you change your account password. You must supply your current\npassword and confirm the new one by typing it twice. If you're an IRC operator\nwith the correct permissions, you can use PASSWD to reset someone else's\npassword by supplying their username and then the desired password. To\nindicate an empty password, use * instead.": "",
"Syntax: $bREGISTER <password> [email]$b\n\nREGISTER lets you register your current nickname as a user account. If the\nserver allows anonymous registration, you can omit the e-mail address.\n\nIf you are currently logged in with a TLS client certificate and wish to use\nit instead of a password to log in, send * as the password.": "",
"Syntax: $bSADROP <nickname>$b\n\nSADROP forcibly de-links the given nickname from the attached user account.": "",
"Syntax: $bSAREGISTER <username> <password>$b\n\nSAREGISTER registers an account on someone else's behalf.\nThis is for use in configurations that require SASL for all connections;\nan administrator can set use this command to set up user accounts.": "",
"Syntax: $bSESSIONS [nickname]$b\n\nSESSIONS lists information about the sessions currently attached, via\nthe server's bouncer functionality, to your nickname. An administrator\ncan use this command to list another user's sessions.": "",
"Syntax: $bSAGET <account> <setting>$b\n\nSAGET queries the values of someone else's account settings. For more\ninformation on the settings and their possible values, see HELP SET.": "",
"Syntax: $bSAREGISTER <username> [password]$b\n\nSAREGISTER registers an account on someone else's behalf.\nThis is for use in configurations that require SASL for all connections;\nan administrator can set use this command to set up user accounts.": "",
"Syntax: $bSASET <account> <setting> <value>$b\n\nSASET modifies the values of someone else's account settings. For more\ninformation on the settings and their possible values, see HELP SET.": "",
"Syntax: $bSESSIONS [nickname]$b\n\nSESSIONS lists information about the sessions currently attached, via\nthe server's multiclient functionality, to your nickname. An administrator\ncan use this command to list another user's sessions.": "",
"Syntax: $bSUSPEND <nickname>$b\n\nSUSPEND disables an account and disconnects the associated clients.": "",
"Syntax: $bUNREGISTER <username> [code]$b\n\nUNREGISTER lets you delete your user account (or someone else's, if you're an\nIRC operator with the correct permissions). To prevent accidental\nunregistrations, a verification code is required; invoking the command without\na code will display the necessary code.": "",
"Syntax: $bVERIFY <username> <code>$b\n\nVERIFY lets you complete an account registration, if the server requires email\nor other verification.": ""
"Syntax: $bUNSUSPEND <nickname>$b\n\nUNSUSPEND reverses a previous SUSPEND, restoring access to the account.": "",
"Syntax: $bVERIFY <username> <code>$b\n\nVERIFY lets you complete an account registration, if the server requires email\nor other verification.": "",
"You're not logged into an account": ""
}

View File

@ -1,8 +1,8 @@
{
"= Help Topics =\n\nCommands:\n%[1]s\n\nRPL_ISUPPORT Tokens:\n%[2]s\n\nInformation:\n%[3]s": "",
"== Channel Modes ==\n\nOragono supports the following channel modes:\n\n +b | Client masks that are banned from the channel (e.g. *!*@127.0.0.1)\n +e | Client masks that are exempted from bans.\n +I | Client masks that are exempted from the invite-only flag.\n +i | Invite-only mode, only invited clients can join the channel.\n +k | Key required when joining the channel.\n +l | Client join limit for the channel.\n +m | Moderated mode, only privileged clients can talk on the channel.\n +n | No-outside-messages mode, only users that are on the channel can send\n | messages to it.\n +R | Only registered users can join the channel.\n +s | Secret mode, channel won't show up in /LIST or whois replies.\n +t | Only channel opers can modify the topic.\n\n= Prefixes =\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.": "",
"== Channel Modes ==\n\nOragono supports the following channel modes:\n\n +b | Client masks that are banned from the channel (e.g. *!*@127.0.0.1)\n +e | Client masks that are exempted from bans.\n +I | Client masks that are exempted from the invite-only flag.\n +i | Invite-only mode, only invited clients can join the channel.\n +k | Key required when joining the channel.\n +l | Client join limit for the channel.\n +m | Moderated mode, only privileged clients can talk on the channel.\n +n | No-outside-messages mode, only users that are on the channel can send\n | messages to it.\n +R | Only registered users can join the channel.\n +s | Secret mode, channel won't show up in /LIST or whois replies.\n +t | Only channel opers can modify the topic.\n +E | Roleplaying commands are enabled in the channel.\n +C | Clients are blocked from sending CTCP messages in the channel.\n\n= Prefixes =\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.": "",
"== Server Notice Masks ==\n\nOragono supports the following server notice masks for operators:\n\n a | Local announcements.\n c | Local client connections.\n j | Local channel actions.\n k | Local kills.\n n | Local nick changes.\n o | Local oper actions.\n q | Local quits.\n t | Local /STATS usage.\n u | Local client account actions.\n x | Local X-lines (DLINE/KLINE/etc).\n v | Local vhost changes.\n\nTo set a snomask, do this with your nickname:\n\n /MODE <nick> +s <chars>\n\nFor instance, this would set the kill, oper, account and xline snomasks on dan:\n\n /MODE dan +s koux": "",
"== User Modes ==\n\nOragono supports the following user modes:\n\n +a | User is marked as being away. This mode is set with the /AWAY command.\n +i | User is marked as invisible (their channels are hidden from whois replies).\n +o | User is an IRC operator.\n +R | User only accepts messages from other registered users. \n +s | Server Notice Masks (see help with /HELPOP snomasks).\n +Z | User is connected via TLS.": "",
"== User Modes ==\n\nOragono supports the following user modes:\n\n +a | User is marked as being away. This mode is set with the /AWAY command.\n +i | User is marked as invisible (their channels are hidden from whois replies).\n +o | User is an IRC operator.\n +R | User only accepts messages from other registered users. \n +s | Server Notice Masks (see help with /HELPOP snomasks).\n +Z | User is connected via TLS.\n +B | User is a bot.\n +E | User can receive roleplaying commands.\n +T | User is blocked from sending CTCP messages.": "",
"@+client-only-tags TAGMSG <target>{,<target>}\n\nSends the given client-only tags to the given targets as a TAGMSG. See the IRCv3\nspecs for more info: http://ircv3.net/specs/core/message-tags-3.3.html": "",
"ACC LS\nACC REGISTER <accountname> [callback_namespace:]<callback> [cred_type] :<credential>\nACC VERIFY <accountname> <auth_code>\n\nUsed in account registration. See the relevant specs for more info:\nhttps://oragono.io/specs.html": "",
"AMBIANCE <target> <text to be sent>\n\nThe AMBIANCE command is used to send a scene notification to the given target.": "",
@ -13,8 +13,10 @@
"CAP <subcommand> [:<capabilities>]\n\nUsed in capability negotiation. See the IRCv3 specs for more info:\nhttp://ircv3.net/specs/core/capability-negotiation-3.1.html\nhttp://ircv3.net/specs/core/capability-negotiation-3.2.html": "",
"CHATHISTORY [params]\n\nCHATHISTORY is a history replay command associated with the IRCv3\nspecification draft/chathistory. See this document:\nhttps://github.com/ircv3/ircv3-specifications/pull/393": "",
"DEBUG <option>\n\nProvides various debugging commands for the IRCd. <option> can be one of:\n\n* GCSTATS: Garbage control statistics.\n* NUMGOROUTINE: Number of goroutines in use.\n* STARTCPUPROFILE: Starts the CPU profiler.\n* STOPCPUPROFILE: Stops the CPU profiler.\n* PROFILEHEAP: Writes a memory profile.\n* CRASHSERVER: Crashes the server (for use in failover testing)": "",
"DEFCON [level]\n\nThe DEFCON system can disable server features at runtime, to mitigate\nspam or other hostile activity. It has five levels, which are cumulative\n(i.e., level 3 includes all restrictions from level 4 and so on):\n\n5: Normal operation\n4: No new account or channel registrations\n3: All users are +R; no changes to vhosts\n2: No new unauthenticated connections; all channels are +R\n1: No new connections except from localhost or other trusted IPs": "",
"DEOPER\n\nDEOPER removes the IRCop privileges granted to you by a successful /OPER.": "",
"DLINE [ANDKILL] [MYSELF] [duration] <ip>/<net> [ON <server>] [reason [| oper reason]]\nDLINE LIST\n\nBans an IP address or network from connecting to the server. If the duration is\ngiven then only for that long. The reason is shown to the user themselves, but\neveryone else will see a standard message. The oper reason is shown to\noperators getting info about the DLINEs that exist.\n\nBans are saved across subsequent launches of the server.\n\n\"ANDKILL\" means that all matching clients are also removed from the server.\n\n\"MYSELF\" is required when the DLINE matches the address the person applying it is connected\nfrom. If \"MYSELF\" is not given, trying to DLINE yourself will result in an error.\n\n[duration] can be of the following forms:\n\t1y 12mo 31d 10h 8m 13s\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24\n\nON <server> specifies that the ban is to be set on that specific server.\n\n[reason] and [oper reason], if they exist, are separated by a vertical bar (|).\n\nIf \"DLINE LIST\" is sent, the server sends back a list of our current DLINEs.": "",
"EXTJWT <target> [service_name]\n\nGet a JSON Web Token for target (either * or a channel name).": "",
"HELP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.": "",
"HELPOP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.": "",
"HISTORY <target> [limit]\n\nReplay message history. <target> can be a channel name, \"me\" to replay direct\nmessage history, or a nickname to replay another client's direct message\nhistory (they must be logged into the same account as you). [limit] can be\neither an integer (the maximum number of messages to replay), or a time\nduration like 10m or 1h (the time window within which to replay messages).": "",
@ -53,12 +55,14 @@
"SANICK <currentnick> <newnick>\n\nGives the given user a new nickname.": "",
"SCENE <target> <text to be sent>\n\nThe SCENE command is used to send a scene notification to the given target.": "",
"SETNAME <realname>\n\nThe SETNAME command updates the realname to be the newly-given one.": "",
"SUMMON [parameters]\n\nThe SUMMON command is not implemented.": "",
"TIME [server]\n\nShows the time of the current, or the given, server.": "",
"TOPIC <channel> [topic]\n\nIf [topic] is given, sets the topic in the channel to that. If [topic] is not\ngiven, views the current topic on the channel.": "",
"UNDLINE <ip>/<net>\n\nRemoves an existing ban on an IP address or a network.\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24": "",
"UNKLINE <mask>\n\nRemoves an existing ban on a mask.\n\nFor example:\n\tdan\n\tdan!5*@127.*": "",
"USER <username> 0 * <realname>\n\nUsed in connection registration, sets your username and realname to the given\nvalues (though your username may also be looked up with Ident).": "",
"USERHOST <nickname>{ <nickname>}\n\t\t\nShows information about the given users. Takes up to 10 nicknames.": "",
"USERS [parameters]\n\nThe USERS command is not implemented.": "",
"VERSION [server]\n\nViews the version of software and the RPL_ISUPPORT tokens for the given server.": "",
"WEBIRC <password> <gateway> <hostname> <ip> [:<flags>]\n\nUsed by web<->IRC gateways and bouncers, the WEBIRC command allows gateways to\npass-through the real IP addresses of clients:\nircv3.net/specs/extensions/webirc.html\n\n<flags> is a list of space-separated strings indicating various details about\nthe connection from the client to the gateway, such as:\n\n- tls: this flag indicates that the client->gateway connection is secure": "",
"WHO <name> [o]\n\nReturns information for the given user.": "",

View File

@ -86,6 +86,7 @@
"Channel renamed": "",
"Channel renamed: %s": "",
"Channels with persistent history cannot be renamed": "",
"Client %s is always-on and cannot be fully removed by /KILL; consider /NS SUSPEND instead": "",
"Client reconnected": "",
"Client reconnected (message history may have been lost)": "",
"Client reconnected (up to %d seconds of message history lost)": "",
@ -95,6 +96,7 @@
"Could not accept ownership of channel %s": "",
"Could not delete message": "",
"Could not find given client": "",
"Could not generate EXTJWT token": "",
"Could not look up account name, proceeding anyway": "",
"Could not parse IP address or CIDR network": "",
"Could not register": "",
@ -106,9 +108,12 @@
"Could not transfer channel": "",
"Could not ungroup nick": "",
"Created at: %s": "",
"Current DEFCON level is %d": "",
"Current global users %[1]s, max %[2]s": "",
"Current local users %[1]s, max %[2]s": "",
"Data export for %[1]s completed and written to %[2]s": "",
"Device ID: %s": "",
"Direct messages from unregistered users are temporarily restricted": "",
"End of /HELPOP": "",
"End of /INFO": "",
"End of /WHOIS list": "",
@ -154,6 +159,7 @@
"Insufficient privileges": "",
"Internal error": "",
"Invalid CAP subcommand": "",
"Invalid DEFCON parameter": "",
"Invalid account name": "",
"Invalid certificate fingerprint": "",
"Invalid channel name": "",
@ -165,6 +171,7 @@
"Invalid params": "",
"Invalid regex": "",
"Invalid vhost": "",
"It was built from git hash %s.": "",
"It was rejected for reason: %s": "",
"JOIN 0 is not allowed": "",
"Language %s is not supported by this server": "",
@ -173,6 +180,7 @@
"MOTD File is missing": "",
"Malformed username": "",
"Mask isn't valid": "",
"Message rejected for containing invalid UTF-8": "",
"Messages could not be retrieved": "",
"Multiclient functionality is currently disabled for your account": "",
"Multiclient functionality is currently disabled for your account, but you can opt in": "",
@ -189,6 +197,7 @@
"No such channel": "",
"No such module [%s]": "",
"No such nick": "",
"No such service": "",
"No such setting": "",
"No text to send": "",
"No topic is set": "",
@ -207,6 +216,7 @@
"Purge reason: %s": "",
"Purged at: %s": "",
"Purged by operator: %s": "",
"Realname is not valid": "",
"Received malformed line": "",
"Registered at: %s": "",
"Registered channel: %s": "",
@ -228,6 +238,7 @@
"SASL authentication failed: Passphrase too long": "",
"SASL authentication failed: authcid and authzid should be the same": "",
"SASL message too long": "",
"SUMMON has been disabled": "",
"Server notice masks": "",
"Session %d (currently attached session):": "",
"Session %d:": "",
@ -247,9 +258,11 @@
"Successfully registered account %s": "",
"Successfully rejected vhost request for %s": "",
"Successfully reset channel access": "",
"Successfully set persistent mode %s%s on %s": "",
"Successfully set persistent mode %[1]s on %[2]s": "",
"Successfully set vhost": "",
"Successfully suspended account %s": "",
"Successfully transferred channel %[1]s to account %[2]s": "",
"Successfully un-suspended account %s": "",
"Successfully ungrouped nick %s with your account": "",
"Successfully unpurged channel %s from the server": "",
"Successfully unregistered account %s": "",
@ -272,6 +285,7 @@
"This ban matches you. To KLINE yourself, you must use the command: /KLINE MYSELF <arguments>": "",
"This command has been disabled by the server administrators": "",
"This feature has been disabled by the server administrators": "",
"This is Oragono version %s.": "",
"This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.": "",
"This server requires that you wait %v after connecting before you can use /LIST. You have %v left.": "",
"This server was created %s": "",
@ -285,10 +299,12 @@
"Transfer of channel %[1]s to account %[2]s succeeded, pending acceptance": "",
"Translators:": "",
"Try again later": "",
"USERS has been disabled": "",
"Unknown command": "",
"Unknown command. To see available commands, run: /%s HELP": "",
"Unknown subcommand": "",
"Unrecognized DEBUG subcommand": "",
"Usage: REGISTER <passphrase> [email]": "",
"User %s is no longer allowed to use vhosts": "",
"User %s is now allowed to use vhosts": "",
"User doesn't have roleplaying mode enabled": "",
@ -317,13 +333,11 @@
"You have been marked as being away": "",
"You have been offered ownership of channel %[1]s. To accept, /CS TRANSFER ACCEPT %[1]s": "",
"You have enabled autoreplay of missed messages, but you can't receive them because your client isn't set to always-on": "",
"You have joined too many channels": "",
"You have sent too many registration messages": "",
"You have too many nicks reserved already (you can remove some with /NS DROP)": "",
"You may not reregister": "",
"You must be an oper on the channel to register it": "",
"You must be connected with TLS and a client certificate to do this": "",
"You must be registered to join that channel": "",
"You must have rehash permissions in order to execute DEBUG CRASHSERVER": "",
"You must log in with SASL to join this server": "",
"You must specify an account": "",

View File

@ -1,16 +1,59 @@
{
"NickServ lets you register and login to an account.\n\nTo see in-depth help for a specific NickServ command, try:\n $b/NS HELP <command>$b\n\nHere are the commands you can use:\n%s": "",
"$bALWAYS-ON$b\n'always-on' controls whether your nickname/identity will remain active\neven while you are disconnected from the server. Your options are 'true',\n'false', and 'default' (use the server default value).": "",
"$bAUTO-AWAY$b\n'auto-away' is only effective for always-on clients. If enabled, you will\nautomatically be marked away when all your sessions are disconnected, and\nautomatically return from away when you connect again.": "",
"$bAUTOREPLAY-LINES$b\n'autoreplay-lines' controls the number of lines of channel history that will\nbe replayed to you automatically when joining a channel. Your options are any\npositive number, 0 to disable the feature, and 'default' to use the server\ndefault.": "",
"$bAUTOREPLAY-MISSED$b\n'autoreplay-missed' is only effective for always-on clients. If enabled,\nif you have at most one active session, the server will remember the time\nyou disconnect and then replay missed messages to you when you reconnect.\nYour options are 'on' and 'off'.": "",
"$bCERT$b controls a user account's certificate fingerprints": "",
"$bDM-HISTORY$b\n'dm-history' is only effective for always-on clients. It lets you control\nhow the history of your direct messages is stored. Your options are:\n1. 'off' [no history]\n2. 'ephemeral' [a limited amount of temporary history, not stored on disk]\n3. 'on' [history stored in a permanent database, if available]\n4. 'default' [use the server default]": "",
"$bDROP$b de-links your current (or the given) nickname from your user account.": "",
"$bENFORCE$b\n'enforce' lets you specify a custom enforcement mechanism for your registered\nnicknames. Your options are:\n1. 'none' [no enforcement, overriding the server default]\n2. 'strict' [you must already be authenticated to use the nick]\n3. 'default' [use the server default]": "",
"$bERASE$b erases all records of an account, allowing reuse.": "",
"$bGET$b queries the current values of your account settings": "",
"$bGHOST$b reclaims your nickname.": "",
"$bGROUP$b links your current nickname to your user account.": "",
"$bIDENTIFY$b lets you login to your account.": "",
"$bINFO$b gives you information on a user account.": "",
"$bLIST$b searches the list of registered nicknames.": "",
"$bMULTICLIENT$b\nIf 'multiclient' is enabled and you are already logged in and using a nick, a\nsecond client of yours that authenticates with SASL and requests the same nick\nis allowed to attach to the nick as well (this is comparable to the behavior\nof IRC \"bouncers\" like ZNC). Your options are 'on' (allow this behavior),\n'off' (disallow it), and 'default' (use the server default value).": "",
"$bPASSWD$b lets you change your password.": "",
"$bREGISTER$b lets you register a user account.": "",
"$bREPLAY-JOINS$b\n'replay-joins' controls whether replayed channel history will include\nlines for join and part. This provides more information about the context of\nmessages, but may be spammy. Your options are 'always', 'never', and the default\nof 'commands-only' (the messages will be replayed in /HISTORY output, but not\nduring autoreplay).": "",
"$bSADROP$b forcibly de-links the given nickname from its user account.": "",
"$bSAGET$b queries the current values of another user's account settings": "",
"$bSAREGISTER$b registers an account on someone else's behalf.": "",
"$bSASET$b modifies another user's account settings": "",
"$bSESSIONS$b lists the sessions attached to a nickname.": "",
"$bSET$b modifies your account settings": "",
"$bSUSPEND$b disables an account and disconnects the clients": "",
"$bUNREGISTER$b lets you delete your user account.": "",
"$bUNSUSPEND$b restores access to a suspended account": "",
"$bVERIFY$b lets you complete account registration.": "",
"Insufficient privileges": "",
"Invalid parameters": "",
"NickServ lets you register, log in to, and manage an account.": "",
"Password incorrect": "",
"Passwords do not match": "",
"Syntax $bSET <setting> <value>$b\n\nSET modifies your account settings. The following settings are available:": "",
"Syntax: $bCERT <LIST | ADD | DEL> [account] [certfp]$b\n\nCERT examines or modifies the TLS certificate fingerprints that can be used to\nlog into an account. Specifically, $bCERT LIST$b lists the authorized\nfingerprints, $bCERT ADD <fingerprint>$b adds a new fingerprint, and\n$bCERT DEL <fingerprint>$b removes a fingerprint. If you're an IRC operator\nwith the correct permissions, you can act on another user's account, for\nexample with $bCERT ADD <account> <fingerprint>$b.": "",
"Syntax: $bDROP [nickname]$b\n\nDROP de-links the given (or your current) nickname from your user account.": "",
"Syntax: $bENFORCE [method]$b\n\nENFORCE lets you specify a custom enforcement mechanism for your registered\nnicknames. Your options are:\n1. 'none' [no enforcement, overriding the server default]\n2. 'timeout' [anyone using the nick must authenticate before a deadline,\n or else they will be renamed]\n3. 'strict' [you must already be authenticated to use the nick]\n4. 'default' [use the server default]\nWith no arguments, queries your current enforcement status.": "",
"Syntax: $bENFORCE [method]$b\n\nENFORCE is an alias for $bGET enforce$b and $bSET enforce$b. See the help\nentry for $bSET$b for more information.": "",
"Syntax: $bERASE <username> [code]$b\n\nERASE deletes all records of an account, allowing it to be re-registered.\nThis should be used with caution, because it violates an expectation that\naccount names are permanent identifiers. Typically, UNREGISTER should be\nused instead. A confirmation code is required; invoking the command\nwithout a code will display the necessary code.": "",
"Syntax: $bGET <setting>$b\n\nGET queries the current values of your account settings. For more information\non the settings and their possible values, see HELP SET.": "",
"Syntax: $bGHOST <nickname>$b\n\nGHOST disconnects the given user from the network if they're logged in with the\nsame user account, letting you reclaim your nickname.": "",
"Syntax: $bGROUP$b\n\nGROUP links your current nickname with your logged-in account, so other people\nwill not be able to use it.": "",
"Syntax: $bIDENTIFY <username> [password]$b\n\nIDENTIFY lets you login to the given username using either password auth, or\ncertfp (your client certificate) if a password is not given.": "",
"Syntax: $bINFO [username]$b\n\nINFO gives you information about the given (or your own) user account.": "",
"Syntax: $bPASSWD <current> <new> <new_again>$b\nOr: $bPASSWD <username> <new>$b\n\nPASSWD lets you change your account password. You must supply your current\npassword and confirm the new one by typing it twice. If you're an IRC operator\nwith the correct permissions, you can use PASSWD to reset someone else's\npassword by supplying their username and then the desired password.": "",
"Syntax: $bREGISTER <username> <email> [password]$b\n\nREGISTER lets you register a user account. If the server allows anonymous\nregistration, you can send an asterisk (*) as the email address.\n\nIf the password is left out, your account will be registered to your TLS client\ncertificate (and you will need to use that certificate to login in future).": "",
"Syntax: $bLIST [regex]$b\n\nLIST returns the list of registered nicknames, which match the given regex.\nIf no regex is provided, all registered nicknames are returned.": "",
"Syntax: $bPASSWD <current> <new> <new_again>$b\nOr: $bPASSWD <username> <new>$b\n\nPASSWD lets you change your account password. You must supply your current\npassword and confirm the new one by typing it twice. If you're an IRC operator\nwith the correct permissions, you can use PASSWD to reset someone else's\npassword by supplying their username and then the desired password. To\nindicate an empty password, use * instead.": "",
"Syntax: $bREGISTER <password> [email]$b\n\nREGISTER lets you register your current nickname as a user account. If the\nserver allows anonymous registration, you can omit the e-mail address.\n\nIf you are currently logged in with a TLS client certificate and wish to use\nit instead of a password to log in, send * as the password.": "",
"Syntax: $bSADROP <nickname>$b\n\nSADROP forcibly de-links the given nickname from the attached user account.": "",
"Syntax: $bSAREGISTER <username> <password>$b\n\nSAREGISTER registers an account on someone else's behalf.\nThis is for use in configurations that require SASL for all connections;\nan administrator can set use this command to set up user accounts.": "",
"Syntax: $bSESSIONS [nickname]$b\n\nSESSIONS lists information about the sessions currently attached, via\nthe server's bouncer functionality, to your nickname. An administrator\ncan use this command to list another user's sessions.": "",
"Syntax: $bSAGET <account> <setting>$b\n\nSAGET queries the values of someone else's account settings. For more\ninformation on the settings and their possible values, see HELP SET.": "",
"Syntax: $bSAREGISTER <username> [password]$b\n\nSAREGISTER registers an account on someone else's behalf.\nThis is for use in configurations that require SASL for all connections;\nan administrator can set use this command to set up user accounts.": "",
"Syntax: $bSASET <account> <setting> <value>$b\n\nSASET modifies the values of someone else's account settings. For more\ninformation on the settings and their possible values, see HELP SET.": "",
"Syntax: $bSESSIONS [nickname]$b\n\nSESSIONS lists information about the sessions currently attached, via\nthe server's multiclient functionality, to your nickname. An administrator\ncan use this command to list another user's sessions.": "",
"Syntax: $bSUSPEND <nickname>$b\n\nSUSPEND disables an account and disconnects the associated clients.": "",
"Syntax: $bUNREGISTER <username> [code]$b\n\nUNREGISTER lets you delete your user account (or someone else's, if you're an\nIRC operator with the correct permissions). To prevent accidental\nunregistrations, a verification code is required; invoking the command without\na code will display the necessary code.": "",
"Syntax: $bVERIFY <username> <code>$b\n\nVERIFY lets you complete an account registration, if the server requires email\nor other verification.": ""
"Syntax: $bUNSUSPEND <nickname>$b\n\nUNSUSPEND reverses a previous SUSPEND, restoring access to the account.": "",
"Syntax: $bVERIFY <username> <code>$b\n\nVERIFY lets you complete an account registration, if the server requires email\nor other verification.": "",
"You're not logged into an account": ""
}

View File

@ -1,8 +1,8 @@
{
"= Help Topics =\n\nCommands:\n%[1]s\n\nRPL_ISUPPORT Tokens:\n%[2]s\n\nInformation:\n%[3]s": "= Hilfethemen =\n\nBefehle:\n%[1]s\n\nRPL_ISUPPORT Token:\n%[2]s\n\nInformation:\n%[3]s",
"== Channel Modes ==\n\nOragono supports the following channel modes:\n\n +b | Client masks that are banned from the channel (e.g. *!*@127.0.0.1)\n +e | Client masks that are exempted from bans.\n +I | Client masks that are exempted from the invite-only flag.\n +i | Invite-only mode, only invited clients can join the channel.\n +k | Key required when joining the channel.\n +l | Client join limit for the channel.\n +m | Moderated mode, only privileged clients can talk on the channel.\n +n | No-outside-messages mode, only users that are on the channel can send\n | messages to it.\n +R | Only registered users can join the channel.\n +s | Secret mode, channel won't show up in /LIST or whois replies.\n +t | Only channel opers can modify the topic.\n\n= Prefixes =\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.": "",
"== Channel Modes ==\n\nOragono supports the following channel modes:\n\n +b | Client masks that are banned from the channel (e.g. *!*@127.0.0.1)\n +e | Client masks that are exempted from bans.\n +I | Client masks that are exempted from the invite-only flag.\n +i | Invite-only mode, only invited clients can join the channel.\n +k | Key required when joining the channel.\n +l | Client join limit for the channel.\n +m | Moderated mode, only privileged clients can talk on the channel.\n +n | No-outside-messages mode, only users that are on the channel can send\n | messages to it.\n +R | Only registered users can join the channel.\n +s | Secret mode, channel won't show up in /LIST or whois replies.\n +t | Only channel opers can modify the topic.\n +E | Roleplaying commands are enabled in the channel.\n +C | Clients are blocked from sending CTCP messages in the channel.\n\n= Prefixes =\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.": "",
"== Server Notice Masks ==\n\nOragono supports the following server notice masks for operators:\n\n a | Local announcements.\n c | Local client connections.\n j | Local channel actions.\n k | Local kills.\n n | Local nick changes.\n o | Local oper actions.\n q | Local quits.\n t | Local /STATS usage.\n u | Local client account actions.\n x | Local X-lines (DLINE/KLINE/etc).\n v | Local vhost changes.\n\nTo set a snomask, do this with your nickname:\n\n /MODE <nick> +s <chars>\n\nFor instance, this would set the kill, oper, account and xline snomasks on dan:\n\n /MODE dan +s koux": "",
"== User Modes ==\n\nOragono supports the following user modes:\n\n +a | User is marked as being away. This mode is set with the /AWAY command.\n +i | User is marked as invisible (their channels are hidden from whois replies).\n +o | User is an IRC operator.\n +R | User only accepts messages from other registered users. \n +s | Server Notice Masks (see help with /HELPOP snomasks).\n +Z | User is connected via TLS.": "",
"== User Modes ==\n\nOragono supports the following user modes:\n\n +a | User is marked as being away. This mode is set with the /AWAY command.\n +i | User is marked as invisible (their channels are hidden from whois replies).\n +o | User is an IRC operator.\n +R | User only accepts messages from other registered users. \n +s | Server Notice Masks (see help with /HELPOP snomasks).\n +Z | User is connected via TLS.\n +B | User is a bot.\n +E | User can receive roleplaying commands.\n +T | User is blocked from sending CTCP messages.": "",
"@+client-only-tags TAGMSG <target>{,<target>}\n\nSends the given client-only tags to the given targets as a TAGMSG. See the IRCv3\nspecs for more info: http://ircv3.net/specs/core/message-tags-3.3.html": "",
"ACC LS\nACC REGISTER <accountname> [callback_namespace:]<callback> [cred_type] :<credential>\nACC VERIFY <accountname> <auth_code>\n\nUsed in account registration. See the relevant specs for more info:\nhttps://oragono.io/specs.html": "",
"AMBIANCE <target> <text to be sent>\n\nThe AMBIANCE command is used to send a scene notification to the given target.": "",
@ -13,8 +13,10 @@
"CAP <subcommand> [:<capabilities>]\n\nUsed in capability negotiation. See the IRCv3 specs for more info:\nhttp://ircv3.net/specs/core/capability-negotiation-3.1.html\nhttp://ircv3.net/specs/core/capability-negotiation-3.2.html": "",
"CHATHISTORY [params]\n\nCHATHISTORY is a history replay command associated with the IRCv3\nspecification draft/chathistory. See this document:\nhttps://github.com/ircv3/ircv3-specifications/pull/393": "",
"DEBUG <option>\n\nProvides various debugging commands for the IRCd. <option> can be one of:\n\n* GCSTATS: Garbage control statistics.\n* NUMGOROUTINE: Number of goroutines in use.\n* STARTCPUPROFILE: Starts the CPU profiler.\n* STOPCPUPROFILE: Stops the CPU profiler.\n* PROFILEHEAP: Writes a memory profile.\n* CRASHSERVER: Crashes the server (for use in failover testing)": "",
"DEFCON [level]\n\nThe DEFCON system can disable server features at runtime, to mitigate\nspam or other hostile activity. It has five levels, which are cumulative\n(i.e., level 3 includes all restrictions from level 4 and so on):\n\n5: Normal operation\n4: No new account or channel registrations\n3: All users are +R; no changes to vhosts\n2: No new unauthenticated connections; all channels are +R\n1: No new connections except from localhost or other trusted IPs": "",
"DEOPER\n\nDEOPER removes the IRCop privileges granted to you by a successful /OPER.": "",
"DLINE [ANDKILL] [MYSELF] [duration] <ip>/<net> [ON <server>] [reason [| oper reason]]\nDLINE LIST\n\nBans an IP address or network from connecting to the server. If the duration is\ngiven then only for that long. The reason is shown to the user themselves, but\neveryone else will see a standard message. The oper reason is shown to\noperators getting info about the DLINEs that exist.\n\nBans are saved across subsequent launches of the server.\n\n\"ANDKILL\" means that all matching clients are also removed from the server.\n\n\"MYSELF\" is required when the DLINE matches the address the person applying it is connected\nfrom. If \"MYSELF\" is not given, trying to DLINE yourself will result in an error.\n\n[duration] can be of the following forms:\n\t1y 12mo 31d 10h 8m 13s\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24\n\nON <server> specifies that the ban is to be set on that specific server.\n\n[reason] and [oper reason], if they exist, are separated by a vertical bar (|).\n\nIf \"DLINE LIST\" is sent, the server sends back a list of our current DLINEs.": "",
"EXTJWT <target> [service_name]\n\nGet a JSON Web Token for target (either * or a channel name).": "",
"HELP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.": "",
"HELPOP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.": "HELPOP <thema>\n\nzeige eine Beschreibung für <thema> oder \"index\" für eine Liste alle Hilfethemen.",
"HISTORY <target> [limit]\n\nReplay message history. <target> can be a channel name, \"me\" to replay direct\nmessage history, or a nickname to replay another client's direct message\nhistory (they must be logged into the same account as you). [limit] can be\neither an integer (the maximum number of messages to replay), or a time\nduration like 10m or 1h (the time window within which to replay messages).": "",
@ -53,12 +55,14 @@
"SANICK <currentnick> <newnick>\n\nGives the given user a new nickname.": "",
"SCENE <target> <text to be sent>\n\nThe SCENE command is used to send a scene notification to the given target.": "",
"SETNAME <realname>\n\nThe SETNAME command updates the realname to be the newly-given one.": "",
"SUMMON [parameters]\n\nThe SUMMON command is not implemented.": "",
"TIME [server]\n\nShows the time of the current, or the given, server.": "TIME [server]\n\nZeigt die Uhrzeit für diesen oder den angegebenen Server an.",
"TOPIC <channel> [topic]\n\nIf [topic] is given, sets the topic in the channel to that. If [topic] is not\ngiven, views the current topic on the channel.": "TOPIC <channel> [thema]\n\nÄndert das Thema für einen Channel. Ohne den Parameter [thema]\nwird das aktuelle Thema für den Channel angezeigt.",
"UNDLINE <ip>/<net>\n\nRemoves an existing ban on an IP address or a network.\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24": "",
"UNKLINE <mask>\n\nRemoves an existing ban on a mask.\n\nFor example:\n\tdan\n\tdan!5*@127.*": "",
"USER <username> 0 * <realname>\n\nUsed in connection registration, sets your username and realname to the given\nvalues (though your username may also be looked up with Ident).": "",
"USERHOST <nickname>{ <nickname>}\n\t\t\nShows information about the given users. Takes up to 10 nicknames.": "",
"USERS [parameters]\n\nThe USERS command is not implemented.": "",
"VERSION [server]\n\nViews the version of software and the RPL_ISUPPORT tokens for the given server.": "",
"WEBIRC <password> <gateway> <hostname> <ip> [:<flags>]\n\nUsed by web<->IRC gateways and bouncers, the WEBIRC command allows gateways to\npass-through the real IP addresses of clients:\nircv3.net/specs/extensions/webirc.html\n\n<flags> is a list of space-separated strings indicating various details about\nthe connection from the client to the gateway, such as:\n\n- tls: this flag indicates that the client->gateway connection is secure": "",
"WHO <name> [o]\n\nReturns information for the given user.": "",

View File

@ -86,6 +86,7 @@
"Channel renamed": "Channel umbenannt",
"Channel renamed: %s": "Channel umbenannt: %s",
"Channels with persistent history cannot be renamed": "",
"Client %s is always-on and cannot be fully removed by /KILL; consider /NS SUSPEND instead": "",
"Client reconnected": "Client neu verbunden",
"Client reconnected (message history may have been lost)": "",
"Client reconnected (up to %d seconds of message history lost)": "",
@ -95,6 +96,7 @@
"Could not accept ownership of channel %s": "",
"Could not delete message": "",
"Could not find given client": "Kann angegebenen Client nicht finden",
"Could not generate EXTJWT token": "",
"Could not look up account name, proceeding anyway": "",
"Could not parse IP address or CIDR network": "Kann IP-Adresse oder CIDR-Network nicht parsen",
"Could not register": "Registrierung nicht möglich",
@ -106,9 +108,12 @@
"Could not transfer channel": "",
"Could not ungroup nick": "",
"Created at: %s": "Erstellt am: %s",
"Current DEFCON level is %d": "",
"Current global users %[1]s, max %[2]s": "",
"Current local users %[1]s, max %[2]s": "",
"Data export for %[1]s completed and written to %[2]s": "",
"Device ID: %s": "",
"Direct messages from unregistered users are temporarily restricted": "",
"End of /HELPOP": "Ende von /HELPOP",
"End of /INFO": "Ende von /INFO",
"End of /WHOIS list": "Ende von /WHOIS list",
@ -154,6 +159,7 @@
"Insufficient privileges": "Unzureichende Rechte",
"Internal error": "Interner Fehler",
"Invalid CAP subcommand": "Ungültiges CAP Subcommand",
"Invalid DEFCON parameter": "",
"Invalid account name": "Ungültiger Konto-Name",
"Invalid certificate fingerprint": "",
"Invalid channel name": "",
@ -165,6 +171,7 @@
"Invalid params": "",
"Invalid regex": "",
"Invalid vhost": "Ungültiger vHost",
"It was built from git hash %s.": "",
"It was rejected for reason: %s": "Es wurde abgelehnt mit dem Grund: %s",
"JOIN 0 is not allowed": "JOIN 0 ist nicht erlaubt",
"Language %s is not supported by this server": "Sprache %s wird von diesem Server nicht unterstützt",
@ -173,6 +180,7 @@
"MOTD File is missing": "MOTD Datei nicht gefunden",
"Malformed username": "Ungültiger User-Name",
"Mask isn't valid": "Maske ist ungültig",
"Message rejected for containing invalid UTF-8": "",
"Messages could not be retrieved": "",
"Multiclient functionality is currently disabled for your account": "",
"Multiclient functionality is currently disabled for your account, but you can opt in": "",
@ -189,6 +197,7 @@
"No such channel": "Channel nicht gefunden",
"No such module [%s]": "",
"No such nick": "Nick-Name nicht gefunden",
"No such service": "",
"No such setting": "Diese Einstellung gibt es nicht",
"No text to send": "",
"No topic is set": "",
@ -207,6 +216,7 @@
"Purge reason: %s": "",
"Purged at: %s": "",
"Purged by operator: %s": "",
"Realname is not valid": "",
"Received malformed line": "",
"Registered at: %s": "Registriert am: %s",
"Registered channel: %s": "Registrierter Channel: %s",
@ -228,6 +238,7 @@
"SASL authentication failed: Passphrase too long": "SASL Authentifizierung fehlgeschlagen: Passphrase zu lang",
"SASL authentication failed: authcid and authzid should be the same": "SASL Authentifizierung fehlgeschlagen: authcid und authzid müssen übereinstimmen",
"SASL message too long": "SASL Message zu lang",
"SUMMON has been disabled": "",
"Server notice masks": "",
"Session %d (currently attached session):": "Session %d (aktive Session):",
"Session %d:": "Session %d:",
@ -247,9 +258,11 @@
"Successfully registered account %s": "Konto %s erfolgreich registriert",
"Successfully rejected vhost request for %s": "",
"Successfully reset channel access": "",
"Successfully set persistent mode %s%s on %s": "",
"Successfully set persistent mode %[1]s on %[2]s": "",
"Successfully set vhost": "VHost erfolgreich gesetzt",
"Successfully suspended account %s": "",
"Successfully transferred channel %[1]s to account %[2]s": "",
"Successfully un-suspended account %s": "",
"Successfully ungrouped nick %s with your account": "",
"Successfully unpurged channel %s from the server": "",
"Successfully unregistered account %s": "Konto %s erfolgreich gelöscht",
@ -272,6 +285,7 @@
"This ban matches you. To KLINE yourself, you must use the command: /KLINE MYSELF <arguments>": "",
"This command has been disabled by the server administrators": "Dieser Befehl wurde von den Server-Admins deaktiviert",
"This feature has been disabled by the server administrators": "Dieses Feature wurde von den Server-Admins deaktiviert",
"This is Oragono version %s.": "",
"This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.": "Dieser Server ist im Debug-Modus und zeichnet sämtlichen Datenverkehr auf. Wenn Du nicht möchtest, dass alles was Du sendest von den Server-Betreibern gelesen werden kann, dann beende jetzt die Verbindung.",
"This server requires that you wait %v after connecting before you can use /LIST. You have %v left.": "",
"This server was created %s": "",
@ -285,10 +299,12 @@
"Transfer of channel %[1]s to account %[2]s succeeded, pending acceptance": "",
"Translators:": "Übersetzer:",
"Try again later": "",
"USERS has been disabled": "",
"Unknown command": "Unbekannter Befehl",
"Unknown command. To see available commands, run: /%s HELP": "",
"Unknown subcommand": "Unbekanntes Subcommand",
"Unrecognized DEBUG subcommand": "",
"Usage: REGISTER <passphrase> [email]": "",
"User %s is no longer allowed to use vhosts": "",
"User %s is now allowed to use vhosts": "",
"User doesn't have roleplaying mode enabled": "",
@ -317,13 +333,11 @@
"You have been marked as being away": "",
"You have been offered ownership of channel %[1]s. To accept, /CS TRANSFER ACCEPT %[1]s": "",
"You have enabled autoreplay of missed messages, but you can't receive them because your client isn't set to always-on": "",
"You have joined too many channels": "Du bist schon in zu vielen Channels",
"You have sent too many registration messages": "Du hast zu viele REGISTRATION Nachrichten gesendet",
"You have too many nicks reserved already (you can remove some with /NS DROP)": "Du hast schon zu viele Nicks registriert (Du kannst welche mit /NS DROP entfernen)",
"You may not reregister": "",
"You must be an oper on the channel to register it": "",
"You must be connected with TLS and a client certificate to do this": "Du musst über TLS und einem Client Zertifikat verbunden sein, um dies zu tun",
"You must be registered to join that channel": "Du musst registriert sein, um diesem Channel beizutreten",
"You must have rehash permissions in order to execute DEBUG CRASHSERVER": "",
"You must log in with SASL to join this server": "Du musst Dich mit SASL anmelden, um Zugang zum Server zu erhalten",
"You must specify an account": "Du musst ein Konto angeben",

View File

@ -1,16 +1,59 @@
{
"NickServ lets you register and login to an account.\n\nTo see in-depth help for a specific NickServ command, try:\n $b/NS HELP <command>$b\n\nHere are the commands you can use:\n%s": "Mit NickServ kannst Du ein Konto registrieren und Dich mit diesem anmelden.\n\nFür ausführliche Hilfe zu den einzelnen NickServ Befehlen, verwende:\n $b/NS HELP <befehl>$b\n\nFolgende Befehle kannst Du nutzen:\n%s",
"$bALWAYS-ON$b\n'always-on' controls whether your nickname/identity will remain active\neven while you are disconnected from the server. Your options are 'true',\n'false', and 'default' (use the server default value).": "",
"$bAUTO-AWAY$b\n'auto-away' is only effective for always-on clients. If enabled, you will\nautomatically be marked away when all your sessions are disconnected, and\nautomatically return from away when you connect again.": "",
"$bAUTOREPLAY-LINES$b\n'autoreplay-lines' controls the number of lines of channel history that will\nbe replayed to you automatically when joining a channel. Your options are any\npositive number, 0 to disable the feature, and 'default' to use the server\ndefault.": "",
"$bAUTOREPLAY-MISSED$b\n'autoreplay-missed' is only effective for always-on clients. If enabled,\nif you have at most one active session, the server will remember the time\nyou disconnect and then replay missed messages to you when you reconnect.\nYour options are 'on' and 'off'.": "",
"$bCERT$b controls a user account's certificate fingerprints": "",
"$bDM-HISTORY$b\n'dm-history' is only effective for always-on clients. It lets you control\nhow the history of your direct messages is stored. Your options are:\n1. 'off' [no history]\n2. 'ephemeral' [a limited amount of temporary history, not stored on disk]\n3. 'on' [history stored in a permanent database, if available]\n4. 'default' [use the server default]": "",
"$bDROP$b de-links your current (or the given) nickname from your user account.": "",
"$bENFORCE$b\n'enforce' lets you specify a custom enforcement mechanism for your registered\nnicknames. Your options are:\n1. 'none' [no enforcement, overriding the server default]\n2. 'strict' [you must already be authenticated to use the nick]\n3. 'default' [use the server default]": "",
"$bERASE$b erases all records of an account, allowing reuse.": "",
"$bGET$b queries the current values of your account settings": "",
"$bGHOST$b reclaims your nickname.": "",
"$bGROUP$b links your current nickname to your user account.": "",
"$bIDENTIFY$b lets you login to your account.": "",
"$bINFO$b gives you information on a user account.": "",
"$bLIST$b searches the list of registered nicknames.": "",
"$bMULTICLIENT$b\nIf 'multiclient' is enabled and you are already logged in and using a nick, a\nsecond client of yours that authenticates with SASL and requests the same nick\nis allowed to attach to the nick as well (this is comparable to the behavior\nof IRC \"bouncers\" like ZNC). Your options are 'on' (allow this behavior),\n'off' (disallow it), and 'default' (use the server default value).": "",
"$bPASSWD$b lets you change your password.": "",
"$bREGISTER$b lets you register a user account.": "",
"$bREPLAY-JOINS$b\n'replay-joins' controls whether replayed channel history will include\nlines for join and part. This provides more information about the context of\nmessages, but may be spammy. Your options are 'always', 'never', and the default\nof 'commands-only' (the messages will be replayed in /HISTORY output, but not\nduring autoreplay).": "",
"$bSADROP$b forcibly de-links the given nickname from its user account.": "",
"$bSAGET$b queries the current values of another user's account settings": "",
"$bSAREGISTER$b registers an account on someone else's behalf.": "",
"$bSASET$b modifies another user's account settings": "",
"$bSESSIONS$b lists the sessions attached to a nickname.": "",
"$bSET$b modifies your account settings": "",
"$bSUSPEND$b disables an account and disconnects the clients": "",
"$bUNREGISTER$b lets you delete your user account.": "",
"$bUNSUSPEND$b restores access to a suspended account": "",
"$bVERIFY$b lets you complete account registration.": "",
"Insufficient privileges": "Unzureichende Rechte",
"Invalid parameters": "",
"NickServ lets you register, log in to, and manage an account.": "",
"Password incorrect": "Passwort falsch",
"Passwords do not match": "",
"Syntax $bSET <setting> <value>$b\n\nSET modifies your account settings. The following settings are available:": "",
"Syntax: $bCERT <LIST | ADD | DEL> [account] [certfp]$b\n\nCERT examines or modifies the TLS certificate fingerprints that can be used to\nlog into an account. Specifically, $bCERT LIST$b lists the authorized\nfingerprints, $bCERT ADD <fingerprint>$b adds a new fingerprint, and\n$bCERT DEL <fingerprint>$b removes a fingerprint. If you're an IRC operator\nwith the correct permissions, you can act on another user's account, for\nexample with $bCERT ADD <account> <fingerprint>$b.": "",
"Syntax: $bDROP [nickname]$b\n\nDROP de-links the given (or your current) nickname from your user account.": "Syntax: $bDROP [nickname]$b\n\nDROP entfernt den angegebenen (oder Deinen aktuellen) Nick-Namen von Deinem\nKonto.",
"Syntax: $bENFORCE [method]$b\n\nENFORCE lets you specify a custom enforcement mechanism for your registered\nnicknames. Your options are:\n1. 'none' [no enforcement, overriding the server default]\n2. 'timeout' [anyone using the nick must authenticate before a deadline,\n or else they will be renamed]\n3. 'strict' [you must already be authenticated to use the nick]\n4. 'default' [use the server default]\nWith no arguments, queries your current enforcement status.": "Syntax: $bENFORCE [methode]$b\n\nmit ENFORCE kannst Du einstellen mit welchen Einschränkungen die von\nDir registrierten Nick-Namen verwendet werden dürfen. Die Optionen sind:\n1. 'none' [keine Einschränkungen, Standard-Einstellung des Server wird ignoriert]\n2. 'timeout' [jede, die den Nick benutzt, muss sich innerhalb einer kurzen\n Zeitspanne anmelden oder es wird ein neuer Nick zugewiesen]\n3. 'strict' [Du musst angemeldet sein, um den Nick zu verwenden]\n4. 'default' [benutze die Standard-Einstellung des Servers]\nDer Befehl ohne Parameter zeigt die aktuelle Konfiguration an.",
"Syntax: $bENFORCE [method]$b\n\nENFORCE is an alias for $bGET enforce$b and $bSET enforce$b. See the help\nentry for $bSET$b for more information.": "",
"Syntax: $bERASE <username> [code]$b\n\nERASE deletes all records of an account, allowing it to be re-registered.\nThis should be used with caution, because it violates an expectation that\naccount names are permanent identifiers. Typically, UNREGISTER should be\nused instead. A confirmation code is required; invoking the command\nwithout a code will display the necessary code.": "",
"Syntax: $bGET <setting>$b\n\nGET queries the current values of your account settings. For more information\non the settings and their possible values, see HELP SET.": "",
"Syntax: $bGHOST <nickname>$b\n\nGHOST disconnects the given user from the network if they're logged in with the\nsame user account, letting you reclaim your nickname.": "Syntax: $bGHOST <nickname>$b\n\nGHOST beendet eine andere User-Verbindung wenn diese den angegebenen\nNick benutzt und mit dem gleichen Konto angemeldet ist. Dies ermöglicht es\nDir, den Nick mit Deiner aktuellen Verbindung zu benutzen.",
"Syntax: $bGROUP$b\n\nGROUP links your current nickname with your logged-in account, so other people\nwill not be able to use it.": "Syntax: $bGROUP$b\n\nGROUP verknüpft/registriert Deinen aktuellen Nick-Namen mit Deinem angemeldeten\nKonto, damit andere User diesen Nick nicht nutzen können.",
"Syntax: $bIDENTIFY <username> [password]$b\n\nIDENTIFY lets you login to the given username using either password auth, or\ncertfp (your client certificate) if a password is not given.": "Syntax: $bIDENTIFY <kontoname> [passwort]$b\n\nmit IDENTIFY kannst Du Dich mit Deinem Konto anmelden. Entweder mit\nDeinem Passwort oder über CertFP (TLS Client-Zertifikat), falls kein Passwort\nangegeben wird.",
"Syntax: $bINFO [username]$b\n\nINFO gives you information about the given (or your own) user account.": "Syntax: $bINFO [kontoname]$b\n\nINFO zeigt Informationen über das angegebene (oder Dein eigenes) Konto an.",
"Syntax: $bPASSWD <current> <new> <new_again>$b\nOr: $bPASSWD <username> <new>$b\n\nPASSWD lets you change your account password. You must supply your current\npassword and confirm the new one by typing it twice. If you're an IRC operator\nwith the correct permissions, you can use PASSWD to reset someone else's\npassword by supplying their username and then the desired password.": "Syntax: $bPASSWD <aktuell> <neu> <neu_wiederholung>$b\nOder: $bPASSWD <kontoname> <neu>$b\n\nmit PASSWD kannst Du das Passwört Deines Kontos ändern. Du musst Dein\naktuelles und zweimal das neue Passwort zur Bestätigung eingeben.\nIRC Operatoren mit den erforderlichen Rechten können PASSWD benutzen,\num das Konto-Passwort anderer User zurückzusetzen, indem Kontonamen\nund das neue Passwort als Parameter angeben wird.",
"Syntax: $bREGISTER <username> <email> [password]$b\n\nREGISTER lets you register a user account. If the server allows anonymous\nregistration, you can send an asterisk (*) as the email address.\n\nIf the password is left out, your account will be registered to your TLS client\ncertificate (and you will need to use that certificate to login in future).": "Syntax: $bREGISTER <kontoname> <email> [passwort]$b\n\nmit REGISTER kannst Du ein Konto erstellen. Wenn der Server anonyme\nRegistrierungen zulässt, kannst Du auch eine Sternchen (*) anstelle einer\nE-Mail Adresse angeben.\nFalls das Passwort ausgelassen wird, wird Dein Konto mit Deinem TLS\nClient-Zertifikat verknüpft (und Du brauchst dieses Zertifikat für zukünftige\nLogins).",
"Syntax: $bLIST [regex]$b\n\nLIST returns the list of registered nicknames, which match the given regex.\nIf no regex is provided, all registered nicknames are returned.": "",
"Syntax: $bPASSWD <current> <new> <new_again>$b\nOr: $bPASSWD <username> <new>$b\n\nPASSWD lets you change your account password. You must supply your current\npassword and confirm the new one by typing it twice. If you're an IRC operator\nwith the correct permissions, you can use PASSWD to reset someone else's\npassword by supplying their username and then the desired password. To\nindicate an empty password, use * instead.": "",
"Syntax: $bREGISTER <password> [email]$b\n\nREGISTER lets you register your current nickname as a user account. If the\nserver allows anonymous registration, you can omit the e-mail address.\n\nIf you are currently logged in with a TLS client certificate and wish to use\nit instead of a password to log in, send * as the password.": "",
"Syntax: $bSADROP <nickname>$b\n\nSADROP forcibly de-links the given nickname from the attached user account.": "Syntax: $bSADROP <nickname>$b\n\nSADROP entfernt den Nick von dem Konto auf das er registriert wurde.\n(Befehl für Admins)",
"Syntax: $bSAREGISTER <username> <password>$b\n\nSAREGISTER registers an account on someone else's behalf.\nThis is for use in configurations that require SASL for all connections;\nan administrator can set use this command to set up user accounts.": "Syntax: $bSAREGISTER <kontoname> <passwort>$b\n\nSAREGISTER erstellt ein Konto für andere.\nDies ist für Konfigurationen gedacht, die SASL beim Verbindungsaufbau verlangen;\nAdministratoren können diesen Befehl nutzen, um Konten für User einzurichten.",
"Syntax: $bSESSIONS [nickname]$b\n\nSESSIONS lists information about the sessions currently attached, via\nthe server's bouncer functionality, to your nickname. An administrator\ncan use this command to list another user's sessions.": "Syntax: $bSESSIONS [nickname]$b\n\nSESSIONS zeigt Informationen zu allen aktiven Sessions (über die Bouncer-\nFunktionalität des Servers) für Deinen Nick-Namen. Ein Administrator kann sich\nmit diesem Befehl auch die Sessions eines anderen Users anzeigen lassen.",
"Syntax: $bSAGET <account> <setting>$b\n\nSAGET queries the values of someone else's account settings. For more\ninformation on the settings and their possible values, see HELP SET.": "",
"Syntax: $bSAREGISTER <username> [password]$b\n\nSAREGISTER registers an account on someone else's behalf.\nThis is for use in configurations that require SASL for all connections;\nan administrator can set use this command to set up user accounts.": "",
"Syntax: $bSASET <account> <setting> <value>$b\n\nSASET modifies the values of someone else's account settings. For more\ninformation on the settings and their possible values, see HELP SET.": "",
"Syntax: $bSESSIONS [nickname]$b\n\nSESSIONS lists information about the sessions currently attached, via\nthe server's multiclient functionality, to your nickname. An administrator\ncan use this command to list another user's sessions.": "",
"Syntax: $bSUSPEND <nickname>$b\n\nSUSPEND disables an account and disconnects the associated clients.": "",
"Syntax: $bUNREGISTER <username> [code]$b\n\nUNREGISTER lets you delete your user account (or someone else's, if you're an\nIRC operator with the correct permissions). To prevent accidental\nunregistrations, a verification code is required; invoking the command without\na code will display the necessary code.": "Syntax: $bUNREGISTER <kontoname> [code]$b\n\nmit UNREGISTER kannst Du Dein Konto löschen (oder ein Konto eines anderen\nUsers, falls Du die nötigen Rechte als IRC Operator hast).\nUm versehentliches Löschen zu verhindern, ist ein Verifizierungscode nötig.\nSendest Du den Befehl ohne einen Code, wird der benötigte Code angezeigt.",
"Syntax: $bVERIFY <username> <code>$b\n\nVERIFY lets you complete an account registration, if the server requires email\nor other verification.": "Syntax: $bVERIFY <kontoname> <code>$b\n\nmit VERIFY kannst Du die Kontoregistrierung abschließen, falls der Server\neine Verifzierung über Email (oder andere Wege) verlangt."
"Syntax: $bUNSUSPEND <nickname>$b\n\nUNSUSPEND reverses a previous SUSPEND, restoring access to the account.": "",
"Syntax: $bVERIFY <username> <code>$b\n\nVERIFY lets you complete an account registration, if the server requires email\nor other verification.": "Syntax: $bVERIFY <kontoname> <code>$b\n\nmit VERIFY kannst Du die Kontoregistrierung abschließen, falls der Server\neine Verifzierung über Email (oder andere Wege) verlangt.",
"You're not logged into an account": "Du bist mit keinem Konto angemeldet"
}

View File

@ -1,8 +1,8 @@
{
"= Help Topics =\n\nCommands:\n%[1]s\n\nRPL_ISUPPORT Tokens:\n%[2]s\n\nInformation:\n%[3]s": "",
"== Channel Modes ==\n\nOragono supports the following channel modes:\n\n +b | Client masks that are banned from the channel (e.g. *!*@127.0.0.1)\n +e | Client masks that are exempted from bans.\n +I | Client masks that are exempted from the invite-only flag.\n +i | Invite-only mode, only invited clients can join the channel.\n +k | Key required when joining the channel.\n +l | Client join limit for the channel.\n +m | Moderated mode, only privileged clients can talk on the channel.\n +n | No-outside-messages mode, only users that are on the channel can send\n | messages to it.\n +R | Only registered users can join the channel.\n +s | Secret mode, channel won't show up in /LIST or whois replies.\n +t | Only channel opers can modify the topic.\n\n= Prefixes =\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.": "",
"== Channel Modes ==\n\nOragono supports the following channel modes:\n\n +b | Client masks that are banned from the channel (e.g. *!*@127.0.0.1)\n +e | Client masks that are exempted from bans.\n +I | Client masks that are exempted from the invite-only flag.\n +i | Invite-only mode, only invited clients can join the channel.\n +k | Key required when joining the channel.\n +l | Client join limit for the channel.\n +m | Moderated mode, only privileged clients can talk on the channel.\n +n | No-outside-messages mode, only users that are on the channel can send\n | messages to it.\n +R | Only registered users can join the channel.\n +s | Secret mode, channel won't show up in /LIST or whois replies.\n +t | Only channel opers can modify the topic.\n +E | Roleplaying commands are enabled in the channel.\n +C | Clients are blocked from sending CTCP messages in the channel.\n\n= Prefixes =\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.": "",
"== Server Notice Masks ==\n\nOragono supports the following server notice masks for operators:\n\n a | Local announcements.\n c | Local client connections.\n j | Local channel actions.\n k | Local kills.\n n | Local nick changes.\n o | Local oper actions.\n q | Local quits.\n t | Local /STATS usage.\n u | Local client account actions.\n x | Local X-lines (DLINE/KLINE/etc).\n v | Local vhost changes.\n\nTo set a snomask, do this with your nickname:\n\n /MODE <nick> +s <chars>\n\nFor instance, this would set the kill, oper, account and xline snomasks on dan:\n\n /MODE dan +s koux": "",
"== User Modes ==\n\nOragono supports the following user modes:\n\n +a | User is marked as being away. This mode is set with the /AWAY command.\n +i | User is marked as invisible (their channels are hidden from whois replies).\n +o | User is an IRC operator.\n +R | User only accepts messages from other registered users. \n +s | Server Notice Masks (see help with /HELPOP snomasks).\n +Z | User is connected via TLS.": "== Τρόποι χρήστη ==\n\nΤο oragono υποστηρίζει τους ακόλουθους τρόπους χρήστη:\n\n+a | Ο χρήστης έχει επισημανθεί ως μακριά. Αυτή η λειτουργία έχει οριστεί με την εντολή /AWAY.\n+i | Ο χρήστης έχει επισημανθεί ως αόρατος (τα κανάλια του είναι κρυμμένα από τις απαντήσεις whois).\n+o | Ο χρήστης είναι χειριστής IRC.\n+R | Ο χρήστης δέχεται μόνο μηνύματα από άλλους εγγεγραμμένους χρήστες.\n+s | Μάσκες ειδοποιήσεων διακομιστών (ανατρέξτε στο βοήθημα με / HELPOP snomasks).\n+Z | Ο χρήστης είναι συνδεδεμένος μέσω TLS.",
"== User Modes ==\n\nOragono supports the following user modes:\n\n +a | User is marked as being away. This mode is set with the /AWAY command.\n +i | User is marked as invisible (their channels are hidden from whois replies).\n +o | User is an IRC operator.\n +R | User only accepts messages from other registered users. \n +s | Server Notice Masks (see help with /HELPOP snomasks).\n +Z | User is connected via TLS.\n +B | User is a bot.\n +E | User can receive roleplaying commands.\n +T | User is blocked from sending CTCP messages.": "",
"@+client-only-tags TAGMSG <target>{,<target>}\n\nSends the given client-only tags to the given targets as a TAGMSG. See the IRCv3\nspecs for more info: http://ircv3.net/specs/core/message-tags-3.3.html": "",
"ACC LS\nACC REGISTER <accountname> [callback_namespace:]<callback> [cred_type] :<credential>\nACC VERIFY <accountname> <auth_code>\n\nUsed in account registration. See the relevant specs for more info:\nhttps://oragono.io/specs.html": "",
"AMBIANCE <target> <text to be sent>\n\nThe AMBIANCE command is used to send a scene notification to the given target.": "",
@ -13,8 +13,10 @@
"CAP <subcommand> [:<capabilities>]\n\nUsed in capability negotiation. See the IRCv3 specs for more info:\nhttp://ircv3.net/specs/core/capability-negotiation-3.1.html\nhttp://ircv3.net/specs/core/capability-negotiation-3.2.html": "",
"CHATHISTORY [params]\n\nCHATHISTORY is a history replay command associated with the IRCv3\nspecification draft/chathistory. See this document:\nhttps://github.com/ircv3/ircv3-specifications/pull/393": "",
"DEBUG <option>\n\nProvides various debugging commands for the IRCd. <option> can be one of:\n\n* GCSTATS: Garbage control statistics.\n* NUMGOROUTINE: Number of goroutines in use.\n* STARTCPUPROFILE: Starts the CPU profiler.\n* STOPCPUPROFILE: Stops the CPU profiler.\n* PROFILEHEAP: Writes a memory profile.\n* CRASHSERVER: Crashes the server (for use in failover testing)": "",
"DEFCON [level]\n\nThe DEFCON system can disable server features at runtime, to mitigate\nspam or other hostile activity. It has five levels, which are cumulative\n(i.e., level 3 includes all restrictions from level 4 and so on):\n\n5: Normal operation\n4: No new account or channel registrations\n3: All users are +R; no changes to vhosts\n2: No new unauthenticated connections; all channels are +R\n1: No new connections except from localhost or other trusted IPs": "",
"DEOPER\n\nDEOPER removes the IRCop privileges granted to you by a successful /OPER.": "",
"DLINE [ANDKILL] [MYSELF] [duration] <ip>/<net> [ON <server>] [reason [| oper reason]]\nDLINE LIST\n\nBans an IP address or network from connecting to the server. If the duration is\ngiven then only for that long. The reason is shown to the user themselves, but\neveryone else will see a standard message. The oper reason is shown to\noperators getting info about the DLINEs that exist.\n\nBans are saved across subsequent launches of the server.\n\n\"ANDKILL\" means that all matching clients are also removed from the server.\n\n\"MYSELF\" is required when the DLINE matches the address the person applying it is connected\nfrom. If \"MYSELF\" is not given, trying to DLINE yourself will result in an error.\n\n[duration] can be of the following forms:\n\t1y 12mo 31d 10h 8m 13s\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24\n\nON <server> specifies that the ban is to be set on that specific server.\n\n[reason] and [oper reason], if they exist, are separated by a vertical bar (|).\n\nIf \"DLINE LIST\" is sent, the server sends back a list of our current DLINEs.": "",
"EXTJWT <target> [service_name]\n\nGet a JSON Web Token for target (either * or a channel name).": "",
"HELP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.": "",
"HELPOP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.": "",
"HISTORY <target> [limit]\n\nReplay message history. <target> can be a channel name, \"me\" to replay direct\nmessage history, or a nickname to replay another client's direct message\nhistory (they must be logged into the same account as you). [limit] can be\neither an integer (the maximum number of messages to replay), or a time\nduration like 10m or 1h (the time window within which to replay messages).": "",
@ -53,12 +55,14 @@
"SANICK <currentnick> <newnick>\n\nGives the given user a new nickname.": "",
"SCENE <target> <text to be sent>\n\nThe SCENE command is used to send a scene notification to the given target.": "",
"SETNAME <realname>\n\nThe SETNAME command updates the realname to be the newly-given one.": "",
"SUMMON [parameters]\n\nThe SUMMON command is not implemented.": "",
"TIME [server]\n\nShows the time of the current, or the given, server.": "",
"TOPIC <channel> [topic]\n\nIf [topic] is given, sets the topic in the channel to that. If [topic] is not\ngiven, views the current topic on the channel.": "",
"UNDLINE <ip>/<net>\n\nRemoves an existing ban on an IP address or a network.\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24": "",
"UNKLINE <mask>\n\nRemoves an existing ban on a mask.\n\nFor example:\n\tdan\n\tdan!5*@127.*": "",
"USER <username> 0 * <realname>\n\nUsed in connection registration, sets your username and realname to the given\nvalues (though your username may also be looked up with Ident).": "",
"USERHOST <nickname>{ <nickname>}\n\t\t\nShows information about the given users. Takes up to 10 nicknames.": "",
"USERS [parameters]\n\nThe USERS command is not implemented.": "",
"VERSION [server]\n\nViews the version of software and the RPL_ISUPPORT tokens for the given server.": "",
"WEBIRC <password> <gateway> <hostname> <ip> [:<flags>]\n\nUsed by web<->IRC gateways and bouncers, the WEBIRC command allows gateways to\npass-through the real IP addresses of clients:\nircv3.net/specs/extensions/webirc.html\n\n<flags> is a list of space-separated strings indicating various details about\nthe connection from the client to the gateway, such as:\n\n- tls: this flag indicates that the client->gateway connection is secure": "",
"WHO <name> [o]\n\nReturns information for the given user.": "",

View File

@ -86,6 +86,7 @@
"Channel renamed": "",
"Channel renamed: %s": "Το κανάλι μετονομάστηκε: %s",
"Channels with persistent history cannot be renamed": "",
"Client %s is always-on and cannot be fully removed by /KILL; consider /NS SUSPEND instead": "",
"Client reconnected": "Ο πελάτης έχει επανασυνδεθεί",
"Client reconnected (message history may have been lost)": "",
"Client reconnected (up to %d seconds of message history lost)": "",
@ -95,6 +96,7 @@
"Could not accept ownership of channel %s": "",
"Could not delete message": "",
"Could not find given client": "Δεν ήταν δυνατή η εύρεση του συγκεκριμένου πελάτη",
"Could not generate EXTJWT token": "",
"Could not look up account name, proceeding anyway": "",
"Could not parse IP address or CIDR network": "Δεν ήταν δυνατή η ανάλυση της διεύθυνσης IP ή του δικτύου CIDR",
"Could not register": "Δεν ήταν δυνατή η εγγραφή",
@ -106,9 +108,12 @@
"Could not transfer channel": "",
"Could not ungroup nick": "Δεν ήταν δυνατή η κατάργηση της ομάδας ψευδώνυμου",
"Created at: %s": "",
"Current DEFCON level is %d": "",
"Current global users %[1]s, max %[2]s": "",
"Current local users %[1]s, max %[2]s": "",
"Data export for %[1]s completed and written to %[2]s": "",
"Device ID: %s": "",
"Direct messages from unregistered users are temporarily restricted": "",
"End of /HELPOP": "Τέλος της /HELPOP",
"End of /INFO": "Τέλος της /INFO",
"End of /WHOIS list": "Τέλος της /WHOIS λίστας",
@ -154,6 +159,7 @@
"Insufficient privileges": "",
"Internal error": "",
"Invalid CAP subcommand": "Μη έγκυρη υποταγή CAP",
"Invalid DEFCON parameter": "",
"Invalid account name": "",
"Invalid certificate fingerprint": "",
"Invalid channel name": "",
@ -165,6 +171,7 @@
"Invalid params": "",
"Invalid regex": "",
"Invalid vhost": "",
"It was built from git hash %s.": "",
"It was rejected for reason: %s": "",
"JOIN 0 is not allowed": "JOIN 0 δεν επιτρέπεται",
"Language %s is not supported by this server": "",
@ -173,6 +180,7 @@
"MOTD File is missing": "MOTD Το αρχείο λείπει",
"Malformed username": "Ακατάλληλο όνομα χρήστη",
"Mask isn't valid": "Η μάσκα δεν είναι έγκυρη",
"Message rejected for containing invalid UTF-8": "",
"Messages could not be retrieved": "",
"Multiclient functionality is currently disabled for your account": "",
"Multiclient functionality is currently disabled for your account, but you can opt in": "",
@ -189,6 +197,7 @@
"No such channel": "Δεν υπάρχει τέτοιο κανάλι",
"No such module [%s]": "",
"No such nick": "Δεν υπάρχει τέτοιο ψευδώνυμο",
"No such service": "",
"No such setting": "",
"No text to send": "",
"No topic is set": "Δεν έχει οριστεί θέμα",
@ -207,6 +216,7 @@
"Purge reason: %s": "",
"Purged at: %s": "",
"Purged by operator: %s": "",
"Realname is not valid": "",
"Received malformed line": "Παραλήφθηκε παραμορφωμένη γραμμή",
"Registered at: %s": "Εγγραφή στο: %s",
"Registered channel: %s": "",
@ -228,6 +238,7 @@
"SASL authentication failed: Passphrase too long": "Αποτυχία ελέγχου ταυτότητας SASL: Η φράση πρόσβασης είναι πολύ μεγάλη",
"SASL authentication failed: authcid and authzid should be the same": "Αποτυχία ελέγχου ταυτότητας SASL: authcid και authzid θα πρέπει να είναι τα ίδια",
"SASL message too long": "Το μήνυμα SASL είναι πολύ μεγάλο",
"SUMMON has been disabled": "",
"Server notice masks": "Μάσκες ειδοποίησης διακομιστή",
"Session %d (currently attached session):": "",
"Session %d:": "",
@ -247,9 +258,11 @@
"Successfully registered account %s": "",
"Successfully rejected vhost request for %s": "",
"Successfully reset channel access": "",
"Successfully set persistent mode %s%s on %s": "",
"Successfully set persistent mode %[1]s on %[2]s": "",
"Successfully set vhost": "",
"Successfully suspended account %s": "",
"Successfully transferred channel %[1]s to account %[2]s": "",
"Successfully un-suspended account %s": "",
"Successfully ungrouped nick %s with your account": "Επιτυχής ομαδοποίηση ψευδώνυμου %s με τον λογαριασμό σας",
"Successfully unpurged channel %s from the server": "",
"Successfully unregistered account %s": "Επιτυχής μη καταχωρισμένος λογαριασμός %s",
@ -272,6 +285,7 @@
"This ban matches you. To KLINE yourself, you must use the command: /KLINE MYSELF <arguments>": "Αυτή η απαγόρευση σας ταιριάζει. Για να ορίσετε τον εαυτό σας με το KLINE, πρέπει να χρησιμοποιήσετε την εντολή: /KLINE τον εαυτό μου <επιχειρήματα>",
"This command has been disabled by the server administrators": "",
"This feature has been disabled by the server administrators": "",
"This is Oragono version %s.": "",
"This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.": "Αυτός ο διακομιστής βρίσκεται σε λειτουργία εντοπισμού σφαλμάτων και καταγράφει όλες τις εισόδους / εξόδους χρηστών. Εάν δεν θέλετε για όλα όσα στείλατε να είναι αναγνώσιμα από τον ιδιοκτήτη του / των διακομιστή (ών), παρακαλείστε να αποσυνδεθείτε.",
"This server requires that you wait %v after connecting before you can use /LIST. You have %v left.": "",
"This server was created %s": "Αυτός ο διακομιστής δημιουργήθηκε %s",
@ -285,10 +299,12 @@
"Transfer of channel %[1]s to account %[2]s succeeded, pending acceptance": "",
"Translators:": "Μεταφραστές:",
"Try again later": "",
"USERS has been disabled": "",
"Unknown command": "Άγνωστη εντολή",
"Unknown command. To see available commands, run: /%s HELP": "",
"Unknown subcommand": γνωστh δευτερεύουσα εντολή",
"Unrecognized DEBUG subcommand": "",
"Usage: REGISTER <passphrase> [email]": "",
"User %s is no longer allowed to use vhosts": "",
"User %s is now allowed to use vhosts": "",
"User doesn't have roleplaying mode enabled": "Ο χρήστης δεν έχει ενεργοποιημένη τη λειτουργία ρόλου παιχνιδιού",
@ -317,13 +333,11 @@
"You have been marked as being away": "Έχετε επισημανθεί ως μακριά",
"You have been offered ownership of channel %[1]s. To accept, /CS TRANSFER ACCEPT %[1]s": "",
"You have enabled autoreplay of missed messages, but you can't receive them because your client isn't set to always-on": "",
"You have joined too many channels": "",
"You have sent too many registration messages": "",
"You have too many nicks reserved already (you can remove some with /NS DROP)": "Έχετε ήδη πάρα πολλά ψευδώνυμα δεσμευμένα (μπορείτε να καταργήσετε μερικά με /NS DROP)",
"You may not reregister": "Δεν μπορείτε να κάνετε εκ νέου εγγραφή",
"You must be an oper on the channel to register it": "Πρέπει να είστε χειριστής στο κανάλι για να την καταχωρήσετε",
"You must be connected with TLS and a client certificate to do this": "",
"You must be registered to join that channel": "",
"You must have rehash permissions in order to execute DEBUG CRASHSERVER": "",
"You must log in with SASL to join this server": "",
"You must specify an account": "",

View File

@ -1,16 +1,59 @@
{
"NickServ lets you register and login to an account.\n\nTo see in-depth help for a specific NickServ command, try:\n $b/NS HELP <command>$b\n\nHere are the commands you can use:\n%s": "",
"$bALWAYS-ON$b\n'always-on' controls whether your nickname/identity will remain active\neven while you are disconnected from the server. Your options are 'true',\n'false', and 'default' (use the server default value).": "",
"$bAUTO-AWAY$b\n'auto-away' is only effective for always-on clients. If enabled, you will\nautomatically be marked away when all your sessions are disconnected, and\nautomatically return from away when you connect again.": "",
"$bAUTOREPLAY-LINES$b\n'autoreplay-lines' controls the number of lines of channel history that will\nbe replayed to you automatically when joining a channel. Your options are any\npositive number, 0 to disable the feature, and 'default' to use the server\ndefault.": "",
"$bAUTOREPLAY-MISSED$b\n'autoreplay-missed' is only effective for always-on clients. If enabled,\nif you have at most one active session, the server will remember the time\nyou disconnect and then replay missed messages to you when you reconnect.\nYour options are 'on' and 'off'.": "",
"$bCERT$b controls a user account's certificate fingerprints": "",
"$bDM-HISTORY$b\n'dm-history' is only effective for always-on clients. It lets you control\nhow the history of your direct messages is stored. Your options are:\n1. 'off' [no history]\n2. 'ephemeral' [a limited amount of temporary history, not stored on disk]\n3. 'on' [history stored in a permanent database, if available]\n4. 'default' [use the server default]": "",
"$bDROP$b de-links your current (or the given) nickname from your user account.": "",
"$bENFORCE$b\n'enforce' lets you specify a custom enforcement mechanism for your registered\nnicknames. Your options are:\n1. 'none' [no enforcement, overriding the server default]\n2. 'strict' [you must already be authenticated to use the nick]\n3. 'default' [use the server default]": "",
"$bERASE$b erases all records of an account, allowing reuse.": "",
"$bGET$b queries the current values of your account settings": "",
"$bGHOST$b reclaims your nickname.": "",
"$bGROUP$b links your current nickname to your user account.": "",
"$bIDENTIFY$b lets you login to your account.": "",
"$bINFO$b gives you information on a user account.": "",
"$bLIST$b searches the list of registered nicknames.": "",
"$bMULTICLIENT$b\nIf 'multiclient' is enabled and you are already logged in and using a nick, a\nsecond client of yours that authenticates with SASL and requests the same nick\nis allowed to attach to the nick as well (this is comparable to the behavior\nof IRC \"bouncers\" like ZNC). Your options are 'on' (allow this behavior),\n'off' (disallow it), and 'default' (use the server default value).": "",
"$bPASSWD$b lets you change your password.": "",
"$bREGISTER$b lets you register a user account.": "",
"$bREPLAY-JOINS$b\n'replay-joins' controls whether replayed channel history will include\nlines for join and part. This provides more information about the context of\nmessages, but may be spammy. Your options are 'always', 'never', and the default\nof 'commands-only' (the messages will be replayed in /HISTORY output, but not\nduring autoreplay).": "",
"$bSADROP$b forcibly de-links the given nickname from its user account.": "",
"$bSAGET$b queries the current values of another user's account settings": "",
"$bSAREGISTER$b registers an account on someone else's behalf.": "",
"$bSASET$b modifies another user's account settings": "",
"$bSESSIONS$b lists the sessions attached to a nickname.": "",
"$bSET$b modifies your account settings": "",
"$bSUSPEND$b disables an account and disconnects the clients": "",
"$bUNREGISTER$b lets you delete your user account.": "",
"$bUNSUSPEND$b restores access to a suspended account": "",
"$bVERIFY$b lets you complete account registration.": "",
"Insufficient privileges": "",
"Invalid parameters": "",
"NickServ lets you register, log in to, and manage an account.": "",
"Password incorrect": "Λανθασμένος κωδικός πρόσβασης",
"Passwords do not match": "",
"Syntax $bSET <setting> <value>$b\n\nSET modifies your account settings. The following settings are available:": "",
"Syntax: $bCERT <LIST | ADD | DEL> [account] [certfp]$b\n\nCERT examines or modifies the TLS certificate fingerprints that can be used to\nlog into an account. Specifically, $bCERT LIST$b lists the authorized\nfingerprints, $bCERT ADD <fingerprint>$b adds a new fingerprint, and\n$bCERT DEL <fingerprint>$b removes a fingerprint. If you're an IRC operator\nwith the correct permissions, you can act on another user's account, for\nexample with $bCERT ADD <account> <fingerprint>$b.": "",
"Syntax: $bDROP [nickname]$b\n\nDROP de-links the given (or your current) nickname from your user account.": "",
"Syntax: $bENFORCE [method]$b\n\nENFORCE lets you specify a custom enforcement mechanism for your registered\nnicknames. Your options are:\n1. 'none' [no enforcement, overriding the server default]\n2. 'timeout' [anyone using the nick must authenticate before a deadline,\n or else they will be renamed]\n3. 'strict' [you must already be authenticated to use the nick]\n4. 'default' [use the server default]\nWith no arguments, queries your current enforcement status.": "",
"Syntax: $bENFORCE [method]$b\n\nENFORCE is an alias for $bGET enforce$b and $bSET enforce$b. See the help\nentry for $bSET$b for more information.": "",
"Syntax: $bERASE <username> [code]$b\n\nERASE deletes all records of an account, allowing it to be re-registered.\nThis should be used with caution, because it violates an expectation that\naccount names are permanent identifiers. Typically, UNREGISTER should be\nused instead. A confirmation code is required; invoking the command\nwithout a code will display the necessary code.": "",
"Syntax: $bGET <setting>$b\n\nGET queries the current values of your account settings. For more information\non the settings and their possible values, see HELP SET.": "",
"Syntax: $bGHOST <nickname>$b\n\nGHOST disconnects the given user from the network if they're logged in with the\nsame user account, letting you reclaim your nickname.": "",
"Syntax: $bGROUP$b\n\nGROUP links your current nickname with your logged-in account, so other people\nwill not be able to use it.": "",
"Syntax: $bIDENTIFY <username> [password]$b\n\nIDENTIFY lets you login to the given username using either password auth, or\ncertfp (your client certificate) if a password is not given.": "",
"Syntax: $bINFO [username]$b\n\nINFO gives you information about the given (or your own) user account.": "",
"Syntax: $bPASSWD <current> <new> <new_again>$b\nOr: $bPASSWD <username> <new>$b\n\nPASSWD lets you change your account password. You must supply your current\npassword and confirm the new one by typing it twice. If you're an IRC operator\nwith the correct permissions, you can use PASSWD to reset someone else's\npassword by supplying their username and then the desired password.": "",
"Syntax: $bREGISTER <username> <email> [password]$b\n\nREGISTER lets you register a user account. If the server allows anonymous\nregistration, you can send an asterisk (*) as the email address.\n\nIf the password is left out, your account will be registered to your TLS client\ncertificate (and you will need to use that certificate to login in future).": "",
"Syntax: $bLIST [regex]$b\n\nLIST returns the list of registered nicknames, which match the given regex.\nIf no regex is provided, all registered nicknames are returned.": "",
"Syntax: $bPASSWD <current> <new> <new_again>$b\nOr: $bPASSWD <username> <new>$b\n\nPASSWD lets you change your account password. You must supply your current\npassword and confirm the new one by typing it twice. If you're an IRC operator\nwith the correct permissions, you can use PASSWD to reset someone else's\npassword by supplying their username and then the desired password. To\nindicate an empty password, use * instead.": "",
"Syntax: $bREGISTER <password> [email]$b\n\nREGISTER lets you register your current nickname as a user account. If the\nserver allows anonymous registration, you can omit the e-mail address.\n\nIf you are currently logged in with a TLS client certificate and wish to use\nit instead of a password to log in, send * as the password.": "",
"Syntax: $bSADROP <nickname>$b\n\nSADROP forcibly de-links the given nickname from the attached user account.": "",
"Syntax: $bSAREGISTER <username> <password>$b\n\nSAREGISTER registers an account on someone else's behalf.\nThis is for use in configurations that require SASL for all connections;\nan administrator can set use this command to set up user accounts.": "",
"Syntax: $bSESSIONS [nickname]$b\n\nSESSIONS lists information about the sessions currently attached, via\nthe server's bouncer functionality, to your nickname. An administrator\ncan use this command to list another user's sessions.": "",
"Syntax: $bSAGET <account> <setting>$b\n\nSAGET queries the values of someone else's account settings. For more\ninformation on the settings and their possible values, see HELP SET.": "",
"Syntax: $bSAREGISTER <username> [password]$b\n\nSAREGISTER registers an account on someone else's behalf.\nThis is for use in configurations that require SASL for all connections;\nan administrator can set use this command to set up user accounts.": "",
"Syntax: $bSASET <account> <setting> <value>$b\n\nSASET modifies the values of someone else's account settings. For more\ninformation on the settings and their possible values, see HELP SET.": "",
"Syntax: $bSESSIONS [nickname]$b\n\nSESSIONS lists information about the sessions currently attached, via\nthe server's multiclient functionality, to your nickname. An administrator\ncan use this command to list another user's sessions.": "",
"Syntax: $bSUSPEND <nickname>$b\n\nSUSPEND disables an account and disconnects the associated clients.": "",
"Syntax: $bUNREGISTER <username> [code]$b\n\nUNREGISTER lets you delete your user account (or someone else's, if you're an\nIRC operator with the correct permissions). To prevent accidental\nunregistrations, a verification code is required; invoking the command without\na code will display the necessary code.": "",
"Syntax: $bVERIFY <username> <code>$b\n\nVERIFY lets you complete an account registration, if the server requires email\nor other verification.": ""
"Syntax: $bUNSUSPEND <nickname>$b\n\nUNSUSPEND reverses a previous SUSPEND, restoring access to the account.": "",
"Syntax: $bVERIFY <username> <code>$b\n\nVERIFY lets you complete an account registration, if the server requires email\nor other verification.": "",
"You're not logged into an account": "Δεν είστε συνδεδεμένοι σε λογαριασμό"
}

View File

@ -1,8 +1,8 @@
{
"= Help Topics =\n\nCommands:\n%[1]s\n\nRPL_ISUPPORT Tokens:\n%[2]s\n\nInformation:\n%[3]s": "",
"== Channel Modes ==\n\nOragono supports the following channel modes:\n\n +b | Client masks that are banned from the channel (e.g. *!*@127.0.0.1)\n +e | Client masks that are exempted from bans.\n +I | Client masks that are exempted from the invite-only flag.\n +i | Invite-only mode, only invited clients can join the channel.\n +k | Key required when joining the channel.\n +l | Client join limit for the channel.\n +m | Moderated mode, only privileged clients can talk on the channel.\n +n | No-outside-messages mode, only users that are on the channel can send\n | messages to it.\n +R | Only registered users can join the channel.\n +s | Secret mode, channel won't show up in /LIST or whois replies.\n +t | Only channel opers can modify the topic.\n\n= Prefixes =\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.": "",
"== Channel Modes ==\n\nOragono supports the following channel modes:\n\n +b | Client masks that are banned from the channel (e.g. *!*@127.0.0.1)\n +e | Client masks that are exempted from bans.\n +I | Client masks that are exempted from the invite-only flag.\n +i | Invite-only mode, only invited clients can join the channel.\n +k | Key required when joining the channel.\n +l | Client join limit for the channel.\n +m | Moderated mode, only privileged clients can talk on the channel.\n +n | No-outside-messages mode, only users that are on the channel can send\n | messages to it.\n +R | Only registered users can join the channel.\n +s | Secret mode, channel won't show up in /LIST or whois replies.\n +t | Only channel opers can modify the topic.\n +E | Roleplaying commands are enabled in the channel.\n +C | Clients are blocked from sending CTCP messages in the channel.\n\n= Prefixes =\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.": "",
"== Server Notice Masks ==\n\nOragono supports the following server notice masks for operators:\n\n a | Local announcements.\n c | Local client connections.\n j | Local channel actions.\n k | Local kills.\n n | Local nick changes.\n o | Local oper actions.\n q | Local quits.\n t | Local /STATS usage.\n u | Local client account actions.\n x | Local X-lines (DLINE/KLINE/etc).\n v | Local vhost changes.\n\nTo set a snomask, do this with your nickname:\n\n /MODE <nick> +s <chars>\n\nFor instance, this would set the kill, oper, account and xline snomasks on dan:\n\n /MODE dan +s koux": "",
"== User Modes ==\n\nOragono supports the following user modes:\n\n +a | User is marked as being away. This mode is set with the /AWAY command.\n +i | User is marked as invisible (their channels are hidden from whois replies).\n +o | User is an IRC operator.\n +R | User only accepts messages from other registered users. \n +s | Server Notice Masks (see help with /HELPOP snomasks).\n +Z | User is connected via TLS.": "",
"== User Modes ==\n\nOragono supports the following user modes:\n\n +a | User is marked as being away. This mode is set with the /AWAY command.\n +i | User is marked as invisible (their channels are hidden from whois replies).\n +o | User is an IRC operator.\n +R | User only accepts messages from other registered users. \n +s | Server Notice Masks (see help with /HELPOP snomasks).\n +Z | User is connected via TLS.\n +B | User is a bot.\n +E | User can receive roleplaying commands.\n +T | User is blocked from sending CTCP messages.": "",
"@+client-only-tags TAGMSG <target>{,<target>}\n\nSends the given client-only tags to the given targets as a TAGMSG. See the IRCv3\nspecs for more info: http://ircv3.net/specs/core/message-tags-3.3.html": "",
"ACC LS\nACC REGISTER <accountname> [callback_namespace:]<callback> [cred_type] :<credential>\nACC VERIFY <accountname> <auth_code>\n\nUsed in account registration. See the relevant specs for more info:\nhttps://oragono.io/specs.html": "",
"AMBIANCE <target> <text to be sent>\n\nThe AMBIANCE command is used to send a scene notification to the given target.": "",
@ -13,8 +13,10 @@
"CAP <subcommand> [:<capabilities>]\n\nUsed in capability negotiation. See the IRCv3 specs for more info:\nhttp://ircv3.net/specs/core/capability-negotiation-3.1.html\nhttp://ircv3.net/specs/core/capability-negotiation-3.2.html": "",
"CHATHISTORY [params]\n\nCHATHISTORY is a history replay command associated with the IRCv3\nspecification draft/chathistory. See this document:\nhttps://github.com/ircv3/ircv3-specifications/pull/393": "",
"DEBUG <option>\n\nProvides various debugging commands for the IRCd. <option> can be one of:\n\n* GCSTATS: Garbage control statistics.\n* NUMGOROUTINE: Number of goroutines in use.\n* STARTCPUPROFILE: Starts the CPU profiler.\n* STOPCPUPROFILE: Stops the CPU profiler.\n* PROFILEHEAP: Writes a memory profile.\n* CRASHSERVER: Crashes the server (for use in failover testing)": "",
"DEFCON [level]\n\nThe DEFCON system can disable server features at runtime, to mitigate\nspam or other hostile activity. It has five levels, which are cumulative\n(i.e., level 3 includes all restrictions from level 4 and so on):\n\n5: Normal operation\n4: No new account or channel registrations\n3: All users are +R; no changes to vhosts\n2: No new unauthenticated connections; all channels are +R\n1: No new connections except from localhost or other trusted IPs": "",
"DEOPER\n\nDEOPER removes the IRCop privileges granted to you by a successful /OPER.": "",
"DLINE [ANDKILL] [MYSELF] [duration] <ip>/<net> [ON <server>] [reason [| oper reason]]\nDLINE LIST\n\nBans an IP address or network from connecting to the server. If the duration is\ngiven then only for that long. The reason is shown to the user themselves, but\neveryone else will see a standard message. The oper reason is shown to\noperators getting info about the DLINEs that exist.\n\nBans are saved across subsequent launches of the server.\n\n\"ANDKILL\" means that all matching clients are also removed from the server.\n\n\"MYSELF\" is required when the DLINE matches the address the person applying it is connected\nfrom. If \"MYSELF\" is not given, trying to DLINE yourself will result in an error.\n\n[duration] can be of the following forms:\n\t1y 12mo 31d 10h 8m 13s\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24\n\nON <server> specifies that the ban is to be set on that specific server.\n\n[reason] and [oper reason], if they exist, are separated by a vertical bar (|).\n\nIf \"DLINE LIST\" is sent, the server sends back a list of our current DLINEs.": "",
"EXTJWT <target> [service_name]\n\nGet a JSON Web Token for target (either * or a channel name).": "",
"HELP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.": "",
"HELPOP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.": "",
"HISTORY <target> [limit]\n\nReplay message history. <target> can be a channel name, \"me\" to replay direct\nmessage history, or a nickname to replay another client's direct message\nhistory (they must be logged into the same account as you). [limit] can be\neither an integer (the maximum number of messages to replay), or a time\nduration like 10m or 1h (the time window within which to replay messages).": "",
@ -53,12 +55,14 @@
"SANICK <currentnick> <newnick>\n\nGives the given user a new nickname.": "",
"SCENE <target> <text to be sent>\n\nThe SCENE command is used to send a scene notification to the given target.": "",
"SETNAME <realname>\n\nThe SETNAME command updates the realname to be the newly-given one.": "",
"SUMMON [parameters]\n\nThe SUMMON command is not implemented.": "",
"TIME [server]\n\nShows the time of the current, or the given, server.": "",
"TOPIC <channel> [topic]\n\nIf [topic] is given, sets the topic in the channel to that. If [topic] is not\ngiven, views the current topic on the channel.": "",
"UNDLINE <ip>/<net>\n\nRemoves an existing ban on an IP address or a network.\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24": "",
"UNKLINE <mask>\n\nRemoves an existing ban on a mask.\n\nFor example:\n\tdan\n\tdan!5*@127.*": "",
"USER <username> 0 * <realname>\n\nUsed in connection registration, sets your username and realname to the given\nvalues (though your username may also be looked up with Ident).": "",
"USERHOST <nickname>{ <nickname>}\n\t\t\nShows information about the given users. Takes up to 10 nicknames.": "",
"USERS [parameters]\n\nThe USERS command is not implemented.": "",
"VERSION [server]\n\nViews the version of software and the RPL_ISUPPORT tokens for the given server.": "",
"WEBIRC <password> <gateway> <hostname> <ip> [:<flags>]\n\nUsed by web<->IRC gateways and bouncers, the WEBIRC command allows gateways to\npass-through the real IP addresses of clients:\nircv3.net/specs/extensions/webirc.html\n\n<flags> is a list of space-separated strings indicating various details about\nthe connection from the client to the gateway, such as:\n\n- tls: this flag indicates that the client->gateway connection is secure": "",
"WHO <name> [o]\n\nReturns information for the given user.": "",

View File

@ -86,6 +86,7 @@
"Channel renamed": "",
"Channel renamed: %s": "",
"Channels with persistent history cannot be renamed": "",
"Client %s is always-on and cannot be fully removed by /KILL; consider /NS SUSPEND instead": "",
"Client reconnected": "",
"Client reconnected (message history may have been lost)": "",
"Client reconnected (up to %d seconds of message history lost)": "",
@ -95,6 +96,7 @@
"Could not accept ownership of channel %s": "",
"Could not delete message": "",
"Could not find given client": "",
"Could not generate EXTJWT token": "",
"Could not look up account name, proceeding anyway": "",
"Could not parse IP address or CIDR network": "",
"Could not register": "",
@ -106,9 +108,12 @@
"Could not transfer channel": "",
"Could not ungroup nick": "",
"Created at: %s": "",
"Current DEFCON level is %d": "",
"Current global users %[1]s, max %[2]s": "",
"Current local users %[1]s, max %[2]s": "",
"Data export for %[1]s completed and written to %[2]s": "",
"Device ID: %s": "",
"Direct messages from unregistered users are temporarily restricted": "",
"End of /HELPOP": "",
"End of /INFO": "",
"End of /WHOIS list": "",
@ -154,6 +159,7 @@
"Insufficient privileges": "",
"Internal error": "",
"Invalid CAP subcommand": "",
"Invalid DEFCON parameter": "",
"Invalid account name": "",
"Invalid certificate fingerprint": "",
"Invalid channel name": "",
@ -165,6 +171,7 @@
"Invalid params": "",
"Invalid regex": "",
"Invalid vhost": "",
"It was built from git hash %s.": "",
"It was rejected for reason: %s": "",
"JOIN 0 is not allowed": "Can't run JOIN 0 here, mate",
"Language %s is not supported by this server": "",
@ -173,6 +180,7 @@
"MOTD File is missing": "",
"Malformed username": "",
"Mask isn't valid": "",
"Message rejected for containing invalid UTF-8": "",
"Messages could not be retrieved": "",
"Multiclient functionality is currently disabled for your account": "",
"Multiclient functionality is currently disabled for your account, but you can opt in": "",
@ -189,6 +197,7 @@
"No such channel": "",
"No such module [%s]": "",
"No such nick": "",
"No such service": "",
"No such setting": "",
"No text to send": "",
"No topic is set": "",
@ -207,6 +216,7 @@
"Purge reason: %s": "",
"Purged at: %s": "",
"Purged by operator: %s": "",
"Realname is not valid": "",
"Received malformed line": "",
"Registered at: %s": "",
"Registered channel: %s": "",
@ -228,6 +238,7 @@
"SASL authentication failed: Passphrase too long": "",
"SASL authentication failed: authcid and authzid should be the same": "",
"SASL message too long": "",
"SUMMON has been disabled": "",
"Server notice masks": "",
"Session %d (currently attached session):": "",
"Session %d:": "",
@ -247,9 +258,11 @@
"Successfully registered account %s": "",
"Successfully rejected vhost request for %s": "",
"Successfully reset channel access": "",
"Successfully set persistent mode %s%s on %s": "",
"Successfully set persistent mode %[1]s on %[2]s": "",
"Successfully set vhost": "",
"Successfully suspended account %s": "",
"Successfully transferred channel %[1]s to account %[2]s": "",
"Successfully un-suspended account %s": "",
"Successfully ungrouped nick %s with your account": "",
"Successfully unpurged channel %s from the server": "",
"Successfully unregistered account %s": "",
@ -272,6 +285,7 @@
"This ban matches you. To KLINE yourself, you must use the command: /KLINE MYSELF <arguments>": "",
"This command has been disabled by the server administrators": "",
"This feature has been disabled by the server administrators": "",
"This is Oragono version %s.": "",
"This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.": "",
"This server requires that you wait %v after connecting before you can use /LIST. You have %v left.": "",
"This server was created %s": "",
@ -285,10 +299,12 @@
"Transfer of channel %[1]s to account %[2]s succeeded, pending acceptance": "",
"Translators:": "",
"Try again later": "",
"USERS has been disabled": "",
"Unknown command": "Sorry mate, I dunno that command",
"Unknown command. To see available commands, run: /%s HELP": "",
"Unknown subcommand": "Sorry mate, I dunno that subcommand",
"Unrecognized DEBUG subcommand": "",
"Usage: REGISTER <passphrase> [email]": "",
"User %s is no longer allowed to use vhosts": "",
"User %s is now allowed to use vhosts": "",
"User doesn't have roleplaying mode enabled": "",
@ -317,13 +333,11 @@
"You have been marked as being away": "",
"You have been offered ownership of channel %[1]s. To accept, /CS TRANSFER ACCEPT %[1]s": "",
"You have enabled autoreplay of missed messages, but you can't receive them because your client isn't set to always-on": "",
"You have joined too many channels": "",
"You have sent too many registration messages": "",
"You have too many nicks reserved already (you can remove some with /NS DROP)": "",
"You may not reregister": "You can't re-reg mate",
"You must be an oper on the channel to register it": "",
"You must be connected with TLS and a client certificate to do this": "",
"You must be registered to join that channel": "",
"You must have rehash permissions in order to execute DEBUG CRASHSERVER": "",
"You must log in with SASL to join this server": "",
"You must specify an account": "",

View File

@ -1,16 +1,59 @@
{
"NickServ lets you register and login to an account.\n\nTo see in-depth help for a specific NickServ command, try:\n $b/NS HELP <command>$b\n\nHere are the commands you can use:\n%s": "",
"$bALWAYS-ON$b\n'always-on' controls whether your nickname/identity will remain active\neven while you are disconnected from the server. Your options are 'true',\n'false', and 'default' (use the server default value).": "",
"$bAUTO-AWAY$b\n'auto-away' is only effective for always-on clients. If enabled, you will\nautomatically be marked away when all your sessions are disconnected, and\nautomatically return from away when you connect again.": "",
"$bAUTOREPLAY-LINES$b\n'autoreplay-lines' controls the number of lines of channel history that will\nbe replayed to you automatically when joining a channel. Your options are any\npositive number, 0 to disable the feature, and 'default' to use the server\ndefault.": "",
"$bAUTOREPLAY-MISSED$b\n'autoreplay-missed' is only effective for always-on clients. If enabled,\nif you have at most one active session, the server will remember the time\nyou disconnect and then replay missed messages to you when you reconnect.\nYour options are 'on' and 'off'.": "",
"$bCERT$b controls a user account's certificate fingerprints": "",
"$bDM-HISTORY$b\n'dm-history' is only effective for always-on clients. It lets you control\nhow the history of your direct messages is stored. Your options are:\n1. 'off' [no history]\n2. 'ephemeral' [a limited amount of temporary history, not stored on disk]\n3. 'on' [history stored in a permanent database, if available]\n4. 'default' [use the server default]": "",
"$bDROP$b de-links your current (or the given) nickname from your user account.": "",
"$bENFORCE$b\n'enforce' lets you specify a custom enforcement mechanism for your registered\nnicknames. Your options are:\n1. 'none' [no enforcement, overriding the server default]\n2. 'strict' [you must already be authenticated to use the nick]\n3. 'default' [use the server default]": "",
"$bERASE$b erases all records of an account, allowing reuse.": "",
"$bGET$b queries the current values of your account settings": "",
"$bGHOST$b reclaims your nickname.": "",
"$bGROUP$b links your current nickname to your user account.": "",
"$bIDENTIFY$b lets you login to your account.": "",
"$bINFO$b gives you information on a user account.": "",
"$bLIST$b searches the list of registered nicknames.": "",
"$bMULTICLIENT$b\nIf 'multiclient' is enabled and you are already logged in and using a nick, a\nsecond client of yours that authenticates with SASL and requests the same nick\nis allowed to attach to the nick as well (this is comparable to the behavior\nof IRC \"bouncers\" like ZNC). Your options are 'on' (allow this behavior),\n'off' (disallow it), and 'default' (use the server default value).": "",
"$bPASSWD$b lets you change your password.": "",
"$bREGISTER$b lets you register a user account.": "",
"$bREPLAY-JOINS$b\n'replay-joins' controls whether replayed channel history will include\nlines for join and part. This provides more information about the context of\nmessages, but may be spammy. Your options are 'always', 'never', and the default\nof 'commands-only' (the messages will be replayed in /HISTORY output, but not\nduring autoreplay).": "",
"$bSADROP$b forcibly de-links the given nickname from its user account.": "",
"$bSAGET$b queries the current values of another user's account settings": "",
"$bSAREGISTER$b registers an account on someone else's behalf.": "",
"$bSASET$b modifies another user's account settings": "",
"$bSESSIONS$b lists the sessions attached to a nickname.": "",
"$bSET$b modifies your account settings": "",
"$bSUSPEND$b disables an account and disconnects the clients": "",
"$bUNREGISTER$b lets you delete your user account.": "",
"$bUNSUSPEND$b restores access to a suspended account": "",
"$bVERIFY$b lets you complete account registration.": "",
"Insufficient privileges": "",
"Invalid parameters": "",
"NickServ lets you register, log in to, and manage an account.": "",
"Password incorrect": "",
"Passwords do not match": "",
"Syntax $bSET <setting> <value>$b\n\nSET modifies your account settings. The following settings are available:": "",
"Syntax: $bCERT <LIST | ADD | DEL> [account] [certfp]$b\n\nCERT examines or modifies the TLS certificate fingerprints that can be used to\nlog into an account. Specifically, $bCERT LIST$b lists the authorized\nfingerprints, $bCERT ADD <fingerprint>$b adds a new fingerprint, and\n$bCERT DEL <fingerprint>$b removes a fingerprint. If you're an IRC operator\nwith the correct permissions, you can act on another user's account, for\nexample with $bCERT ADD <account> <fingerprint>$b.": "",
"Syntax: $bDROP [nickname]$b\n\nDROP de-links the given (or your current) nickname from your user account.": "",
"Syntax: $bENFORCE [method]$b\n\nENFORCE lets you specify a custom enforcement mechanism for your registered\nnicknames. Your options are:\n1. 'none' [no enforcement, overriding the server default]\n2. 'timeout' [anyone using the nick must authenticate before a deadline,\n or else they will be renamed]\n3. 'strict' [you must already be authenticated to use the nick]\n4. 'default' [use the server default]\nWith no arguments, queries your current enforcement status.": "",
"Syntax: $bENFORCE [method]$b\n\nENFORCE is an alias for $bGET enforce$b and $bSET enforce$b. See the help\nentry for $bSET$b for more information.": "",
"Syntax: $bERASE <username> [code]$b\n\nERASE deletes all records of an account, allowing it to be re-registered.\nThis should be used with caution, because it violates an expectation that\naccount names are permanent identifiers. Typically, UNREGISTER should be\nused instead. A confirmation code is required; invoking the command\nwithout a code will display the necessary code.": "",
"Syntax: $bGET <setting>$b\n\nGET queries the current values of your account settings. For more information\non the settings and their possible values, see HELP SET.": "",
"Syntax: $bGHOST <nickname>$b\n\nGHOST disconnects the given user from the network if they're logged in with the\nsame user account, letting you reclaim your nickname.": "",
"Syntax: $bGROUP$b\n\nGROUP links your current nickname with your logged-in account, so other people\nwill not be able to use it.": "",
"Syntax: $bIDENTIFY <username> [password]$b\n\nIDENTIFY lets you login to the given username using either password auth, or\ncertfp (your client certificate) if a password is not given.": "",
"Syntax: $bINFO [username]$b\n\nINFO gives you information about the given (or your own) user account.": "",
"Syntax: $bPASSWD <current> <new> <new_again>$b\nOr: $bPASSWD <username> <new>$b\n\nPASSWD lets you change your account password. You must supply your current\npassword and confirm the new one by typing it twice. If you're an IRC operator\nwith the correct permissions, you can use PASSWD to reset someone else's\npassword by supplying their username and then the desired password.": "",
"Syntax: $bREGISTER <username> <email> [password]$b\n\nREGISTER lets you register a user account. If the server allows anonymous\nregistration, you can send an asterisk (*) as the email address.\n\nIf the password is left out, your account will be registered to your TLS client\ncertificate (and you will need to use that certificate to login in future).": "",
"Syntax: $bLIST [regex]$b\n\nLIST returns the list of registered nicknames, which match the given regex.\nIf no regex is provided, all registered nicknames are returned.": "",
"Syntax: $bPASSWD <current> <new> <new_again>$b\nOr: $bPASSWD <username> <new>$b\n\nPASSWD lets you change your account password. You must supply your current\npassword and confirm the new one by typing it twice. If you're an IRC operator\nwith the correct permissions, you can use PASSWD to reset someone else's\npassword by supplying their username and then the desired password. To\nindicate an empty password, use * instead.": "",
"Syntax: $bREGISTER <password> [email]$b\n\nREGISTER lets you register your current nickname as a user account. If the\nserver allows anonymous registration, you can omit the e-mail address.\n\nIf you are currently logged in with a TLS client certificate and wish to use\nit instead of a password to log in, send * as the password.": "",
"Syntax: $bSADROP <nickname>$b\n\nSADROP forcibly de-links the given nickname from the attached user account.": "",
"Syntax: $bSAREGISTER <username> <password>$b\n\nSAREGISTER registers an account on someone else's behalf.\nThis is for use in configurations that require SASL for all connections;\nan administrator can set use this command to set up user accounts.": "",
"Syntax: $bSESSIONS [nickname]$b\n\nSESSIONS lists information about the sessions currently attached, via\nthe server's bouncer functionality, to your nickname. An administrator\ncan use this command to list another user's sessions.": "",
"Syntax: $bSAGET <account> <setting>$b\n\nSAGET queries the values of someone else's account settings. For more\ninformation on the settings and their possible values, see HELP SET.": "",
"Syntax: $bSAREGISTER <username> [password]$b\n\nSAREGISTER registers an account on someone else's behalf.\nThis is for use in configurations that require SASL for all connections;\nan administrator can set use this command to set up user accounts.": "",
"Syntax: $bSASET <account> <setting> <value>$b\n\nSASET modifies the values of someone else's account settings. For more\ninformation on the settings and their possible values, see HELP SET.": "",
"Syntax: $bSESSIONS [nickname]$b\n\nSESSIONS lists information about the sessions currently attached, via\nthe server's multiclient functionality, to your nickname. An administrator\ncan use this command to list another user's sessions.": "",
"Syntax: $bSUSPEND <nickname>$b\n\nSUSPEND disables an account and disconnects the associated clients.": "",
"Syntax: $bUNREGISTER <username> [code]$b\n\nUNREGISTER lets you delete your user account (or someone else's, if you're an\nIRC operator with the correct permissions). To prevent accidental\nunregistrations, a verification code is required; invoking the command without\na code will display the necessary code.": "",
"Syntax: $bVERIFY <username> <code>$b\n\nVERIFY lets you complete an account registration, if the server requires email\nor other verification.": ""
"Syntax: $bUNSUSPEND <nickname>$b\n\nUNSUSPEND reverses a previous SUSPEND, restoring access to the account.": "",
"Syntax: $bVERIFY <username> <code>$b\n\nVERIFY lets you complete an account registration, if the server requires email\nor other verification.": "",
"You're not logged into an account": ""
}

View File

@ -1,8 +1,8 @@
{
"= Help Topics =\n\nCommands:\n%[1]s\n\nRPL_ISUPPORT Tokens:\n%[2]s\n\nInformation:\n%[3]s": "= Ayuda de Topic =\n\nComandos:\n%[1]s\n\nRPL_ISUPPORT Tokens:\n%[2]s\n\nInformación:\n%[3]s",
"== Channel Modes ==\n\nOragono supports the following channel modes:\n\n +b | Client masks that are banned from the channel (e.g. *!*@127.0.0.1)\n +e | Client masks that are exempted from bans.\n +I | Client masks that are exempted from the invite-only flag.\n +i | Invite-only mode, only invited clients can join the channel.\n +k | Key required when joining the channel.\n +l | Client join limit for the channel.\n +m | Moderated mode, only privileged clients can talk on the channel.\n +n | No-outside-messages mode, only users that are on the channel can send\n | messages to it.\n +R | Only registered users can join the channel.\n +s | Secret mode, channel won't show up in /LIST or whois replies.\n +t | Only channel opers can modify the topic.\n\n= Prefixes =\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.": "",
"== Channel Modes ==\n\nOragono supports the following channel modes:\n\n +b | Client masks that are banned from the channel (e.g. *!*@127.0.0.1)\n +e | Client masks that are exempted from bans.\n +I | Client masks that are exempted from the invite-only flag.\n +i | Invite-only mode, only invited clients can join the channel.\n +k | Key required when joining the channel.\n +l | Client join limit for the channel.\n +m | Moderated mode, only privileged clients can talk on the channel.\n +n | No-outside-messages mode, only users that are on the channel can send\n | messages to it.\n +R | Only registered users can join the channel.\n +s | Secret mode, channel won't show up in /LIST or whois replies.\n +t | Only channel opers can modify the topic.\n +E | Roleplaying commands are enabled in the channel.\n +C | Clients are blocked from sending CTCP messages in the channel.\n\n= Prefixes =\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.": "",
"== Server Notice Masks ==\n\nOragono supports the following server notice masks for operators:\n\n a | Local announcements.\n c | Local client connections.\n j | Local channel actions.\n k | Local kills.\n n | Local nick changes.\n o | Local oper actions.\n q | Local quits.\n t | Local /STATS usage.\n u | Local client account actions.\n x | Local X-lines (DLINE/KLINE/etc).\n v | Local vhost changes.\n\nTo set a snomask, do this with your nickname:\n\n /MODE <nick> +s <chars>\n\nFor instance, this would set the kill, oper, account and xline snomasks on dan:\n\n /MODE dan +s koux": "",
"== User Modes ==\n\nOragono supports the following user modes:\n\n +a | User is marked as being away. This mode is set with the /AWAY command.\n +i | User is marked as invisible (their channels are hidden from whois replies).\n +o | User is an IRC operator.\n +R | User only accepts messages from other registered users. \n +s | Server Notice Masks (see help with /HELPOP snomasks).\n +Z | User is connected via TLS.": "== Modos de Usuario ==\n\nOragono soporta los siguientes modos de usuario:\n\n +a = El usuario está marcado como ausente. Este modo se establece con el comando /AWAY.\n +i = El usuario está marcado como invisible (sus canales están ocultos de las respuestas whois).\n +o El usuario es un operador de IRC.\n +R El usuario sólo acepta mensajes de otros usuarios registrados. \n +s Máscaras de Aviso de Servidor (ver ayuda con las máscaras de nariz /HELPOP).\n +Z El usuario está conectado a través de TLS.",
"== User Modes ==\n\nOragono supports the following user modes:\n\n +a | User is marked as being away. This mode is set with the /AWAY command.\n +i | User is marked as invisible (their channels are hidden from whois replies).\n +o | User is an IRC operator.\n +R | User only accepts messages from other registered users. \n +s | Server Notice Masks (see help with /HELPOP snomasks).\n +Z | User is connected via TLS.\n +B | User is a bot.\n +E | User can receive roleplaying commands.\n +T | User is blocked from sending CTCP messages.": "",
"@+client-only-tags TAGMSG <target>{,<target>}\n\nSends the given client-only tags to the given targets as a TAGMSG. See the IRCv3\nspecs for more info: http://ircv3.net/specs/core/message-tags-3.3.html": "\n@+client-only-tags TAGMSG <target>{,<target>}\n\nEnvía las etiquetas dadas solo para clientes a los objetivos dados como un TAGMSG. Vea el Ircv3\nespecificaciones para más información: http://ircv3.net/specs/core/message-tags-3.3.html",
"ACC LS\nACC REGISTER <accountname> [callback_namespace:]<callback> [cred_type] :<credential>\nACC VERIFY <accountname> <auth_code>\n\nUsed in account registration. See the relevant specs for more info:\nhttps://oragono.io/specs.html": "ACC LS\nACC REGISTER <accountname> [callback_namespace:]<callback> [cred_type] :<credential>\nACC VERIFY <accountname> <auth_code>\n\nUtilizado en el registro de la cuenta. Consulte las especificaciones relevantes para obtener más información: https://oragono.io/specs.html",
"AMBIANCE <target> <text to be sent>\n\nThe AMBIANCE command is used to send a scene notification to the given target.": "AMBIANCE <target> <mensaje a enviar>\n\nEl comando AMBIANCE se usa para enviar una notificación de escena al objetivo dado.",
@ -13,8 +13,10 @@
"CAP <subcommand> [:<capabilities>]\n\nUsed in capability negotiation. See the IRCv3 specs for more info:\nhttp://ircv3.net/specs/core/capability-negotiation-3.1.html\nhttp://ircv3.net/specs/core/capability-negotiation-3.2.html": "CAP <subcommand> [:<capabilities>]\n\nUtilizado en la negociación de capacidades. Consulte las especificaciones de IRCv3 para obtener más información:\nhttp://ircv3.net/specs/core/capability-negotiation-3.1.html\nhttp://ircv3.net/specs/core/capability-negotiation-3.2.html",
"CHATHISTORY [params]\n\nCHATHISTORY is a history replay command associated with the IRCv3\nspecification draft/chathistory. See this document:\nhttps://github.com/ircv3/ircv3-specifications/pull/393": "",
"DEBUG <option>\n\nProvides various debugging commands for the IRCd. <option> can be one of:\n\n* GCSTATS: Garbage control statistics.\n* NUMGOROUTINE: Number of goroutines in use.\n* STARTCPUPROFILE: Starts the CPU profiler.\n* STOPCPUPROFILE: Stops the CPU profiler.\n* PROFILEHEAP: Writes a memory profile.\n* CRASHSERVER: Crashes the server (for use in failover testing)": "",
"DEFCON [level]\n\nThe DEFCON system can disable server features at runtime, to mitigate\nspam or other hostile activity. It has five levels, which are cumulative\n(i.e., level 3 includes all restrictions from level 4 and so on):\n\n5: Normal operation\n4: No new account or channel registrations\n3: All users are +R; no changes to vhosts\n2: No new unauthenticated connections; all channels are +R\n1: No new connections except from localhost or other trusted IPs": "",
"DEOPER\n\nDEOPER removes the IRCop privileges granted to you by a successful /OPER.": "",
"DLINE [ANDKILL] [MYSELF] [duration] <ip>/<net> [ON <server>] [reason [| oper reason]]\nDLINE LIST\n\nBans an IP address or network from connecting to the server. If the duration is\ngiven then only for that long. The reason is shown to the user themselves, but\neveryone else will see a standard message. The oper reason is shown to\noperators getting info about the DLINEs that exist.\n\nBans are saved across subsequent launches of the server.\n\n\"ANDKILL\" means that all matching clients are also removed from the server.\n\n\"MYSELF\" is required when the DLINE matches the address the person applying it is connected\nfrom. If \"MYSELF\" is not given, trying to DLINE yourself will result in an error.\n\n[duration] can be of the following forms:\n\t1y 12mo 31d 10h 8m 13s\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24\n\nON <server> specifies that the ban is to be set on that specific server.\n\n[reason] and [oper reason], if they exist, are separated by a vertical bar (|).\n\nIf \"DLINE LIST\" is sent, the server sends back a list of our current DLINEs.": "DLINE [ANDKILL] [MISMO] [duración] <ip> / <net> [ON <server>] [motivo [| oper razón]]\nLISTA DE DLINE\n\nProhíbe que una dirección IP o red se conecte al servidor. Si la duración es\ndado entonces solo por ese tiempo. El motivo se muestra al usuario, pero\ntodos los demás verán un mensaje estándar. La razón principal se muestra para\noperadores que obtienen información sobre los DLINE que existen.\n\nLas prohibiciones se guardan en lanzamientos posteriores del servidor.\n\n\"ANDKILL\" significa que todos los clientes coincidentes también se eliminan del servidor.\n\nSe requiere \"MISMO MISMO\" cuando el DLINE coincide con la dirección que está conectada la persona que lo aplica\nde. Si no aparece \"MISMO MISMO\", intentar DLINE usted mismo dará como resultado un error.\n\n[duración] puede ser de las siguientes formas:\n1y 12mo 31d 10h 8m 13s\n\n<net> se especifica en la notación CIDR típica. Por ejemplo:\n127.0.0.1/8\n8.8.8.8/24\n\nON <server> especifica que la prohibición debe establecerse en ese servidor específico.\n\n[razón] y [razón de operación], si existen, están separadas por una barra vertical (|).\n\nSi se envía \"DLINE LIST\", el servidor devuelve una lista de nuestros DLINE actuales.\n \n-> DLINE [ANDKILL] [MISMO] [duración] <ip> / <net> [ON <server>] [motivo [| oper razón]]\nLISTA DE DLINE\n\nProhíbe que una dirección IP o red se conecte al servidor. Si la duración es\ndado entonces solo por ese tiempo. El motivo se muestra al usuario, pero\ntodos los demás verán un mensaje estándar. La razón principal se muestra para\noperadores que obtienen información sobre los DLINE que existen.\n\nLas prohibiciones se guardan en lanzamientos posteriores del servidor.\n\n\"ANDKILL\" significa que todos los clientes coincidentes también se eliminan del servidor.\n\nSe requiere \"MISMO MISMO\" cuando el DLINE coincide con la dirección que está conectada la persona que lo aplica\nde. Si no aparece \"MISMO MISMO\", intentar DLINE usted mismo dará como resultado un error.\n\n[duración] puede ser de las siguientes formas:\n1y 12mo 31d 10h 8m 13s\n\n<net> se especifica en la notación CIDR típica. Por ejemplo:\n127.0.0.1/8\n8.8.8.8/24\n\nON <server> especifica que la prohibición debe establecerse en ese servidor específico.\n\n[razón] y [razón de operación], si existen, están separadas por una barra vertical (|).\n\nSi se envía \"DLINE LIST\", el servidor devuelve una lista de nuestros DLINE actuales.",
"EXTJWT <target> [service_name]\n\nGet a JSON Web Token for target (either * or a channel name).": "",
"HELP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.": "AYUDA <argumento>\n\nObtenga una explicación de <argumento> o \"índice\" para obtener una lista de temas de ayuda.",
"HELPOP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.": "AYUDA <argumento>\n\nObtenga una explicación de <argumento> o \"índice\" para obtener una lista de temas de ayuda.",
"HISTORY <target> [limit]\n\nReplay message history. <target> can be a channel name, \"me\" to replay direct\nmessage history, or a nickname to replay another client's direct message\nhistory (they must be logged into the same account as you). [limit] can be\neither an integer (the maximum number of messages to replay), or a time\nduration like 10m or 1h (the time window within which to replay messages).": "",
@ -53,12 +55,14 @@
"SANICK <currentnick> <newnick>\n\nGives the given user a new nickname.": "",
"SCENE <target> <text to be sent>\n\nThe SCENE command is used to send a scene notification to the given target.": "",
"SETNAME <realname>\n\nThe SETNAME command updates the realname to be the newly-given one.": "",
"SUMMON [parameters]\n\nThe SUMMON command is not implemented.": "",
"TIME [server]\n\nShows the time of the current, or the given, server.": "",
"TOPIC <channel> [topic]\n\nIf [topic] is given, sets the topic in the channel to that. If [topic] is not\ngiven, views the current topic on the channel.": "",
"UNDLINE <ip>/<net>\n\nRemoves an existing ban on an IP address or a network.\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24": "",
"UNKLINE <mask>\n\nRemoves an existing ban on a mask.\n\nFor example:\n\tdan\n\tdan!5*@127.*": "",
"USER <username> 0 * <realname>\n\nUsed in connection registration, sets your username and realname to the given\nvalues (though your username may also be looked up with Ident).": "",
"USERHOST <nickname>{ <nickname>}\n\t\t\nShows information about the given users. Takes up to 10 nicknames.": "",
"USERS [parameters]\n\nThe USERS command is not implemented.": "",
"VERSION [server]\n\nViews the version of software and the RPL_ISUPPORT tokens for the given server.": "",
"WEBIRC <password> <gateway> <hostname> <ip> [:<flags>]\n\nUsed by web<->IRC gateways and bouncers, the WEBIRC command allows gateways to\npass-through the real IP addresses of clients:\nircv3.net/specs/extensions/webirc.html\n\n<flags> is a list of space-separated strings indicating various details about\nthe connection from the client to the gateway, such as:\n\n- tls: this flag indicates that the client->gateway connection is secure": "",
"WHO <name> [o]\n\nReturns information for the given user.": "",

View File

@ -86,6 +86,7 @@
"Channel renamed": "Canal renombrado",
"Channel renamed: %s": "Canal renombrado: %s",
"Channels with persistent history cannot be renamed": "",
"Client %s is always-on and cannot be fully removed by /KILL; consider /NS SUSPEND instead": "",
"Client reconnected": "Cliente reconectado",
"Client reconnected (message history may have been lost)": "",
"Client reconnected (up to %d seconds of message history lost)": "",
@ -95,6 +96,7 @@
"Could not accept ownership of channel %s": "",
"Could not delete message": "",
"Could not find given client": "No se pudo encontrar el cliente dado",
"Could not generate EXTJWT token": "",
"Could not look up account name, proceeding anyway": "",
"Could not parse IP address or CIDR network": "No podría analizar CIDR red o dirección IP",
"Could not register": "No se pudo registrar",
@ -106,9 +108,12 @@
"Could not transfer channel": "",
"Could not ungroup nick": "No se puede desagrupar nick",
"Created at: %s": "Creado en: %s",
"Current DEFCON level is %d": "",
"Current global users %[1]s, max %[2]s": "",
"Current local users %[1]s, max %[2]s": "",
"Data export for %[1]s completed and written to %[2]s": "",
"Device ID: %s": "",
"Direct messages from unregistered users are temporarily restricted": "",
"End of /HELPOP": "Final de /HELPOP",
"End of /INFO": "Final del /INFO",
"End of /WHOIS list": "Final de lista /WHOIS",
@ -154,6 +159,7 @@
"Insufficient privileges": "Privilegios Insuficientes",
"Internal error": "Error Interno",
"Invalid CAP subcommand": "Subcomando CAP inválido",
"Invalid DEFCON parameter": "",
"Invalid account name": "Nombre de cuenta inválido",
"Invalid certificate fingerprint": "",
"Invalid channel name": "",
@ -165,6 +171,7 @@
"Invalid params": "",
"Invalid regex": "",
"Invalid vhost": "Vhost inválido",
"It was built from git hash %s.": "",
"It was rejected for reason: %s": "Fue rechazado por la razón: %s",
"JOIN 0 is not allowed": "JOIN 0 no está permitido",
"Language %s is not supported by this server": "El idioma %s no es compatible con este servidor",
@ -173,6 +180,7 @@
"MOTD File is missing": "No se encuentra el archivo MOTD",
"Malformed username": "Nombre de usuario incorrecto",
"Mask isn't valid": "La máscara no es válida",
"Message rejected for containing invalid UTF-8": "",
"Messages could not be retrieved": "",
"Multiclient functionality is currently disabled for your account": "",
"Multiclient functionality is currently disabled for your account, but you can opt in": "",
@ -189,6 +197,7 @@
"No such channel": "No hay tal canal",
"No such module [%s]": "No hay tal módulo [%s]",
"No such nick": "No hay tal nick",
"No such service": "",
"No such setting": "No existe tal ajuste",
"No text to send": "",
"No topic is set": "No se establece ningún tema",
@ -207,6 +216,7 @@
"Purge reason: %s": "",
"Purged at: %s": "",
"Purged by operator: %s": "",
"Realname is not valid": "",
"Received malformed line": "Línea malformada recibida",
"Registered at: %s": "Registrado en: %s",
"Registered channel: %s": "Canal registrado: %s",
@ -228,6 +238,7 @@
"SASL authentication failed: Passphrase too long": "Error de autenticación SASL: frase de contraseña demasiado larga",
"SASL authentication failed: authcid and authzid should be the same": "La autenticación SASL falló: authcid y authzid deberían ser iguales",
"SASL message too long": "Mensaje SASL demasiado largo",
"SUMMON has been disabled": "",
"Server notice masks": "Máscaras de aviso del servidor",
"Session %d (currently attached session):": "Sesión %d (sesión actualmente adjunta):",
"Session %d:": "Sesión %d:",
@ -247,9 +258,11 @@
"Successfully registered account %s": "Cuenta registrada con éxito %s",
"Successfully rejected vhost request for %s": "Rechazado con éxito la solicitud de vhost para %s",
"Successfully reset channel access": "",
"Successfully set persistent mode %s%s on %s": "",
"Successfully set persistent mode %[1]s on %[2]s": "",
"Successfully set vhost": "Establecer con éxito vhost",
"Successfully suspended account %s": "",
"Successfully transferred channel %[1]s to account %[2]s": "",
"Successfully un-suspended account %s": "",
"Successfully ungrouped nick %s with your account": "Exitosamente desagrupado nick %s con su cuenta",
"Successfully unpurged channel %s from the server": "",
"Successfully unregistered account %s": "Exitosamente sin registrar %s de cuenta",
@ -272,6 +285,7 @@
"This ban matches you. To KLINE yourself, you must use the command: /KLINE MYSELF <arguments>": "Esta prohibición coincide contigo. Para KLINE usted mismo, debe usar el comando: /KLINE MISMA <argumentos>",
"This command has been disabled by the server administrators": "Este comando ha sido desactivado por los administradores del servidor",
"This feature has been disabled by the server administrators": "Esta característica ha sido desactivada por los administradores del servidor",
"This is Oragono version %s.": "",
"This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.": "Este servidor está en modo debug y está registrando todas las E/S de usuario. Si usted no desea que todo lo que envía sea legible para los propietarios del servidor, por favor desconéctese.",
"This server requires that you wait %v after connecting before you can use /LIST. You have %v left.": "",
"This server was created %s": "Este servidor fue creado %s",
@ -285,10 +299,12 @@
"Transfer of channel %[1]s to account %[2]s succeeded, pending acceptance": "",
"Translators:": "Traductores:",
"Try again later": "",
"USERS has been disabled": "",
"Unknown command": "Comando desconocido",
"Unknown command. To see available commands, run: /%s HELP": "Comando desconocido. Para ver los comandos disponibles, ejecute: /%s HELP",
"Unknown subcommand": "Subcomando desconocido",
"Unrecognized DEBUG subcommand": "",
"Usage: REGISTER <passphrase> [email]": "",
"User %s is no longer allowed to use vhosts": "",
"User %s is now allowed to use vhosts": "",
"User doesn't have roleplaying mode enabled": "El usuario no tiene habilitado el modo de juego de roles",
@ -317,13 +333,11 @@
"You have been marked as being away": "Has sido marcado como ausente",
"You have been offered ownership of channel %[1]s. To accept, /CS TRANSFER ACCEPT %[1]s": "",
"You have enabled autoreplay of missed messages, but you can't receive them because your client isn't set to always-on": "",
"You have joined too many channels": "Te has unido a demasiados canales",
"You have sent too many registration messages": "Ha enviado demasiados mensajes de registro",
"You have too many nicks reserved already (you can remove some with /NS DROP)": "Ya tiene demasiados nicks reservados (puede eliminar algunos con /NS DROP)",
"You may not reregister": "No puede volver a registrarse",
"You must be an oper on the channel to register it": "Usted debe ser un oper en el canal para registrarlo",
"You must be connected with TLS and a client certificate to do this": "Para ello, debe estar conectado con TLS y un certificado de cliente",
"You must be registered to join that channel": "Debes estar registrado para entrar en ese canal",
"You must have rehash permissions in order to execute DEBUG CRASHSERVER": "",
"You must log in with SASL to join this server": "Debe iniciar sesión con SASL para unirse a este servidor",
"You must specify an account": "Debe especificar una cuenta",

View File

@ -1,16 +1,59 @@
{
"NickServ lets you register and login to an account.\n\nTo see in-depth help for a specific NickServ command, try:\n $b/NS HELP <command>$b\n\nHere are the commands you can use:\n%s": "NickServ le permite registrarse y acceder a una cuenta.\n\nPara ver la ayuda en profundidad de un comando específico de NickServ, inténtelo:\n HELP $b/NS HELP <comando>$b\n\nAquí están los comandos que puede usar:\n%s",
"$bALWAYS-ON$b\n'always-on' controls whether your nickname/identity will remain active\neven while you are disconnected from the server. Your options are 'true',\n'false', and 'default' (use the server default value).": "",
"$bAUTO-AWAY$b\n'auto-away' is only effective for always-on clients. If enabled, you will\nautomatically be marked away when all your sessions are disconnected, and\nautomatically return from away when you connect again.": "",
"$bAUTOREPLAY-LINES$b\n'autoreplay-lines' controls the number of lines of channel history that will\nbe replayed to you automatically when joining a channel. Your options are any\npositive number, 0 to disable the feature, and 'default' to use the server\ndefault.": "",
"$bAUTOREPLAY-MISSED$b\n'autoreplay-missed' is only effective for always-on clients. If enabled,\nif you have at most one active session, the server will remember the time\nyou disconnect and then replay missed messages to you when you reconnect.\nYour options are 'on' and 'off'.": "",
"$bCERT$b controls a user account's certificate fingerprints": "",
"$bDM-HISTORY$b\n'dm-history' is only effective for always-on clients. It lets you control\nhow the history of your direct messages is stored. Your options are:\n1. 'off' [no history]\n2. 'ephemeral' [a limited amount of temporary history, not stored on disk]\n3. 'on' [history stored in a permanent database, if available]\n4. 'default' [use the server default]": "",
"$bDROP$b de-links your current (or the given) nickname from your user account.": "",
"$bENFORCE$b\n'enforce' lets you specify a custom enforcement mechanism for your registered\nnicknames. Your options are:\n1. 'none' [no enforcement, overriding the server default]\n2. 'strict' [you must already be authenticated to use the nick]\n3. 'default' [use the server default]": "",
"$bERASE$b erases all records of an account, allowing reuse.": "",
"$bGET$b queries the current values of your account settings": "",
"$bGHOST$b reclaims your nickname.": "",
"$bGROUP$b links your current nickname to your user account.": "",
"$bIDENTIFY$b lets you login to your account.": "",
"$bINFO$b gives you information on a user account.": "",
"$bLIST$b searches the list of registered nicknames.": "",
"$bMULTICLIENT$b\nIf 'multiclient' is enabled and you are already logged in and using a nick, a\nsecond client of yours that authenticates with SASL and requests the same nick\nis allowed to attach to the nick as well (this is comparable to the behavior\nof IRC \"bouncers\" like ZNC). Your options are 'on' (allow this behavior),\n'off' (disallow it), and 'default' (use the server default value).": "",
"$bPASSWD$b lets you change your password.": "",
"$bREGISTER$b lets you register a user account.": "",
"$bREPLAY-JOINS$b\n'replay-joins' controls whether replayed channel history will include\nlines for join and part. This provides more information about the context of\nmessages, but may be spammy. Your options are 'always', 'never', and the default\nof 'commands-only' (the messages will be replayed in /HISTORY output, but not\nduring autoreplay).": "",
"$bSADROP$b forcibly de-links the given nickname from its user account.": "",
"$bSAGET$b queries the current values of another user's account settings": "",
"$bSAREGISTER$b registers an account on someone else's behalf.": "",
"$bSASET$b modifies another user's account settings": "",
"$bSESSIONS$b lists the sessions attached to a nickname.": "",
"$bSET$b modifies your account settings": "",
"$bSUSPEND$b disables an account and disconnects the clients": "",
"$bUNREGISTER$b lets you delete your user account.": "",
"$bUNSUSPEND$b restores access to a suspended account": "",
"$bVERIFY$b lets you complete account registration.": "",
"Insufficient privileges": "Privilegios Insuficientes",
"Invalid parameters": "",
"NickServ lets you register, log in to, and manage an account.": "",
"Password incorrect": "Contraseña incorrecta",
"Passwords do not match": "",
"Syntax $bSET <setting> <value>$b\n\nSET modifies your account settings. The following settings are available:": "",
"Syntax: $bCERT <LIST | ADD | DEL> [account] [certfp]$b\n\nCERT examines or modifies the TLS certificate fingerprints that can be used to\nlog into an account. Specifically, $bCERT LIST$b lists the authorized\nfingerprints, $bCERT ADD <fingerprint>$b adds a new fingerprint, and\n$bCERT DEL <fingerprint>$b removes a fingerprint. If you're an IRC operator\nwith the correct permissions, you can act on another user's account, for\nexample with $bCERT ADD <account> <fingerprint>$b.": "",
"Syntax: $bDROP [nickname]$b\n\nDROP de-links the given (or your current) nickname from your user account.": "Sintaxis: $bDROP[apodo]$b\n\nDROP desvincula el nombre de usuario dado (o el actual) de su cuenta de usuario.",
"Syntax: $bENFORCE [method]$b\n\nENFORCE lets you specify a custom enforcement mechanism for your registered\nnicknames. Your options are:\n1. 'none' [no enforcement, overriding the server default]\n2. 'timeout' [anyone using the nick must authenticate before a deadline,\n or else they will be renamed]\n3. 'strict' [you must already be authenticated to use the nick]\n4. 'default' [use the server default]\nWith no arguments, queries your current enforcement status.": "Sintaxis: bENFORCE[método]$b\n\nENFORCE le permite especificar un mecanismo de aplicación personalizado para su registro.\napodos. Tus opciones son:\n1. 'none'[ninguna ejecución, anulando el valor por defecto del servidor]\n2.' Timeout'[cualquiera que utilice el nick debe autentificarse antes de una fecha límite,\n o de lo contrario se les cambiará el nombre]\n3. 'strict'[ya debe estar autenticado para usar el nick]\n4. 'default['usar el valor por defecto del servidor]\nSin argumentos, cuestiona su estado actual de aplicación de la ley.",
"Syntax: $bENFORCE [method]$b\n\nENFORCE is an alias for $bGET enforce$b and $bSET enforce$b. See the help\nentry for $bSET$b for more information.": "",
"Syntax: $bERASE <username> [code]$b\n\nERASE deletes all records of an account, allowing it to be re-registered.\nThis should be used with caution, because it violates an expectation that\naccount names are permanent identifiers. Typically, UNREGISTER should be\nused instead. A confirmation code is required; invoking the command\nwithout a code will display the necessary code.": "",
"Syntax: $bGET <setting>$b\n\nGET queries the current values of your account settings. For more information\non the settings and their possible values, see HELP SET.": "",
"Syntax: $bGHOST <nickname>$b\n\nGHOST disconnects the given user from the network if they're logged in with the\nsame user account, letting you reclaim your nickname.": "Sintaxis: $bGHOST <nombre>$b\n\nGHOST desconecta al usuario en cuestión de la red si está conectado con la función\nla misma cuenta de usuario, lo que le permite recuperar su nombre de usuario.",
"Syntax: $bGROUP$b\n\nGROUP links your current nickname with your logged-in account, so other people\nwill not be able to use it.": "Sintaxis: $bGROUP$b\n\nGROUP vincula tu nombre de usuario actual con tu cuenta de inicio de sesión, para que otras personas\nno podrá usarla.",
"Syntax: $bIDENTIFY <username> [password]$b\n\nIDENTIFY lets you login to the given username using either password auth, or\ncertfp (your client certificate) if a password is not given.": "Sintaxis: $bIDENTIFICAR <nombredeusuario>[contraseña]$b\n\nIDENTIFY le permite iniciar sesión en el nombre de usuario dado utilizando la autenticación de contraseña, o bien\ncertfp (su certificado de cliente) si no se proporciona una contraseña.",
"Syntax: $bINFO [username]$b\n\nINFO gives you information about the given (or your own) user account.": "Sintaxis: $bINFO [nombre de usuario]$b\n\nINFO le da información sobre la cuenta de usuario dada (o la suya propia).",
"Syntax: $bPASSWD <current> <new> <new_again>$b\nOr: $bPASSWD <username> <new>$b\n\nPASSWD lets you change your account password. You must supply your current\npassword and confirm the new one by typing it twice. If you're an IRC operator\nwith the correct permissions, you can use PASSWD to reset someone else's\npassword by supplying their username and then the desired password.": "Sintaxis: bPASSWD <current> <new> <new_again>$b\nO: $bPASSWD <nombredeusuario> <nuevo>$b\n\nPASSWD le permite cambiar la contraseña de su cuenta. Usted debe proporcionar su actual\ny confirme la nueva contraseña escribiéndola dos veces. Si eres un operador de IRC\ncon los permisos correctos, puede usar PASSWD para reiniciar la cuenta de otra persona\nproporcionando su nombre de usuario y luego la contraseña deseada.",
"Syntax: $bREGISTER <username> <email> [password]$b\n\nREGISTER lets you register a user account. If the server allows anonymous\nregistration, you can send an asterisk (*) as the email address.\n\nIf the password is left out, your account will be registered to your TLS client\ncertificate (and you will need to use that certificate to login in future).": "Sintaxis: REGISTER <nombre de usuario> <correo electrónico>[contraseña]$b\n\nREGISTRARSE le permite registrar una cuenta de usuario. Si el servidor permite el acceso anónimo\npuede enviar un asterisco (*) como dirección de correo electrónico.\n\nSi se omite la contraseña, su cuenta se registrará en su cliente TLS.\n(y necesitará usar ese certificado para iniciar sesión en el futuro).",
"Syntax: $bLIST [regex]$b\n\nLIST returns the list of registered nicknames, which match the given regex.\nIf no regex is provided, all registered nicknames are returned.": "",
"Syntax: $bPASSWD <current> <new> <new_again>$b\nOr: $bPASSWD <username> <new>$b\n\nPASSWD lets you change your account password. You must supply your current\npassword and confirm the new one by typing it twice. If you're an IRC operator\nwith the correct permissions, you can use PASSWD to reset someone else's\npassword by supplying their username and then the desired password. To\nindicate an empty password, use * instead.": "",
"Syntax: $bREGISTER <password> [email]$b\n\nREGISTER lets you register your current nickname as a user account. If the\nserver allows anonymous registration, you can omit the e-mail address.\n\nIf you are currently logged in with a TLS client certificate and wish to use\nit instead of a password to log in, send * as the password.": "",
"Syntax: $bSADROP <nickname>$b\n\nSADROP forcibly de-links the given nickname from the attached user account.": "Sintaxis: $bSADROP <nombre>$b\n\nSADROP desvincula por la fuerza el nombre de usuario dado de la cuenta de usuario adjunta.",
"Syntax: $bSAREGISTER <username> <password>$b\n\nSAREGISTER registers an account on someone else's behalf.\nThis is for use in configurations that require SASL for all connections;\nan administrator can set use this command to set up user accounts.": "Sintaxis: $bSAREGISTER <nombredeusuario> <contraseña>$b\n\nSAREGISTER registra una cuenta en nombre de otra persona.\nEsto es para uso en configuraciones que requieren SASL para todas las conexiones;\nun administrador puede utilizar este comando para configurar cuentas de usuario.",
"Syntax: $bSESSIONS [nickname]$b\n\nSESSIONS lists information about the sessions currently attached, via\nthe server's bouncer functionality, to your nickname. An administrator\ncan use this command to list another user's sessions.": "Sintaxis: $bSESSIONS [usuario]$b",
"Syntax: $bSAGET <account> <setting>$b\n\nSAGET queries the values of someone else's account settings. For more\ninformation on the settings and their possible values, see HELP SET.": "",
"Syntax: $bSAREGISTER <username> [password]$b\n\nSAREGISTER registers an account on someone else's behalf.\nThis is for use in configurations that require SASL for all connections;\nan administrator can set use this command to set up user accounts.": "",
"Syntax: $bSASET <account> <setting> <value>$b\n\nSASET modifies the values of someone else's account settings. For more\ninformation on the settings and their possible values, see HELP SET.": "",
"Syntax: $bSESSIONS [nickname]$b\n\nSESSIONS lists information about the sessions currently attached, via\nthe server's multiclient functionality, to your nickname. An administrator\ncan use this command to list another user's sessions.": "",
"Syntax: $bSUSPEND <nickname>$b\n\nSUSPEND disables an account and disconnects the associated clients.": "",
"Syntax: $bUNREGISTER <username> [code]$b\n\nUNREGISTER lets you delete your user account (or someone else's, if you're an\nIRC operator with the correct permissions). To prevent accidental\nunregistrations, a verification code is required; invoking the command without\na code will display the necessary code.": "Sintaxis: $bUNREGISTER <nombredeusuario>[código]$b\n\nUNREGISTER te permite eliminar tu cuenta de usuario (o la de otra persona, si eres un\nIRC con los permisos correctos). Para prevenir accidentes\nse requiere un código de verificación; invocando el comando sin\nun código mostrará el código necesario.",
"Syntax: $bVERIFY <username> <code>$b\n\nVERIFY lets you complete an account registration, if the server requires email\nor other verification.": "Sintaxis: $bVERIFY <nombredeusuario> <código>$b\n\nVERIFY le permite completar el registro de una cuenta, si el servidor requiere correo electrónico.\nu otra verificación."
"Syntax: $bUNSUSPEND <nickname>$b\n\nUNSUSPEND reverses a previous SUSPEND, restoring access to the account.": "",
"Syntax: $bVERIFY <username> <code>$b\n\nVERIFY lets you complete an account registration, if the server requires email\nor other verification.": "Sintaxis: $bVERIFY <nombredeusuario> <código>$b\n\nVERIFY le permite completar el registro de una cuenta, si el servidor requiere correo electrónico.\nu otra verificación.",
"You're not logged into an account": "No has iniciado sesión en una cuenta"
}

View File

@ -1,8 +1,8 @@
{
"= Help Topics =\n\nCommands:\n%[1]s\n\nRPL_ISUPPORT Tokens:\n%[2]s\n\nInformation:\n%[3]s": "= Help Topics =\n\nCommands:\n%[1]s\n\nRPL_ISUPPORT Tokens:\n%[2]s\n\nInformation:\n%[3]s",
"== Channel Modes ==\n\nOragono supports the following channel modes:\n\n +b | Client masks that are banned from the channel (e.g. *!*@127.0.0.1)\n +e | Client masks that are exempted from bans.\n +I | Client masks that are exempted from the invite-only flag.\n +i | Invite-only mode, only invited clients can join the channel.\n +k | Key required when joining the channel.\n +l | Client join limit for the channel.\n +m | Moderated mode, only privileged clients can talk on the channel.\n +n | No-outside-messages mode, only users that are on the channel can send\n | messages to it.\n +R | Only registered users can join the channel.\n +s | Secret mode, channel won't show up in /LIST or whois replies.\n +t | Only channel opers can modify the topic.\n\n= Prefixes =\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.": "== Channel Modes ==\n\nOragono supports the following channel modes:\n\n +b | Client masks that are banned from the channel (e.g. *!*@127.0.0.1)\n +e | Client masks that are exempted from bans.\n +I | Client masks that are exempted from the invite-only flag.\n +i | Invite-only mode, only invited clients can join the channel.\n +k | Key required when joining the channel.\n +l | Client join limit for the channel.\n +m | Moderated mode, only privileged clients can talk on the channel.\n +n | No-outside-messages mode, only users that are on the channel can send\n | messages to it.\n +R | Only registered users can join the channel.\n +s | Secret mode, channel won't show up in /LIST or whois replies.\n +t | Only channel opers can modify the topic.\n\n= Prefixes =\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.",
"== Channel Modes ==\n\nOragono supports the following channel modes:\n\n +b | Client masks that are banned from the channel (e.g. *!*@127.0.0.1)\n +e | Client masks that are exempted from bans.\n +I | Client masks that are exempted from the invite-only flag.\n +i | Invite-only mode, only invited clients can join the channel.\n +k | Key required when joining the channel.\n +l | Client join limit for the channel.\n +m | Moderated mode, only privileged clients can talk on the channel.\n +n | No-outside-messages mode, only users that are on the channel can send\n | messages to it.\n +R | Only registered users can join the channel.\n +s | Secret mode, channel won't show up in /LIST or whois replies.\n +t | Only channel opers can modify the topic.\n +E | Roleplaying commands are enabled in the channel.\n +C | Clients are blocked from sending CTCP messages in the channel.\n\n= Prefixes =\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.": "== Channel Modes ==\n\nOragono supports the following channel modes:\n\n +b | Client masks that are banned from the channel (e.g. *!*@127.0.0.1)\n +e | Client masks that are exempted from bans.\n +I | Client masks that are exempted from the invite-only flag.\n +i | Invite-only mode, only invited clients can join the channel.\n +k | Key required when joining the channel.\n +l | Client join limit for the channel.\n +m | Moderated mode, only privileged clients can talk on the channel.\n +n | No-outside-messages mode, only users that are on the channel can send\n | messages to it.\n +R | Only registered users can join the channel.\n +s | Secret mode, channel won't show up in /LIST or whois replies.\n +t | Only channel opers can modify the topic.\n +E | Roleplaying commands are enabled in the channel.\n +C | Clients are blocked from sending CTCP messages in the channel.\n\n= Prefixes =\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.",
"== Server Notice Masks ==\n\nOragono supports the following server notice masks for operators:\n\n a | Local announcements.\n c | Local client connections.\n j | Local channel actions.\n k | Local kills.\n n | Local nick changes.\n o | Local oper actions.\n q | Local quits.\n t | Local /STATS usage.\n u | Local client account actions.\n x | Local X-lines (DLINE/KLINE/etc).\n v | Local vhost changes.\n\nTo set a snomask, do this with your nickname:\n\n /MODE <nick> +s <chars>\n\nFor instance, this would set the kill, oper, account and xline snomasks on dan:\n\n /MODE dan +s koux": "== Server Notice Masks ==\n\nOragono supports the following server notice masks for operators:\n\n a | Local announcements.\n c | Local client connections.\n j | Local channel actions.\n k | Local kills.\n n | Local nick changes.\n o | Local oper actions.\n q | Local quits.\n t | Local /STATS usage.\n u | Local client account actions.\n x | Local X-lines (DLINE/KLINE/etc).\n v | Local vhost changes.\n\nTo set a snomask, do this with your nickname:\n\n /MODE <nick> +s <chars>\n\nFor instance, this would set the kill, oper, account and xline snomasks on dan:\n\n /MODE dan +s koux",
"== User Modes ==\n\nOragono supports the following user modes:\n\n +a | User is marked as being away. This mode is set with the /AWAY command.\n +i | User is marked as invisible (their channels are hidden from whois replies).\n +o | User is an IRC operator.\n +R | User only accepts messages from other registered users. \n +s | Server Notice Masks (see help with /HELPOP snomasks).\n +Z | User is connected via TLS.": "== User Modes ==\n\nOragono supports the following user modes:\n\n +a | User is marked as being away. This mode is set with the /AWAY command.\n +i | User is marked as invisible (their channels are hidden from whois replies).\n +o | User is an IRC operator.\n +R | User only accepts messages from other registered users. \n +s | Server Notice Masks (see help with /HELPOP snomasks).\n +Z | User is connected via TLS.",
"== User Modes ==\n\nOragono supports the following user modes:\n\n +a | User is marked as being away. This mode is set with the /AWAY command.\n +i | User is marked as invisible (their channels are hidden from whois replies).\n +o | User is an IRC operator.\n +R | User only accepts messages from other registered users. \n +s | Server Notice Masks (see help with /HELPOP snomasks).\n +Z | User is connected via TLS.\n +B | User is a bot.\n +E | User can receive roleplaying commands.\n +T | User is blocked from sending CTCP messages.": "== User Modes ==\n\nOragono supports the following user modes:\n\n +a | User is marked as being away. This mode is set with the /AWAY command.\n +i | User is marked as invisible (their channels are hidden from whois replies).\n +o | User is an IRC operator.\n +R | User only accepts messages from other registered users. \n +s | Server Notice Masks (see help with /HELPOP snomasks).\n +Z | User is connected via TLS.\n +B | User is a bot.\n +E | User can receive roleplaying commands.\n +T | User is blocked from sending CTCP messages.",
"@+client-only-tags TAGMSG <target>{,<target>}\n\nSends the given client-only tags to the given targets as a TAGMSG. See the IRCv3\nspecs for more info: http://ircv3.net/specs/core/message-tags-3.3.html": "@+client-only-tags TAGMSG <target>{,<target>}\n\nSends the given client-only tags to the given targets as a TAGMSG. See the IRCv3\nspecs for more info: http://ircv3.net/specs/core/message-tags-3.3.html",
"ACC LS\nACC REGISTER <accountname> [callback_namespace:]<callback> [cred_type] :<credential>\nACC VERIFY <accountname> <auth_code>\n\nUsed in account registration. See the relevant specs for more info:\nhttps://oragono.io/specs.html": "ACC LS\nACC REGISTER <accountname> [callback_namespace:]<callback> [cred_type] :<credential>\nACC VERIFY <accountname> <auth_code>\n\nUsed in account registration. See the relevant specs for more info:\nhttps://oragono.io/specs.html",
"AMBIANCE <target> <text to be sent>\n\nThe AMBIANCE command is used to send a scene notification to the given target.": "AMBIANCE <target> <text to be sent>\n\nThe AMBIANCE command is used to send a scene notification to the given target.",
@ -13,8 +13,10 @@
"CAP <subcommand> [:<capabilities>]\n\nUsed in capability negotiation. See the IRCv3 specs for more info:\nhttp://ircv3.net/specs/core/capability-negotiation-3.1.html\nhttp://ircv3.net/specs/core/capability-negotiation-3.2.html": "CAP <subcommand> [:<capabilities>]\n\nUsed in capability negotiation. See the IRCv3 specs for more info:\nhttp://ircv3.net/specs/core/capability-negotiation-3.1.html\nhttp://ircv3.net/specs/core/capability-negotiation-3.2.html",
"CHATHISTORY [params]\n\nCHATHISTORY is a history replay command associated with the IRCv3\nspecification draft/chathistory. See this document:\nhttps://github.com/ircv3/ircv3-specifications/pull/393": "CHATHISTORY [params]\n\nCHATHISTORY is a history replay command associated with the IRCv3\nspecification draft/chathistory. See this document:\nhttps://github.com/ircv3/ircv3-specifications/pull/393",
"DEBUG <option>\n\nProvides various debugging commands for the IRCd. <option> can be one of:\n\n* GCSTATS: Garbage control statistics.\n* NUMGOROUTINE: Number of goroutines in use.\n* STARTCPUPROFILE: Starts the CPU profiler.\n* STOPCPUPROFILE: Stops the CPU profiler.\n* PROFILEHEAP: Writes a memory profile.\n* CRASHSERVER: Crashes the server (for use in failover testing)": "DEBUG <option>\n\nProvides various debugging commands for the IRCd. <option> can be one of:\n\n* GCSTATS: Garbage control statistics.\n* NUMGOROUTINE: Number of goroutines in use.\n* STARTCPUPROFILE: Starts the CPU profiler.\n* STOPCPUPROFILE: Stops the CPU profiler.\n* PROFILEHEAP: Writes a memory profile.\n* CRASHSERVER: Crashes the server (for use in failover testing)",
"DEFCON [level]\n\nThe DEFCON system can disable server features at runtime, to mitigate\nspam or other hostile activity. It has five levels, which are cumulative\n(i.e., level 3 includes all restrictions from level 4 and so on):\n\n5: Normal operation\n4: No new account or channel registrations\n3: All users are +R; no changes to vhosts\n2: No new unauthenticated connections; all channels are +R\n1: No new connections except from localhost or other trusted IPs": "DEFCON [level]\n\nThe DEFCON system can disable server features at runtime, to mitigate\nspam or other hostile activity. It has five levels, which are cumulative\n(i.e., level 3 includes all restrictions from level 4 and so on):\n\n5: Normal operation\n4: No new account or channel registrations\n3: All users are +R; no changes to vhosts\n2: No new unauthenticated connections; all channels are +R\n1: No new connections except from localhost or other trusted IPs",
"DEOPER\n\nDEOPER removes the IRCop privileges granted to you by a successful /OPER.": "DEOPER\n\nDEOPER removes the IRCop privileges granted to you by a successful /OPER.",
"DLINE [ANDKILL] [MYSELF] [duration] <ip>/<net> [ON <server>] [reason [| oper reason]]\nDLINE LIST\n\nBans an IP address or network from connecting to the server. If the duration is\ngiven then only for that long. The reason is shown to the user themselves, but\neveryone else will see a standard message. The oper reason is shown to\noperators getting info about the DLINEs that exist.\n\nBans are saved across subsequent launches of the server.\n\n\"ANDKILL\" means that all matching clients are also removed from the server.\n\n\"MYSELF\" is required when the DLINE matches the address the person applying it is connected\nfrom. If \"MYSELF\" is not given, trying to DLINE yourself will result in an error.\n\n[duration] can be of the following forms:\n\t1y 12mo 31d 10h 8m 13s\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24\n\nON <server> specifies that the ban is to be set on that specific server.\n\n[reason] and [oper reason], if they exist, are separated by a vertical bar (|).\n\nIf \"DLINE LIST\" is sent, the server sends back a list of our current DLINEs.": "DLINE [ANDKILL] [MYSELF] [duration] <ip>/<net> [ON <server>] [reason [| oper reason]]\nDLINE LIST\n\nBans an IP address or network from connecting to the server. If the duration is\ngiven then only for that long. The reason is shown to the user themselves, but\neveryone else will see a standard message. The oper reason is shown to\noperators getting info about the DLINEs that exist.\n\nBans are saved across subsequent launches of the server.\n\n\"ANDKILL\" means that all matching clients are also removed from the server.\n\n\"MYSELF\" is required when the DLINE matches the address the person applying it is connected\nfrom. If \"MYSELF\" is not given, trying to DLINE yourself will result in an error.\n\n[duration] can be of the following forms:\n\t1y 12mo 31d 10h 8m 13s\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24\n\nON <server> specifies that the ban is to be set on that specific server.\n\n[reason] and [oper reason], if they exist, are separated by a vertical bar (|).\n\nIf \"DLINE LIST\" is sent, the server sends back a list of our current DLINEs.",
"EXTJWT <target> [service_name]\n\nGet a JSON Web Token for target (either * or a channel name).": "EXTJWT <target> [service_name]\n\nGet a JSON Web Token for target (either * or a channel name).",
"HELP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.": "HELP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.",
"HELPOP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.": "HELPOP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.",
"HISTORY <target> [limit]\n\nReplay message history. <target> can be a channel name, \"me\" to replay direct\nmessage history, or a nickname to replay another client's direct message\nhistory (they must be logged into the same account as you). [limit] can be\neither an integer (the maximum number of messages to replay), or a time\nduration like 10m or 1h (the time window within which to replay messages).": "HISTORY <target> [limit]\n\nReplay message history. <target> can be a channel name, \"me\" to replay direct\nmessage history, or a nickname to replay another client's direct message\nhistory (they must be logged into the same account as you). [limit] can be\neither an integer (the maximum number of messages to replay), or a time\nduration like 10m or 1h (the time window within which to replay messages).",
@ -53,12 +55,14 @@
"SANICK <currentnick> <newnick>\n\nGives the given user a new nickname.": "SANICK <currentnick> <newnick>\n\nGives the given user a new nickname.",
"SCENE <target> <text to be sent>\n\nThe SCENE command is used to send a scene notification to the given target.": "SCENE <target> <text to be sent>\n\nThe SCENE command is used to send a scene notification to the given target.",
"SETNAME <realname>\n\nThe SETNAME command updates the realname to be the newly-given one.": "SETNAME <realname>\n\nThe SETNAME command updates the realname to be the newly-given one.",
"SUMMON [parameters]\n\nThe SUMMON command is not implemented.": "SUMMON [parameters]\n\nThe SUMMON command is not implemented.",
"TIME [server]\n\nShows the time of the current, or the given, server.": "TIME [server]\n\nShows the time of the current, or the given, server.",
"TOPIC <channel> [topic]\n\nIf [topic] is given, sets the topic in the channel to that. If [topic] is not\ngiven, views the current topic on the channel.": "TOPIC <channel> [topic]\n\nIf [topic] is given, sets the topic in the channel to that. If [topic] is not\ngiven, views the current topic on the channel.",
"UNDLINE <ip>/<net>\n\nRemoves an existing ban on an IP address or a network.\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24": "UNDLINE <ip>/<net>\n\nRemoves an existing ban on an IP address or a network.\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24",
"UNKLINE <mask>\n\nRemoves an existing ban on a mask.\n\nFor example:\n\tdan\n\tdan!5*@127.*": "UNKLINE <mask>\n\nRemoves an existing ban on a mask.\n\nFor example:\n\tdan\n\tdan!5*@127.*",
"USER <username> 0 * <realname>\n\nUsed in connection registration, sets your username and realname to the given\nvalues (though your username may also be looked up with Ident).": "USER <username> 0 * <realname>\n\nUsed in connection registration, sets your username and realname to the given\nvalues (though your username may also be looked up with Ident).",
"USERHOST <nickname>{ <nickname>}\n\t\t\nShows information about the given users. Takes up to 10 nicknames.": "USERHOST <nickname>{ <nickname>}\n\t\t\nShows information about the given users. Takes up to 10 nicknames.",
"USERS [parameters]\n\nThe USERS command is not implemented.": "USERS [parameters]\n\nThe USERS command is not implemented.",
"VERSION [server]\n\nViews the version of software and the RPL_ISUPPORT tokens for the given server.": "VERSION [server]\n\nViews the version of software and the RPL_ISUPPORT tokens for the given server.",
"WEBIRC <password> <gateway> <hostname> <ip> [:<flags>]\n\nUsed by web<->IRC gateways and bouncers, the WEBIRC command allows gateways to\npass-through the real IP addresses of clients:\nircv3.net/specs/extensions/webirc.html\n\n<flags> is a list of space-separated strings indicating various details about\nthe connection from the client to the gateway, such as:\n\n- tls: this flag indicates that the client->gateway connection is secure": "WEBIRC <password> <gateway> <hostname> <ip> [:<flags>]\n\nUsed by web<->IRC gateways and bouncers, the WEBIRC command allows gateways to\npass-through the real IP addresses of clients:\nircv3.net/specs/extensions/webirc.html\n\n<flags> is a list of space-separated strings indicating various details about\nthe connection from the client to the gateway, such as:\n\n- tls: this flag indicates that the client->gateway connection is secure",
"WHO <name> [o]\n\nReturns information for the given user.": "WHO <name> [o]\n\nReturns information for the given user.",

View File

@ -86,6 +86,7 @@
"Channel renamed": "Channel renamed",
"Channel renamed: %s": "Channel renamed: %s",
"Channels with persistent history cannot be renamed": "Channels with persistent history cannot be renamed",
"Client %s is always-on and cannot be fully removed by /KILL; consider /NS SUSPEND instead": "Client %s is always-on and cannot be fully removed by /KILL; consider /NS SUSPEND instead",
"Client reconnected": "Client reconnected",
"Client reconnected (message history may have been lost)": "Client reconnected (message history may have been lost)",
"Client reconnected (up to %d seconds of message history lost)": "Client reconnected (up to %d seconds of message history lost)",
@ -95,6 +96,7 @@
"Could not accept ownership of channel %s": "Could not accept ownership of channel %s",
"Could not delete message": "Could not delete message",
"Could not find given client": "Could not find given client",
"Could not generate EXTJWT token": "Could not generate EXTJWT token",
"Could not look up account name, proceeding anyway": "Could not look up account name, proceeding anyway",
"Could not parse IP address or CIDR network": "Could not parse IP address or CIDR network",
"Could not register": "Could not register",
@ -106,9 +108,12 @@
"Could not transfer channel": "Could not transfer channel",
"Could not ungroup nick": "Could not ungroup nick",
"Created at: %s": "Created at: %s",
"Current DEFCON level is %d": "Current DEFCON level is %d",
"Current global users %[1]s, max %[2]s": "Current global users %[1]s, max %[2]s",
"Current local users %[1]s, max %[2]s": "Current local users %[1]s, max %[2]s",
"Data export for %[1]s completed and written to %[2]s": "Data export for %[1]s completed and written to %[2]s",
"Device ID: %s": "Device ID: %s",
"Direct messages from unregistered users are temporarily restricted": "Direct messages from unregistered users are temporarily restricted",
"End of /HELPOP": "End of /HELPOP",
"End of /INFO": "End of /INFO",
"End of /WHOIS list": "End of /WHOIS list",
@ -154,6 +159,7 @@
"Insufficient privileges": "Insufficient privileges",
"Internal error": "Internal error",
"Invalid CAP subcommand": "Invalid CAP subcommand",
"Invalid DEFCON parameter": "Invalid DEFCON parameter",
"Invalid account name": "Invalid account name",
"Invalid certificate fingerprint": "Invalid certificate fingerprint",
"Invalid channel name": "Invalid channel name",
@ -165,6 +171,7 @@
"Invalid params": "Invalid params",
"Invalid regex": "Invalid regex",
"Invalid vhost": "Invalid vhost",
"It was built from git hash %s.": "It was built from git hash %s.",
"It was rejected for reason: %s": "It was rejected for reason: %s",
"JOIN 0 is not allowed": "JOIN 0 is not allowed",
"Language %s is not supported by this server": "Language %s is not supported by this server",
@ -173,6 +180,7 @@
"MOTD File is missing": "MOTD File is missing",
"Malformed username": "Malformed username",
"Mask isn't valid": "Mask isn't valid",
"Message rejected for containing invalid UTF-8": "Message rejected for containing invalid UTF-8",
"Messages could not be retrieved": "Messages could not be retrieved",
"Multiclient functionality is currently disabled for your account": "Multiclient functionality is currently disabled for your account",
"Multiclient functionality is currently disabled for your account, but you can opt in": "Multiclient functionality is currently disabled for your account, but you can opt in",
@ -189,6 +197,7 @@
"No such channel": "No such channel",
"No such module [%s]": "No such module [%s]",
"No such nick": "No such nick",
"No such service": "No such service",
"No such setting": "No such setting",
"No text to send": "No text to send",
"No topic is set": "No topic is set",
@ -207,6 +216,7 @@
"Purge reason: %s": "Purge reason: %s",
"Purged at: %s": "Purged at: %s",
"Purged by operator: %s": "Purged by operator: %s",
"Realname is not valid": "Realname is not valid",
"Received malformed line": "Received malformed line",
"Registered at: %s": "Registered at: %s",
"Registered channel: %s": "Registered channel: %s",
@ -228,6 +238,7 @@
"SASL authentication failed: Passphrase too long": "SASL authentication failed: Passphrase too long",
"SASL authentication failed: authcid and authzid should be the same": "SASL authentication failed: authcid and authzid should be the same",
"SASL message too long": "SASL message too long",
"SUMMON has been disabled": "SUMMON has been disabled",
"Server notice masks": "Server notice masks",
"Session %d (currently attached session):": "Session %d (currently attached session):",
"Session %d:": "Session %d:",
@ -247,9 +258,11 @@
"Successfully registered account %s": "Successfully registered account %s",
"Successfully rejected vhost request for %s": "Successfully rejected vhost request for %s",
"Successfully reset channel access": "Successfully reset channel access",
"Successfully set persistent mode %s%s on %s": "Successfully set persistent mode %s%s on %s",
"Successfully set persistent mode %[1]s on %[2]s": "Successfully set persistent mode %[1]s on %[2]s",
"Successfully set vhost": "Successfully set vhost",
"Successfully suspended account %s": "Successfully suspended account %s",
"Successfully transferred channel %[1]s to account %[2]s": "Successfully transferred channel %[1]s to account %[2]s",
"Successfully un-suspended account %s": "Successfully un-suspended account %s",
"Successfully ungrouped nick %s with your account": "Successfully ungrouped nick %s with your account",
"Successfully unpurged channel %s from the server": "Successfully unpurged channel %s from the server",
"Successfully unregistered account %s": "Successfully unregistered account %s",
@ -272,6 +285,7 @@
"This ban matches you. To KLINE yourself, you must use the command: /KLINE MYSELF <arguments>": "This ban matches you. To KLINE yourself, you must use the command: /KLINE MYSELF <arguments>",
"This command has been disabled by the server administrators": "This command has been disabled by the server administrators",
"This feature has been disabled by the server administrators": "This feature has been disabled by the server administrators",
"This is Oragono version %s.": "This is Oragono version %s.",
"This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.": "This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.",
"This server requires that you wait %v after connecting before you can use /LIST. You have %v left.": "This server requires that you wait %v after connecting before you can use /LIST. You have %v left.",
"This server was created %s": "This server was created %s",
@ -285,10 +299,12 @@
"Transfer of channel %[1]s to account %[2]s succeeded, pending acceptance": "Transfer of channel %[1]s to account %[2]s succeeded, pending acceptance",
"Translators:": "Translators:",
"Try again later": "Try again later",
"USERS has been disabled": "USERS has been disabled",
"Unknown command": "Unknown command",
"Unknown command. To see available commands, run: /%s HELP": "Unknown command. To see available commands, run: /%s HELP",
"Unknown subcommand": "Unknown subcommand",
"Unrecognized DEBUG subcommand": "Unrecognized DEBUG subcommand",
"Usage: REGISTER <passphrase> [email]": "Usage: REGISTER <passphrase> [email]",
"User %s is no longer allowed to use vhosts": "User %s is no longer allowed to use vhosts",
"User %s is now allowed to use vhosts": "User %s is now allowed to use vhosts",
"User doesn't have roleplaying mode enabled": "User doesn't have roleplaying mode enabled",
@ -317,13 +333,11 @@
"You have been marked as being away": "You have been marked as being away",
"You have been offered ownership of channel %[1]s. To accept, /CS TRANSFER ACCEPT %[1]s": "You have been offered ownership of channel %[1]s. To accept, /CS TRANSFER ACCEPT %[1]s",
"You have enabled autoreplay of missed messages, but you can't receive them because your client isn't set to always-on": "You have enabled autoreplay of missed messages, but you can't receive them because your client isn't set to always-on",
"You have joined too many channels": "You have joined too many channels",
"You have sent too many registration messages": "You have sent too many registration messages",
"You have too many nicks reserved already (you can remove some with /NS DROP)": "You have too many nicks reserved already (you can remove some with /NS DROP)",
"You may not reregister": "You may not reregister",
"You must be an oper on the channel to register it": "You must be an oper on the channel to register it",
"You must be connected with TLS and a client certificate to do this": "You must be connected with TLS and a client certificate to do this",
"You must be registered to join that channel": "You must be registered to join that channel",
"You must have rehash permissions in order to execute DEBUG CRASHSERVER": "You must have rehash permissions in order to execute DEBUG CRASHSERVER",
"You must log in with SASL to join this server": "You must log in with SASL to join this server",
"You must specify an account": "You must specify an account",

View File

@ -6,7 +6,7 @@
"$bCERT$b controls a user account's certificate fingerprints": "$bCERT$b controls a user account's certificate fingerprints",
"$bDM-HISTORY$b\n'dm-history' is only effective for always-on clients. It lets you control\nhow the history of your direct messages is stored. Your options are:\n1. 'off' [no history]\n2. 'ephemeral' [a limited amount of temporary history, not stored on disk]\n3. 'on' [history stored in a permanent database, if available]\n4. 'default' [use the server default]": "$bDM-HISTORY$b\n'dm-history' is only effective for always-on clients. It lets you control\nhow the history of your direct messages is stored. Your options are:\n1. 'off' [no history]\n2. 'ephemeral' [a limited amount of temporary history, not stored on disk]\n3. 'on' [history stored in a permanent database, if available]\n4. 'default' [use the server default]",
"$bDROP$b de-links your current (or the given) nickname from your user account.": "$bDROP$b de-links your current (or the given) nickname from your user account.",
"$bENFORCE$b\n'enforce' lets you specify a custom enforcement mechanism for your registered\nnicknames. Your options are:\n1. 'none' [no enforcement, overriding the server default]\n2. 'timeout' [anyone using the nick must authenticate before a deadline,\n or else they will be renamed]\n3. 'strict' [you must already be authenticated to use the nick]\n4. 'default' [use the server default]": "$bENFORCE$b\n'enforce' lets you specify a custom enforcement mechanism for your registered\nnicknames. Your options are:\n1. 'none' [no enforcement, overriding the server default]\n2. 'timeout' [anyone using the nick must authenticate before a deadline,\n or else they will be renamed]\n3. 'strict' [you must already be authenticated to use the nick]\n4. 'default' [use the server default]",
"$bENFORCE$b\n'enforce' lets you specify a custom enforcement mechanism for your registered\nnicknames. Your options are:\n1. 'none' [no enforcement, overriding the server default]\n2. 'strict' [you must already be authenticated to use the nick]\n3. 'default' [use the server default]": "$bENFORCE$b\n'enforce' lets you specify a custom enforcement mechanism for your registered\nnicknames. Your options are:\n1. 'none' [no enforcement, overriding the server default]\n2. 'strict' [you must already be authenticated to use the nick]\n3. 'default' [use the server default]",
"$bERASE$b erases all records of an account, allowing reuse.": "$bERASE$b erases all records of an account, allowing reuse.",
"$bGET$b queries the current values of your account settings": "$bGET$b queries the current values of your account settings",
"$bGHOST$b reclaims your nickname.": "$bGHOST$b reclaims your nickname.",
@ -24,7 +24,9 @@
"$bSASET$b modifies another user's account settings": "$bSASET$b modifies another user's account settings",
"$bSESSIONS$b lists the sessions attached to a nickname.": "$bSESSIONS$b lists the sessions attached to a nickname.",
"$bSET$b modifies your account settings": "$bSET$b modifies your account settings",
"$bSUSPEND$b disables an account and disconnects the clients": "$bSUSPEND$b disables an account and disconnects the clients",
"$bUNREGISTER$b lets you delete your user account.": "$bUNREGISTER$b lets you delete your user account.",
"$bUNSUSPEND$b restores access to a suspended account": "$bUNSUSPEND$b restores access to a suspended account",
"$bVERIFY$b lets you complete account registration.": "$bVERIFY$b lets you complete account registration.",
"Insufficient privileges": "Insufficient privileges",
"Invalid parameters": "Invalid parameters",
@ -49,8 +51,9 @@
"Syntax: $bSAREGISTER <username> [password]$b\n\nSAREGISTER registers an account on someone else's behalf.\nThis is for use in configurations that require SASL for all connections;\nan administrator can set use this command to set up user accounts.": "Syntax: $bSAREGISTER <username> [password]$b\n\nSAREGISTER registers an account on someone else's behalf.\nThis is for use in configurations that require SASL for all connections;\nan administrator can set use this command to set up user accounts.",
"Syntax: $bSASET <account> <setting> <value>$b\n\nSASET modifies the values of someone else's account settings. For more\ninformation on the settings and their possible values, see HELP SET.": "Syntax: $bSASET <account> <setting> <value>$b\n\nSASET modifies the values of someone else's account settings. For more\ninformation on the settings and their possible values, see HELP SET.",
"Syntax: $bSESSIONS [nickname]$b\n\nSESSIONS lists information about the sessions currently attached, via\nthe server's multiclient functionality, to your nickname. An administrator\ncan use this command to list another user's sessions.": "Syntax: $bSESSIONS [nickname]$b\n\nSESSIONS lists information about the sessions currently attached, via\nthe server's multiclient functionality, to your nickname. An administrator\ncan use this command to list another user's sessions.",
"Syntax: $bSUSPEND <nickname>$b\n\nSUSPEND disables an account and disconnects the associated clients.": "Syntax: $bSUSPEND <nickname>$b\n\nSUSPEND disables an account and disconnects the associated clients.",
"Syntax: $bUNREGISTER <username> [code]$b\n\nUNREGISTER lets you delete your user account (or someone else's, if you're an\nIRC operator with the correct permissions). To prevent accidental\nunregistrations, a verification code is required; invoking the command without\na code will display the necessary code.": "Syntax: $bUNREGISTER <username> [code]$b\n\nUNREGISTER lets you delete your user account (or someone else's, if you're an\nIRC operator with the correct permissions). To prevent accidental\nunregistrations, a verification code is required; invoking the command without\na code will display the necessary code.",
"Syntax: $bUNSUSPEND <nickname>$b\n\nUNSUSPEND reverses a previous SUSPEND, restoring access to the account.": "Syntax: $bUNSUSPEND <nickname>$b\n\nUNSUSPEND reverses a previous SUSPEND, restoring access to the account.",
"Syntax: $bVERIFY <username> <code>$b\n\nVERIFY lets you complete an account registration, if the server requires email\nor other verification.": "Syntax: $bVERIFY <username> <code>$b\n\nVERIFY lets you complete an account registration, if the server requires email\nor other verification.",
"This nickname is reserved. Please login within %v (using $b/msg NickServ IDENTIFY <password>$b or SASL), or switch to a different nickname.": "This nickname is reserved. Please login within %v (using $b/msg NickServ IDENTIFY <password>$b or SASL), or switch to a different nickname.",
"You're not logged into an account": "You're not logged into an account",
"You're not logged into an account": "You're not logged into an account"
}

View File

@ -1,8 +1,8 @@
{
"= Help Topics =\n\nCommands:\n%[1]s\n\nRPL_ISUPPORT Tokens:\n%[2]s\n\nInformation:\n%[3]s": "",
"== Channel Modes ==\n\nOragono supports the following channel modes:\n\n +b | Client masks that are banned from the channel (e.g. *!*@127.0.0.1)\n +e | Client masks that are exempted from bans.\n +I | Client masks that are exempted from the invite-only flag.\n +i | Invite-only mode, only invited clients can join the channel.\n +k | Key required when joining the channel.\n +l | Client join limit for the channel.\n +m | Moderated mode, only privileged clients can talk on the channel.\n +n | No-outside-messages mode, only users that are on the channel can send\n | messages to it.\n +R | Only registered users can join the channel.\n +s | Secret mode, channel won't show up in /LIST or whois replies.\n +t | Only channel opers can modify the topic.\n\n= Prefixes =\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.": "",
"== Channel Modes ==\n\nOragono supports the following channel modes:\n\n +b | Client masks that are banned from the channel (e.g. *!*@127.0.0.1)\n +e | Client masks that are exempted from bans.\n +I | Client masks that are exempted from the invite-only flag.\n +i | Invite-only mode, only invited clients can join the channel.\n +k | Key required when joining the channel.\n +l | Client join limit for the channel.\n +m | Moderated mode, only privileged clients can talk on the channel.\n +n | No-outside-messages mode, only users that are on the channel can send\n | messages to it.\n +R | Only registered users can join the channel.\n +s | Secret mode, channel won't show up in /LIST or whois replies.\n +t | Only channel opers can modify the topic.\n +E | Roleplaying commands are enabled in the channel.\n +C | Clients are blocked from sending CTCP messages in the channel.\n\n= Prefixes =\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.": "",
"== Server Notice Masks ==\n\nOragono supports the following server notice masks for operators:\n\n a | Local announcements.\n c | Local client connections.\n j | Local channel actions.\n k | Local kills.\n n | Local nick changes.\n o | Local oper actions.\n q | Local quits.\n t | Local /STATS usage.\n u | Local client account actions.\n x | Local X-lines (DLINE/KLINE/etc).\n v | Local vhost changes.\n\nTo set a snomask, do this with your nickname:\n\n /MODE <nick> +s <chars>\n\nFor instance, this would set the kill, oper, account and xline snomasks on dan:\n\n /MODE dan +s koux": "",
"== User Modes ==\n\nOragono supports the following user modes:\n\n +a | User is marked as being away. This mode is set with the /AWAY command.\n +i | User is marked as invisible (their channels are hidden from whois replies).\n +o | User is an IRC operator.\n +R | User only accepts messages from other registered users. \n +s | Server Notice Masks (see help with /HELPOP snomasks).\n +Z | User is connected via TLS.": "",
"== User Modes ==\n\nOragono supports the following user modes:\n\n +a | User is marked as being away. This mode is set with the /AWAY command.\n +i | User is marked as invisible (their channels are hidden from whois replies).\n +o | User is an IRC operator.\n +R | User only accepts messages from other registered users. \n +s | Server Notice Masks (see help with /HELPOP snomasks).\n +Z | User is connected via TLS.\n +B | User is a bot.\n +E | User can receive roleplaying commands.\n +T | User is blocked from sending CTCP messages.": "",
"@+client-only-tags TAGMSG <target>{,<target>}\n\nSends the given client-only tags to the given targets as a TAGMSG. See the IRCv3\nspecs for more info: http://ircv3.net/specs/core/message-tags-3.3.html": "",
"ACC LS\nACC REGISTER <accountname> [callback_namespace:]<callback> [cred_type] :<credential>\nACC VERIFY <accountname> <auth_code>\n\nUsed in account registration. See the relevant specs for more info:\nhttps://oragono.io/specs.html": "",
"AMBIANCE <target> <text to be sent>\n\nThe AMBIANCE command is used to send a scene notification to the given target.": "",
@ -13,8 +13,10 @@
"CAP <subcommand> [:<capabilities>]\n\nUsed in capability negotiation. See the IRCv3 specs for more info:\nhttp://ircv3.net/specs/core/capability-negotiation-3.1.html\nhttp://ircv3.net/specs/core/capability-negotiation-3.2.html": "",
"CHATHISTORY [params]\n\nCHATHISTORY is a history replay command associated with the IRCv3\nspecification draft/chathistory. See this document:\nhttps://github.com/ircv3/ircv3-specifications/pull/393": "",
"DEBUG <option>\n\nProvides various debugging commands for the IRCd. <option> can be one of:\n\n* GCSTATS: Garbage control statistics.\n* NUMGOROUTINE: Number of goroutines in use.\n* STARTCPUPROFILE: Starts the CPU profiler.\n* STOPCPUPROFILE: Stops the CPU profiler.\n* PROFILEHEAP: Writes a memory profile.\n* CRASHSERVER: Crashes the server (for use in failover testing)": "",
"DEFCON [level]\n\nThe DEFCON system can disable server features at runtime, to mitigate\nspam or other hostile activity. It has five levels, which are cumulative\n(i.e., level 3 includes all restrictions from level 4 and so on):\n\n5: Normal operation\n4: No new account or channel registrations\n3: All users are +R; no changes to vhosts\n2: No new unauthenticated connections; all channels are +R\n1: No new connections except from localhost or other trusted IPs": "",
"DEOPER\n\nDEOPER removes the IRCop privileges granted to you by a successful /OPER.": "",
"DLINE [ANDKILL] [MYSELF] [duration] <ip>/<net> [ON <server>] [reason [| oper reason]]\nDLINE LIST\n\nBans an IP address or network from connecting to the server. If the duration is\ngiven then only for that long. The reason is shown to the user themselves, but\neveryone else will see a standard message. The oper reason is shown to\noperators getting info about the DLINEs that exist.\n\nBans are saved across subsequent launches of the server.\n\n\"ANDKILL\" means that all matching clients are also removed from the server.\n\n\"MYSELF\" is required when the DLINE matches the address the person applying it is connected\nfrom. If \"MYSELF\" is not given, trying to DLINE yourself will result in an error.\n\n[duration] can be of the following forms:\n\t1y 12mo 31d 10h 8m 13s\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24\n\nON <server> specifies that the ban is to be set on that specific server.\n\n[reason] and [oper reason], if they exist, are separated by a vertical bar (|).\n\nIf \"DLINE LIST\" is sent, the server sends back a list of our current DLINEs.": "",
"EXTJWT <target> [service_name]\n\nGet a JSON Web Token for target (either * or a channel name).": "",
"HELP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.": "",
"HELPOP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.": "",
"HISTORY <target> [limit]\n\nReplay message history. <target> can be a channel name, \"me\" to replay direct\nmessage history, or a nickname to replay another client's direct message\nhistory (they must be logged into the same account as you). [limit] can be\neither an integer (the maximum number of messages to replay), or a time\nduration like 10m or 1h (the time window within which to replay messages).": "",
@ -53,12 +55,14 @@
"SANICK <currentnick> <newnick>\n\nGives the given user a new nickname.": "",
"SCENE <target> <text to be sent>\n\nThe SCENE command is used to send a scene notification to the given target.": "",
"SETNAME <realname>\n\nThe SETNAME command updates the realname to be the newly-given one.": "",
"SUMMON [parameters]\n\nThe SUMMON command is not implemented.": "",
"TIME [server]\n\nShows the time of the current, or the given, server.": "",
"TOPIC <channel> [topic]\n\nIf [topic] is given, sets the topic in the channel to that. If [topic] is not\ngiven, views the current topic on the channel.": "",
"UNDLINE <ip>/<net>\n\nRemoves an existing ban on an IP address or a network.\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24": "",
"UNKLINE <mask>\n\nRemoves an existing ban on a mask.\n\nFor example:\n\tdan\n\tdan!5*@127.*": "",
"USER <username> 0 * <realname>\n\nUsed in connection registration, sets your username and realname to the given\nvalues (though your username may also be looked up with Ident).": "",
"USERHOST <nickname>{ <nickname>}\n\t\t\nShows information about the given users. Takes up to 10 nicknames.": "",
"USERS [parameters]\n\nThe USERS command is not implemented.": "",
"VERSION [server]\n\nViews the version of software and the RPL_ISUPPORT tokens for the given server.": "",
"WEBIRC <password> <gateway> <hostname> <ip> [:<flags>]\n\nUsed by web<->IRC gateways and bouncers, the WEBIRC command allows gateways to\npass-through the real IP addresses of clients:\nircv3.net/specs/extensions/webirc.html\n\n<flags> is a list of space-separated strings indicating various details about\nthe connection from the client to the gateway, such as:\n\n- tls: this flag indicates that the client->gateway connection is secure": "",
"WHO <name> [o]\n\nReturns information for the given user.": "",

View File

@ -86,6 +86,7 @@
"Channel renamed": "",
"Channel renamed: %s": "",
"Channels with persistent history cannot be renamed": "",
"Client %s is always-on and cannot be fully removed by /KILL; consider /NS SUSPEND instead": "",
"Client reconnected": "",
"Client reconnected (message history may have been lost)": "",
"Client reconnected (up to %d seconds of message history lost)": "",
@ -95,6 +96,7 @@
"Could not accept ownership of channel %s": "",
"Could not delete message": "",
"Could not find given client": "",
"Could not generate EXTJWT token": "",
"Could not look up account name, proceeding anyway": "",
"Could not parse IP address or CIDR network": "",
"Could not register": "",
@ -106,9 +108,12 @@
"Could not transfer channel": "",
"Could not ungroup nick": "",
"Created at: %s": "",
"Current DEFCON level is %d": "",
"Current global users %[1]s, max %[2]s": "",
"Current local users %[1]s, max %[2]s": "",
"Data export for %[1]s completed and written to %[2]s": "",
"Device ID: %s": "",
"Direct messages from unregistered users are temporarily restricted": "",
"End of /HELPOP": "",
"End of /INFO": "",
"End of /WHOIS list": "",
@ -154,6 +159,7 @@
"Insufficient privileges": "",
"Internal error": "",
"Invalid CAP subcommand": "",
"Invalid DEFCON parameter": "",
"Invalid account name": "",
"Invalid certificate fingerprint": "",
"Invalid channel name": "",
@ -165,6 +171,7 @@
"Invalid params": "",
"Invalid regex": "",
"Invalid vhost": "",
"It was built from git hash %s.": "",
"It was rejected for reason: %s": "",
"JOIN 0 is not allowed": "",
"Language %s is not supported by this server": "",
@ -173,6 +180,7 @@
"MOTD File is missing": "",
"Malformed username": "",
"Mask isn't valid": "",
"Message rejected for containing invalid UTF-8": "",
"Messages could not be retrieved": "",
"Multiclient functionality is currently disabled for your account": "",
"Multiclient functionality is currently disabled for your account, but you can opt in": "",
@ -189,6 +197,7 @@
"No such channel": "",
"No such module [%s]": "",
"No such nick": "",
"No such service": "",
"No such setting": "",
"No text to send": "",
"No topic is set": "",
@ -207,6 +216,7 @@
"Purge reason: %s": "",
"Purged at: %s": "",
"Purged by operator: %s": "",
"Realname is not valid": "",
"Received malformed line": "",
"Registered at: %s": "",
"Registered channel: %s": "",
@ -228,6 +238,7 @@
"SASL authentication failed: Passphrase too long": "",
"SASL authentication failed: authcid and authzid should be the same": "",
"SASL message too long": "",
"SUMMON has been disabled": "",
"Server notice masks": "",
"Session %d (currently attached session):": "",
"Session %d:": "",
@ -247,9 +258,11 @@
"Successfully registered account %s": "",
"Successfully rejected vhost request for %s": "",
"Successfully reset channel access": "",
"Successfully set persistent mode %s%s on %s": "",
"Successfully set persistent mode %[1]s on %[2]s": "",
"Successfully set vhost": "",
"Successfully suspended account %s": "",
"Successfully transferred channel %[1]s to account %[2]s": "",
"Successfully un-suspended account %s": "",
"Successfully ungrouped nick %s with your account": "",
"Successfully unpurged channel %s from the server": "",
"Successfully unregistered account %s": "",
@ -272,6 +285,7 @@
"This ban matches you. To KLINE yourself, you must use the command: /KLINE MYSELF <arguments>": "",
"This command has been disabled by the server administrators": "",
"This feature has been disabled by the server administrators": "",
"This is Oragono version %s.": "",
"This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.": "",
"This server requires that you wait %v after connecting before you can use /LIST. You have %v left.": "",
"This server was created %s": "",
@ -285,10 +299,12 @@
"Transfer of channel %[1]s to account %[2]s succeeded, pending acceptance": "",
"Translators:": "",
"Try again later": "",
"USERS has been disabled": "",
"Unknown command": "",
"Unknown command. To see available commands, run: /%s HELP": "",
"Unknown subcommand": "",
"Unrecognized DEBUG subcommand": "",
"Usage: REGISTER <passphrase> [email]": "",
"User %s is no longer allowed to use vhosts": "",
"User %s is now allowed to use vhosts": "",
"User doesn't have roleplaying mode enabled": "",
@ -317,13 +333,11 @@
"You have been marked as being away": "",
"You have been offered ownership of channel %[1]s. To accept, /CS TRANSFER ACCEPT %[1]s": "",
"You have enabled autoreplay of missed messages, but you can't receive them because your client isn't set to always-on": "",
"You have joined too many channels": "",
"You have sent too many registration messages": "",
"You have too many nicks reserved already (you can remove some with /NS DROP)": "",
"You may not reregister": "",
"You must be an oper on the channel to register it": "",
"You must be connected with TLS and a client certificate to do this": "",
"You must be registered to join that channel": "",
"You must have rehash permissions in order to execute DEBUG CRASHSERVER": "",
"You must log in with SASL to join this server": "",
"You must specify an account": "",

View File

@ -1,16 +1,59 @@
{
"NickServ lets you register and login to an account.\n\nTo see in-depth help for a specific NickServ command, try:\n $b/NS HELP <command>$b\n\nHere are the commands you can use:\n%s": "",
"$bALWAYS-ON$b\n'always-on' controls whether your nickname/identity will remain active\neven while you are disconnected from the server. Your options are 'true',\n'false', and 'default' (use the server default value).": "",
"$bAUTO-AWAY$b\n'auto-away' is only effective for always-on clients. If enabled, you will\nautomatically be marked away when all your sessions are disconnected, and\nautomatically return from away when you connect again.": "",
"$bAUTOREPLAY-LINES$b\n'autoreplay-lines' controls the number of lines of channel history that will\nbe replayed to you automatically when joining a channel. Your options are any\npositive number, 0 to disable the feature, and 'default' to use the server\ndefault.": "",
"$bAUTOREPLAY-MISSED$b\n'autoreplay-missed' is only effective for always-on clients. If enabled,\nif you have at most one active session, the server will remember the time\nyou disconnect and then replay missed messages to you when you reconnect.\nYour options are 'on' and 'off'.": "",
"$bCERT$b controls a user account's certificate fingerprints": "",
"$bDM-HISTORY$b\n'dm-history' is only effective for always-on clients. It lets you control\nhow the history of your direct messages is stored. Your options are:\n1. 'off' [no history]\n2. 'ephemeral' [a limited amount of temporary history, not stored on disk]\n3. 'on' [history stored in a permanent database, if available]\n4. 'default' [use the server default]": "",
"$bDROP$b de-links your current (or the given) nickname from your user account.": "",
"$bENFORCE$b\n'enforce' lets you specify a custom enforcement mechanism for your registered\nnicknames. Your options are:\n1. 'none' [no enforcement, overriding the server default]\n2. 'strict' [you must already be authenticated to use the nick]\n3. 'default' [use the server default]": "",
"$bERASE$b erases all records of an account, allowing reuse.": "",
"$bGET$b queries the current values of your account settings": "",
"$bGHOST$b reclaims your nickname.": "",
"$bGROUP$b links your current nickname to your user account.": "",
"$bIDENTIFY$b lets you login to your account.": "",
"$bINFO$b gives you information on a user account.": "",
"$bLIST$b searches the list of registered nicknames.": "",
"$bMULTICLIENT$b\nIf 'multiclient' is enabled and you are already logged in and using a nick, a\nsecond client of yours that authenticates with SASL and requests the same nick\nis allowed to attach to the nick as well (this is comparable to the behavior\nof IRC \"bouncers\" like ZNC). Your options are 'on' (allow this behavior),\n'off' (disallow it), and 'default' (use the server default value).": "",
"$bPASSWD$b lets you change your password.": "",
"$bREGISTER$b lets you register a user account.": "",
"$bREPLAY-JOINS$b\n'replay-joins' controls whether replayed channel history will include\nlines for join and part. This provides more information about the context of\nmessages, but may be spammy. Your options are 'always', 'never', and the default\nof 'commands-only' (the messages will be replayed in /HISTORY output, but not\nduring autoreplay).": "",
"$bSADROP$b forcibly de-links the given nickname from its user account.": "",
"$bSAGET$b queries the current values of another user's account settings": "",
"$bSAREGISTER$b registers an account on someone else's behalf.": "",
"$bSASET$b modifies another user's account settings": "",
"$bSESSIONS$b lists the sessions attached to a nickname.": "",
"$bSET$b modifies your account settings": "",
"$bSUSPEND$b disables an account and disconnects the clients": "",
"$bUNREGISTER$b lets you delete your user account.": "",
"$bUNSUSPEND$b restores access to a suspended account": "",
"$bVERIFY$b lets you complete account registration.": "",
"Insufficient privileges": "",
"Invalid parameters": "",
"NickServ lets you register, log in to, and manage an account.": "",
"Password incorrect": "",
"Passwords do not match": "",
"Syntax $bSET <setting> <value>$b\n\nSET modifies your account settings. The following settings are available:": "",
"Syntax: $bCERT <LIST | ADD | DEL> [account] [certfp]$b\n\nCERT examines or modifies the TLS certificate fingerprints that can be used to\nlog into an account. Specifically, $bCERT LIST$b lists the authorized\nfingerprints, $bCERT ADD <fingerprint>$b adds a new fingerprint, and\n$bCERT DEL <fingerprint>$b removes a fingerprint. If you're an IRC operator\nwith the correct permissions, you can act on another user's account, for\nexample with $bCERT ADD <account> <fingerprint>$b.": "",
"Syntax: $bDROP [nickname]$b\n\nDROP de-links the given (or your current) nickname from your user account.": "",
"Syntax: $bENFORCE [method]$b\n\nENFORCE lets you specify a custom enforcement mechanism for your registered\nnicknames. Your options are:\n1. 'none' [no enforcement, overriding the server default]\n2. 'timeout' [anyone using the nick must authenticate before a deadline,\n or else they will be renamed]\n3. 'strict' [you must already be authenticated to use the nick]\n4. 'default' [use the server default]\nWith no arguments, queries your current enforcement status.": "",
"Syntax: $bENFORCE [method]$b\n\nENFORCE is an alias for $bGET enforce$b and $bSET enforce$b. See the help\nentry for $bSET$b for more information.": "",
"Syntax: $bERASE <username> [code]$b\n\nERASE deletes all records of an account, allowing it to be re-registered.\nThis should be used with caution, because it violates an expectation that\naccount names are permanent identifiers. Typically, UNREGISTER should be\nused instead. A confirmation code is required; invoking the command\nwithout a code will display the necessary code.": "",
"Syntax: $bGET <setting>$b\n\nGET queries the current values of your account settings. For more information\non the settings and their possible values, see HELP SET.": "",
"Syntax: $bGHOST <nickname>$b\n\nGHOST disconnects the given user from the network if they're logged in with the\nsame user account, letting you reclaim your nickname.": "",
"Syntax: $bGROUP$b\n\nGROUP links your current nickname with your logged-in account, so other people\nwill not be able to use it.": "",
"Syntax: $bIDENTIFY <username> [password]$b\n\nIDENTIFY lets you login to the given username using either password auth, or\ncertfp (your client certificate) if a password is not given.": "",
"Syntax: $bINFO [username]$b\n\nINFO gives you information about the given (or your own) user account.": "",
"Syntax: $bPASSWD <current> <new> <new_again>$b\nOr: $bPASSWD <username> <new>$b\n\nPASSWD lets you change your account password. You must supply your current\npassword and confirm the new one by typing it twice. If you're an IRC operator\nwith the correct permissions, you can use PASSWD to reset someone else's\npassword by supplying their username and then the desired password.": "",
"Syntax: $bREGISTER <username> <email> [password]$b\n\nREGISTER lets you register a user account. If the server allows anonymous\nregistration, you can send an asterisk (*) as the email address.\n\nIf the password is left out, your account will be registered to your TLS client\ncertificate (and you will need to use that certificate to login in future).": "",
"Syntax: $bLIST [regex]$b\n\nLIST returns the list of registered nicknames, which match the given regex.\nIf no regex is provided, all registered nicknames are returned.": "",
"Syntax: $bPASSWD <current> <new> <new_again>$b\nOr: $bPASSWD <username> <new>$b\n\nPASSWD lets you change your account password. You must supply your current\npassword and confirm the new one by typing it twice. If you're an IRC operator\nwith the correct permissions, you can use PASSWD to reset someone else's\npassword by supplying their username and then the desired password. To\nindicate an empty password, use * instead.": "",
"Syntax: $bREGISTER <password> [email]$b\n\nREGISTER lets you register your current nickname as a user account. If the\nserver allows anonymous registration, you can omit the e-mail address.\n\nIf you are currently logged in with a TLS client certificate and wish to use\nit instead of a password to log in, send * as the password.": "",
"Syntax: $bSADROP <nickname>$b\n\nSADROP forcibly de-links the given nickname from the attached user account.": "",
"Syntax: $bSAREGISTER <username> <password>$b\n\nSAREGISTER registers an account on someone else's behalf.\nThis is for use in configurations that require SASL for all connections;\nan administrator can set use this command to set up user accounts.": "",
"Syntax: $bSESSIONS [nickname]$b\n\nSESSIONS lists information about the sessions currently attached, via\nthe server's bouncer functionality, to your nickname. An administrator\ncan use this command to list another user's sessions.": "",
"Syntax: $bSAGET <account> <setting>$b\n\nSAGET queries the values of someone else's account settings. For more\ninformation on the settings and their possible values, see HELP SET.": "",
"Syntax: $bSAREGISTER <username> [password]$b\n\nSAREGISTER registers an account on someone else's behalf.\nThis is for use in configurations that require SASL for all connections;\nan administrator can set use this command to set up user accounts.": "",
"Syntax: $bSASET <account> <setting> <value>$b\n\nSASET modifies the values of someone else's account settings. For more\ninformation on the settings and their possible values, see HELP SET.": "",
"Syntax: $bSESSIONS [nickname]$b\n\nSESSIONS lists information about the sessions currently attached, via\nthe server's multiclient functionality, to your nickname. An administrator\ncan use this command to list another user's sessions.": "",
"Syntax: $bSUSPEND <nickname>$b\n\nSUSPEND disables an account and disconnects the associated clients.": "",
"Syntax: $bUNREGISTER <username> [code]$b\n\nUNREGISTER lets you delete your user account (or someone else's, if you're an\nIRC operator with the correct permissions). To prevent accidental\nunregistrations, a verification code is required; invoking the command without\na code will display the necessary code.": "",
"Syntax: $bVERIFY <username> <code>$b\n\nVERIFY lets you complete an account registration, if the server requires email\nor other verification.": ""
"Syntax: $bUNSUSPEND <nickname>$b\n\nUNSUSPEND reverses a previous SUSPEND, restoring access to the account.": "",
"Syntax: $bVERIFY <username> <code>$b\n\nVERIFY lets you complete an account registration, if the server requires email\nor other verification.": "",
"You're not logged into an account": ""
}

View File

@ -1,8 +1,8 @@
{
"= Help Topics =\n\nCommands:\n%[1]s\n\nRPL_ISUPPORT Tokens:\n%[2]s\n\nInformation:\n%[3]s": "= Sujets daide =\n\nCommandes :\n%[1]s\n\nJetons RPL_ISUPPORT :\n%[2]s\n\nInformation :\n%[3]s",
"== Channel Modes ==\n\nOragono supports the following channel modes:\n\n +b | Client masks that are banned from the channel (e.g. *!*@127.0.0.1)\n +e | Client masks that are exempted from bans.\n +I | Client masks that are exempted from the invite-only flag.\n +i | Invite-only mode, only invited clients can join the channel.\n +k | Key required when joining the channel.\n +l | Client join limit for the channel.\n +m | Moderated mode, only privileged clients can talk on the channel.\n +n | No-outside-messages mode, only users that are on the channel can send\n | messages to it.\n +R | Only registered users can join the channel.\n +s | Secret mode, channel won't show up in /LIST or whois replies.\n +t | Only channel opers can modify the topic.\n\n= Prefixes =\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.": "",
"== Channel Modes ==\n\nOragono supports the following channel modes:\n\n +b | Client masks that are banned from the channel (e.g. *!*@127.0.0.1)\n +e | Client masks that are exempted from bans.\n +I | Client masks that are exempted from the invite-only flag.\n +i | Invite-only mode, only invited clients can join the channel.\n +k | Key required when joining the channel.\n +l | Client join limit for the channel.\n +m | Moderated mode, only privileged clients can talk on the channel.\n +n | No-outside-messages mode, only users that are on the channel can send\n | messages to it.\n +R | Only registered users can join the channel.\n +s | Secret mode, channel won't show up in /LIST or whois replies.\n +t | Only channel opers can modify the topic.\n +E | Roleplaying commands are enabled in the channel.\n +C | Clients are blocked from sending CTCP messages in the channel.\n\n= Prefixes =\n\n +q (~) | Founder channel mode.\n +a (&) | Admin channel mode.\n +o (@) | Operator channel mode.\n +h (%) | Halfop channel mode.\n +v (+) | Voice channel mode.": "",
"== Server Notice Masks ==\n\nOragono supports the following server notice masks for operators:\n\n a | Local announcements.\n c | Local client connections.\n j | Local channel actions.\n k | Local kills.\n n | Local nick changes.\n o | Local oper actions.\n q | Local quits.\n t | Local /STATS usage.\n u | Local client account actions.\n x | Local X-lines (DLINE/KLINE/etc).\n v | Local vhost changes.\n\nTo set a snomask, do this with your nickname:\n\n /MODE <nick> +s <chars>\n\nFor instance, this would set the kill, oper, account and xline snomasks on dan:\n\n /MODE dan +s koux": "",
"== User Modes ==\n\nOragono supports the following user modes:\n\n +a | User is marked as being away. This mode is set with the /AWAY command.\n +i | User is marked as invisible (their channels are hidden from whois replies).\n +o | User is an IRC operator.\n +R | User only accepts messages from other registered users. \n +s | Server Notice Masks (see help with /HELPOP snomasks).\n +Z | User is connected via TLS.": "== Modes dutilisateurice ==\n\nOragono propose les modes dutilisateurice suivants :\n\n +a | Absent·e. Sactive via /away.\n +i | Invisible. Salons masqués en /whois.\n +o | Opérateurice.\n +R | Accepte seulement les messages dutilisateurices enregistré·e·s.\n +s | Masques de notification serveur. Voir /helpop snomasks.\n +Z | Connecté·e via TLS.",
"== User Modes ==\n\nOragono supports the following user modes:\n\n +a | User is marked as being away. This mode is set with the /AWAY command.\n +i | User is marked as invisible (their channels are hidden from whois replies).\n +o | User is an IRC operator.\n +R | User only accepts messages from other registered users. \n +s | Server Notice Masks (see help with /HELPOP snomasks).\n +Z | User is connected via TLS.\n +B | User is a bot.\n +E | User can receive roleplaying commands.\n +T | User is blocked from sending CTCP messages.": "",
"@+client-only-tags TAGMSG <target>{,<target>}\n\nSends the given client-only tags to the given targets as a TAGMSG. See the IRCv3\nspecs for more info: http://ircv3.net/specs/core/message-tags-3.3.html": "@+Client-only-tags TAGMSG Nom (,Nom)\n\nEnvoie les « client-only tags » aux utilisateurices donné·e·s via TAGMSG.\nPour plus dinformations, voir les spécifications IRCv3 :\nhttps://ircv3.net/specs/extensions/message-tags.html",
"ACC LS\nACC REGISTER <accountname> [callback_namespace:]<callback> [cred_type] :<credential>\nACC VERIFY <accountname> <auth_code>\n\nUsed in account registration. See the relevant specs for more info:\nhttps://oragono.io/specs.html": "ACC LS\nACC REGISTER Nom [callback_namespace:]<rappel> [cred_type] :<référence>\nACC VERIFY Nom Code\n\nCommandes utilisées pour lenregistrement des comptes. Cf. spécifications :\nhttps://oragono.io/specs.html",
"AMBIANCE <target> <text to be sent>\n\nThe AMBIANCE command is used to send a scene notification to the given target.": "AMBIANCE Destination :Message\n\t\t\nCette commande envoie un message dambiance. Requière lactivation\ndu mode jeu de rôle (+E) sur le salon ou lutilisateurice donné·e.\n\nExemple : AMBIANCE #Salon :Juste à côté du feu de cheminée, se trouvait un chat qui ronronnait.",
@ -13,8 +13,10 @@
"CAP <subcommand> [:<capabilities>]\n\nUsed in capability negotiation. See the IRCv3 specs for more info:\nhttp://ircv3.net/specs/core/capability-negotiation-3.1.html\nhttp://ircv3.net/specs/core/capability-negotiation-3.2.html": "CAP Sous-commande (:Capacités)\n\nCommande utilisée pour la négociation des capacités. Voir\nles spécifications IRCv3 pour plus dinformations :\nhttps://ircv3.net/specs/core/capability-negotiation.html",
"CHATHISTORY [params]\n\nCHATHISTORY is a history replay command associated with the IRCv3\nspecification draft/chathistory. See this document:\nhttps://github.com/ircv3/ircv3-specifications/pull/393": "CHATHISTORY (Paramètres)\n\nCette commande affiche lhistorique dune conversation ou dun salon, et\nfait partie des spécifications IRCv3. Cf. documentation :\nhttps://github.com/ircv3/ircv3-specifications/pull/393",
"DEBUG <option>\n\nProvides various debugging commands for the IRCd. <option> can be one of:\n\n* GCSTATS: Garbage control statistics.\n* NUMGOROUTINE: Number of goroutines in use.\n* STARTCPUPROFILE: Starts the CPU profiler.\n* STOPCPUPROFILE: Stops the CPU profiler.\n* PROFILEHEAP: Writes a memory profile.\n* CRASHSERVER: Crashes the server (for use in failover testing)": "DEBUG Option\n\nOffre diverses commandes de déboggage. Les options disponibles sont :\n\n* CRASHSERVER : Cause un crash du serveur.\n* GCSTATS : Contrôle des statistiques.\n* NUMGOROUTINE : Numération des routines.\n* PROFILEHEAP : Profil de la mémoire.\n* STARTCPUPROFILE : Début du profilage du processeur.\n* STOPCPUPROFILE : Fin du profilage du processeur.",
"DEFCON [level]\n\nThe DEFCON system can disable server features at runtime, to mitigate\nspam or other hostile activity. It has five levels, which are cumulative\n(i.e., level 3 includes all restrictions from level 4 and so on):\n\n5: Normal operation\n4: No new account or channel registrations\n3: All users are +R; no changes to vhosts\n2: No new unauthenticated connections; all channels are +R\n1: No new connections except from localhost or other trusted IPs": "",
"DEOPER\n\nDEOPER removes the IRCop privileges granted to you by a successful /OPER.": "DEOPER\n\nDEOPER retire les privilèges précédemment obtenus via /OPER.",
"DLINE [ANDKILL] [MYSELF] [duration] <ip>/<net> [ON <server>] [reason [| oper reason]]\nDLINE LIST\n\nBans an IP address or network from connecting to the server. If the duration is\ngiven then only for that long. The reason is shown to the user themselves, but\neveryone else will see a standard message. The oper reason is shown to\noperators getting info about the DLINEs that exist.\n\nBans are saved across subsequent launches of the server.\n\n\"ANDKILL\" means that all matching clients are also removed from the server.\n\n\"MYSELF\" is required when the DLINE matches the address the person applying it is connected\nfrom. If \"MYSELF\" is not given, trying to DLINE yourself will result in an error.\n\n[duration] can be of the following forms:\n\t1y 12mo 31d 10h 8m 13s\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24\n\nON <server> specifies that the ban is to be set on that specific server.\n\n[reason] and [oper reason], if they exist, are separated by a vertical bar (|).\n\nIf \"DLINE LIST\" is sent, the server sends back a list of our current DLINEs.": "DLINE [ANDKILL] [MYSELF] [Durée] <IP>/<Net> [ON <Serveur>] [Raison [| OPER Raison]]\n\nCommande bannissant une adresse IP ou un réseau, indéfiniment ou pour la durée spécifiée.\nLa première raison est communiquée à lutilisateurice concerné·e; la seconde est destinée aux opérateurices.\n\n«ANDKILL» déconnecte immédiatement lutilisateurice du serveur.\n«MYSELF» est à préciser dans le cas dun auto-bannissement.\n«ON <Serveur> » établit le bannissement uniquement sur ce serveur.\n\nLa durée peut être donnée en années, mois, jours, heures, ou secondes :\n\t1y 12mo 28d 24h 60m 20s\n\n<Net> suit la méthode CIDR. Exemples :\n\t127.0.0.1/8\n\t8.8.8.8/24\n\nDLINE LIST\n\nAffiche la liste des bannissements actuellement en place.",
"EXTJWT <target> [service_name]\n\nGet a JSON Web Token for target (either * or a channel name).": "",
"HELP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.": "HELP Argument\n\nExplique largument donné, ou liste les aides disponibles avec Index.",
"HELPOP <argument>\n\nGet an explanation of <argument>, or \"index\" for a list of help topics.": "HELPOP <commande>\n\nExplique la commande donnée; une liste complète est disponible via « /helpop index».",
"HISTORY <target> [limit]\n\nReplay message history. <target> can be a channel name, \"me\" to replay direct\nmessage history, or a nickname to replay another client's direct message\nhistory (they must be logged into the same account as you). [limit] can be\neither an integer (the maximum number of messages to replay), or a time\nduration like 10m or 1h (the time window within which to replay messages).": "HISTORY Cible (Limite)\n\nAffiche lhistorique des messages. La cible peut être le nom dun salon, dun·e\nutilisateurice lié·e à votre compte, ou «me» pour les échanges privés.\nLa limite est, au choix, une valeur numéraire ou bien temporelle.",
@ -53,12 +55,14 @@
"SANICK <currentnick> <newnick>\n\nGives the given user a new nickname.": "SANICK NomActuel NouveauNom\n\nChange le nom de lutilisateurice en question.",
"SCENE <target> <text to be sent>\n\nThe SCENE command is used to send a scene notification to the given target.": "SCENE Destination :Message\n\t\t\nCette commande envoie la description dune scène. Requière lactivation\ndu mode jeu de rôle (+E) sur le salon ou lutilisateurice donné·e.\n\nExemple : SCENE #Salon :Juste à côté du feu de cheminée, se trouvait un chat qui ronronnait.",
"SETNAME <realname>\n\nThe SETNAME command updates the realname to be the newly-given one.": "SETNAME Nom réel\n\nChange votre «nom réel» pour celui donné.\n\nExemple : SETNAME Carmen Sandiego",
"SUMMON [parameters]\n\nThe SUMMON command is not implemented.": "",
"TIME [server]\n\nShows the time of the current, or the given, server.": "TIME (Serveur)\n\nAffiche lheure du serveur actuel, ou celui spécifié.",
"TOPIC <channel> [topic]\n\nIf [topic] is given, sets the topic in the channel to that. If [topic] is not\ngiven, views the current topic on the channel.": "TOPIC Salon (Sujet)\n\nAffiche le sujet actuel du salon, ou le remplace si un nouveau est donné.",
"UNDLINE <ip>/<net>\n\nRemoves an existing ban on an IP address or a network.\n\n<net> is specified in typical CIDR notation. For example:\n\t127.0.0.1/8\n\t8.8.8.8/24": "UNDLINE <IP>/<Net>\n\nLève un bannissement en cours sur ladresse IP ou le réseau donné·e.\n\n<Net> est à spécifier selon la méthode CIDR.\n\nExemples :\n\t127.0.0.1/8\n\t8.8.8.8/24",
"UNKLINE <mask>\n\nRemoves an existing ban on a mask.\n\nFor example:\n\tdan\n\tdan!5*@127.*": "UNKLINE <Masque>\n\nLève un bannissement en cours sur le masque donné.\n\nExemples :\n\tUNKLINE Nuve\n\tUNKLINE Nuve!6*@28.*",
"USER <username> 0 * <realname>\n\nUsed in connection registration, sets your username and realname to the given\nvalues (though your username may also be looked up with Ident).": "USER <Nom dutilisateurice> 0 * <Nom réel>\n\nCommande utilisée au cours de lidentification au serveur, établissant le\nnom dutilisateurice et le «nom réel» donnés à la connexion.",
"USERHOST <nickname>{ <nickname>}\n\t\t\nShows information about the given users. Takes up to 10 nicknames.": "USERHOST Nom (Nom)\n\t\t\nAffiche les informations des utilisateurices donné·e·s.\nAccepte jusquà dix noms séparés par une espace.",
"USERS [parameters]\n\nThe USERS command is not implemented.": "",
"VERSION [server]\n\nViews the version of software and the RPL_ISUPPORT tokens for the given server.": "VERSION (Serveur)\n\nAffiche la version dOragono utilisée par le serveur actuel, ou celui\nspécifié, ainsi que ses réglages RPL_ISUPPORT.",
"WEBIRC <password> <gateway> <hostname> <ip> [:<flags>]\n\nUsed by web<->IRC gateways and bouncers, the WEBIRC command allows gateways to\npass-through the real IP addresses of clients:\nircv3.net/specs/extensions/webirc.html\n\n<flags> is a list of space-separated strings indicating various details about\nthe connection from the client to the gateway, such as:\n\n- tls: this flag indicates that the client->gateway connection is secure": "WEBIRC <Mot de passe> <Passerelle> <Nom dhôte> <Adresse IP> [:<Marquages>]\n\nCommande utilisée par les passerelles Web<->IRC, et les «bouncers», leur permettant\nde relayer la véritable adresse IP des clients. Voir spécification :\nhttps://ircv3.net/specs/extensions/webirc.html\n\nLes <marquages> sont une liste de termes séparés par une espace, indiquant divers\ndétails au sujet de la connexion du client à la passerelle, comme par exemple :\n\n- TLS : ce marquage indique que le lien entre le client et la passerelle est sécurisé.",
"WHO <name> [o]\n\nReturns information for the given user.": "WHO #Salon (ou Utilisateurice)\n\nAffiche les informations dun·e utilisateurice, ou\nliste celleux présent·e·s dans un salon.",

View File

@ -86,6 +86,7 @@
"Channel renamed": "Canal renommé",
"Channel renamed: %s": "Canal renommé : %s",
"Channels with persistent history cannot be renamed": "",
"Client %s is always-on and cannot be fully removed by /KILL; consider /NS SUSPEND instead": "",
"Client reconnected": "Client reconnecté",
"Client reconnected (message history may have been lost)": "Client reconnecté (lhistorique des messages a pu être perdu)",
"Client reconnected (up to %d seconds of message history lost)": "Client reconnecté (jusquà %d secondes de lhistorique ont pu être perdues)",
@ -95,6 +96,7 @@
"Could not accept ownership of channel %s": "Impossible de prendre possession du salon %s",
"Could not delete message": "",
"Could not find given client": "Client donné introuvable",
"Could not generate EXTJWT token": "",
"Could not look up account name, proceeding anyway": "",
"Could not parse IP address or CIDR network": "Impossible danalyser ladresse IP, ou le réseau CIDR",
"Could not register": "Enregistrement impossible",
@ -106,9 +108,12 @@
"Could not transfer channel": "Transfert du salon impossible",
"Could not ungroup nick": "Impossible de dégrouper ce nom",
"Created at: %s": "Créé·e le : %s",
"Current DEFCON level is %d": "",
"Current global users %[1]s, max %[2]s": "%[1]s utilisateurices actuellement connecté·e·s; record à %[2]s",
"Current local users %[1]s, max %[2]s": "%[1]s utilisateurices actuellement connecté·e·s au serveur local; record à %[2]s",
"Data export for %[1]s completed and written to %[2]s": "",
"Device ID: %s": "",
"Direct messages from unregistered users are temporarily restricted": "",
"End of /HELPOP": "Fin de HELPOP",
"End of /INFO": "Fin de INFO",
"End of /WHOIS list": "Fin de WHOIS",
@ -154,6 +159,7 @@
"Insufficient privileges": "Privilèges insuffisants",
"Internal error": "Erreur interne",
"Invalid CAP subcommand": "Sous-commande CAP invalide",
"Invalid DEFCON parameter": "",
"Invalid account name": "Nom de compte invalide",
"Invalid certificate fingerprint": "Empreinte du certificat invalide",
"Invalid channel name": "Nom de salon invalide",
@ -165,6 +171,7 @@
"Invalid params": "Paramètres invalides",
"Invalid regex": "",
"Invalid vhost": "Vhost invalide",
"It was built from git hash %s.": "",
"It was rejected for reason: %s": "Raison du refus : %s",
"JOIN 0 is not allowed": "JOIN 0 est interdit",
"Language %s is not supported by this server": "Le langage %s nest pas proposé sur ce serveur",
@ -173,6 +180,7 @@
"MOTD File is missing": "Message du jour manquant",
"Malformed username": "Nom erroné",
"Mask isn't valid": "Masque invalide",
"Message rejected for containing invalid UTF-8": "",
"Messages could not be retrieved": "Récupération des messages impossible",
"Multiclient functionality is currently disabled for your account": "La fonctionnalité multiclient est actuellement désactivée",
"Multiclient functionality is currently disabled for your account, but you can opt in": "La fonctionnalité multiclient est disponible, mais actuellement désactivée pour votre compte",
@ -189,6 +197,7 @@
"No such channel": "Ce canal nexiste pas",
"No such module [%s]": "Module introuvable",
"No such nick": "Pseudo introuvable",
"No such service": "",
"No such setting": "Réglage introuvable",
"No text to send": "Aucun message à envoyer",
"No topic is set": "Aucun sujet enregistré",
@ -207,6 +216,7 @@
"Purge reason: %s": "Raison de lélimination : %s",
"Purged at: %s": "Éliminé à : %s",
"Purged by operator: %s": "Éliminé par lopérateurice : %s",
"Realname is not valid": "",
"Received malformed line": "Ligne reçue mal-formée",
"Registered at: %s": "Enregistré·e le : %s",
"Registered channel: %s": "Salon enregistré le : %s",
@ -228,6 +238,7 @@
"SASL authentication failed: Passphrase too long": "Authentification SASL échouée : phrase secrète trop longue",
"SASL authentication failed: authcid and authzid should be the same": "Authentification SASL échouée : authcid et authzid devraient être identiques",
"SASL message too long": "Message SASL trop long",
"SUMMON has been disabled": "",
"Server notice masks": "Masques de notification du serveur",
"Session %d (currently attached session):": "Session %d (actuellement attachée) :",
"Session %d:": "Session %d :",
@ -247,9 +258,11 @@
"Successfully registered account %s": "Compte %s enregistré",
"Successfully rejected vhost request for %s": "Demande dhôte virtuel rejetée pour %s",
"Successfully reset channel access": "Accès du salon réinitialisés",
"Successfully set persistent mode %s%s on %s": "",
"Successfully set persistent mode %[1]s on %[2]s": "",
"Successfully set vhost": "Hôte virtuel mis en place",
"Successfully suspended account %s": "",
"Successfully transferred channel %[1]s to account %[2]s": "Salon %[1]s cédé au compte %[2]s",
"Successfully un-suspended account %s": "",
"Successfully ungrouped nick %s with your account": "Le nom %s a été détaché de votre compte",
"Successfully unpurged channel %s from the server": "Salon %s restauré sur le serveur",
"Successfully unregistered account %s": "Compte %s effacé",
@ -272,6 +285,7 @@
"This ban matches you. To KLINE yourself, you must use the command: /KLINE MYSELF <arguments>": "Ce bannissement vous cible. Pour ajouter une K-Line sur vous-même, utilisez la commande : /kline myself <options>",
"This command has been disabled by the server administrators": "Cette commande a été désactivée par les administrateurices du serveur",
"This feature has been disabled by the server administrators": "Cette fonctionnalité a été désactivée par les administrateurices du serveur",
"This is Oragono version %s.": "",
"This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.": "Ce serveur est en mode débogage, et enregistre toutes entrées/sorties; si vous ne souhaitez pas que lensemble de vos échanges soit lisible par ses propriétaires, merci de vous déconnecter.",
"This server requires that you wait %v after connecting before you can use /LIST. You have %v left.": "",
"This server was created %s": "Ce serveur a été créé le %s",
@ -285,10 +299,12 @@
"Transfer of channel %[1]s to account %[2]s succeeded, pending acceptance": "Transfert du salon %[1]s au compte %[2]s réussi; en attente dacceptation.",
"Translators:": "Traducteurices :",
"Try again later": "Merci de réessayer ultérieurement",
"USERS has been disabled": "",
"Unknown command": "Commande inconnue",
"Unknown command. To see available commands, run: /%s HELP": "Commande inconnue. Pour voir la liste des commandes disponibles, entrez : /%s HELP",
"Unknown subcommand": "Sous-commande inconnue",
"Unrecognized DEBUG subcommand": "Sous-commande DEBUG non reconnue",
"Usage: REGISTER <passphrase> [email]": "",
"User %s is no longer allowed to use vhosts": "Lutilisateurice %s na plus accès à lusage des hôtes virtuels",
"User %s is now allowed to use vhosts": "Lutilisateurice %s a maintenant accès à lusage des hôtes virtuels",
"User doesn't have roleplaying mode enabled": "Cet·te utilisateurice na pas activé le mode «jeu de rôle»",
@ -317,13 +333,11 @@
"You have been marked as being away": "Vous êtes maintenant absent·e",
"You have been offered ownership of channel %[1]s. To accept, /CS TRANSFER ACCEPT %[1]s": "La propriété du salon %[1]s vous a été offerte; pour laccepter, entrez : /CS TRANSFER ACCEPT %[1]s",
"You have enabled autoreplay of missed messages, but you can't receive them because your client isn't set to always-on": "La rediffusion des messages manqués est impossible si votre mode persistant est désactivé",
"You have joined too many channels": "Vous avez rejoint trop de canaux",
"You have sent too many registration messages": "Vous avez envoyé trop de messages denregistrement",
"You have too many nicks reserved already (you can remove some with /NS DROP)": "Vous avez déjà trop de noms réservés (vous pouvez en libérer via /NS DROP)",
"You may not reregister": "Vous ne pouvez pas vous enregistrer à nouveau",
"You must be an oper on the channel to register it": "Vous devez être opérateurice sur le canal pour lenregistrer",
"You must be connected with TLS and a client certificate to do this": "Vous devez être connecté·e via TLS, et avoir un certificat client pour faire cela",
"You must be registered to join that channel": "Vous devez être enregistré·e pour rejoindre ce canal",
"You must have rehash permissions in order to execute DEBUG CRASHSERVER": "La permission «rehash» est nécessaire afin dutiliser cette commande",
"You must log in with SASL to join this server": "Vous devez vous authentifier via SASL pour rejoindre ce serveur",
"You must specify an account": "Vous devez spécifier un compte",

Some files were not shown because too many files have changed in this diff Show More