mirror of
https://github.com/anope/anope.git
synced 2026-06-17 22:24:48 +02:00
Compare commits
85 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| da799bd6f1 | |||
| 4dada51e08 | |||
| 1275fd01a7 | |||
| 5b8018eb80 | |||
| b2026fe739 | |||
| 3390d82ebe | |||
| 4762e306f9 | |||
| fd759f7bd3 | |||
| 003ddde09d | |||
| c021d7134e | |||
| a792088a62 | |||
| 03e2ebe2d2 | |||
| 0238f0a9e3 | |||
| c2dfb9a447 | |||
| bd8435a061 | |||
| 2cd67c9f36 | |||
| 45ab094d63 | |||
| 54389f474b | |||
| ac2b38b1b6 | |||
| df551bee03 | |||
| 59bb9d3d06 | |||
| 3b2d798e76 | |||
| ff6d6bdb3c | |||
| 411b3ce65a | |||
| ad19603818 | |||
| 535ff9b839 | |||
| 51916a50d2 | |||
| 42e58952cc | |||
| ba9f4e93c2 | |||
| 679a313ae2 | |||
| 390975e6c4 | |||
| d631885495 | |||
| 8e337c6592 | |||
| 9d89d8233d | |||
| 6f27418381 | |||
| 1932081c27 | |||
| f73159159f | |||
| eccb338cdd | |||
| 4021c0bb68 | |||
| 7dfb40cb58 | |||
| 0fbd3095c4 | |||
| 6d426ec386 | |||
| 1b33a51695 | |||
| 168a1173ed | |||
| 59c777d4ff | |||
| 43f88524e2 | |||
| 04de41ad7f | |||
| dd13846cad | |||
| 6864bc6171 | |||
| 003cb55409 | |||
| d15b8ee949 | |||
| 70de2b7de2 | |||
| e5c5689985 | |||
| 928d4260dc | |||
| 5e8561a7f1 | |||
| 4dd3075ddf | |||
| dbef7a7c64 | |||
| cd0f00a819 | |||
| b2d40d4189 | |||
| e6e812c43c | |||
| 2dbd41d244 | |||
| 02aeb254ee | |||
| 1245b43ade | |||
| 5a3f9f3f87 | |||
| 2c6f4d7c27 | |||
| 64f8449bf4 | |||
| 0e1f6b403b | |||
| 206192abdc | |||
| 32d3ddc0e4 | |||
| 4410e5ccce | |||
| d5f2232140 | |||
| 6d754b7d73 | |||
| 4d2870fa45 | |||
| 5948c2ea53 | |||
| ace7d99797 | |||
| 52595b90fa | |||
| b39f002d1b | |||
| 5df95d9f86 | |||
| a56d9a4096 | |||
| ce7bb15c18 | |||
| 6873630f2e | |||
| 44a4d62654 | |||
| 97389cd105 | |||
| 80c0adf7c8 | |||
| 6a539277b9 |
@@ -13,7 +13,7 @@ jobs:
|
||||
CXX: ${{ matrix.compiler }}
|
||||
CXXFLAGS: -Werror
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
CXX: ${{ matrix.compiler }}
|
||||
CXXFLAGS: -Werror
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
CONAN_USER_HOME: ${{ github.workspace }}/win/build
|
||||
CONAN_USER_HOME_SHORT: None
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Setup NSIS
|
||||
run: |-
|
||||
|
||||
BIN
Binary file not shown.
+15
-69
@@ -83,8 +83,9 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* The services.host define is used in multiple different locations throughout the
|
||||
* configuration for the server name and pseudoclient hostnames.
|
||||
* The "services.host" define is used in multiple different locations throughout
|
||||
* the configuration for the server name and pseudoclient hostnames. You should
|
||||
* change the value to the hostname you wish to use for your services server.
|
||||
*/
|
||||
define
|
||||
{
|
||||
@@ -747,6 +748,7 @@ log
|
||||
* nickserv/drop/display - Allows dropping display nicks when preservedisplay is enabled
|
||||
* nickserv/drop/override - Allows dropping nicks without using a confirmation code
|
||||
* nickserv/recover - Can recover other users nicks
|
||||
* nickserv/resend - Can resend confirmation codes via email
|
||||
* operserv/config - Can modify services's configuration
|
||||
* operserv/oper/modify - Can add and remove operators with at most the same privileges
|
||||
* protected - Can not be kicked from channels by services
|
||||
@@ -811,7 +813,7 @@ opertype
|
||||
inherits = "Helper, Another Helper"
|
||||
|
||||
/* What commands (see above) this opertype may use */
|
||||
commands = "chanserv/list chanserv/suspend chanserv/topic memoserv/staff nickserv/list nickserv/suspend operserv/mode operserv/chankill operserv/akill operserv/session operserv/modinfo operserv/sqline operserv/oper operserv/kick operserv/ignore operserv/snline"
|
||||
commands = "chanserv/list chanserv/suspend chanserv/topic memoserv/staff nickserv/list nickserv/resend nickserv/suspend operserv/mode operserv/chankill operserv/akill operserv/session operserv/modinfo operserv/sqline operserv/oper operserv/kick operserv/ignore operserv/snline"
|
||||
|
||||
/* What privs (see above) this opertype has */
|
||||
privs = "chanserv/auspex chanserv/no-register-limit memoserv/* nickserv/auspex nickserv/confirm/*"
|
||||
@@ -1063,91 +1065,38 @@ mail
|
||||
* have will not be stored!
|
||||
*/
|
||||
|
||||
/*
|
||||
* [DEPRECATED] db_old
|
||||
*
|
||||
* This is the old binary database format from late Anope 1.7.x, Anope 1.8.x, and
|
||||
* early Anope 1.9.x. This module only loads these databases, and will NOT save them.
|
||||
* You should only use this to upgrade old databases to a newer database format by loading
|
||||
* other database modules in addition to this one, which will be used when saving databases.
|
||||
*/
|
||||
#module
|
||||
{
|
||||
name = "db_old"
|
||||
|
||||
/*
|
||||
* This is the encryption type used by the databases. This must be set correctly or
|
||||
* your passwords will not work. Valid options are: md5, oldmd5, sha1, and plain.
|
||||
* You must also be sure to load the correct encryption module below in the Encryption
|
||||
* Modules section so that your passwords work.
|
||||
*/
|
||||
#hash = "md5"
|
||||
}
|
||||
|
||||
/*
|
||||
* db_atheme
|
||||
*
|
||||
* This allows importing databases from Atheme. You should load another database module as
|
||||
* well as this as it can only read Atheme databases not write them.
|
||||
* This allows importing databases from Atheme. You should load another database
|
||||
* module like db_json as well as this as it can only read Atheme databases not
|
||||
* write them.
|
||||
*/
|
||||
#module
|
||||
{
|
||||
name = "db_atheme"
|
||||
|
||||
/*
|
||||
* The database name db_atheme should use.
|
||||
* The file that db_atheme will import your main database from.
|
||||
*/
|
||||
database = "atheme.db"
|
||||
}
|
||||
|
||||
/*
|
||||
* db_flatfile
|
||||
* [DEPRECATED] db_flatfile
|
||||
*
|
||||
* Stores your database in a custom flatfile format.
|
||||
*
|
||||
* This was the recommended database module in 2.0 but it is now recommended
|
||||
* that you use db_json instead. You can migrate your db_flatfile database to
|
||||
* db_json by loading db_flatfile BEFORE db_json, sending SIGUSR1 to force a
|
||||
* database write, and then unloading db_flatfile.
|
||||
* This allows importing databases from the custom flat file format used between
|
||||
* Anope 1.9.6 and 2.1.17. You should load another database module like db_json
|
||||
* as well as this as it can only read db_flatfile databases not write them.
|
||||
*/
|
||||
#module
|
||||
{
|
||||
name = "db_flatfile"
|
||||
|
||||
/*
|
||||
* The database name db_flatfile should use
|
||||
* The file that db_flatfile will import your main database from.
|
||||
*/
|
||||
database = "anope.db"
|
||||
|
||||
/*
|
||||
* Sets the number of days backups of databases are kept. If you don't give it,
|
||||
* or if you set it to 0, Anope won't backup the databases.
|
||||
*
|
||||
* This directive is optional, but recommended.
|
||||
*/
|
||||
keepbackups = 7
|
||||
|
||||
/*
|
||||
* Allows Anope to continue file write operations (i.e. database saving)
|
||||
* even if the original file cannot be backed up. Enabling this option may
|
||||
* allow Anope to continue operation under conditions where it might
|
||||
* otherwise fail, such as a nearly-full disk.
|
||||
*
|
||||
* NOTE: Enabling this option can cause irrecoverable data loss under some
|
||||
* conditions, so make CERTAIN you know what you're doing when you enable it!
|
||||
*
|
||||
* This directive is optional, and you are discouraged against enabling it.
|
||||
*/
|
||||
#nobackupokay = yes
|
||||
|
||||
/*
|
||||
* If enabled, services will fork a child process to save databases.
|
||||
*
|
||||
* This is only useful with very large databases, with hundreds
|
||||
* of thousands of objects, that have a noticeable delay from
|
||||
* writing databases.
|
||||
*/
|
||||
fork = no
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1371,7 +1320,7 @@ module
|
||||
#module { name = "enc_posix" }
|
||||
|
||||
/*
|
||||
* [DEPRECATED] enc_md5, enc_none, enc_old, enc_sha1, enc_sha256
|
||||
* [DEPRECATED] enc_md5, enc_none, enc_sha1, enc_sha256
|
||||
*
|
||||
* Provides verify-only support for passwords encrypted using encryption methods
|
||||
* from older versions of Anope. These methods are no longer considered secure
|
||||
@@ -1381,8 +1330,6 @@ module
|
||||
* enc_md5: Verifies passwords encrypted with the MD5 algorithm
|
||||
* enc_none: Verifies passwords that are not encrypted
|
||||
* enc_sha1: Verifies passwords encrypted with the SHA1 algorithm
|
||||
* enc_old: Verifies passwords encrypted with the broken MD5 algorithm used
|
||||
* before 1.7.17.
|
||||
* enc_sha256: Verifies passwords encrypted with the SHA256 algorithm using a
|
||||
* custom initialisation vector as a salt.
|
||||
*
|
||||
@@ -1391,7 +1338,6 @@ module
|
||||
*/
|
||||
#module { name = "enc_md5" }
|
||||
#module { name = "enc_none" }
|
||||
#module { name = "enc_old" }
|
||||
#module { name = "enc_sha1" }
|
||||
#module { name = "enc_sha256" }
|
||||
|
||||
|
||||
@@ -296,11 +296,9 @@ command { service = "BotServ"; name = "KICK ITALICS"; command = "botserv/kick/it
|
||||
command { service = "BotServ"; name = "KICK REPEAT"; command = "botserv/kick/repeat"; }
|
||||
command { service = "BotServ"; name = "KICK REVERSES"; command = "botserv/kick/reverses"; }
|
||||
command { service = "BotServ"; name = "KICK UNDERLINES"; command = "botserv/kick/underlines"; }
|
||||
|
||||
command { service = "BotServ"; name = "SET DONTKICKOPS"; command = "botserv/set/dontkickops"; }
|
||||
command { service = "BotServ"; name = "SET DONTKICKVOICES"; command = "botserv/set/dontkickvoices"; }
|
||||
|
||||
|
||||
/*
|
||||
* bs_set
|
||||
*
|
||||
@@ -341,7 +339,6 @@ privilege
|
||||
xop = "AOP"
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* fantasy
|
||||
*
|
||||
@@ -387,7 +384,7 @@ fantasy { name = "ENFORCE"; command = "chanserv/enforce"; }
|
||||
fantasy { name = "ENTRYMSG"; command = "chanserv/entrymsg"; }
|
||||
fantasy { name = "FLAGS"; command = "chanserv/flags"; }
|
||||
fantasy { name = "HALFOP"; command = "chanserv/modes"; }
|
||||
fantasy { name = "HELP"; command = "generic/help"; prepend_channel = no; }
|
||||
fantasy { name = "HELP"; command = "generic/help"; prepend_channel = no; require_privilege = no; }
|
||||
fantasy { name = "HOP"; command = "chanserv/xop"; }
|
||||
fantasy { name = "INFO"; command = "chanserv/info"; prepend_channel = no; }
|
||||
fantasy { name = "INVITE"; command = "chanserv/invite"; }
|
||||
|
||||
@@ -1067,10 +1067,8 @@ module
|
||||
listmax = 50
|
||||
}
|
||||
command { service = "ChanServ"; name = "LIST"; command = "chanserv/list"; }
|
||||
|
||||
command { service = "ChanServ"; name = "SET PRIVATE"; command = "chanserv/set/private"; }
|
||||
|
||||
|
||||
/*
|
||||
* cs_log
|
||||
*
|
||||
@@ -1156,7 +1154,6 @@ command { service = "ChanServ"; name = "DEHALFOP"; command = "chanserv/modes"; g
|
||||
command { service = "ChanServ"; name = "VOICE"; command = "chanserv/modes"; group = "chanserv/status"; set = "VOICE" }
|
||||
command { service = "ChanServ"; name = "DEVOICE"; command = "chanserv/modes"; group = "chanserv/status"; unset = "VOICE" }
|
||||
|
||||
|
||||
/*
|
||||
* cs_register
|
||||
*
|
||||
|
||||
@@ -168,7 +168,13 @@ command { service = "HostServ"; name = "ON"; command = "hostserv/on"; }
|
||||
/*
|
||||
* hs_request
|
||||
*
|
||||
* Provides the commands hostserv/request, hostserv/activate, hostserv/reject, and hostserv/waiting.
|
||||
* Provides the commands:
|
||||
* hostserv/request - Requests a vhost.
|
||||
* hostserv/activate - Approves a requested vhost.
|
||||
* hostserv/reject - Rejects a requested vhost.
|
||||
* hostserv/waiting - Lists pending vhost requests.
|
||||
* hostserv/validate - Allows self-service approval of vhosts using DNS
|
||||
* validation (requires the dns module).
|
||||
*
|
||||
* Used to manage vhosts requested by users.
|
||||
*/
|
||||
@@ -186,11 +192,24 @@ module
|
||||
* If set, Anope will send a memo to all services staff when a new vhost is requested.
|
||||
*/
|
||||
#memooper = yes
|
||||
|
||||
/*
|
||||
* If DNS validation is enabled, how long should users have to wait between
|
||||
* attempts at DNS validation. Defaults to 5 minutes.
|
||||
*/
|
||||
#validationcooldown = 5m
|
||||
|
||||
/*
|
||||
* If DNS validation is enabled, the TXT record to look for when determining
|
||||
* if the requester controls the domain. Defaults to anope-dns-validation.
|
||||
*/
|
||||
#validationrecord = "anope-dns-validation"
|
||||
}
|
||||
command { service = "HostServ"; name = "REQUEST"; command = "hostserv/request"; }
|
||||
command { service = "HostServ"; name = "ACTIVATE"; command = "hostserv/activate"; permission = "hostserv/set"; }
|
||||
command { service = "HostServ"; name = "REJECT"; command = "hostserv/reject"; permission = "hostserv/set"; }
|
||||
command { service = "HostServ"; name = "WAITING"; command = "hostserv/waiting"; permission = "hostserv/set"; }
|
||||
#command { service = "HostServ"; name = "VALIDATE"; command = "hostserv/validate"; }
|
||||
|
||||
/*
|
||||
* hs_set
|
||||
|
||||
@@ -41,7 +41,6 @@ module { name = "help" }
|
||||
*/
|
||||
timeout = 5
|
||||
|
||||
|
||||
/* Only edit below if you are expecting to use os_dns or otherwise answer DNS queries. */
|
||||
|
||||
/*
|
||||
@@ -54,7 +53,6 @@ module { name = "help" }
|
||||
ip = "0.0.0.0"
|
||||
port = 53
|
||||
|
||||
|
||||
/*
|
||||
* SOA record information.
|
||||
*/
|
||||
|
||||
+84
-26
@@ -399,7 +399,12 @@ command { service = "NickServ"; name = "SASET EMAIL"; command = "nickserv/saset/
|
||||
/*
|
||||
* ns_group
|
||||
*
|
||||
* Provides the commands nickserv/group, nickserv/glist, and nickserv/ungroup.
|
||||
* Provides the commands:
|
||||
* nickserv/group
|
||||
* nickserv/ungroup
|
||||
* nickserv/glist
|
||||
* nickserv/saset/display
|
||||
* nickserv/set/display
|
||||
*
|
||||
* Used for controlling grouped nicknames.
|
||||
*/
|
||||
@@ -426,6 +431,12 @@ module
|
||||
command { service = "NickServ"; name = "GLIST"; command = "nickserv/glist"; }
|
||||
command { service = "NickServ"; name = "GROUP"; command = "nickserv/group"; }
|
||||
command { service = "NickServ"; name = "UNGROUP"; command = "nickserv/ungroup"; }
|
||||
command { service = "NickServ"; name = "SET DISPLAY"; command = "nickserv/set/display"; }
|
||||
command { service = "NickServ"; name = "SASET DISPLAY"; command = "nickserv/saset/display"; permission = "nickserv/saset/display"; }
|
||||
|
||||
# For compatibility with Atheme.
|
||||
command { service = "NickServ"; name = "SET ACCOUNTNAME"; command = "nickserv/set/display"; hide = yes; }
|
||||
command { service = "NickServ"; name = "SASET ACCOUNTNAME"; command = "nickserv/saset/display"; permission = "nickserv/saset/display"; hide = yes; }
|
||||
|
||||
/*
|
||||
* ns_identify
|
||||
@@ -460,7 +471,6 @@ command { service = "NickServ"; name = "INFO"; command = "nickserv/info"; }
|
||||
command { service = "NickServ"; name = "SET HIDE"; command = "nickserv/set/hide"; }
|
||||
command { service = "NickServ"; name = "SASET HIDE"; command = "nickserv/saset/hide"; permission = "nickserv/saset/hide"; }
|
||||
|
||||
|
||||
/*
|
||||
* ns_list
|
||||
*
|
||||
@@ -479,11 +489,9 @@ module
|
||||
listmax = 50
|
||||
}
|
||||
command { service = "NickServ"; name = "LIST"; command = "nickserv/list"; }
|
||||
|
||||
command { service = "NickServ"; name = "SET PRIVATE"; command = "nickserv/set/private"; }
|
||||
command { service = "NickServ"; name = "SASET PRIVATE"; command = "nickserv/saset/private"; permission = "nickserv/saset/private"; }
|
||||
|
||||
|
||||
/*
|
||||
* ns_logout
|
||||
*
|
||||
@@ -539,7 +547,7 @@ module
|
||||
* "mail" to require confirmation with a code provided via email.
|
||||
* "none" to automatically confirm (this is the default).
|
||||
*/
|
||||
registration = "none"
|
||||
registration = "code"
|
||||
|
||||
/*
|
||||
* The minimum length of time between consecutive uses of NickServ's RESEND command.
|
||||
@@ -554,7 +562,7 @@ module
|
||||
*
|
||||
* This directive is optional.
|
||||
*/
|
||||
nickregdelay = 15s
|
||||
#nickregdelay = 1m
|
||||
|
||||
/*
|
||||
* The length of time a user using an unconfirmed account has
|
||||
@@ -586,6 +594,9 @@ module
|
||||
command { service = "NickServ"; name = "CONFIRM RESETPASS"; command = "nickserv/confirm/resetpass"; }
|
||||
command { service = "NickServ"; name = "RESETPASS"; command = "nickserv/resetpass"; }
|
||||
|
||||
# For compatibility with Anope 2.0.
|
||||
command { service = "NickServ"; name = "GETPASS"; command = "nickserv/resetpass"; hide = yes; }
|
||||
|
||||
/*
|
||||
* ns_sasl
|
||||
*
|
||||
@@ -605,17 +616,46 @@ module
|
||||
* The nick of the client which operates as the SASL agent.
|
||||
*/
|
||||
#agent = "NickServ"
|
||||
|
||||
/*
|
||||
* Sets the number of invalid SASL authentication attempts before services
|
||||
* removes a partially-connected user from the network. If not defined then
|
||||
* the value specified in options:badpasslimit will be used instead.
|
||||
*/
|
||||
#badpasslimit = 1
|
||||
|
||||
/*
|
||||
* Sets the time after which invalid SASL authentication attempts are
|
||||
* forgotten about. If a user does not fail to authenticate in this amount
|
||||
* of time, the incorrect password count will reset to zero. If not defined
|
||||
* then the value specified in options:badpasstimeout will be used instead.
|
||||
*/
|
||||
#badpasstimeout = 15m
|
||||
}
|
||||
|
||||
/*
|
||||
* ns_sasl_anonymous, ns_sasl_external, ns_sasl_plain
|
||||
*
|
||||
* Provides support for the following SASL mechanisms:
|
||||
*
|
||||
* ns_sasl_anonymous: Adds the ANONYMOUS mechanism which allows logging out of
|
||||
* an account. See RFC 4505 for more details.
|
||||
* ns_sasl_external: Adds the EXTERNAL mechanism which allows logging into an
|
||||
account using a TLS client certificate. See RFC 4422 for
|
||||
for more details.
|
||||
* ns_sasl_plain: Adds the PLAIN mechanism which allows logging in to an
|
||||
account using a plain text username and password. See RFC
|
||||
4422 for more details.
|
||||
*/
|
||||
module { name = "ns_sasl_anonymous" }
|
||||
module { name = "ns_sasl_external" }
|
||||
module { name = "ns_sasl_plain" }
|
||||
|
||||
/*
|
||||
* ns_set
|
||||
*
|
||||
* Provides the commands:
|
||||
* nickserv/set, nickserv/saset - Dummy help wrappers for the SET and SASET commands.
|
||||
* nickserv/set/autoop, nickserv/saset/autoop - Determines whether or not modes are automatically set users when joining a channel.
|
||||
* nickserv/set/display, nickserv/saset/display - Used for setting a users display name.
|
||||
* nickserv/set/keepmodes, nickserv/saset/keepmodes - Configure whether or not services should retain a user's modes across sessions.
|
||||
* nickserv/set/neverop, nickserv/saset/neverop - Used to configure whether a user can be added to access lists
|
||||
* nickserv/saset/noexpire - Used for configuring noexpire, which prevents nicks from expiring.
|
||||
* nickserv/set/password, nickserv/saset/password - Used for changing a users password.
|
||||
*/
|
||||
@@ -624,18 +664,9 @@ module { name = "ns_set" }
|
||||
command { service = "NickServ"; name = "SET"; command = "nickserv/set"; }
|
||||
command { service = "NickServ"; name = "SASET"; command = "nickserv/saset"; permission = "nickserv/saset/"; group = "nickserv/admin"; }
|
||||
|
||||
command { service = "NickServ"; name = "SET AUTOOP"; command = "nickserv/set/autoop"; }
|
||||
command { service = "NickServ"; name = "SASET AUTOOP"; command = "nickserv/saset/autoop"; permission = "nickserv/saset/autoop"; }
|
||||
|
||||
command { service = "NickServ"; name = "SET DISPLAY"; command = "nickserv/set/display"; }
|
||||
command { service = "NickServ"; name = "SASET DISPLAY"; command = "nickserv/saset/display"; permission = "nickserv/saset/display"; }
|
||||
|
||||
command { service = "NickServ"; name = "SET PASSWORD"; command = "nickserv/set/password"; }
|
||||
command { service = "NickServ"; name = "SASET PASSWORD"; command = "nickserv/saset/password"; permission = "nickserv/saset/password"; }
|
||||
|
||||
command { service = "NickServ"; name = "SET NEVEROP"; command = "nickserv/set/neverop"; }
|
||||
command { service = "NickServ"; name = "SASET NEVEROP"; command = "nickserv/saset/neverop"; permission = "nickserv/saset/neverop"; }
|
||||
|
||||
command { service = "NickServ"; name = "SASET NOEXPIRE"; command = "nickserv/saset/noexpire"; permission = "nickserv/saset/noexpire"; }
|
||||
|
||||
/*
|
||||
@@ -670,6 +701,17 @@ module { name = "ns_set_language" }
|
||||
command { service = "NickServ"; name = "SET LANGUAGE"; command = "nickserv/set/language"; }
|
||||
command { service = "NickServ"; name = "SASET LANGUAGE"; command = "nickserv/saset/language"; permission = "nickserv/saset/language"; }
|
||||
|
||||
/*
|
||||
* ns_set_layout
|
||||
*
|
||||
* Provides the command nickserv/set/layout and nickserv/saset/layout.
|
||||
*
|
||||
* Allows configuring the layout that services uses.
|
||||
*/
|
||||
module { name = "ns_set_layout" }
|
||||
command { service = "NickServ"; name = "SET LAYOUT"; command = "nickserv/set/layout"; }
|
||||
command { service = "NickServ"; name = "SASET LAYOUT"; command = "nickserv/saset/layout"; permission = "nickserv/saset/layout"; }
|
||||
|
||||
/*
|
||||
* ns_set_message
|
||||
*
|
||||
@@ -696,24 +738,40 @@ command { service = "NickServ"; name = "SASET LANGUAGE"; command = "nickserv/sas
|
||||
module { name = "ns_set_misc" }
|
||||
command { service = "NickServ"; name = "SET URL"; command = "nickserv/set/misc"; misc_description = _("Associate a URL with your account"); }
|
||||
command { service = "NickServ"; name = "SASET URL"; command = "nickserv/saset/misc"; misc_description = _("Associate a URL with this account"); permission = "nickserv/saset/url"; group = "nickserv/admin"; }
|
||||
#command { service = "NickServ"; name = "SET DISCORD"; command = "nickserv/set/misc"; misc_description = _("Associate a Discord account with your account"); }
|
||||
#command { service = "NickServ"; name = "SASET DISCORD"; command = "nickserv/saset/misc"; misc_description = _("Associate a Discord account with this account"); permission = "nickserv/saset/discord"; group = "nickserv/admin"; }
|
||||
#command { service = "NickServ"; name = "SET MASTODON"; command = "nickserv/set/misc"; misc_description = _("Associate a Mastodon account with your account"); }
|
||||
#command { service = "NickServ"; name = "SASET MASTODON"; command = "nickserv/saset/misc"; misc_description = _("Associate a Mastodon account with this account"); permission = "nickserv/saset/mastodon"; group = "nickserv/admin"; }
|
||||
#command { service = "NickServ"; name = "SET TIMEZONE"; command = "nickserv/set/misc"; misc_description = _("Associate a time zone with your account"); }
|
||||
#command { service = "NickServ"; name = "SASET TIMEZONE"; command = "nickserv/saset/misc"; misc_description = _("Associate a time zone with this account"); permission = "nickserv/saset/timezone"; group = "nickserv/admin"; }
|
||||
#command { service = "NickServ"; name = "SET LOCATION"; command = "nickserv/set/misc"; misc_description = _("Associate a location with your account"); }
|
||||
#command { service = "NickServ"; name = "SASET LOCATION"; command = "nickserv/saset/misc"; misc_description = _("Associate a location with this account"); permission = "nickserv/saset/location"; group = "nickserv/admin"; }
|
||||
|
||||
/*
|
||||
* ns_set_op
|
||||
*
|
||||
* Provides the commands:
|
||||
* nickserv/set/autoop, nickserv/saset/autoop - Allows configuring whether status modes are automatically granted when joining a channel.
|
||||
* nickserv/set/neverop, nickserv/saset/neverop - Allows configuring whether a user can be added to access lists.
|
||||
*/
|
||||
module { name = "ns_set_op" }
|
||||
|
||||
command { service = "NickServ"; name = "SET AUTOOP"; command = "nickserv/set/autoop"; }
|
||||
command { service = "NickServ"; name = "SASET AUTOOP"; command = "nickserv/saset/autoop"; permission = "nickserv/saset/autoop"; }
|
||||
|
||||
command { service = "NickServ"; name = "SET NEVEROP"; command = "nickserv/set/neverop"; }
|
||||
command { service = "NickServ"; name = "SASET NEVEROP"; command = "nickserv/saset/neverop"; permission = "nickserv/saset/neverop"; }
|
||||
|
||||
# For compatibility with DALnet Services.
|
||||
command { service = "NickServ"; name = "SET NOOP"; command = "nickserv/set/neverop"; hide = yes; }
|
||||
command { service = "NickServ"; name = "SASET NOOP"; command = "nickserv/saset/neverop"; permission = "nickserv/saset/neverop"; hide = yes; }
|
||||
|
||||
/*
|
||||
* ns_set_protect
|
||||
*
|
||||
* Provides the commands nickserv/set/protect and kickserv/saset/protect.
|
||||
* Provides the commands nickserv/set/protect and nickserv/saset/protect.
|
||||
*
|
||||
* Used for configuring nickname protection.
|
||||
*/
|
||||
module { name = "ns_set_protect" }
|
||||
|
||||
command { service = "NickServ"; name = "SET PROTECT"; command = "nickserv/set/protect"; }
|
||||
command { service = "NickServ"; name = "SASET PROTECT"; command = "nickserv/saset/protect"; permission = "nickserv/saset/kill"; }
|
||||
command { service = "NickServ"; name = "SASET PROTECT"; command = "nickserv/saset/protect"; permission = "nickserv/saset/protect"; }
|
||||
|
||||
# For compatibility with Anope 2.0.
|
||||
command { service = "NickServ"; name = "SET KILL"; command = "nickserv/set/protect"; hide = yes; }
|
||||
|
||||
@@ -1,3 +1,35 @@
|
||||
Anope Version 2.1.18
|
||||
--------------------
|
||||
Added a check that a non-deprecated database module is loaded.
|
||||
Added support for flexible and monospace layouts to make text easier to read on clients that use a variable-width font.
|
||||
Added support for logging about deprecated modules on boot.
|
||||
Added support for per-IRCd hints when a link fails.
|
||||
Added support for self-service validation of vhosts using DNS TXT records.
|
||||
Added support for separate bad password limits for pre-connection SASL authentication.
|
||||
Added support for SRV and TXT records to the dns module.
|
||||
Added the --nodb option to disable database and encrytption module checks.
|
||||
Added the nickname registration date to the nickserv/glist output.
|
||||
Changed db_flatfile to be import-only (migrate to db_json).
|
||||
Changed the default registration confirmation type to code validation.
|
||||
Changed the fantasy !help command to not require the FANTASY privilege by default.
|
||||
Changed various length measurement code to be UTF-8 aware.
|
||||
Disabled the nickname registration delay by default.
|
||||
Fixed reporting the MySQL version that the mysql module was built against.
|
||||
Improved the layout of the nickserv/info command.
|
||||
Modularised the ns_sasl module to make it easier to pick SASL mechanisms.
|
||||
Moved duration rounding logic from Anope::Expires to Anope::Duration.
|
||||
Removed support for importing old databases from 1.8.
|
||||
Removed support for verifying "old MD5" passwords from 1.7.
|
||||
Reworked how memory is allocated when formatting messages.
|
||||
|
||||
Anope Version 2.1.17
|
||||
--------------------
|
||||
Allowed opers to resend passwords for users in nickserv/resend.
|
||||
Fixed HostServ using a different valid username character set to the protocol module.
|
||||
Fixed losing the channel and nickname registration time when upgrading from an earlier 2.1 release.
|
||||
Improved the messages sent when a user is forced off a protected nickname.
|
||||
Simplified copying modules to the runtime directory on Windows.
|
||||
|
||||
Anope Version 2.1.16
|
||||
--------------------
|
||||
Added support for on-IRC code confirmation.
|
||||
|
||||
@@ -1,3 +1,30 @@
|
||||
Anope Version 2.1.18
|
||||
--------------------
|
||||
Added the hostserv/validate command.
|
||||
Added the nickserv/saset/layout command.
|
||||
Added the nickserv/set/layout command.
|
||||
Added the ns_sasl_anonymous module.
|
||||
Added the ns_sasl_external module.
|
||||
Added the ns_sasl_plain module.
|
||||
Added the ns_set_layout module.
|
||||
Added the ns_set_op module.
|
||||
Added {hs_request}:validationcooldown (defaults to 5 minutes).
|
||||
Added {hs_request}:validationrecord (defaults to "anope-dns-validation").
|
||||
Added {ns_sasl}:badpasslimit (defaults to options:badpasslimit).
|
||||
Added {ns_sasl}:badpasstimeout (defaults to options:badpasstimeout).
|
||||
Moved nickserv/set/autoop and nickserv/saset/autoop to the ns_set_op module.
|
||||
Moved nickserv/set/display and nickserv/saset/display to the ns_set_group module.
|
||||
Moved nickserv/set/neverop and nickserv/saset/neverop to the ns_set_op module.
|
||||
Removed the db_old module.
|
||||
Removed the enc_old module.
|
||||
Removed {db_flatfile}:fork (module is now import-only).
|
||||
Removed {db_flatfile}:keepbackups (module is now import-only).
|
||||
Removed {db_flatfile}:nobackupokay (module is now import-only).
|
||||
|
||||
Anope Version 2.1.17
|
||||
--------------------
|
||||
Added the nickserv/resend oper privilege.
|
||||
|
||||
Anope Version 2.1.16
|
||||
--------------------
|
||||
Added fantasy:require_privilege (defaults to yes).
|
||||
|
||||
+4
-5
@@ -48,11 +48,10 @@ private:
|
||||
public:
|
||||
Anope::string nick;
|
||||
Anope::string last_quit;
|
||||
Anope::string last_realname;
|
||||
/* Last usermask this nick was seen on, eg user@host */
|
||||
Anope::string last_usermask;
|
||||
/* Last uncloaked usermask, requires nickserv/auspex to see */
|
||||
Anope::string last_realhost;
|
||||
/* Last cloaked user@host this nick was seen using. */
|
||||
Anope::string last_userhost;
|
||||
/* Last real user@host this nick was seen using. */
|
||||
Anope::string last_userhost_real;
|
||||
time_t registered = Anope::CurTime;
|
||||
time_t last_seen = Anope::CurTime;
|
||||
|
||||
|
||||
+5
-78
@@ -14,6 +14,7 @@
|
||||
#include <signal.h>
|
||||
|
||||
#include "hashcomp.h"
|
||||
#include "utfcpp/unchecked.h"
|
||||
|
||||
namespace Anope
|
||||
{
|
||||
@@ -129,6 +130,7 @@ namespace Anope
|
||||
* Returns the string's length.
|
||||
*/
|
||||
inline size_type length() const { return this->_string.length(); }
|
||||
inline size_type utf8length() const { return utf8::unchecked::distance(str().begin(), str().end()); }
|
||||
|
||||
/**
|
||||
* Returns the size of the currently allocated storage space in the string object.
|
||||
@@ -383,7 +385,7 @@ namespace Anope
|
||||
|
||||
/** Other command line options.
|
||||
*/
|
||||
extern CoreExport bool ReadOnly, NoFork, NoThird, NoPID, NoExpire, ProtocolDebug;
|
||||
extern CoreExport bool ReadOnly, NoFork, NoThird, NoDB, NoPID, NoExpire, ProtocolDebug;
|
||||
|
||||
/** The root of the Anope installation. Usually ~/anope
|
||||
*/
|
||||
@@ -463,33 +465,12 @@ namespace Anope
|
||||
extern CoreExport void Unhex(const string &src, string &dest);
|
||||
extern CoreExport void Unhex(const string &src, char *dest, size_t sz);
|
||||
|
||||
/** Base 64 encode a string
|
||||
* @param src The string to encode
|
||||
* @param target Where the encoded string is placed
|
||||
*/
|
||||
extern CoreExport void B64Encode(const string &src, string &target);
|
||||
|
||||
/** Base 64 decode a string
|
||||
* @param src The base64 encoded string
|
||||
* @param target The plain text result
|
||||
*/
|
||||
extern CoreExport void B64Decode(const string &src, string &target);
|
||||
|
||||
/** Encrypts what is in 'src' to 'dest'
|
||||
* @param src The source string to encrypt
|
||||
* @param dest The destination where the encrypted string is placed
|
||||
*/
|
||||
extern CoreExport bool Encrypt(const Anope::string &src, Anope::string &dest);
|
||||
|
||||
/** Returns a sequence of data formatted as the format argument specifies.
|
||||
** After the format parameter, the function expects at least as many
|
||||
** additional arguments as specified in format.
|
||||
* @param fmt Format of the Message
|
||||
* @param ... any number of parameters
|
||||
* @return a Anope::string
|
||||
*/
|
||||
extern CoreExport string printf(const char *fmt, ...) ATTR_FORMAT(1, 2);
|
||||
|
||||
/** Return the last error code
|
||||
* @return The error code
|
||||
*/
|
||||
@@ -513,9 +494,10 @@ namespace Anope
|
||||
/** Retrieves a human readable string representing the time in seconds
|
||||
* @param seconds The time on seconds, eg 60
|
||||
* @param nc The account to use language settings for to translate this string, if applicable
|
||||
* @param round Whether to round the duration to produce a shorter output.
|
||||
* @return A human readable string, eg "1 minute"
|
||||
*/
|
||||
extern CoreExport Anope::string Duration(time_t seconds, const NickCore *nc = NULL);
|
||||
extern CoreExport Anope::string Duration(time_t seconds, const NickCore *nc = nullptr, bool round = false);
|
||||
|
||||
/** Generates a human readable string of type "expires in ..."
|
||||
* @param expires time in seconds
|
||||
@@ -531,12 +513,6 @@ namespace Anope
|
||||
*/
|
||||
extern CoreExport Anope::string strftime(time_t t, const NickCore *nc = NULL, bool short_output = false);
|
||||
|
||||
/** Normalize buffer, stripping control characters and colors
|
||||
* @param A string to be parsed for control and color codes
|
||||
* @return A string stripped of control and color codes
|
||||
*/
|
||||
extern CoreExport Anope::string NormalizeBuffer(const Anope::string &);
|
||||
|
||||
/** Parses a raw message from the uplink and calls its command handler.
|
||||
* @param message Raw message from the uplink
|
||||
*/
|
||||
@@ -572,57 +548,8 @@ namespace Anope
|
||||
/** Generate a random number. */
|
||||
extern CoreExport int RandomNumber();
|
||||
|
||||
/** Calculates the levenshtein distance between two strings.
|
||||
* @param s1 The first string.
|
||||
* @param s2 The second string.
|
||||
*/
|
||||
extern CoreExport size_t Distance(const Anope::string &s1, const Anope::string &s2);
|
||||
|
||||
/** Update the current time. */
|
||||
extern CoreExport void UpdateTime();
|
||||
|
||||
/** Expands a path fragment that is relative to the base directory.
|
||||
* @param base The base directory that it is relative to.
|
||||
* @param fragment The fragment to expand.
|
||||
*/
|
||||
extern CoreExport Anope::string Expand(const Anope::string &base, const Anope::string &fragment);
|
||||
|
||||
/** Expands a config path. */
|
||||
inline auto ExpandConfig(const Anope::string &path) { return Expand(ConfigDir, path); }
|
||||
|
||||
/** Expands a data path. */
|
||||
inline auto ExpandData(const Anope::string &path) { return Expand(DataDir, path); }
|
||||
|
||||
/** Expands a locale path. */
|
||||
inline auto ExpandLocale(const Anope::string &path) { return Expand(LocaleDir, path); }
|
||||
|
||||
/** Expands a log path. */
|
||||
inline auto ExpandLog(const Anope::string &path) { return Expand(LogDir, path); }
|
||||
|
||||
/** Expands a module path. */
|
||||
inline auto ExpandModule(const Anope::string &path) { return Expand(ModuleDir, path); }
|
||||
|
||||
/** Formats a CTCP message for sending to a client.
|
||||
* @param name The name of the CTCP.
|
||||
* @param body If present then the body of the CTCP.
|
||||
* @return A formatted CTCP ready to send to a client.
|
||||
*/
|
||||
extern CoreExport Anope::string FormatCTCP(const Anope::string &name, const Anope::string &body = "");
|
||||
|
||||
/** Parses a CTCP message received from a client.
|
||||
* @param text The raw message to parse.
|
||||
* @param name The location to store the name of the CTCP.
|
||||
* @param body The location to store body of the CTCP if one is present.
|
||||
* @return True if the message was a well formed CTCP; otherwise, false.
|
||||
*/
|
||||
extern CoreExport bool ParseCTCP(const Anope::string &text, Anope::string &name, Anope::string &body);
|
||||
|
||||
/** Replaces template variables within a string with values from a map.
|
||||
* @param str The string to template from.
|
||||
* @param vars The variables to replace within the string.
|
||||
* @return The specified string with all variables replaced within it.
|
||||
*/
|
||||
extern CoreExport Anope::string Template(const Anope::string &str, const Anope::map<Anope::string> &vars);
|
||||
}
|
||||
|
||||
/** sepstream allows for splitting token separated lists.
|
||||
|
||||
@@ -114,7 +114,6 @@ namespace Language
|
||||
"cannot contain the space or tab characters.")
|
||||
#define PASSWORD_TOO_SHORT _("Your password is too short. It must be longer than %u characters.")
|
||||
#define PASSWORD_TOO_LONG _("Your password is too long. It must be shorter than %u characters.")
|
||||
#define NICK_NOT_REGISTERED _("Your nick isn't registered.")
|
||||
#define NICK_X_NOT_REGISTERED _("Nick \002%s\002 isn't registered.")
|
||||
#define NICK_X_NOT_IN_USE _("Nick \002%s\002 isn't currently in use.")
|
||||
#define NICK_X_NOT_ON_CHAN _("\002%s\002 is not currently on channel %s.")
|
||||
@@ -127,10 +126,6 @@ namespace Language
|
||||
#define UNKNOWN _("<unknown>")
|
||||
#define NO_EXPIRE _("does not expire")
|
||||
#define LIST_INCORRECT_RANGE _("Incorrect range specified. The correct syntax is \002#\037from\037-\037to\037\002.")
|
||||
#define NICK_IS_SECURE _("This nickname is registered and protected. If it is your " \
|
||||
"nick, type \002%s\032\037password\037\002. Otherwise, " \
|
||||
"please choose a different nick.")
|
||||
#define FORCENICKCHANGE_NOW _("This nickname has been registered; you may not use it.")
|
||||
#define NICK_CANNOT_BE_REGISTERED _("Nickname \002%s\002 may not be registered.")
|
||||
#define NICK_ALREADY_REGISTERED _("Nickname \002%s\002 is already registered!")
|
||||
#define NICK_SET_DISPLAY_CHANGED _("The new display is now \002%s\002.")
|
||||
|
||||
@@ -57,35 +57,3 @@ public:
|
||||
*/
|
||||
virtual bool InvalidRange(const Anope::string &list);
|
||||
};
|
||||
|
||||
/** This class handles formatting LIST/VIEW replies.
|
||||
*/
|
||||
class CoreExport ListFormatter final
|
||||
{
|
||||
public:
|
||||
typedef std::map<Anope::string, Anope::string> ListEntry;
|
||||
private:
|
||||
NickCore *nc;
|
||||
std::vector<Anope::string> columns;
|
||||
std::vector<ListEntry> entries;
|
||||
public:
|
||||
ListFormatter(NickCore *nc);
|
||||
ListFormatter &AddColumn(const Anope::string &name);
|
||||
void AddEntry(const ListEntry &entry);
|
||||
bool IsEmpty() const;
|
||||
void Process(std::vector<Anope::string> &);
|
||||
};
|
||||
|
||||
/** This class handles formatting INFO replies
|
||||
*/
|
||||
class CoreExport InfoFormatter final
|
||||
{
|
||||
NickCore *nc;
|
||||
std::vector<std::pair<Anope::string, Anope::string> > replies;
|
||||
unsigned longest = 0;
|
||||
public:
|
||||
InfoFormatter(NickCore *nc);
|
||||
void Process(std::vector<Anope::string> &);
|
||||
Anope::string &operator[](const Anope::string &key);
|
||||
void AddOption(const Anope::string &opt);
|
||||
};
|
||||
|
||||
+15
-10
@@ -70,11 +70,6 @@ public:
|
||||
*/
|
||||
Mode(const Anope::string &mname, ModeClass mclass, char mc, ModeType type);
|
||||
virtual ~Mode() = default;
|
||||
|
||||
/** Can a user set this mode, used for mlock
|
||||
* @param u The user
|
||||
*/
|
||||
virtual bool CanSet(User *u) const;
|
||||
};
|
||||
|
||||
/** This class is a user mode, all user modes use this/inherit from this
|
||||
@@ -88,6 +83,12 @@ public:
|
||||
* @param mc The mode char
|
||||
*/
|
||||
UserMode(const Anope::string &name, char mc);
|
||||
|
||||
/** Can a user set this mode, used for mlock
|
||||
* @param source The user who is setting the mode.
|
||||
* @param target The user the mode is being set on.
|
||||
*/
|
||||
virtual bool CanSet(User *source, User *target) const { return true; }
|
||||
};
|
||||
|
||||
class CoreExport UserModeParam
|
||||
@@ -122,7 +123,11 @@ public:
|
||||
*/
|
||||
ChannelMode(const Anope::string &name, char mc);
|
||||
|
||||
bool CanSet(User *u) const override;
|
||||
/** Can a user set this mode, used for mlock
|
||||
* @param u The user who is setting the mode.
|
||||
* @param c The channel the mode is being set on.
|
||||
*/
|
||||
virtual bool CanSet(User *u, Channel *c) const;
|
||||
|
||||
virtual void Check() { }
|
||||
|
||||
@@ -270,7 +275,7 @@ class CoreExport UserModeOperOnly
|
||||
public:
|
||||
UserModeOperOnly(const Anope::string &mname, char um) : UserMode(mname, um) { }
|
||||
|
||||
bool CanSet(User *u) const override;
|
||||
bool CanSet(User *source, User *target) const override;
|
||||
};
|
||||
|
||||
class CoreExport UserModeNoone
|
||||
@@ -279,7 +284,7 @@ class CoreExport UserModeNoone
|
||||
public:
|
||||
UserModeNoone(const Anope::string &mname, char um) : UserMode(mname, um) { }
|
||||
|
||||
bool CanSet(User *u) const override;
|
||||
bool CanSet(User *source, User *target) const override;
|
||||
};
|
||||
|
||||
/** Channel mode +k (key)
|
||||
@@ -302,7 +307,7 @@ public:
|
||||
ChannelModeOperOnly(const Anope::string &mname, char mc) : ChannelMode(mname, mc) { }
|
||||
|
||||
/* Opers only */
|
||||
bool CanSet(User *u) const override;
|
||||
bool CanSet(User *u, Channel *c) const override;
|
||||
};
|
||||
|
||||
/** This class is used for channel modes only servers may set
|
||||
@@ -313,7 +318,7 @@ class CoreExport ChannelModeNoone
|
||||
public:
|
||||
ChannelModeNoone(const Anope::string &mname, char mc) : ChannelMode(mname, mc) { }
|
||||
|
||||
bool CanSet(User *u) const override;
|
||||
bool CanSet(User *u, Channel *c) const override;
|
||||
};
|
||||
|
||||
/** This is the mode manager
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "services.h"
|
||||
#include "socketengine.h"
|
||||
#include "sockets.h"
|
||||
#include "textproc.h"
|
||||
#include "threadengine.h"
|
||||
#include "timers.h"
|
||||
#include "uplink.h"
|
||||
|
||||
+7
-3
@@ -158,7 +158,9 @@ enum
|
||||
PSEUDOCLIENT = 1 << 5,
|
||||
/* Module provides IRCd protocol support */
|
||||
PROTOCOL = 1 << 6,
|
||||
MT_END = 1 << 7
|
||||
/* Module is deprecated */
|
||||
DEPRECATED = 1 << 7,
|
||||
MT_END = DEPRECATED,
|
||||
};
|
||||
typedef unsigned short ModType;
|
||||
|
||||
@@ -1046,9 +1048,10 @@ public:
|
||||
|
||||
/** Called to determine if a channel mode can be set by a user
|
||||
* @param u The user
|
||||
* @param c The channel
|
||||
* @param cm The mode
|
||||
*/
|
||||
virtual EventReturn OnCanSet(User *u, const ChannelMode *cm) ATTR_NOT_NULL(2, 3) { throw NotImplementedException(); }
|
||||
virtual EventReturn OnCanSet(User *u, Channel *c, const ChannelMode *cm) ATTR_NOT_NULL(2, 3) { throw NotImplementedException(); }
|
||||
|
||||
virtual EventReturn OnCheckDelete(Channel *c) ATTR_NOT_NULL(2) { throw NotImplementedException(); }
|
||||
|
||||
@@ -1245,9 +1248,10 @@ public:
|
||||
|
||||
/** Find the first module of a certain type
|
||||
* @param type The module type
|
||||
* @param ignoredeprecated Whether to ignore deprecated modules.
|
||||
* @return The module
|
||||
*/
|
||||
static Module *FindFirstOf(ModType type);
|
||||
static Module *FindFirstOf(ModType type, bool ignoredeprecated = false);
|
||||
|
||||
/** Checks whether this version of Anope is at least major.minor.patch.build
|
||||
* Throws a ModuleException if not
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace DNS
|
||||
{
|
||||
/** Valid query types
|
||||
@@ -29,8 +31,12 @@ namespace DNS
|
||||
QUERY_SOA = 6,
|
||||
/* Reverse DNS lookup */
|
||||
QUERY_PTR = 12,
|
||||
/* TXT lookup */
|
||||
QUERY_TXT = 16,
|
||||
/* IPv6 AAAA lookup */
|
||||
QUERY_AAAA = 28,
|
||||
/** SRV lookup */
|
||||
QUERY_SRV = 33,
|
||||
/* Zone transfer */
|
||||
QUERY_AXFR = 252,
|
||||
/* A lookup for any record */
|
||||
@@ -88,11 +94,23 @@ namespace DNS
|
||||
};
|
||||
};
|
||||
|
||||
namespace Record
|
||||
{
|
||||
struct SRV final
|
||||
{
|
||||
uint16_t priority = UINT16_MAX;
|
||||
uint16_t weight = 0;
|
||||
uint16_t port = 0;
|
||||
Anope::string host;
|
||||
};
|
||||
}
|
||||
|
||||
struct ResourceRecord final
|
||||
: Question
|
||||
{
|
||||
unsigned int ttl = 0;
|
||||
Anope::string rdata;
|
||||
std::shared_ptr<void> rdataobj;
|
||||
time_t created;
|
||||
|
||||
ResourceRecord(const Anope::string &n, QueryType t, unsigned short c = 1) : Question(n, t, c), created(Anope::CurTime) { }
|
||||
|
||||
@@ -18,6 +18,8 @@ public:
|
||||
Anope::string ident;
|
||||
Anope::string host;
|
||||
time_t time = 0;
|
||||
Anope::string validation_token;
|
||||
time_t last_validation = 0;
|
||||
|
||||
virtual ~HostRequest() = default;
|
||||
};
|
||||
|
||||
@@ -151,7 +151,7 @@ struct SASL::Session
|
||||
if (u)
|
||||
return u->GetMask();
|
||||
if (!hostname.empty() && !ip.empty())
|
||||
return Anope::printf("%s (%s)", hostname.c_str(), ip.c_str());
|
||||
return Anope::Format("%s (%s)", hostname.c_str(), ip.c_str());
|
||||
return "A user";
|
||||
};
|
||||
};
|
||||
|
||||
@@ -228,9 +228,7 @@ public:
|
||||
if (header.compare(0, 7, "Bearer ", 7) != 0)
|
||||
return false; // No token provided.
|
||||
|
||||
Anope::string rawtoken;
|
||||
Anope::B64Decode(header.substr(7), rawtoken);
|
||||
|
||||
auto rawtoken = Anope::B64Decode(header.substr(7));
|
||||
for (const auto &token : tokens)
|
||||
{
|
||||
if (!CompareToken(token, rawtoken))
|
||||
|
||||
+5
-1
@@ -131,11 +131,15 @@ public:
|
||||
/* The maximum length of a username. */
|
||||
size_t MaxUser = 0;
|
||||
|
||||
|
||||
/* Retrieves the next free UID or SID */
|
||||
virtual Anope::string UID_Retrieve();
|
||||
virtual Anope::string SID_Retrieve();
|
||||
|
||||
/** Retrieves advice on what might be the problem when a link fails.
|
||||
* @param advice The location to store the advice.
|
||||
*/
|
||||
virtual void GetLinkAdvice(std::vector<Anope::string> &advice) { }
|
||||
|
||||
/** Extracts a timestamp from a string. */
|
||||
virtual time_t ExtractTimestamp(const Anope::string &str);
|
||||
|
||||
|
||||
@@ -11,6 +11,93 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define ANOPE_FORMAT(LAST, FORMAT, BUFFER) \
|
||||
do { \
|
||||
va_list _valist; \
|
||||
va_start(_valist, LAST); \
|
||||
(BUFFER) = Anope::Format(_valist, (FORMAT)); \
|
||||
va_end(_valist); \
|
||||
} while (false);
|
||||
|
||||
namespace Anope
|
||||
{
|
||||
/** Encode a string as base-64.
|
||||
* @param str The string to encode as base-64 .
|
||||
*/
|
||||
extern CoreExport Anope::string B64Encode(const Anope::string &str);
|
||||
|
||||
/** Decode a string from base-64.
|
||||
* @param str The string to decode from base-64.
|
||||
*/
|
||||
extern CoreExport Anope::string B64Decode(const Anope::string &str);
|
||||
|
||||
/** Calculates the levenshtein distance between two strings.
|
||||
* @param s1 The first string.
|
||||
* @param s2 The second string.
|
||||
*/
|
||||
extern CoreExport size_t Distance(const Anope::string &s1, const Anope::string &s2);
|
||||
|
||||
/** Expands a path fragment that is relative to the base directory.
|
||||
* @param base The base directory that it is relative to.
|
||||
* @param fragment The fragment to expand.
|
||||
*/
|
||||
extern CoreExport Anope::string Expand(const Anope::string &base, const Anope::string &fragment);
|
||||
|
||||
/** Expands a config path. */
|
||||
inline auto ExpandConfig(const Anope::string &path) { return Expand(ConfigDir, path); }
|
||||
|
||||
/** Expands a data path. */
|
||||
inline auto ExpandData(const Anope::string &path) { return Expand(DataDir, path); }
|
||||
|
||||
/** Expands a locale path. */
|
||||
inline auto ExpandLocale(const Anope::string &path) { return Expand(LocaleDir, path); }
|
||||
|
||||
/** Expands a log path. */
|
||||
inline auto ExpandLog(const Anope::string &path) { return Expand(LogDir, path); }
|
||||
|
||||
/** Expands a module path. */
|
||||
inline auto ExpandModule(const Anope::string &path) { return Expand(ModuleDir, path); }
|
||||
|
||||
/** Formats a string using one or more values. This uses snprintf internally but with a flexible buffer.
|
||||
* @param fmt The message to format.
|
||||
*/
|
||||
extern CoreExport Anope::string Format(const char *fmt, ...) ATTR_FORMAT(1, 2);
|
||||
|
||||
/** Formats a string using a list of values. This uses snprintf internally but with a flexible buffer.
|
||||
* @param valist A list of values to format the message with.
|
||||
* @param fmt The message to format.
|
||||
*/
|
||||
extern CoreExport Anope::string Format(va_list &valist, const char *fmt) ATTR_FORMAT(2, 0);
|
||||
|
||||
/** Formats a CTCP message for sending to a client.
|
||||
* @param name The name of the CTCP.
|
||||
* @param body If present then the body of the CTCP.
|
||||
* @return A formatted CTCP ready to send to a client.
|
||||
*/
|
||||
extern CoreExport Anope::string FormatCTCP(const Anope::string &name, const Anope::string &body = "");
|
||||
|
||||
/** Parses a CTCP message received from a client.
|
||||
* @param text The raw message to parse.
|
||||
* @param name The location to store the name of the CTCP.
|
||||
* @param body The location to store body of the CTCP if one is present.
|
||||
* @return True if the message was a well formed CTCP; otherwise, false.
|
||||
*/
|
||||
extern CoreExport bool ParseCTCP(const Anope::string &text, Anope::string &name, Anope::string &body);
|
||||
|
||||
/** Remove all formatting characters from a string.
|
||||
* @param text A string containing formatting characters.
|
||||
* @return A copy of \p text with all formatting characters removed.
|
||||
*/
|
||||
extern CoreExport Anope::string RemoveFormatting(const Anope::string &text);
|
||||
|
||||
/** Replaces template variables within a string with values from a map.
|
||||
* @param str The string to template from.
|
||||
* @param vars The variables to replace within the string.
|
||||
* @return The specified string with all variables replaced within it.
|
||||
*/
|
||||
extern CoreExport Anope::string Template(const Anope::string &str, const Anope::map<Anope::string> &vars);
|
||||
}
|
||||
|
||||
class CoreExport HelpWrapper final
|
||||
{
|
||||
private:
|
||||
@@ -22,6 +109,21 @@ public:
|
||||
void SendTo(CommandSource &source);
|
||||
};
|
||||
|
||||
class CoreExport InfoFormatter final
|
||||
{
|
||||
private:
|
||||
size_t longest = 0;
|
||||
NickCore *nc;
|
||||
std::vector<Anope::string> options;
|
||||
std::vector<std::pair<Anope::string, Anope::string>> replies;
|
||||
|
||||
public:
|
||||
InfoFormatter(NickCore *nc);
|
||||
Anope::string &operator[](const Anope::string &key);
|
||||
void AddOption(const Anope::string &opt);
|
||||
void SendTo(CommandSource &source);
|
||||
};
|
||||
|
||||
class CoreExport LineWrapper final
|
||||
{
|
||||
private:
|
||||
@@ -34,3 +136,26 @@ public:
|
||||
bool GetLine(Anope::string &out);
|
||||
};
|
||||
|
||||
class CoreExport ListFormatter final
|
||||
{
|
||||
public:
|
||||
using ListEntry = std::map<Anope::string, Anope::string>;
|
||||
using FlexibleFormatFn = std::function<Anope::string(ListEntry &)>;
|
||||
|
||||
private:
|
||||
std::vector<Anope::string> columns;
|
||||
std::vector<ListEntry> entries;
|
||||
FlexibleFormatFn flexiblerow;
|
||||
NickCore *nc;
|
||||
void SendFixed(CommandSource &source);
|
||||
void SendFlexible(CommandSource &source);
|
||||
|
||||
public:
|
||||
ListFormatter(NickCore *nc);
|
||||
ListFormatter &AddColumn(const Anope::string &name);
|
||||
void AddEntry(const ListEntry &entry);
|
||||
bool IsEmpty() const;
|
||||
void SendTo(CommandSource &source);
|
||||
void SetFlexible(const Anope::string &format);
|
||||
void SetFlexible(const FlexibleFormatFn &formatter);
|
||||
};
|
||||
|
||||
+1
-1
@@ -253,7 +253,7 @@ public:
|
||||
*/
|
||||
bool HasPriv(const Anope::string &privstr);
|
||||
|
||||
/** Update the last usermask stored for a user. */
|
||||
/** Update the last mask stored for a user. */
|
||||
void UpdateHost();
|
||||
|
||||
/** Update the away state for a user. */
|
||||
|
||||
+263
-45
@@ -7,8 +7,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Anope\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-06-25 15:33+0100\n"
|
||||
"PO-Revision-Date: 2025-06-25 15:33+0100\n"
|
||||
"POT-Creation-Date: 2025-09-29 12:59+0100\n"
|
||||
"PO-Revision-Date: 2025-09-29 13:00+0100\n"
|
||||
"Last-Translator: Sadie Powell <sadie@witchery.services>\n"
|
||||
"Language-Team: English\n"
|
||||
"Language: en_US\n"
|
||||
@@ -342,6 +342,66 @@ msgstr ""
|
||||
msgid "[auto-memo] The memo you sent to %s has been viewed."
|
||||
msgstr ""
|
||||
|
||||
msgid "{host}: {session} sessions"
|
||||
msgstr ""
|
||||
|
||||
msgid "{mask}"
|
||||
msgstr ""
|
||||
|
||||
msgid "{mask} -- created by {creator}; {expires}"
|
||||
msgstr ""
|
||||
|
||||
msgid "{mask} -- created by {creator}; {expires} ({reason})"
|
||||
msgstr ""
|
||||
|
||||
msgid "{mask} on {type} -- created by {creator}; expires in {expires}"
|
||||
msgstr ""
|
||||
|
||||
msgid "{mask} on {type} -- created by {creator}; expires in {expires} ({reason})"
|
||||
msgstr ""
|
||||
|
||||
msgid "{name}"
|
||||
msgstr ""
|
||||
|
||||
msgid "{name} ({description})"
|
||||
msgstr ""
|
||||
|
||||
msgid "{name} ({mask}) [{realname}]"
|
||||
msgstr ""
|
||||
|
||||
msgid "{name} -- {users} user(s); +{modes}"
|
||||
msgstr ""
|
||||
|
||||
msgid "{name} -- {users} user(s); +{modes} ({topic})"
|
||||
msgstr ""
|
||||
|
||||
msgid "{name} = {level}"
|
||||
msgstr ""
|
||||
|
||||
msgid "{name} = {value}"
|
||||
msgstr ""
|
||||
|
||||
msgid "{name}: {description}"
|
||||
msgstr ""
|
||||
|
||||
msgid "{nick}"
|
||||
msgstr ""
|
||||
|
||||
msgid "{nick} (last mask: {last_mask})"
|
||||
msgstr ""
|
||||
|
||||
msgid "{nick} ({mask}) [{real_name}]"
|
||||
msgstr ""
|
||||
|
||||
msgid "{nick}: registered on {registered}"
|
||||
msgstr ""
|
||||
|
||||
msgid "{nick}: registered on {registered}; expires in {expires}"
|
||||
msgstr ""
|
||||
|
||||
msgid "{}{module_name}}:{name} = {value}"
|
||||
msgstr ""
|
||||
|
||||
msgid "[target] [password]"
|
||||
msgstr ""
|
||||
|
||||
@@ -597,7 +657,10 @@ msgstr ""
|
||||
msgid "nickname [+expiry] [reason]"
|
||||
msgstr ""
|
||||
|
||||
msgid "nickname {EMAIL | STATUS | USERMASK | QUIT} {ON | OFF}"
|
||||
msgid "nickname {EMAIL | STATUS | MASK | QUIT} {ON | OFF}"
|
||||
msgstr ""
|
||||
|
||||
msgid "nickname {FIXED | FLEXIBLE | MONOSPACE}"
|
||||
msgstr ""
|
||||
|
||||
msgid "nickname {ON | delay | OFF}"
|
||||
@@ -929,10 +992,6 @@ msgstr ""
|
||||
msgid "%s has no new memos."
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "%s is %s"
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "%s is a Services Operator of type %s."
|
||||
msgstr ""
|
||||
@@ -1263,9 +1322,6 @@ msgstr ""
|
||||
msgid "Account %s has already reached the maximum number of simultaneous logins (%u)."
|
||||
msgstr ""
|
||||
|
||||
msgid "Account id"
|
||||
msgstr ""
|
||||
|
||||
msgid "Account registered"
|
||||
msgstr ""
|
||||
|
||||
@@ -1341,6 +1397,9 @@ msgstr ""
|
||||
msgid "Additionally, Services Operators with the nickserv/drop/override permission can replace code with OVERRIDE to drop without a confirmation code."
|
||||
msgstr ""
|
||||
|
||||
msgid "Additionally, Services Operators with the nickserv/resend permission can specify a nickname to resend a confirmation email for another account."
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "Additionally, if fantasy is enabled fantasy commands can be executed by prefixing the command name with one of the following characters: %s"
|
||||
msgstr ""
|
||||
@@ -1520,7 +1579,7 @@ msgstr ""
|
||||
msgid ""
|
||||
"Allows Services Operators to manipulate the AKILL list. If a user matching an AKILL mask attempts to connect, services will issue a KILL for that user and, on supported server types, will instruct all servers to add a ban for the mask which the user matched.\n"
|
||||
"\n"
|
||||
"%sADD adds the given mask to the AKILL list for the given reason, which must be given. Mask should be in the format of nick!user@host#realname, though all that is required is user@host. If a real name is specified, the reason must be prepended with a :. expiry is specified as an integer followed by one of d (days), h (hours), or m (minutes). Combinations (such as 1h30m) are not permitted. If a unit specifier is not included, the default is days (so +30 by itself means 30 days). To add an AKILL which does not expire, use +0. If the usermask to be added starts with a +, an expiry time must be given, even if it is the same as the default. The current AKILL default expiry time can be found with the STATSAKILL command."
|
||||
"%sADD adds the given mask to the AKILL list for the given reason, which must be given. Mask should be in the format of nick!user@host#realname, though all that is required is user@host. If a real name is specified, the reason must be prepended with a :. expiry is specified as an integer followed by one of d (days), h (hours), or m (minutes). Combinations (such as 1h30m) are not permitted. If a unit specifier is not included, the default is days (so +30 by itself means 30 days). To add an AKILL which does not expire, use +0. If the mask to be added starts with a +, an expiry time must be given, even if it is the same as the default. The current AKILL default expiry time can be found with the STATSAKILL command."
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
@@ -1631,11 +1690,11 @@ msgid "Allows you to kill a user from the network. Parameters are the same as fo
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "Allows you to prevent certain pieces of information from being displayed when someone does a %sINFO on the nick. You can hide the email address (EMAIL), last seen user@host mask (USERMASK), the services access status (STATUS) and last quit message (QUIT). The second parameter specifies whether the information should be displayed (OFF) or hidden (ON)."
|
||||
msgid "Allows you to prevent certain pieces of information from being displayed when someone does a %sINFO on the nick. You can hide the email address (EMAIL), last seen user@host mask (MASK), the services access status (STATUS) and last quit message (QUIT). The second parameter specifies whether the information should be displayed (OFF) or hidden (ON)."
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "Allows you to prevent certain pieces of information from being displayed when someone does a %sINFO on your nick. You can hide your email address(EMAIL), last seen user@host mask (USERMASK), your services access status (STATUS) and last quit message (QUIT). The second parameter specifies whether the information should be displayed (OFF) or hidden (ON)."
|
||||
msgid "Allows you to prevent certain pieces of information from being displayed when someone does a %sINFO on your nick. You can hide your email address(EMAIL), last seen user@host mask (MASK), your services access status (STATUS) and last quit message (QUIT). The second parameter specifies whether the information should be displayed (OFF) or hidden (ON)."
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
@@ -1688,7 +1747,7 @@ msgstr ""
|
||||
msgid "Associates the given email address with your nickname. This address will be displayed whenever someone requests information on the nickname with the INFO command."
|
||||
msgstr ""
|
||||
|
||||
msgid "Auto-op"
|
||||
msgid "Auto op"
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
@@ -1910,9 +1969,6 @@ msgstr ""
|
||||
msgid "Bot won't kick for repeats anymore."
|
||||
msgstr ""
|
||||
|
||||
msgid "By"
|
||||
msgstr ""
|
||||
|
||||
msgid "CLEAR target"
|
||||
msgstr ""
|
||||
|
||||
@@ -2203,6 +2259,29 @@ msgstr ""
|
||||
msgid "Configures reverses kicker"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"Configures the layout used by the account for services messages.\n"
|
||||
"\n"
|
||||
"When the layout is set to FIXED services will use tables and position text such that it looks good in a client that uses a fixed-width font.\n"
|
||||
"\n"
|
||||
"When the layout is set to FLEXIBLE services will use an alternate format for messages and avoid any positioning that might be broken by a variable-width font.\n"
|
||||
"\n"
|
||||
"When the layout is set to MONOSPACE services will format messages similar to FIXED but will prefix all messages with a monospace formatting character to force it to display correctly. This requires client support for monospace text formatting."
|
||||
msgstr ""
|
||||
|
||||
msgid "Configures the layout used for services messages"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"Configures the layout used for services messages.\n"
|
||||
"\n"
|
||||
"When the layout is set to FIXED services will use tables and position text such that it looks good in a client that uses a fixed-width font.\n"
|
||||
"\n"
|
||||
"When the layout is set to FLEXIBLE services will use an alternate format for messages and avoid any positioning that might be broken by a variable-width font.\n"
|
||||
"\n"
|
||||
"When the layout is set to MONOSPACE services will format messages similar to FIXED but will prefix all messages with a monospace formatting character to force it to display correctly. This requires client support for monospace text formatting."
|
||||
msgstr ""
|
||||
|
||||
msgid "Configures the time bot bans expire in"
|
||||
msgstr ""
|
||||
|
||||
@@ -2885,6 +2964,9 @@ msgstr ""
|
||||
msgid "Fingerprint %s is already in use."
|
||||
msgstr ""
|
||||
|
||||
msgid "Fixed layout"
|
||||
msgstr ""
|
||||
|
||||
msgid "Flags"
|
||||
msgstr ""
|
||||
|
||||
@@ -2896,6 +2978,9 @@ msgstr ""
|
||||
msgid "Flags list for %s"
|
||||
msgstr ""
|
||||
|
||||
msgid "Flexible layout"
|
||||
msgstr ""
|
||||
|
||||
msgid "Flood kicker"
|
||||
msgstr ""
|
||||
|
||||
@@ -3012,10 +3097,6 @@ msgstr ""
|
||||
msgid "Identify yourself with your password"
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "If you do not change within %s, I will change your nick."
|
||||
msgstr ""
|
||||
|
||||
msgid "Ignore list has been cleared."
|
||||
msgstr ""
|
||||
|
||||
@@ -3039,6 +3120,10 @@ msgstr ""
|
||||
msgid "Information about channel %s:"
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "Information about nick %s:"
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "Invalid duration %s, using %d days."
|
||||
msgstr ""
|
||||
@@ -3133,6 +3218,9 @@ msgstr ""
|
||||
msgid "Language for %s changed to %s."
|
||||
msgstr ""
|
||||
|
||||
msgid "Last mask"
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "Last memo to %s has been cancelled."
|
||||
msgstr ""
|
||||
@@ -3143,7 +3231,7 @@ msgstr ""
|
||||
msgid "Last seen"
|
||||
msgstr ""
|
||||
|
||||
msgid "Last seen address"
|
||||
msgid "Last seen mask"
|
||||
msgstr ""
|
||||
|
||||
msgid "Last topic"
|
||||
@@ -3152,7 +3240,16 @@ msgstr ""
|
||||
msgid "Last used"
|
||||
msgstr ""
|
||||
|
||||
msgid "Last usermask"
|
||||
#, c-format
|
||||
msgid "Layout is now fixed for %s."
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "Layout is now flexible for %s."
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "Layout is now monospace for %s."
|
||||
msgstr ""
|
||||
|
||||
msgid "Level"
|
||||
@@ -3390,7 +3487,7 @@ msgstr ""
|
||||
msgid ""
|
||||
"Maintains the AutoKick list for a channel. If a user on the AutoKick list attempts to join the channel, %s will ban that user from the channel, then kick the user.\n"
|
||||
"\n"
|
||||
"The %sADD command adds the given nick or usermask to the AutoKick list. If a reason is given with the command, that reason will be used when the user is kicked; if not, the default reason is \"User has been banned from the channel\". When akicking a registered nick the %s account will be added to the akick list instead of the mask. All users within that nickgroup will then be akicked. \n"
|
||||
"The %sADD command adds the given nick or mask to the AutoKick list. If a reason is given with the command, that reason will be used when the user is kicked; if not, the default reason is \"User has been banned from the channel\". When akicking a registered nick the %s account will be added to the akick list instead of the mask. All users within that nickgroup will then be akicked. \n"
|
||||
"\n"
|
||||
"The %sDEL command removes the given nick or mask from the AutoKick list. It does not, however, remove any bans placed by an AutoKick; those must be removed manually.\n"
|
||||
"\n"
|
||||
@@ -3414,7 +3511,7 @@ msgstr ""
|
||||
msgid ""
|
||||
"Maintains the bad words list for a channel. The bad words list determines which words are to be kicked when the bad words kicker is enabled. For more information, type %sKICK%s.\n"
|
||||
"\n"
|
||||
"The ADD command adds the given word to the bad words list. If SINGLE is specified, a kick will be done only if a user says the entire word. If START is specified, a kick will be done if a user says a word that starts with word. If END is specified, a kick will be done if a user says a word that ends with word. If you don't specify anything, a kick will be issued every time word is said by a user.\n"
|
||||
"The ADD command adds the given word to the bad words list. If SINGLE is specified, a kick will be done only if a user says the entire word. If START is specified, a kick will be done if a user says a word that starts with word. If END is specified, a kick will be done if a user says a word that ends with word. If you don't specify anything, a kick will be issued every time word is said by a user. This will be shown in the LIST output as ANY.\n"
|
||||
"\n"
|
||||
"The DEL command removes the given word from the bad words list. If a list of entry numbers is given, those entries are deleted. (See the example for LIST below.)\n"
|
||||
"\n"
|
||||
@@ -3643,6 +3740,9 @@ msgstr ""
|
||||
msgid "Module: %s [%s] [%s]"
|
||||
msgstr ""
|
||||
|
||||
msgid "Monospace layout"
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "Multiple languages matched %s. Please be more specific."
|
||||
msgstr ""
|
||||
@@ -3663,7 +3763,7 @@ msgstr ""
|
||||
msgid "Never"
|
||||
msgstr ""
|
||||
|
||||
msgid "Never-op"
|
||||
msgid "Never op"
|
||||
msgstr ""
|
||||
|
||||
msgid "Nick"
|
||||
@@ -3677,6 +3777,10 @@ msgstr ""
|
||||
msgid "Nick %s is already an operator."
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "Nick %s is already confirmed."
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "Nick %s is an illegal nickname and cannot be used."
|
||||
msgstr ""
|
||||
@@ -3782,7 +3886,7 @@ msgstr ""
|
||||
msgid "No bot"
|
||||
msgstr ""
|
||||
|
||||
msgid "No expire"
|
||||
msgid "No expiry"
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
@@ -4497,6 +4601,9 @@ msgstr ""
|
||||
msgid "Server %s must be quit before it can be deleted."
|
||||
msgstr ""
|
||||
|
||||
msgid "Server: {server} = {ip} -- limit: {limit}; state: {state}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Servers"
|
||||
msgstr ""
|
||||
|
||||
@@ -5165,6 +5272,10 @@ msgstr ""
|
||||
msgid "The Defcon level is now at: %d"
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "The confirmation code for %s has been re-sent to %s."
|
||||
msgstr ""
|
||||
|
||||
msgid "The defcon system can be used to implement a pre-defined set of restrictions to services useful during an attempted attack on the network."
|
||||
msgstr ""
|
||||
|
||||
@@ -5453,7 +5564,7 @@ msgstr ""
|
||||
msgid "This command unloads the module named modname."
|
||||
msgstr ""
|
||||
|
||||
msgid "This command will resend you the registration confirmation email."
|
||||
msgid "This command will resend a registration confirmation email."
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
@@ -5468,14 +5579,11 @@ msgstr ""
|
||||
msgid "This nickname has been recovered by %s. If you did not do this then %s may have your password, and you should change it."
|
||||
msgstr ""
|
||||
|
||||
msgid "This nickname has been registered; you may not use it."
|
||||
msgstr ""
|
||||
|
||||
msgid "This nickname is suspended."
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "This nickname is registered and protected. If it is your nick, type %spassword. Otherwise, please choose a different nick."
|
||||
msgid "This nickname is registered and has protection enabled. If it belongs to you, type %spassword to identify to your account."
|
||||
msgstr ""
|
||||
|
||||
msgid "This option makes a channel unassignable. If a bot is already assigned to the channel, it is unassigned automatically when you enable it."
|
||||
@@ -5605,6 +5713,10 @@ msgstr ""
|
||||
msgid "Unable to find regex engine %s."
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "Unable to find the DNS record required to validate %s. If you have not already done this add a TXT record for %s with the value %s and re-execute this command."
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "Unable to load module %s."
|
||||
msgstr ""
|
||||
@@ -5638,11 +5750,11 @@ msgid "Unknown command %s. Did you mean %s?"
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "Unknown command %s. Did you mean %s? Type \"%s\" for help."
|
||||
msgid "Unknown command %s. Did you mean %s? Type %s for help."
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "Unknown command %s. Type \"%s\" for help."
|
||||
msgid "Unknown command %s. Type %s for help."
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
@@ -5763,6 +5875,10 @@ msgstr ""
|
||||
msgid "VHost for %s has been rejected."
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "VHost for %s has been validated using DNS."
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "VHost for account %s set to %s."
|
||||
msgstr ""
|
||||
@@ -5780,6 +5896,12 @@ msgstr ""
|
||||
msgid "VIEW [mask | list]"
|
||||
msgstr ""
|
||||
|
||||
msgid "Validates a previously requested vhost using DNS"
|
||||
msgstr ""
|
||||
|
||||
msgid "Validates a previously requested vhost using DNS. If you own the domain you have requested as a vhost you can validate your ownership of it using a DNS TXT record to approve your own vhost."
|
||||
msgstr ""
|
||||
|
||||
msgid "Value"
|
||||
msgstr ""
|
||||
|
||||
@@ -6103,6 +6225,10 @@ msgstr ""
|
||||
msgid "You must wait %s before registering your nick."
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "You must wait for %s before trying DNS validation again."
|
||||
msgstr ""
|
||||
|
||||
msgid "You need to be identified to use this command."
|
||||
msgstr ""
|
||||
|
||||
@@ -6174,9 +6300,6 @@ msgstr ""
|
||||
msgid "Your account %s has been successfully created."
|
||||
msgstr ""
|
||||
|
||||
msgid "Your account is already confirmed."
|
||||
msgstr ""
|
||||
|
||||
msgid "Your account is not confirmed. To confirm it, follow the instructions that were emailed to you."
|
||||
msgstr ""
|
||||
|
||||
@@ -6188,10 +6311,6 @@ msgstr ""
|
||||
msgid "Your account will expire, if not confirmed, in %s."
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "Your confirmation code has been re-sent to %s."
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "Your email address has been updated to %s"
|
||||
msgstr ""
|
||||
@@ -6239,9 +6358,6 @@ msgstr ""
|
||||
msgid "Your nick is already registered."
|
||||
msgstr ""
|
||||
|
||||
msgid "Your nick isn't registered."
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "Your nickname is now being changed to %s"
|
||||
msgstr ""
|
||||
@@ -6250,6 +6366,10 @@ msgstr ""
|
||||
msgid "Your nickname now belongs to the account %s."
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "Your nickname will be changed in %s if you do not identify."
|
||||
msgstr ""
|
||||
|
||||
msgid "Your oper block doesn't require logging in."
|
||||
msgstr ""
|
||||
|
||||
@@ -6271,7 +6391,15 @@ msgstr ""
|
||||
msgid "Your requested vhost has been rejected. Reason: %s"
|
||||
msgstr ""
|
||||
|
||||
msgid "Your vhost has been requested."
|
||||
msgid "Your requested vhost has been validated via DNS."
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "Your vhost %s has been requested."
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "Your vhost %s has been requested. If the requested vhost is for a valid DNS name you can add a TXT record for %s with the value %s and automatically approve your vhost using %s."
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
@@ -6296,6 +6424,12 @@ msgstr ""
|
||||
msgid "Zone %s removed."
|
||||
msgstr ""
|
||||
|
||||
msgid "Zone: {zone}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Zone: {zone} = {servers}"
|
||||
msgstr ""
|
||||
|
||||
msgid "[1|2|3|4|5]"
|
||||
msgstr ""
|
||||
|
||||
@@ -6429,3 +6563,87 @@ msgstr ""
|
||||
|
||||
msgid "{ON | delay | OFF}"
|
||||
msgstr ""
|
||||
|
||||
msgid "{mode} -- created by {creator} on {created}"
|
||||
msgstr ""
|
||||
|
||||
msgid "{mode} {param} -- created by {creator} on {created}"
|
||||
msgstr ""
|
||||
|
||||
msgid "{number}: {channel}"
|
||||
msgstr ""
|
||||
|
||||
msgid "{number}: {channel} (key: {key})"
|
||||
msgstr ""
|
||||
|
||||
msgid "{number}: {channel} = {access}"
|
||||
msgstr ""
|
||||
|
||||
msgid "{number}: {channel} = {access} ({description})"
|
||||
msgstr ""
|
||||
|
||||
msgid "{number}: {mask}"
|
||||
msgstr ""
|
||||
|
||||
msgid "{number}: {mask} ({description})"
|
||||
msgstr ""
|
||||
|
||||
msgid "{number}: {mask} ({reason})"
|
||||
msgstr ""
|
||||
|
||||
msgid "{number}: {mask} -- added by {creator} on {created}; last used: {last_used}"
|
||||
msgstr ""
|
||||
|
||||
msgid "{number}: {mask} -- added by {creator} on {created}; last used: {last_used} ({reason})"
|
||||
msgstr ""
|
||||
|
||||
msgid "{number}: {mask} -- created by {creator} on {created}; {expires} ({reason})"
|
||||
msgstr ""
|
||||
|
||||
msgid "{number}: {mask} -- {limit} sessions ({reason})"
|
||||
msgstr ""
|
||||
|
||||
msgid "{number}: {mask} -- {limit} sessions; created by {creator} on {created}; {expires} ({reason})"
|
||||
msgstr ""
|
||||
|
||||
msgid "{number}: {mask} = {flags} -- added by {creator} at {created}"
|
||||
msgstr ""
|
||||
|
||||
msgid "{number}: {mask} = {flags} -- added by {creator} at {created} ({description})"
|
||||
msgstr ""
|
||||
|
||||
msgid "{number}: {mask} = {level}"
|
||||
msgstr ""
|
||||
|
||||
msgid "{number}: {mask} = {level} ({description})"
|
||||
msgstr ""
|
||||
|
||||
msgid "{number}: {mask} = {level} -- created by {creator}; last seen {last_seen}"
|
||||
msgstr ""
|
||||
|
||||
msgid "{number}: {mask} = {level} -- created by {creator}; last seen {last_seen} ({description})"
|
||||
msgstr ""
|
||||
|
||||
msgid "{number}: {nick} = {vhost} -- created by {creator} at {created}"
|
||||
msgstr ""
|
||||
|
||||
msgid "{number}: {word} -- type: {type}"
|
||||
msgstr ""
|
||||
|
||||
msgid "{number}: [{id}] {mask} -- created by {creator} on {created}; {expires} ({reason})"
|
||||
msgstr ""
|
||||
|
||||
msgid "{number}: sent by {sender} at {date/time}"
|
||||
msgstr ""
|
||||
|
||||
msgid "{number}: {command} on {service}: {method}"
|
||||
msgstr ""
|
||||
|
||||
msgid "{number}: {message}"
|
||||
msgstr ""
|
||||
|
||||
msgid "{number}: {message} -- created by {creator} at {created}"
|
||||
msgstr ""
|
||||
|
||||
msgid "{number}: {text} -- created by {creator} on {created}"
|
||||
msgstr ""
|
||||
|
||||
+1627
-3622
File diff suppressed because it is too large
Load Diff
@@ -83,7 +83,7 @@ public:
|
||||
{
|
||||
ChannelMode *cm = ModeManager::FindChannelModeByName("OP");
|
||||
char symbol = cm ? anope_dynamic_static_cast<ChannelModeStatus *>(cm)->symbol : 0;
|
||||
const auto message = Anope::printf("%s invited %s into the channel.", user->nick.c_str(), user->nick.c_str());
|
||||
const auto message = Anope::Format("%s invited %s into the channel.", user->nick.c_str(), user->nick.c_str());
|
||||
IRCD->SendNotice(bi, (symbol ? Anope::string(symbol) : "") + c->name, message);
|
||||
}
|
||||
|
||||
|
||||
@@ -246,11 +246,12 @@ private:
|
||||
{
|
||||
bool override = !source.AccessFor(ci).HasPriv("BADWORDS");
|
||||
Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "LIST";
|
||||
|
||||
ListFormatter list(source.GetAccount());
|
||||
BadWords *bw = ci->GetExt<BadWords>("badwords");
|
||||
|
||||
list.AddColumn(_("Number")).AddColumn(_("Word")).AddColumn(_("Type"));
|
||||
list.SetFlexible(_("{number}: \002{word}\002 -- type: {type}"));
|
||||
|
||||
BadWords *bw = ci->GetExt<BadWords>("badwords");
|
||||
if (!bw || !bw->GetBadWordCount())
|
||||
{
|
||||
source.Reply(_("%s bad words list is empty."), ci->name.c_str());
|
||||
@@ -305,14 +306,8 @@ private:
|
||||
source.Reply(_("No matching entries on %s bad words list."), ci->name.c_str());
|
||||
else
|
||||
{
|
||||
std::vector<Anope::string> replies;
|
||||
list.Process(replies);
|
||||
|
||||
source.Reply(_("Bad words list for %s:"), ci->name.c_str());
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
|
||||
list.SendTo(source);
|
||||
source.Reply(_("End of bad words list."));
|
||||
}
|
||||
}
|
||||
@@ -486,7 +481,8 @@ public:
|
||||
"that starts with \037word\037. If END is specified, a kick "
|
||||
"will be done if a user says a word that ends with "
|
||||
"\037word\037. If you don't specify anything, a kick will "
|
||||
"be issued every time \037word\037 is said by a user."
|
||||
"be issued every time \037word\037 is said by a user. This "
|
||||
"will be shown in the \002LIST\002 output as ANY."
|
||||
"\n\n"
|
||||
"The \002DEL\002 command removes the given word from the "
|
||||
"bad words list. If a list of entry numbers is given, those "
|
||||
|
||||
@@ -41,11 +41,11 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
unsigned count = 0;
|
||||
ListFormatter list(source.GetAccount());
|
||||
|
||||
list.AddColumn(_("Nick")).AddColumn(_("Mask")).AddColumn(_("Real name"));
|
||||
list.SetFlexible(_("\002{nick}\002 ({mask}) [{real_name}]"));
|
||||
|
||||
unsigned count = 0;
|
||||
for (const auto &[_, bi] : *BotListByNick)
|
||||
{
|
||||
if (is_admin || !bi->oper_only)
|
||||
@@ -66,9 +66,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
list.Process(replies);
|
||||
|
||||
if (!count)
|
||||
{
|
||||
source.Reply(_(
|
||||
@@ -79,10 +76,7 @@ public:
|
||||
else
|
||||
{
|
||||
source.Reply(_("Bot list:"));
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
|
||||
list.SendTo(source);
|
||||
source.Reply(_("%d bots available."), count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,15 +55,10 @@ public:
|
||||
info[_("Real name")] = bi->realname;
|
||||
info[_("Created")] = Anope::strftime(bi->created, source.GetAccount());
|
||||
info[_("Options")] = bi->oper_only ? _("Private") : _("None");
|
||||
info[_("Used on")] = Anope::printf(Language::Translate(source.nc, bi->GetChannelCount(), N_("%u channel", "%u channels")), bi->GetChannelCount());
|
||||
info[_("Used on")] = Anope::Format(Language::Translate(source.nc, bi->GetChannelCount(), N_("%u channel", "%u channels")), bi->GetChannelCount());
|
||||
|
||||
FOREACH_MOD(OnBotInfo, (source, bi, ci, info));
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
info.Process(replies);
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
info.SendTo(source);
|
||||
|
||||
if (source.HasPriv("botserv/administration"))
|
||||
{
|
||||
@@ -89,12 +84,7 @@ public:
|
||||
Anope::string disabled = Language::Translate(source.nc, _("Disabled"));
|
||||
|
||||
FOREACH_MOD(OnBotInfo, (source, bi, ci, info));
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
info.Process(replies);
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
info.SendTo(source);
|
||||
}
|
||||
else
|
||||
source.Reply(_("\002%s\002 is not a valid bot or registered channel."), query.c_str());
|
||||
@@ -116,7 +106,7 @@ public:
|
||||
|
||||
const Anope::string GetDesc(CommandSource &source) const override
|
||||
{
|
||||
return Anope::printf(Language::Translate(source.GetAccount(), _("Allows you to see %s information about a channel or a bot")), source.service->nick.c_str());
|
||||
return Anope::Format(Language::Translate(source.GetAccount(), _("Allows you to see %s information about a channel or a bot")), source.service->nick.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+18
-22
@@ -1112,18 +1112,14 @@ class BSKick final
|
||||
|
||||
static void bot_kick(ChannelInfo *ci, User *u, const char *message, ...) ATTR_FORMAT(3, 4)
|
||||
{
|
||||
va_list args;
|
||||
char buf[1024];
|
||||
|
||||
if (!ci || !ci->bi || !ci->c || !u || u->IsProtected() || !ci->c->FindUser(u))
|
||||
return;
|
||||
|
||||
Anope::string fmt = Language::Translate(u, message);
|
||||
va_start(args, message);
|
||||
vsnprintf(buf, sizeof(buf), fmt.c_str(), args);
|
||||
va_end(args);
|
||||
const auto *fmt = Language::Translate(u, message);
|
||||
|
||||
ci->c->Kick(ci->bi, u, Anope::string(buf));
|
||||
Anope::string buf;
|
||||
ANOPE_FORMAT(message, fmt, buf);
|
||||
ci->c->Kick(ci->bi, u, buf);
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -1157,7 +1153,7 @@ public:
|
||||
if (kd && kd->badwords)
|
||||
{
|
||||
if (kd->ttb[TTB_BADWORDS])
|
||||
info[_("Bad words kicker")] = Anope::printf("%s (%d kick(s) to ban)", enabled.c_str(), kd->ttb[TTB_BADWORDS]);
|
||||
info[_("Bad words kicker")] = Anope::Format("%s (%d kick(s) to ban)", enabled.c_str(), kd->ttb[TTB_BADWORDS]);
|
||||
else
|
||||
info[_("Bad words kicker")] = enabled;
|
||||
}
|
||||
@@ -1167,7 +1163,7 @@ public:
|
||||
if (kd && kd->bolds)
|
||||
{
|
||||
if (kd->ttb[TTB_BOLDS])
|
||||
info[_("Bolds kicker")] = Anope::printf("%s (%d kick(s) to ban)", enabled.c_str(), kd->ttb[TTB_BOLDS]);
|
||||
info[_("Bolds kicker")] = Anope::Format("%s (%d kick(s) to ban)", enabled.c_str(), kd->ttb[TTB_BOLDS]);
|
||||
else
|
||||
info[_("Bolds kicker")] = enabled;
|
||||
}
|
||||
@@ -1177,9 +1173,9 @@ public:
|
||||
if (kd && kd->caps)
|
||||
{
|
||||
if (kd->ttb[TTB_CAPS])
|
||||
info[_("Caps kicker")] = Anope::printf(_("%s (%d kick(s) to ban; minimum %d/%d%%)"), enabled.c_str(), kd->ttb[TTB_CAPS], kd->capsmin, kd->capspercent);
|
||||
info[_("Caps kicker")] = Anope::Format(_("%s (%d kick(s) to ban; minimum %d/%d%%)"), enabled.c_str(), kd->ttb[TTB_CAPS], kd->capsmin, kd->capspercent);
|
||||
else
|
||||
info[_("Caps kicker")] = Anope::printf(_("%s (minimum %d/%d%%)"), enabled.c_str(), kd->capsmin, kd->capspercent);
|
||||
info[_("Caps kicker")] = Anope::Format(_("%s (minimum %d/%d%%)"), enabled.c_str(), kd->capsmin, kd->capspercent);
|
||||
}
|
||||
else
|
||||
info[_("Caps kicker")] = disabled;
|
||||
@@ -1187,7 +1183,7 @@ public:
|
||||
if (kd && kd->colors)
|
||||
{
|
||||
if (kd->ttb[TTB_COLORS])
|
||||
info[_("Colors kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->ttb[TTB_COLORS]);
|
||||
info[_("Colors kicker")] = Anope::Format(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->ttb[TTB_COLORS]);
|
||||
else
|
||||
info[_("Colors kicker")] = enabled;
|
||||
}
|
||||
@@ -1197,9 +1193,9 @@ public:
|
||||
if (kd && kd->flood)
|
||||
{
|
||||
if (kd->ttb[TTB_FLOOD])
|
||||
info[_("Flood kicker")] = Anope::printf(_("%s (%d kick(s) to ban; %d lines in %ds)"), enabled.c_str(), kd->ttb[TTB_FLOOD], kd->floodlines, kd->floodsecs);
|
||||
info[_("Flood kicker")] = Anope::Format(_("%s (%d kick(s) to ban; %d lines in %ds)"), enabled.c_str(), kd->ttb[TTB_FLOOD], kd->floodlines, kd->floodsecs);
|
||||
else
|
||||
info[_("Flood kicker")] = Anope::printf(_("%s (%d lines in %ds)"), enabled.c_str(), kd->floodlines, kd->floodsecs);
|
||||
info[_("Flood kicker")] = Anope::Format(_("%s (%d lines in %ds)"), enabled.c_str(), kd->floodlines, kd->floodsecs);
|
||||
}
|
||||
else
|
||||
info[_("Flood kicker")] = disabled;
|
||||
@@ -1207,9 +1203,9 @@ public:
|
||||
if (kd && kd->repeat)
|
||||
{
|
||||
if (kd->ttb[TTB_REPEAT])
|
||||
info[_("Repeat kicker")] = Anope::printf(_("%s (%d kick(s) to ban; %d times)"), enabled.c_str(), kd->ttb[TTB_REPEAT], kd->repeattimes);
|
||||
info[_("Repeat kicker")] = Anope::Format(_("%s (%d kick(s) to ban; %d times)"), enabled.c_str(), kd->ttb[TTB_REPEAT], kd->repeattimes);
|
||||
else
|
||||
info[_("Repeat kicker")] = Anope::printf(_("%s (%d times)"), enabled.c_str(), kd->repeattimes);
|
||||
info[_("Repeat kicker")] = Anope::Format(_("%s (%d times)"), enabled.c_str(), kd->repeattimes);
|
||||
}
|
||||
else
|
||||
info[_("Repeat kicker")] = disabled;
|
||||
@@ -1217,7 +1213,7 @@ public:
|
||||
if (kd && kd->reverses)
|
||||
{
|
||||
if (kd->ttb[TTB_REVERSES])
|
||||
info[_("Reverses kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->ttb[TTB_REVERSES]);
|
||||
info[_("Reverses kicker")] = Anope::Format(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->ttb[TTB_REVERSES]);
|
||||
else
|
||||
info[_("Reverses kicker")] = enabled;
|
||||
}
|
||||
@@ -1227,7 +1223,7 @@ public:
|
||||
if (kd && kd->underlines)
|
||||
{
|
||||
if (kd->ttb[TTB_UNDERLINES])
|
||||
info[_("Underlines kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->ttb[TTB_UNDERLINES]);
|
||||
info[_("Underlines kicker")] = Anope::Format(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->ttb[TTB_UNDERLINES]);
|
||||
else
|
||||
info[_("Underlines kicker")] = enabled;
|
||||
}
|
||||
@@ -1237,7 +1233,7 @@ public:
|
||||
if (kd && kd->italics)
|
||||
{
|
||||
if (kd->ttb[TTB_ITALICS])
|
||||
info[_("Italics kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->ttb[TTB_ITALICS]);
|
||||
info[_("Italics kicker")] = Anope::Format(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->ttb[TTB_ITALICS]);
|
||||
else
|
||||
info[_("Italics kicker")] = enabled;
|
||||
}
|
||||
@@ -1247,7 +1243,7 @@ public:
|
||||
if (kd && kd->amsgs)
|
||||
{
|
||||
if (kd->ttb[TTB_AMSGS])
|
||||
info[_("AMSG kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->ttb[TTB_AMSGS]);
|
||||
info[_("AMSG kicker")] = Anope::Format(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->ttb[TTB_AMSGS]);
|
||||
else
|
||||
info[_("AMSG kicker")] = enabled;
|
||||
}
|
||||
@@ -1369,7 +1365,7 @@ public:
|
||||
BadWords *badwords = ci->GetExt<BadWords>("badwords");
|
||||
|
||||
/* Normalize the buffer */
|
||||
Anope::string nbuf = Anope::NormalizeBuffer(realbuf);
|
||||
Anope::string nbuf = Anope::RemoveFormatting(realbuf);
|
||||
bool casesensitive = Config->GetModule("botserv").Get<bool>("casesensitive");
|
||||
|
||||
/* Normalize can return an empty string if this only contains control codes etc */
|
||||
|
||||
@@ -330,7 +330,7 @@ public:
|
||||
ci->Extend<bool>(def.upper());
|
||||
}
|
||||
|
||||
EventReturn OnCanSet(User *u, const ChannelMode *cm) override
|
||||
EventReturn OnCanSet(User *u, Channel *c, const ChannelMode *cm) override
|
||||
{
|
||||
if (Config->GetModule(this).Get<const Anope::string>("nomlock").find(cm->mchar) != Anope::string::npos
|
||||
|| Config->GetModule(this).Get<const Anope::string>("require").find(cm->mchar) != Anope::string::npos)
|
||||
|
||||
@@ -109,7 +109,7 @@ private:
|
||||
entry["Number"] = Anope::ToString(number);
|
||||
entry["Level"] = access->AccessSerialize();
|
||||
entry["Mask"] = access->Mask();
|
||||
entry["By"] = access->creator;
|
||||
entry["Creator"] = access->creator;
|
||||
entry["Last seen"] = timebuf;
|
||||
entry["Description"] = access->description;
|
||||
list.AddEntry(entry);
|
||||
@@ -435,14 +435,8 @@ private:
|
||||
source.Reply(_("No matching entries on %s access list."), ci->name.c_str());
|
||||
else
|
||||
{
|
||||
std::vector<Anope::string> replies;
|
||||
list.Process(replies);
|
||||
|
||||
source.Reply(_("Access list for %s:"), ci->name.c_str());
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
|
||||
list.SendTo(source);
|
||||
source.Reply(_("End of access list"));
|
||||
}
|
||||
|
||||
@@ -459,6 +453,13 @@ private:
|
||||
|
||||
ListFormatter list(source.GetAccount());
|
||||
list.AddColumn(_("Number")).AddColumn(_("Level")).AddColumn(_("Mask")).AddColumn(_("Description"));
|
||||
list.SetFlexible([](ListFormatter::ListEntry &row)
|
||||
{
|
||||
return row["Description"].empty()
|
||||
? _("{number}: \002{mask}\002 = {level}")
|
||||
: _("{number}: \002{mask}\002 = {level} ({description})");
|
||||
});
|
||||
|
||||
this->ProcessList(source, ci, params, list);
|
||||
}
|
||||
|
||||
@@ -471,7 +472,14 @@ private:
|
||||
}
|
||||
|
||||
ListFormatter list(source.GetAccount());
|
||||
list.AddColumn(_("Number")).AddColumn(_("Level")).AddColumn(_("Mask")).AddColumn(_("By")).AddColumn(_("Last seen")).AddColumn(_("Description"));
|
||||
list.AddColumn(_("Number")).AddColumn(_("Level")).AddColumn(_("Mask")).AddColumn(_("Creator")).AddColumn(_("Last seen")).AddColumn(_("Description"));
|
||||
list.SetFlexible([](ListFormatter::ListEntry &row)
|
||||
{
|
||||
return row["Description"].empty()
|
||||
? _("{number}: \002{mask}\002 = {level} -- created by {creator}; last seen {last_seen}")
|
||||
: _("{number}: \002{mask}\002 = {level} -- created by {creator}; last seen {last_seen} ({description})");
|
||||
});
|
||||
|
||||
this->ProcessList(source, ci, params, list);
|
||||
}
|
||||
|
||||
@@ -724,6 +732,7 @@ class CommandCSLevels final
|
||||
|
||||
ListFormatter list(source.GetAccount());
|
||||
list.AddColumn(_("Name")).AddColumn(_("Level"));
|
||||
list.SetFlexible(_("\002{name}\002 = {level}"));
|
||||
|
||||
const std::vector<Privilege> &privs = PrivilegeManager::GetPrivileges();
|
||||
|
||||
@@ -744,11 +753,7 @@ class CommandCSLevels final
|
||||
list.AddEntry(entry);
|
||||
}
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
list.Process(replies);
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
list.SendTo(source);
|
||||
}
|
||||
|
||||
void DoReset(CommandSource &source, ChannelInfo *ci)
|
||||
@@ -825,6 +830,7 @@ public:
|
||||
|
||||
ListFormatter list(source.GetAccount());
|
||||
list.AddColumn(_("Name")).AddColumn(_("Description"));
|
||||
list.SetFlexible(_("\002{name}\002: {description}"));
|
||||
|
||||
for (const auto &p : PrivilegeManager::GetPrivileges())
|
||||
{
|
||||
@@ -834,11 +840,7 @@ public:
|
||||
list.AddEntry(entry);
|
||||
}
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
list.Process(replies);
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
list.SendTo(source);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -141,7 +141,7 @@ class CommandCSAKick final
|
||||
}
|
||||
}
|
||||
|
||||
/* Match against the lastusermask of all nickalias's with equal
|
||||
/* Match against the last mask of all nickalias's with equal
|
||||
* or higher access. - Viper */
|
||||
for (const auto &[_, na2] : *NickAliasList)
|
||||
{
|
||||
@@ -150,7 +150,7 @@ class CommandCSAKick final
|
||||
AccessGroup nc_access = ci->AccessFor(na->nc), u_access = source.AccessFor(ci);
|
||||
if (na->nc && (na->nc == ci->GetFounder() || nc_access >= u_access))
|
||||
{
|
||||
Anope::string buf = na->nick + "!" + na->last_usermask;
|
||||
Anope::string buf = na->nick + "!" + na->last_userhost;
|
||||
if (Anope::Match(buf, mask))
|
||||
{
|
||||
source.Reply(ACCESS_DENIED);
|
||||
@@ -378,14 +378,8 @@ class CommandCSAKick final
|
||||
source.Reply(_("No matching entries on %s autokick list."), ci->name.c_str());
|
||||
else
|
||||
{
|
||||
std::vector<Anope::string> replies;
|
||||
list.Process(replies);
|
||||
|
||||
source.Reply(_("Autokick list for %s:"), ci->name.c_str());
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
|
||||
list.SendTo(source);
|
||||
source.Reply(_("End of autokick list"));
|
||||
}
|
||||
}
|
||||
@@ -400,6 +394,13 @@ class CommandCSAKick final
|
||||
|
||||
ListFormatter list(source.GetAccount());
|
||||
list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("Reason"));
|
||||
list.SetFlexible([](ListFormatter::ListEntry &row)
|
||||
{
|
||||
return row["Reason"].empty()
|
||||
? _("{number}: \002{mask}\002")
|
||||
: _("{number}: \002{mask}\002 ({reason})");
|
||||
});
|
||||
|
||||
this->ProcessList(source, ci, params, list);
|
||||
}
|
||||
|
||||
@@ -413,6 +414,13 @@ class CommandCSAKick final
|
||||
|
||||
ListFormatter list(source.GetAccount());
|
||||
list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("Creator")).AddColumn(_("Created")).AddColumn(_("Last used")).AddColumn(_("Reason"));
|
||||
list.SetFlexible([](ListFormatter::ListEntry &row)
|
||||
{
|
||||
return row["Reason"].empty()
|
||||
? _("{number}: \002{mask}\002 -- added by {creator} on {created}; last used: {last_used}")
|
||||
: _("{number}: \002{mask}\002 -- added by {creator} on {created}; last used: {last_used} ({reason})");
|
||||
});
|
||||
|
||||
this->ProcessList(source, ci, params, list);
|
||||
}
|
||||
|
||||
@@ -506,7 +514,7 @@ public:
|
||||
"%s will ban that user from the channel, then kick "
|
||||
"the user."
|
||||
"\n\n"
|
||||
"The \002%s\032ADD\002 command adds the given nick or usermask "
|
||||
"The \002%s\032ADD\002 command adds the given nick or mask "
|
||||
"to the AutoKick list. If a \037reason\037 is given with "
|
||||
"the command, that reason will be used when the user is "
|
||||
"kicked; if not, the default reason is \"User has been "
|
||||
|
||||
@@ -128,6 +128,8 @@ private:
|
||||
|
||||
ListFormatter list(source.GetAccount());
|
||||
list.AddColumn(_("Number")).AddColumn(_("Creator")).AddColumn(_("Created")).AddColumn(_("Message"));
|
||||
list.SetFlexible(_("{number}: {message} -- created by {creator} at {created}"));
|
||||
|
||||
for (unsigned i = 0; i < (*messages)->size(); ++i)
|
||||
{
|
||||
EntryMsg *msg = (*messages)->at(i);
|
||||
@@ -140,11 +142,7 @@ private:
|
||||
list.AddEntry(entry);
|
||||
}
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
list.Process(replies);
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
|
||||
list.SendTo(source);
|
||||
source.Reply(_("End of entry message list."));
|
||||
}
|
||||
|
||||
|
||||
@@ -309,8 +309,13 @@ class CommandCSFlags final
|
||||
}
|
||||
|
||||
ListFormatter list(source.GetAccount());
|
||||
|
||||
list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("Flags")).AddColumn(_("Creator")).AddColumn(_("Created")).AddColumn(_("Description"));
|
||||
list.SetFlexible([](ListFormatter::ListEntry &row)
|
||||
{
|
||||
return row["Description"].empty()
|
||||
? _("{number}: \002{mask}\002 = {flags} -- added by {creator} at {created}")
|
||||
: _("{number}: \002{mask}\002 = {flags} -- added by {creator} at {created} ({description})");
|
||||
});
|
||||
|
||||
unsigned count = 0;
|
||||
for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i)
|
||||
@@ -348,12 +353,8 @@ class CommandCSFlags final
|
||||
source.Reply(_("No matching entries on %s access list."), ci->name.c_str());
|
||||
else
|
||||
{
|
||||
std::vector<Anope::string> replies;
|
||||
list.Process(replies);
|
||||
|
||||
source.Reply(_("Flags list for %s"), ci->name.c_str());
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
list.SendTo(source);
|
||||
if (count == ci->GetAccessCount())
|
||||
source.Reply(_("End of access list."));
|
||||
else
|
||||
|
||||
@@ -62,12 +62,7 @@ public:
|
||||
}
|
||||
|
||||
FOREACH_MOD(OnChanInfo, (source, ci, info, show_all));
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
info.Process(replies);
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
info.SendTo(source);
|
||||
}
|
||||
|
||||
bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
|
||||
|
||||
@@ -76,6 +76,12 @@ public:
|
||||
|
||||
ListFormatter list(source.GetAccount());
|
||||
list.AddColumn(_("Name")).AddColumn(_("Description"));
|
||||
list.SetFlexible([](ListFormatter::ListEntry &row)
|
||||
{
|
||||
return row["Description"].empty()
|
||||
? _("\002{name}\002")
|
||||
: _("\002{name}\002 ({description})");
|
||||
});
|
||||
|
||||
Anope::map<ChannelInfo *> ordered_map;
|
||||
for (const auto &[cname, ci] : *RegisteredChannelList)
|
||||
@@ -122,12 +128,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
list.Process(replies);
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
|
||||
list.SendTo(source);
|
||||
source.Reply(_("End of list - %d/%d matches shown."), nchans > listmax ? listmax : nchans, nchans);
|
||||
}
|
||||
|
||||
|
||||
@@ -138,7 +138,8 @@ public:
|
||||
else
|
||||
{
|
||||
ListFormatter list(source.GetAccount());
|
||||
list.AddColumn(_("Number")).AddColumn(_("Service")).AddColumn(_("Command")).AddColumn(_("Method")).AddColumn("");
|
||||
list.AddColumn(_("Number")).AddColumn(_("Service")).AddColumn(_("Command")).AddColumn(_("Method"));
|
||||
list.SetFlexible(_("{number}: {command} on {service}: {method}"));
|
||||
|
||||
for (unsigned i = 0; i < (*ls)->size(); ++i)
|
||||
{
|
||||
@@ -147,19 +148,13 @@ public:
|
||||
ListFormatter::ListEntry entry;
|
||||
entry["Number"] = Anope::ToString(i + 1);
|
||||
entry["Service"] = log->command_service;
|
||||
entry["Command"] = !log->command_name.empty() ? log->command_name : log->service_name;
|
||||
entry["Method"] = log->method;
|
||||
entry[""] = log->extra;
|
||||
entry["Command"] = !log->command_name.empty() ? log->command_name.upper() : log->service_name;
|
||||
entry["Method"] = log->method + (log->extra.empty() ? "" : " ") + log->extra;
|
||||
list.AddEntry(entry);
|
||||
}
|
||||
|
||||
source.Reply(_("Log list for %s:"), ci->name.c_str());
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
list.Process(replies);
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
list.SendTo(source);
|
||||
}
|
||||
}
|
||||
else if (params.size() > 2)
|
||||
|
||||
@@ -288,7 +288,7 @@ class CommandCSMode final
|
||||
for (auto *ml : mlocks)
|
||||
{
|
||||
ChannelMode *cm = ModeManager::FindChannelModeByName(ml->name);
|
||||
if (cm && cm->CanSet(source.GetUser()))
|
||||
if (cm && cm->CanSet(source.GetUser(), ci->c))
|
||||
modelocks->RemoveMLock(cm, ml->set, ml->param);
|
||||
}
|
||||
}
|
||||
@@ -320,7 +320,7 @@ class CommandCSMode final
|
||||
source.Reply(_("Unknown mode character %c ignored."), mode);
|
||||
break;
|
||||
}
|
||||
else if (u && !cm->CanSet(u))
|
||||
else if (u && !cm->CanSet(u, ci->c))
|
||||
{
|
||||
source.Reply(_("You may not (un)lock mode %c."), mode);
|
||||
break;
|
||||
@@ -412,7 +412,7 @@ class CommandCSMode final
|
||||
source.Reply(_("Unknown mode character %c ignored."), mode);
|
||||
break;
|
||||
}
|
||||
else if (u && !cm->CanSet(u))
|
||||
else if (u && !cm->CanSet(u, ci->c))
|
||||
{
|
||||
source.Reply(_("You may not (un)lock mode %c."), mode);
|
||||
break;
|
||||
@@ -450,6 +450,12 @@ class CommandCSMode final
|
||||
{
|
||||
ListFormatter list(source.GetAccount());
|
||||
list.AddColumn(_("Mode")).AddColumn(_("Param")).AddColumn(_("Creator")).AddColumn(_("Created"));
|
||||
list.SetFlexible([](ListFormatter::ListEntry &row)
|
||||
{
|
||||
return row["Param"].empty()
|
||||
? _("{mode} -- created by {creator} on {created}")
|
||||
: _("{mode} {param} -- created by {creator} on {created}");
|
||||
});
|
||||
|
||||
for (auto *ml : mlocks)
|
||||
{
|
||||
@@ -458,7 +464,7 @@ class CommandCSMode final
|
||||
continue;
|
||||
|
||||
ListFormatter::ListEntry entry;
|
||||
entry["Mode"] = Anope::printf("%c%c", ml->set ? '+' : '-', cm->mchar);
|
||||
entry["Mode"] = Anope::Format("%c%c", ml->set ? '+' : '-', cm->mchar);
|
||||
entry["Param"] = ml->param;
|
||||
entry["Creator"] = ml->setter;
|
||||
entry["Created"] = Anope::strftime(ml->created, NULL, true);
|
||||
@@ -466,12 +472,7 @@ class CommandCSMode final
|
||||
}
|
||||
|
||||
source.Reply(_("Mode locks for %s:"), ci->name.c_str());
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
list.Process(replies);
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
list.SendTo(source);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -508,7 +509,7 @@ class CommandCSMode final
|
||||
{
|
||||
ChannelMode *cm = ModeManager::GetChannelModes()[j];
|
||||
|
||||
if (!u || cm->CanSet(u) || can_override)
|
||||
if (!u || cm->CanSet(u, ci->c) || can_override)
|
||||
{
|
||||
if (cm->type == MODE_REGULAR || (!adding && cm->type == MODE_PARAM))
|
||||
{
|
||||
@@ -524,7 +525,7 @@ class CommandCSMode final
|
||||
if (adding == -1)
|
||||
break;
|
||||
ChannelMode *cm = ModeManager::FindChannelModeByChar(mode);
|
||||
if (!cm || (u && !cm->CanSet(u) && !can_override))
|
||||
if (!cm || (u && !cm->CanSet(u, ci->c) && !can_override))
|
||||
continue;
|
||||
switch (cm->type)
|
||||
{
|
||||
@@ -917,9 +918,9 @@ public:
|
||||
if (!m.second.empty())
|
||||
{
|
||||
if (m.first)
|
||||
return Anope::printf(Language::Translate(source.GetAccount(), _("Gives you or the specified nick %s status on a channel")), m.second.c_str());
|
||||
return Anope::Format(Language::Translate(source.GetAccount(), _("Gives you or the specified nick %s status on a channel")), m.second.c_str());
|
||||
else
|
||||
return Anope::printf(Language::Translate(source.GetAccount(), _("Removes %s status from you or the specified nick on a channel")), m.second.c_str());
|
||||
return Anope::Format(Language::Translate(source.GetAccount(), _("Removes %s status from you or the specified nick on a channel")), m.second.c_str());
|
||||
}
|
||||
else
|
||||
return "";
|
||||
|
||||
@@ -282,7 +282,7 @@ public:
|
||||
if (u2)
|
||||
onlinestatus = ".";
|
||||
else
|
||||
onlinestatus = Anope::printf(Language::Translate(source.nc, _(" but %s mysteriously dematerialized.")), target.c_str());
|
||||
onlinestatus = Anope::Format(Language::Translate(source.nc, _(" but %s mysteriously dematerialized.")), target.c_str());
|
||||
|
||||
Anope::string timebuf = Anope::Duration(Anope::CurTime - info->last, source.nc);
|
||||
Anope::string timebuf2 = Anope::strftime(info->last, source.nc, true);
|
||||
@@ -296,9 +296,9 @@ public:
|
||||
{
|
||||
u2 = User::Find(info->nick2, true);
|
||||
if (u2)
|
||||
onlinestatus = Anope::printf(Language::Translate(source.nc, _(". %s is still online.")), u2->nick.c_str());
|
||||
onlinestatus = Anope::Format(Language::Translate(source.nc, _(". %s is still online.")), u2->nick.c_str());
|
||||
else
|
||||
onlinestatus = Anope::printf(Language::Translate(source.nc, _(", but %s mysteriously dematerialized.")), info->nick2.c_str());
|
||||
onlinestatus = Anope::Format(Language::Translate(source.nc, _(", but %s mysteriously dematerialized.")), info->nick2.c_str());
|
||||
|
||||
source.Reply(_("%s (%s) was last seen changing nick to %s %s ago%s"),
|
||||
target.c_str(), info->vhost.c_str(), info->nick2.c_str(), timebuf.c_str(), onlinestatus.c_str());
|
||||
|
||||
@@ -1333,7 +1333,7 @@ public:
|
||||
if (persist.HasExt(ci))
|
||||
info.AddOption(_("Persistent"));
|
||||
if (noexpire.HasExt(ci))
|
||||
info.AddOption(_("No expire"));
|
||||
info.AddOption(_("No expiry"));
|
||||
if (keep_modes.HasExt(ci))
|
||||
info.AddOption(_("Keep modes"));
|
||||
if (noautoop.HasExt(ci))
|
||||
|
||||
@@ -397,6 +397,12 @@ private:
|
||||
|
||||
ListFormatter list(source.GetAccount());
|
||||
list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("Description"));
|
||||
list.SetFlexible([](ListFormatter::ListEntry &row)
|
||||
{
|
||||
return row["Description"].empty()
|
||||
? _("{number}: \002{mask}\002")
|
||||
: _("{number}: \002{mask}\002 ({description})");
|
||||
});
|
||||
|
||||
if (!nick.empty() && nick.find_first_not_of("1234567890,-") == Anope::string::npos)
|
||||
{
|
||||
@@ -453,12 +459,8 @@ private:
|
||||
source.Reply(_("No matching entries on %s access list."), ci->name.c_str());
|
||||
else
|
||||
{
|
||||
std::vector<Anope::string> replies;
|
||||
list.Process(replies);
|
||||
|
||||
source.Reply(_("%s list for %s"), source.command.nobreak().c_str(), ci->name.c_str());
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
list.SendTo(source);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -511,7 +513,7 @@ public:
|
||||
|
||||
const Anope::string GetDesc(CommandSource &source) const override
|
||||
{
|
||||
return Anope::printf(Language::Translate(source.GetAccount(), _("Modify the list of %s users")), source.command.nobreak().c_str());
|
||||
return Anope::Format(Language::Translate(source.GetAccount(), _("Modify the list of %s users")), source.command.nobreak().c_str());
|
||||
}
|
||||
|
||||
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override
|
||||
|
||||
@@ -134,9 +134,9 @@ struct UserData final
|
||||
Anope::string info_adder;
|
||||
Anope::string info_message;
|
||||
time_t info_ts = 0;
|
||||
Anope::string last_mask;
|
||||
Anope::string last_quit;
|
||||
Anope::string last_real_mask;
|
||||
Anope::string last_userhost;
|
||||
Anope::string last_userhost_real;
|
||||
bool noexpire = false;
|
||||
bool protect = false;
|
||||
std::optional<time_t> protectafter;
|
||||
@@ -432,7 +432,7 @@ private:
|
||||
// crypt3-md5 Converted to enc_posix
|
||||
// crypt3-sha2-256 Converted to enc_posix
|
||||
// crypt3-sha2-512 Converted to enc_posix
|
||||
// ircservices Converted to enc_old
|
||||
// ircservices NO
|
||||
// pbkdf2 NO
|
||||
// pbkdf2v2 NO
|
||||
// rawmd5 Converted to enc_md5
|
||||
@@ -443,10 +443,9 @@ private:
|
||||
if (pass.compare(0, 18, "$anope$enc_sha256$", 18) == 0)
|
||||
{
|
||||
auto sep = pass.find('$', 18);
|
||||
Anope::string iv, pass;
|
||||
Anope::B64Decode(pass.substr(18, sep - 18), iv);
|
||||
Anope::B64Decode(pass.substr(sep + 1), pass);
|
||||
nc->pass = "sha256:" + Anope::Hex(pass) + ":" + Anope::Hex(iv);
|
||||
auto iv = Anope::B64Decode(pass.substr(18, sep - 18));
|
||||
auto pw = Anope::B64Decode(pass.substr(sep + 1));
|
||||
nc->pass = "sha256:" + Anope::Hex(pw) + ":" + Anope::Hex(iv);
|
||||
}
|
||||
|
||||
else if (pass.compare(0, 9, "$argon2d$", 9) == 0)
|
||||
@@ -460,14 +459,10 @@ private:
|
||||
|
||||
else if (pass.compare(0, 8, "$base64$", 8) == 0)
|
||||
{
|
||||
Anope::string rawpass;
|
||||
Anope::B64Decode(pass.substr(8), rawpass);
|
||||
auto rawpass = Anope::B64Decode(pass.substr(8));
|
||||
Anope::Encrypt(rawpass, nc->pass);
|
||||
}
|
||||
|
||||
else if (pass.compare(0, 13, "$ircservices$", 13) == 0)
|
||||
nc->pass = "oldmd5:" + pass.substr(13);
|
||||
|
||||
else if (pass.compare(0, 8, "$rawmd5$", 8) == 0)
|
||||
nc->pass = "md5:" + pass.substr(8);
|
||||
|
||||
@@ -806,7 +801,7 @@ private:
|
||||
|
||||
auto *ni = news_service->CreateNewsItem();
|
||||
ni->type = NEWS_LOGON;
|
||||
ni->text = Anope::printf("[%s] %s", subject.c_str(), body.c_str());
|
||||
ni->text = Anope::Format("[%s] %s", subject.c_str(), body.c_str());
|
||||
ni->who = setter;
|
||||
ni->time = ts;
|
||||
news_service->AddNewsItem(ni);
|
||||
@@ -832,7 +827,7 @@ private:
|
||||
|
||||
auto *ni = news_service->CreateNewsItem();
|
||||
ni->type = NEWS_OPER;
|
||||
ni->text = Anope::printf("[%s] %s", subject.c_str(), body.c_str());
|
||||
ni->text = Anope::Format("[%s] %s", subject.c_str(), body.c_str());
|
||||
ni->who = setter;
|
||||
ni->time = ts;
|
||||
news_service->AddNewsItem(ni);
|
||||
@@ -1146,9 +1141,9 @@ private:
|
||||
else if (key == "private:freeze:timestamp")
|
||||
data->suspend_ts = Anope::Convert<time_t>(value, 0);
|
||||
else if (key == "private:host:actual")
|
||||
data->last_real_mask = value;
|
||||
data->last_userhost_real = value;
|
||||
else if (key == "private:host:vhost")
|
||||
data->last_mask = value;
|
||||
data->last_userhost = value;
|
||||
else if (key == "private:lastquit:message")
|
||||
data->last_quit = value;
|
||||
else if (key == "private:loginfail:failnum")
|
||||
@@ -1314,14 +1309,14 @@ private:
|
||||
auto *data = userdata.Get(nc);
|
||||
if (data)
|
||||
{
|
||||
if (!data->last_mask.empty())
|
||||
na->last_usermask = data->last_mask;
|
||||
if (!data->last_userhost.empty())
|
||||
na->last_userhost = data->last_userhost;
|
||||
|
||||
if (!data->last_quit.empty())
|
||||
na->last_quit = data->last_quit;
|
||||
|
||||
if (!data->last_real_mask.empty())
|
||||
na->last_realhost = data->last_real_mask;
|
||||
if (!data->last_userhost_real.empty())
|
||||
na->last_userhost_real = data->last_userhost_real;
|
||||
|
||||
if (data->noexpire)
|
||||
na->Extend<bool>("NS_NO_EXPIRE");
|
||||
|
||||
@@ -11,31 +11,6 @@
|
||||
|
||||
#include "module.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
class SaveData final
|
||||
: public Serialize::Data
|
||||
{
|
||||
public:
|
||||
Anope::string last;
|
||||
std::fstream *fs = nullptr;
|
||||
|
||||
std::iostream &operator[](const Anope::string &key) override
|
||||
{
|
||||
if (key != last)
|
||||
{
|
||||
*fs << "\nDATA " << key << " ";
|
||||
last = key;
|
||||
}
|
||||
|
||||
return *fs;
|
||||
}
|
||||
};
|
||||
|
||||
class LoadData final
|
||||
: public Serialize::Data
|
||||
{
|
||||
@@ -97,133 +72,14 @@ public:
|
||||
|
||||
class DBFlatFile final
|
||||
: public Module
|
||||
, public Pipe
|
||||
{
|
||||
/* Day the last backup was on */
|
||||
int last_day = 0;
|
||||
private:
|
||||
bool loaded = false;
|
||||
|
||||
int child_pid = -1;
|
||||
|
||||
void BackupDatabase()
|
||||
{
|
||||
tm *tm = localtime(&Anope::CurTime);
|
||||
|
||||
if (tm->tm_mday != last_day)
|
||||
{
|
||||
last_day = tm->tm_mday;
|
||||
|
||||
std::set<Anope::string> dbs;
|
||||
dbs.insert(Config->GetModule(this).Get<const Anope::string>("database", "anope.db"));
|
||||
|
||||
for (const auto &type_order : Serialize::Type::GetTypeOrder())
|
||||
{
|
||||
Serialize::Type *stype = Serialize::Type::Find(type_order);
|
||||
|
||||
if (stype && stype->GetOwner())
|
||||
dbs.insert("module_" + stype->GetOwner()->name + ".db");
|
||||
}
|
||||
|
||||
const auto backupdir = Anope::ExpandData("backups");
|
||||
for (const auto &db : dbs)
|
||||
{
|
||||
const auto oldname = Anope::ExpandData(db);
|
||||
const auto basename = Anope::Expand(backupdir, db + "-");
|
||||
const auto newname = Anope::printf("%s%04i-%02i-%02i", basename.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
|
||||
|
||||
/* Backup already exists or no database to backup */
|
||||
if (Anope::IsFile(newname) || !Anope::IsFile(oldname))
|
||||
continue;
|
||||
|
||||
Log(LOG_DEBUG) << "db_flatfile: Attempting to rename " << db << " to " << newname;
|
||||
if (rename(oldname.c_str(), newname.c_str()))
|
||||
{
|
||||
Anope::string err = Anope::LastError();
|
||||
Log(this) << "Unable to back up database " << db << " (" << err << ")!";
|
||||
|
||||
if (!Config->GetModule(this).Get<bool>("nobackupokay"))
|
||||
{
|
||||
Anope::Quitting = true;
|
||||
Anope::QuitReason = "Unable to back up database " + db + " (" + err + ")";
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto keepbackups = Config->GetModule(this).Get<unsigned>("keepbackups", "7");
|
||||
if (!keepbackups)
|
||||
continue;
|
||||
|
||||
std::error_code ec;
|
||||
std::set<Anope::string> old_backups;
|
||||
for (const auto &entry : std::filesystem::directory_iterator(backupdir.str(), ec))
|
||||
{
|
||||
Anope::string entryname = entry.path().string();
|
||||
if (entryname.compare(0, basename.length(), basename) != 0)
|
||||
continue;
|
||||
|
||||
old_backups.insert(entryname);
|
||||
if (old_backups.size() <= keepbackups)
|
||||
continue;
|
||||
|
||||
Log(LOG_DEBUG) << "Deleting expired backup " << *old_backups.begin();
|
||||
if (!std::filesystem::remove(old_backups.begin()->str(), ec))
|
||||
{
|
||||
Log(this) << "Failed to delete expired backup " << *old_backups.begin() << ": " << ec.message();
|
||||
continue;
|
||||
}
|
||||
old_backups.erase(old_backups.begin());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
DBFlatFile(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, DATABASE | VENDOR)
|
||||
DBFlatFile(const Anope::string &modname, const Anope::string &creator)
|
||||
: Module(modname, creator, DATABASE | DEPRECATED | VENDOR)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
void OnRestart() override
|
||||
{
|
||||
OnShutdown();
|
||||
}
|
||||
|
||||
void OnShutdown() override
|
||||
{
|
||||
if (child_pid > -1)
|
||||
{
|
||||
Log(this) << "Waiting for child to exit...";
|
||||
|
||||
int status;
|
||||
waitpid(child_pid, &status, 0);
|
||||
|
||||
Log(this) << "Done";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void OnNotify() override
|
||||
{
|
||||
char buf[512];
|
||||
int i = this->Read(buf, sizeof(buf) - 1);
|
||||
if (i <= 0)
|
||||
return;
|
||||
buf[i] = 0;
|
||||
|
||||
child_pid = -1;
|
||||
|
||||
if (!*buf)
|
||||
{
|
||||
Log(this) << "Finished saving databases";
|
||||
return;
|
||||
}
|
||||
|
||||
Log(this) << "Error saving databases: " << buf;
|
||||
|
||||
if (!Config->GetModule(this).Get<bool>("nobackupokay"))
|
||||
Anope::Quitting = true;
|
||||
}
|
||||
|
||||
EventReturn OnLoadDatabase() override
|
||||
@@ -270,110 +126,6 @@ public:
|
||||
return EVENT_STOP;
|
||||
}
|
||||
|
||||
|
||||
void OnSaveDatabase() override
|
||||
{
|
||||
if (child_pid > -1)
|
||||
{
|
||||
Log(this) << "Database save is already in progress!";
|
||||
return;
|
||||
}
|
||||
|
||||
BackupDatabase();
|
||||
|
||||
int i = -1;
|
||||
#ifndef _WIN32
|
||||
if (!Anope::Quitting && Config->GetModule(this).Get<bool>("fork"))
|
||||
{
|
||||
i = fork();
|
||||
if (i > 0)
|
||||
{
|
||||
child_pid = i;
|
||||
return;
|
||||
}
|
||||
else if (i < 0)
|
||||
Log(this) << "Unable to fork for database save";
|
||||
}
|
||||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
std::map<Module *, std::fstream *> databases;
|
||||
|
||||
/* First open the databases of all of the registered types. This way, if we have a type with 0 objects, that database will be properly cleared */
|
||||
for (const auto &[_, s_type] : Serialize::Type::GetTypes())
|
||||
{
|
||||
if (databases[s_type->GetOwner()])
|
||||
continue;
|
||||
|
||||
Anope::string db_name;
|
||||
if (s_type->GetOwner())
|
||||
db_name = Anope::ExpandData("module_" + s_type->GetOwner()->name + ".db");
|
||||
else
|
||||
db_name = Anope::ExpandData(Config->GetModule(this).Get<const Anope::string>("database", "anope.db"));
|
||||
|
||||
std::fstream *fs = databases[s_type->GetOwner()] = new std::fstream((db_name + ".tmp").c_str(), std::ios_base::out | std::ios_base::trunc | std::ios_base::binary);
|
||||
|
||||
if (!fs->is_open())
|
||||
Log(this) << "Unable to open " << db_name << " for writing";
|
||||
}
|
||||
|
||||
SaveData data;
|
||||
const std::list<Serializable *> &items = Serializable::GetItems();
|
||||
for (auto *base : items)
|
||||
{
|
||||
Serialize::Type *s_type = base->GetSerializableType();
|
||||
if (!s_type)
|
||||
continue;
|
||||
|
||||
data.fs = databases[s_type->GetOwner()];
|
||||
if (!data.fs || !data.fs->is_open())
|
||||
continue;
|
||||
|
||||
*data.fs << "OBJECT " << s_type->GetName();
|
||||
if (base->id)
|
||||
*data.fs << "\nID " << base->id;
|
||||
s_type->Serialize(base, data);
|
||||
*data.fs << "\nEND\n";
|
||||
}
|
||||
|
||||
for (auto &[mod, f] : databases)
|
||||
{
|
||||
const auto db_name = Anope::ExpandData((mod ? (mod->name + ".db") : Config->GetModule(this).Get<const Anope::string>("database", "anope.db")));
|
||||
|
||||
if (!f->is_open() || !f->good())
|
||||
{
|
||||
this->Write("Unable to write database " + db_name);
|
||||
|
||||
f->close();
|
||||
}
|
||||
else
|
||||
{
|
||||
f->close();
|
||||
#ifdef _WIN32
|
||||
/* Windows rename() fails if the file already exists. */
|
||||
remove(db_name.c_str());
|
||||
#endif
|
||||
rename((db_name + ".tmp").c_str(), db_name.c_str());
|
||||
}
|
||||
|
||||
delete f;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
if (i)
|
||||
throw;
|
||||
}
|
||||
|
||||
if (!i)
|
||||
{
|
||||
this->Notify();
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Load just one type. Done if a module is reloaded during runtime */
|
||||
void OnSerializeTypeCreate(Serialize::Type *stype) override
|
||||
{
|
||||
if (!loaded)
|
||||
|
||||
@@ -190,7 +190,7 @@ private:
|
||||
return; // No backups.
|
||||
|
||||
auto backupdir = Anope::ExpandData(modconf.Get<Anope::string>("backup_directory", "backups"));
|
||||
if (!fs::is_directory(backupdir.str(), ec) && !ec)
|
||||
if (!fs::is_directory(backupdir.str(), ec))
|
||||
{
|
||||
fs::create_directories(backupdir.str(), ec);
|
||||
if (ec)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -174,9 +174,9 @@ public:
|
||||
if (!this->CheckInit() || obj->GetTimestamp() == Anope::CurTime)
|
||||
return;
|
||||
|
||||
Anope::string sql = Anope::printf("SELECT * from `%s`", GetTableName(obj).c_str());
|
||||
Anope::string sql = Anope::Format("SELECT * from `%s`", GetTableName(obj).c_str());
|
||||
if (obj->GetTimestamp())
|
||||
sql += Anope::printf(" WHERE (`timestamp` >= %s OR `timestamp` IS NULL)", this->SQL->FromUnixtime(obj->GetTimestamp()).c_str());
|
||||
sql += Anope::Format(" WHERE (`timestamp` >= %s OR `timestamp` IS NULL)", this->SQL->FromUnixtime(obj->GetTimestamp()).c_str());
|
||||
|
||||
Query query(sql);
|
||||
|
||||
|
||||
+39
-1
@@ -142,7 +142,7 @@ class Packet final
|
||||
record.ttl = (input[pos] << 24) | (input[pos + 1] << 16) | (input[pos + 2] << 8) | input[pos + 3];
|
||||
pos += 4;
|
||||
|
||||
//record.rdlength = input[pos] << 8 | input[pos + 1];
|
||||
auto rdlength = input[pos] << 8 | input[pos + 1];
|
||||
pos += 2;
|
||||
|
||||
switch (record.type)
|
||||
@@ -192,6 +192,44 @@ class Packet final
|
||||
|
||||
break;
|
||||
}
|
||||
case DNS::QUERY_TXT:
|
||||
{
|
||||
if (pos + rdlength > input_size)
|
||||
throw SocketException("Unable to unpack TXT resource record");
|
||||
|
||||
auto txtlength = input[pos++];
|
||||
if (pos + txtlength > input_size)
|
||||
throw SocketException("Unable to unpack TXT resource record");
|
||||
|
||||
record.rdata = Anope::string(reinterpret_cast<const char* >(input + pos), txtlength);
|
||||
pos += rdlength - 1;
|
||||
break;
|
||||
}
|
||||
case DNS::QUERY_SRV:
|
||||
{
|
||||
if (rdlength < 6 || pos + rdlength > input_size)
|
||||
throw SocketException("Unable to unpack SRV resource record");
|
||||
|
||||
auto srv = std::make_shared<DNS::Record::SRV>();
|
||||
|
||||
srv->priority = input[pos] << 8 | input[pos + 1];
|
||||
pos += 2;
|
||||
|
||||
srv->weight = input[pos] << 8 | input[pos + 1];
|
||||
pos += 2;
|
||||
|
||||
srv->port = input[pos] << 8 | input[pos + 1];
|
||||
pos += 2;
|
||||
|
||||
srv->host = this->UnpackName(input, input_size, pos);
|
||||
if (!IsValidName(srv->host))
|
||||
throw SocketException("Invalid name in SRV resource record");
|
||||
|
||||
record.rdata = Anope::Format("%hu %hu %hu %s", srv->priority,
|
||||
srv->weight, srv->port, srv->host.c_str());
|
||||
record.rdataobj = srv;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ private:
|
||||
|
||||
public:
|
||||
EMD5(const Anope::string &modname, const Anope::string &creator)
|
||||
: Module(modname, creator, ENCRYPTION | VENDOR)
|
||||
: Module(modname, creator, DEPRECATED | ENCRYPTION | VENDOR)
|
||||
, md5provider(this, "md5", 16, 64)
|
||||
{
|
||||
if (ModuleManager::FindFirstOf(ENCRYPTION) == this)
|
||||
|
||||
@@ -14,7 +14,7 @@ class ENone final
|
||||
{
|
||||
public:
|
||||
ENone(const Anope::string &modname, const Anope::string &creator)
|
||||
: Module(modname, creator, ENCRYPTION | VENDOR)
|
||||
: Module(modname, creator, DEPRECATED | ENCRYPTION | VENDOR)
|
||||
{
|
||||
if (ModuleManager::FindFirstOf(ENCRYPTION) == this)
|
||||
throw ModuleException("enc_none is deprecated and can not be used as a primary encryption method");
|
||||
@@ -35,9 +35,7 @@ public:
|
||||
if (!hash_method.equals_cs("plain"))
|
||||
return;
|
||||
|
||||
Anope::string b64pass;
|
||||
Anope::B64Encode(req->GetPassword(), b64pass);
|
||||
auto enc = "plain:" + b64pass;
|
||||
auto enc = "plain:" + Anope::B64Encode(req->GetPassword());
|
||||
if (nc->pass.equals_cs(enc))
|
||||
{
|
||||
// If we are NOT the first encryption module we want to re-encrypt
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
/* Include file for high-level encryption routines.
|
||||
*
|
||||
* (C) 2003-2025 Anope Team
|
||||
* Contact us at team@anope.org
|
||||
*
|
||||
* Please read COPYING and README for further details.
|
||||
*
|
||||
* Based on the original code of Epona by Lara.
|
||||
* Based on the original code of Services by Andy Church.
|
||||
*/
|
||||
|
||||
#include "module.h"
|
||||
#include "modules/encryption.h"
|
||||
|
||||
class EOld final
|
||||
: public Module
|
||||
{
|
||||
private:
|
||||
ServiceReference<Encryption::Provider> md5;
|
||||
|
||||
Anope::string EncryptInternal(const Anope::string &src)
|
||||
{
|
||||
if (!md5)
|
||||
return {};
|
||||
|
||||
char digest[32];
|
||||
memset(digest, 0, sizeof(digest));
|
||||
|
||||
auto hash = md5->Encrypt(src);
|
||||
if (hash.length() != sizeof(digest))
|
||||
return {}; // Probably a bug?
|
||||
memcpy(digest, hash.data(), hash.length());
|
||||
|
||||
char digest2[16];
|
||||
for (size_t i = 0; i < sizeof(digest); i += 2)
|
||||
digest2[i / 2] = XTOI(digest[i]) << 4 | XTOI(digest[i + 1]);
|
||||
|
||||
return Anope::Hex(digest2, sizeof(digest2));
|
||||
}
|
||||
|
||||
inline static char XTOI(char c)
|
||||
{
|
||||
return c > 9 ? c - 'A' + 10 : c - '0';
|
||||
}
|
||||
|
||||
public:
|
||||
EOld(const Anope::string &modname, const Anope::string &creator)
|
||||
: Module(modname, creator, ENCRYPTION | VENDOR)
|
||||
, md5("Encryption::Provider", "md5")
|
||||
{
|
||||
if (ModuleManager::FindFirstOf(ENCRYPTION) == this)
|
||||
throw ModuleException("enc_old is deprecated and can not be used as a primary encryption method");
|
||||
|
||||
ModuleManager::LoadModule("enc_md5", User::Find(creator, true));
|
||||
if (!md5)
|
||||
throw ModuleException("Unable to find md5 reference");
|
||||
}
|
||||
|
||||
void OnCheckAuthentication(User *, IdentifyRequest *req) override
|
||||
{
|
||||
const auto *na = NickAlias::Find(req->GetAccount());
|
||||
if (!na)
|
||||
return;
|
||||
|
||||
NickCore *nc = na->nc;
|
||||
size_t pos = nc->pass.find(':');
|
||||
if (pos == Anope::string::npos)
|
||||
return;
|
||||
|
||||
Anope::string hash_method(nc->pass.begin(), nc->pass.begin() + pos);
|
||||
if (!hash_method.equals_cs("oldmd5"))
|
||||
return;
|
||||
|
||||
auto enc = EncryptInternal(req->GetPassword());
|
||||
if (!enc.empty() && nc->pass.equals_cs(enc))
|
||||
{
|
||||
// If we are NOT the first encryption module we want to re-encrypt
|
||||
// the password with the primary encryption method.
|
||||
if (ModuleManager::FindFirstOf(ENCRYPTION) != this)
|
||||
Anope::Encrypt(req->GetPassword(), nc->pass);
|
||||
req->Success(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MODULE_INIT(EOld)
|
||||
@@ -173,7 +173,7 @@ private:
|
||||
|
||||
public:
|
||||
ESHA1(const Anope::string &modname, const Anope::string &creator)
|
||||
: Module(modname, creator, ENCRYPTION | VENDOR)
|
||||
: Module(modname, creator, DEPRECATED | ENCRYPTION | VENDOR)
|
||||
, sha1provider(this, "sha1", 20, 64)
|
||||
{
|
||||
if (ModuleManager::FindFirstOf(ENCRYPTION) == this)
|
||||
|
||||
@@ -17,14 +17,6 @@ class ESHA256 final
|
||||
{
|
||||
private:
|
||||
unsigned iv[8];
|
||||
bool use_iv;
|
||||
|
||||
/* initializes the IV with a new random value */
|
||||
void NewRandomIV()
|
||||
{
|
||||
for (auto &ivsegment : iv)
|
||||
ivsegment = static_cast<uint32_t>(Anope::RandomNumber());
|
||||
}
|
||||
|
||||
/* returns the IV as base64-encrypted string */
|
||||
Anope::string GetIVString()
|
||||
@@ -50,11 +42,6 @@ private:
|
||||
|
||||
Anope::string EncryptInternal(const Anope::string &src)
|
||||
{
|
||||
if (!use_iv)
|
||||
NewRandomIV();
|
||||
else
|
||||
use_iv = false;
|
||||
|
||||
sha256_ctx ctx;
|
||||
sha256_init(&ctx);
|
||||
for (size_t i = 0; i < 8; ++i)
|
||||
@@ -69,9 +56,8 @@ private:
|
||||
|
||||
public:
|
||||
ESHA256(const Anope::string &modname, const Anope::string &creator)
|
||||
: Module(modname, creator, ENCRYPTION | VENDOR)
|
||||
: Module(modname, creator, DEPRECATED | ENCRYPTION | VENDOR)
|
||||
{
|
||||
use_iv = false;
|
||||
if (ModuleManager::FindFirstOf(ENCRYPTION) == this)
|
||||
throw ModuleException("enc_sha256 is deprecated and can not be used as a primary encryption method");
|
||||
}
|
||||
@@ -92,7 +78,6 @@ public:
|
||||
return;
|
||||
|
||||
GetIVFromPass(nc->pass);
|
||||
use_iv = true;
|
||||
auto enc = EncryptInternal(req->GetPassword());
|
||||
if (nc->pass.equals_cs(enc))
|
||||
{
|
||||
|
||||
@@ -200,7 +200,7 @@ public:
|
||||
|
||||
Anope::string GetColumn(Serialize::DataType dt)
|
||||
{
|
||||
return Anope::printf("%s %s DEFAULT %s",
|
||||
return Anope::Format("%s %s DEFAULT %s",
|
||||
GetColumnType(dt),
|
||||
GetColumnNull(dt) ? "NULL" : "NOT NULL",
|
||||
GetColumnDefault(dt));
|
||||
@@ -245,7 +245,7 @@ public:
|
||||
DThread = new DispatcherThread();
|
||||
DThread->Start();
|
||||
|
||||
Log(this) << "Module was compiled against MySQL version " << (MYSQL_VERSION_ID / 10000) << "." << (MYSQL_VERSION_ID / 100 % 100) << "." << (MYSQL_VERSION_ID % 100) << " and is running against version " << mysql_get_client_info();
|
||||
Log(this) << "Module was compiled against MySQL version " << MYSQL_SERVER_VERSION << " and is running against version " << mysql_get_client_info();
|
||||
}
|
||||
|
||||
~ModuleSQL()
|
||||
@@ -498,24 +498,24 @@ std::vector<Query> MySQLService::CreateTable(const Anope::string &table, const D
|
||||
{
|
||||
// We can't just use MODIFY COLUMN here because the value may not
|
||||
// be valid and we may need to replace with the default.
|
||||
auto res = this->RunQuery(Anope::printf("ALTER TABLE `%s` ADD COLUMN `%s_new` %s; ",
|
||||
auto res = this->RunQuery(Anope::Format("ALTER TABLE `%s` ADD COLUMN `%s_new` %s; ",
|
||||
table.c_str(), column.c_str(), GetColumn(stype).c_str()));
|
||||
|
||||
if (res)
|
||||
{
|
||||
res = this->RunQuery(Anope::printf("UPDATE IGNORE `%s` SET `%s_new` = %s; ",
|
||||
res = this->RunQuery(Anope::Format("UPDATE IGNORE `%s` SET `%s_new` = %s; ",
|
||||
table.c_str(), column.c_str(), column.c_str()));
|
||||
}
|
||||
|
||||
if (res)
|
||||
{
|
||||
res = this->RunQuery(Anope::printf("ALTER TABLE `%s` DROP COLUMN `%s`; ",
|
||||
res = this->RunQuery(Anope::Format("ALTER TABLE `%s` DROP COLUMN `%s`; ",
|
||||
table.c_str(), column.c_str()));
|
||||
}
|
||||
|
||||
if (res)
|
||||
{
|
||||
res = this->RunQuery(Anope::printf("ALTER TABLE `%s` RENAME COLUMN `%s_new` TO `%s`; ",
|
||||
res = this->RunQuery(Anope::Format("ALTER TABLE `%s` RENAME COLUMN `%s_new` TO `%s`; ",
|
||||
table.c_str(), column.c_str(), column.c_str()));
|
||||
}
|
||||
|
||||
|
||||
@@ -148,7 +148,7 @@ public:
|
||||
|
||||
if (request.data.size() < event->GetMinParams())
|
||||
{
|
||||
auto error = Anope::printf("Not enough parameters (given %zu, expected %zu)",
|
||||
auto error = Anope::Format("Not enough parameters (given %zu, expected %zu)",
|
||||
request.data.size(), event->GetMinParams());
|
||||
xmlrpc_env_set_fault(&env, RPC::ERR_INVALID_PARAMS, error.c_str());
|
||||
SendError(reply, env);
|
||||
|
||||
+2
-2
@@ -110,7 +110,7 @@ public:
|
||||
if (params.empty())
|
||||
return;
|
||||
|
||||
Anope::string normalized_param0 = Anope::NormalizeBuffer(params[0]);
|
||||
Anope::string normalized_param0 = Anope::RemoveFormatting(params[0]);
|
||||
Anope::string fantasy_chars = Config->GetModule(this).Get<Anope::string>("fantasycharacter", "!");
|
||||
|
||||
if (!normalized_param0.find(c->ci->bi->nick))
|
||||
@@ -143,7 +143,7 @@ public:
|
||||
full_command.erase(full_command.begin());
|
||||
|
||||
++count;
|
||||
it = Config->Fantasy.find(Anope::NormalizeBuffer(full_command));
|
||||
it = Config->Fantasy.find(Anope::RemoveFormatting(full_command));
|
||||
}
|
||||
|
||||
if (it == Config->Fantasy.end())
|
||||
|
||||
@@ -114,6 +114,8 @@ private:
|
||||
|
||||
ListFormatter list(source.nc);
|
||||
list.AddColumn(_("Number")).AddColumn(_("Message"));
|
||||
list.SetFlexible(_("{number}: {message}"));
|
||||
|
||||
for (size_t i = 0; i < q->size(); ++i)
|
||||
{
|
||||
ListFormatter::ListEntry entry;
|
||||
@@ -121,11 +123,7 @@ private:
|
||||
entry["Message"] = (*q)[i];
|
||||
list.AddEntry(entry);
|
||||
}
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
list.Process(replies);
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
list.SendTo(source);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -141,7 +141,7 @@ public:
|
||||
if (source && !Config->GetModule(this).Get<bool>("anonymousglobal"))
|
||||
{
|
||||
// A source is available and they're not anonymous.
|
||||
line = Anope::printf("[%s] %s", source->GetNick().c_str(), message.c_str());
|
||||
line = Anope::Format("[%s] %s", source->GetNick().c_str(), message.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+1
-1
@@ -204,7 +204,7 @@ public:
|
||||
Anope::string *greet = ns_greet.Get(user->Account());
|
||||
if (bs_greet.HasExt(c->ci) && greet != NULL && !greet->empty() && c->FindUser(c->ci->bi) && c->ci->AccessFor(user).HasPriv("GREET"))
|
||||
{
|
||||
const auto message = Anope::printf("[%s] %s", user->Account()->display.c_str(), greet->c_str());
|
||||
const auto message = Anope::Format("[%s] %s", user->Account()->display.c_str(), greet->c_str());
|
||||
IRCD->SendPrivmsg(*c->ci->bi, c->name, message);
|
||||
c->ci->bi->lastmsg = Anope::CurTime;
|
||||
}
|
||||
|
||||
@@ -52,8 +52,10 @@ public:
|
||||
}
|
||||
|
||||
unsigned display_counter = 0, listmax = Config->GetModule(this->owner).Get<unsigned>("listmax", "50");
|
||||
|
||||
ListFormatter list(source.GetAccount());
|
||||
list.AddColumn(_("Number")).AddColumn(_("Nick")).AddColumn(_("VHost")).AddColumn(_("Creator")).AddColumn(_("Created"));
|
||||
list.SetFlexible(_("{number}: \002{nick}\002 = {vhost} -- created by {creator} at {created}"));
|
||||
|
||||
for (const auto &[_, na] : *NickAliasList)
|
||||
{
|
||||
@@ -112,11 +114,7 @@ public:
|
||||
source.Reply(_("Displayed all records (count: \002%d\002)."), display_counter);
|
||||
}
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
list.Process(replies);
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
list.SendTo(source);
|
||||
}
|
||||
|
||||
bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
|
||||
|
||||
+221
-21
@@ -15,12 +15,20 @@
|
||||
*/
|
||||
|
||||
#include "module.h"
|
||||
#include "modules/dns.h"
|
||||
#include "modules/hostserv/request.h"
|
||||
|
||||
static ServiceReference<DNS::Manager> dnsmanager("DNS::Manager", "dns/manager");
|
||||
static ServiceReference<MemoServService> memoserv("MemoServService", "MemoServ");
|
||||
|
||||
static void req_send_memos(Module *me, CommandSource &source, const Anope::string &vident, const Anope::string &vhost);
|
||||
|
||||
namespace
|
||||
{
|
||||
// The name of the DNS record used for validation.
|
||||
Anope::string validation_record;
|
||||
}
|
||||
|
||||
struct HostRequestImpl final
|
||||
: HostRequest
|
||||
, Serializable
|
||||
@@ -29,6 +37,23 @@ struct HostRequestImpl final
|
||||
: Serializable("HostRequest")
|
||||
{
|
||||
}
|
||||
|
||||
static HostRequestImpl *Get(NickAlias *na)
|
||||
{
|
||||
return na ? na->GetExt<HostRequestImpl>("hostrequest") : nullptr;
|
||||
}
|
||||
|
||||
Anope::string Mask() const
|
||||
{
|
||||
if (ident.empty())
|
||||
return host;
|
||||
return ident + "@" + host;
|
||||
}
|
||||
|
||||
Anope::string GetValidationRecord() const
|
||||
{
|
||||
return Anope::Format("%s=%s", validation_record.c_str(), this->validation_token.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
struct HostRequestTypeImpl final
|
||||
@@ -46,6 +71,8 @@ struct HostRequestTypeImpl final
|
||||
data.Store("ident", req->ident);
|
||||
data.Store("host", req->host);
|
||||
data.Store("time", req->time);
|
||||
data.Store("validation_token", req->validation_token);
|
||||
data.Store("last_validation", req->last_validation);
|
||||
}
|
||||
|
||||
Serializable *Unserialize(Serializable *obj, Serialize::Data &data) const override
|
||||
@@ -68,20 +95,99 @@ struct HostRequestTypeImpl final
|
||||
data["ident"] >> req->ident;
|
||||
data["host"] >> req->host;
|
||||
data["time"] >> req->time;
|
||||
data["validation_token"] >> req->validation_token;
|
||||
data["last_validation"] >> req->last_validation;
|
||||
}
|
||||
|
||||
return req;
|
||||
}
|
||||
};
|
||||
|
||||
class DNSHostResolver final
|
||||
: public DNS::Request
|
||||
{
|
||||
private:
|
||||
Command *command;
|
||||
Reference<NickAlias> nickalias;
|
||||
CommandSource source;
|
||||
|
||||
void HandleError(HostRequestImpl *hr)
|
||||
{
|
||||
source.Reply(_(
|
||||
"Unable to find the DNS record required to validate \002%s\002. If you have not already "
|
||||
"done this add a TXT record for %s with the value %s and re-execute this command."
|
||||
),
|
||||
hr->Mask().c_str(),
|
||||
hr->host.c_str(),
|
||||
hr->GetValidationRecord().c_str()
|
||||
);
|
||||
}
|
||||
|
||||
public:
|
||||
DNSHostResolver(Command *cmd, HostRequest *hr, NickAlias *na, const CommandSource &src)
|
||||
: Request(dnsmanager, cmd->module, hr->host, DNS::QUERY_TXT, false)
|
||||
, command(cmd)
|
||||
, nickalias(na)
|
||||
, source(src)
|
||||
{
|
||||
hr->last_validation = Anope::CurTime;
|
||||
Log(LOG_DEBUG) << "Checking " << hr->host << " for " << hr->validation_token;
|
||||
}
|
||||
|
||||
void OnError(const DNS::Query *record) override
|
||||
{
|
||||
NickAlias *na = nickalias;
|
||||
if (!na)
|
||||
return; // Nick has been dropped.
|
||||
|
||||
auto *hr = HostRequestImpl::Get(na);
|
||||
if (!hr)
|
||||
{
|
||||
source.Reply(_("No request for nick %s found."), source.GetNick().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
HandleError(hr);
|
||||
}
|
||||
|
||||
void OnLookupComplete(const DNS::Query *record) override
|
||||
{
|
||||
NickAlias *na = nickalias;
|
||||
if (!na)
|
||||
return; // Nick has been dropped.
|
||||
|
||||
auto *hr = HostRequestImpl::Get(na);
|
||||
if (!hr)
|
||||
{
|
||||
source.Reply(_("No request for nick %s found."), source.GetNick().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto &answer : record->answers)
|
||||
{
|
||||
if (answer.rdata != hr->GetValidationRecord())
|
||||
continue; // Not for us.
|
||||
|
||||
na->SetVHost(hr->ident, hr->host, source.GetNick(), hr->time);
|
||||
FOREACH_MOD(OnSetVHost, (na));
|
||||
|
||||
if (Config->GetModule(command->module).Get<bool>("memouser") && memoserv)
|
||||
memoserv->Send(source.service->nick, na->nick, _("Your requested vhost has been validated via DNS."), true);
|
||||
|
||||
source.Reply(_("VHost for %s has been validated using DNS."), na->nick.c_str());
|
||||
Log(LOG_COMMAND, source, command) << "for " << na->nick << " for vhost " << hr->Mask();
|
||||
na->Shrink<HostRequestImpl>("hostrequest");
|
||||
|
||||
return; // We're done.
|
||||
}
|
||||
|
||||
HandleError(hr);
|
||||
}
|
||||
};
|
||||
|
||||
class CommandHSRequest final
|
||||
: public Command
|
||||
{
|
||||
static bool isvalidchar(char c)
|
||||
{
|
||||
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '.' || c == '-';
|
||||
}
|
||||
|
||||
public:
|
||||
CommandHSRequest(Module *creator) : Command(creator, "hostserv/request", 1, 1)
|
||||
{
|
||||
@@ -142,13 +248,11 @@ public:
|
||||
source.Reply(HOST_NO_VIDENT);
|
||||
return;
|
||||
}
|
||||
for (const auto &chr : user)
|
||||
|
||||
if (!IRCD->IsIdentValid(user))
|
||||
{
|
||||
if (!isvalidchar(chr))
|
||||
{
|
||||
source.Reply(HOST_SET_IDENT_ERROR);
|
||||
return;
|
||||
}
|
||||
source.Reply(HOST_SET_IDENT_ERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,9 +282,28 @@ public:
|
||||
req.ident = user;
|
||||
req.host = host;
|
||||
req.time = Anope::CurTime;
|
||||
req.validation_token = Anope::Random(Config->GetBlock("options").Get<size_t>("codelength", "15"));
|
||||
na->Extend<HostRequestImpl>("hostrequest", req);
|
||||
|
||||
source.Reply(_("Your vhost has been requested."));
|
||||
BotInfo *bi;
|
||||
Anope::string cmd;
|
||||
if (dnsmanager && Command::FindCommandFromService("hostserv/validate", bi, cmd))
|
||||
{
|
||||
source.Reply(_(
|
||||
"Your vhost \002%s\002 has been requested. If the requested vhost is for a valid "
|
||||
"DNS name you can add a TXT record for %s with the value %s and automatically "
|
||||
"approve your vhost using \002%s\002."
|
||||
),
|
||||
req.Mask().c_str(),
|
||||
req.host.c_str(),
|
||||
req.GetValidationRecord().c_str(),
|
||||
bi->GetQueryCommand("hostserv/validate").c_str()
|
||||
);
|
||||
}
|
||||
else
|
||||
source.Reply(_("Your vhost \002%s\002 has been requested."), req.Mask().c_str());
|
||||
|
||||
|
||||
req_send_memos(owner, source, user, host);
|
||||
Log(LOG_COMMAND, source, this) << "to request new vhost " << (!user.empty() ? user + "@" : "") << host;
|
||||
}
|
||||
@@ -279,7 +402,7 @@ public:
|
||||
{
|
||||
Anope::string message;
|
||||
if (!reason.empty())
|
||||
message = Anope::printf(_("Your requested vhost has been rejected. Reason: %s"), reason.c_str());
|
||||
message = Anope::Format(_("Your requested vhost has been rejected. Reason: %s"), reason.c_str());
|
||||
else
|
||||
message = _("Your requested vhost has been rejected.");
|
||||
|
||||
@@ -318,9 +441,10 @@ public:
|
||||
{
|
||||
unsigned counter = 0;
|
||||
unsigned display_counter = 0, listmax = Config->GetModule(this->owner).Get<unsigned>("listmax");
|
||||
ListFormatter list(source.GetAccount());
|
||||
|
||||
ListFormatter list(source.GetAccount());
|
||||
list.AddColumn(_("Number")).AddColumn(_("Nick")).AddColumn(_("VHost")).AddColumn(_("Created"));
|
||||
list.SetFlexible(_("{number}: \002{nick}\002 = {vhost} -- created by {creator} at {created}"));
|
||||
|
||||
for (const auto &[nick, na] : *NickAliasList)
|
||||
{
|
||||
@@ -345,12 +469,7 @@ public:
|
||||
++counter;
|
||||
}
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
list.Process(replies);
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
|
||||
list.SendTo(source);
|
||||
source.Reply(_("Displayed \002%d\002 records (\002%d\002 total)."), display_counter, counter);
|
||||
}
|
||||
|
||||
@@ -364,6 +483,78 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class CommandHSValidate final
|
||||
: public Command
|
||||
{
|
||||
public:
|
||||
time_t cooldown;
|
||||
|
||||
CommandHSValidate(Module *creator)
|
||||
: Command(creator, "hostserv/validate", 0)
|
||||
{
|
||||
this->SetDesc(_("Validates a previously requested vhost using DNS"));
|
||||
}
|
||||
|
||||
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override
|
||||
{
|
||||
if (Anope::ReadOnly)
|
||||
{
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
auto *na = NickAlias::Find(source.GetNick());
|
||||
if (!na || na->nc != source.GetAccount())
|
||||
{
|
||||
source.Reply(ACCESS_DENIED);
|
||||
return;
|
||||
}
|
||||
|
||||
auto *req = HostRequestImpl::Get(na);
|
||||
if (!req)
|
||||
{
|
||||
source.Reply(_("No request for nick %s found."), source.GetNick().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
auto next_validation = req->last_validation + cooldown;
|
||||
if (req->last_validation && next_validation > Anope::CurTime)
|
||||
{
|
||||
source.Reply(_("You must wait for %s before trying DNS validation again."),
|
||||
Anope::Duration(next_validation - Anope::CurTime).c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
DNSHostResolver *res = nullptr;
|
||||
try
|
||||
{
|
||||
if (!dnsmanager)
|
||||
throw SocketException("DNS is not available");
|
||||
|
||||
res = new DNSHostResolver(this, req, na, source);
|
||||
dnsmanager->Process(res);
|
||||
}
|
||||
catch (const SocketException &ex)
|
||||
{
|
||||
Log(this->module) << ex.GetReason();
|
||||
source.Reply("Unable to validate vhosts right now. Please try again later.");
|
||||
delete res;
|
||||
}
|
||||
}
|
||||
|
||||
bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
|
||||
{
|
||||
this->SendSyntax(source);
|
||||
source.Reply(" ");
|
||||
source.Reply(_(
|
||||
"Validates a previously requested vhost using DNS. If you own the domain you "
|
||||
"have requested as a vhost you can validate your ownership of it using a DNS "
|
||||
"TXT record to approve your own vhost."
|
||||
));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class HSRequest final
|
||||
: public Module
|
||||
{
|
||||
@@ -371,6 +562,7 @@ class HSRequest final
|
||||
CommandHSActivate commandhsactive;
|
||||
CommandHSReject commandhsreject;
|
||||
CommandHSWaiting commandhswaiting;
|
||||
CommandHSValidate commandhsvalidate;
|
||||
ExtensibleItem<HostRequestImpl> hostrequest;
|
||||
HostRequestTypeImpl request_type;
|
||||
|
||||
@@ -381,11 +573,19 @@ public:
|
||||
, commandhsactive(this)
|
||||
, commandhsreject(this)
|
||||
, commandhswaiting(this)
|
||||
, commandhsvalidate(this)
|
||||
, hostrequest(this, "hostrequest")
|
||||
{
|
||||
if (!IRCD || !IRCD->CanSetVHost)
|
||||
throw ModuleException("Your IRCd does not support vhosts");
|
||||
}
|
||||
|
||||
void OnReload(Configuration::Conf &conf) override
|
||||
{
|
||||
const auto &block = conf.GetModule(this);
|
||||
commandhsvalidate.cooldown = block.Get<time_t>("validationcooldown", "5m");
|
||||
validation_record = block.Get<const Anope::string>("validationrecord", "anope-dns-validation");
|
||||
}
|
||||
};
|
||||
|
||||
static void req_send_memos(Module *me, CommandSource &source, const Anope::string &vident, const Anope::string &vhost)
|
||||
@@ -404,7 +604,7 @@ static void req_send_memos(Module *me, CommandSource &source, const Anope::strin
|
||||
if (!na)
|
||||
continue;
|
||||
|
||||
Anope::string message = Anope::printf(_("VHost \002%s\002 has been requested by %s."), host.c_str(), source.GetNick().c_str());
|
||||
Anope::string message = Anope::Format(_("VHost \002%s\002 has been requested by %s."), host.c_str(), source.GetNick().c_str());
|
||||
|
||||
memoserv->Send(source.service->nick, na->nick, message, true);
|
||||
}
|
||||
|
||||
@@ -107,7 +107,6 @@ public:
|
||||
if (na == NULL)
|
||||
{
|
||||
na = new NickAlias(ii->req->GetAccount(), new NickCore(ii->req->GetAccount()));
|
||||
na->last_realname = ii->user ? ii->user->realname : ii->req->GetAccount();
|
||||
FOREACH_MOD(OnNickRegister, (ii->user, na, ii->req->GetPassword()));
|
||||
BotInfo *NickServ = Config->GetClient("NickServ");
|
||||
if (ii->user && NickServ)
|
||||
|
||||
@@ -77,6 +77,8 @@ public:
|
||||
{
|
||||
ListFormatter list(source.GetAccount());
|
||||
list.AddColumn(_("Mask"));
|
||||
list.SetFlexible(_("\002{mask}\002"));
|
||||
|
||||
for (const auto &ignore : mi->ignores)
|
||||
{
|
||||
ListFormatter::ListEntry entry;
|
||||
@@ -85,12 +87,7 @@ public:
|
||||
}
|
||||
|
||||
source.Reply(_("Memo ignore list:"));
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
list.Process(replies);
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
list.SendTo(source);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -61,8 +61,8 @@ public:
|
||||
else
|
||||
{
|
||||
ListFormatter list(source.GetAccount());
|
||||
|
||||
list.AddColumn(_("Number")).AddColumn(_("Sender")).AddColumn(_("Date/Time"));
|
||||
list.SetFlexible(_("{number}: sent by \002{sender}\002 at {date/time}"));
|
||||
|
||||
if (!param.empty() && isdigit(param[0]))
|
||||
{
|
||||
@@ -127,12 +127,8 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
list.Process(replies);
|
||||
|
||||
source.Reply(_("Memos for %s:"), ci ? ci->name.c_str() : source.GetNick().c_str());
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
list.SendTo(source);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ static void rsend_notify(CommandSource &source, MemoInfo *mi, Memo *m, const Ano
|
||||
|
||||
/* Text of the memo varies if the recipient was a
|
||||
nick or channel */
|
||||
Anope::string text = Anope::printf(Language::Translate(na->nc, _("\002[auto-memo]\002 The memo you sent to %s has been viewed.")), targ.c_str());
|
||||
Anope::string text = Anope::Format(Language::Translate(na->nc, _("\002[auto-memo]\002 The memo you sent to %s has been viewed.")), targ.c_str());
|
||||
|
||||
/* Send notification */
|
||||
MemoServService->Send(source.GetNick(), m->sender, text, true);
|
||||
|
||||
@@ -224,16 +224,21 @@ public:
|
||||
auto protect = protectafter ? *protectafter : block.Get<time_t>("defaultprotect", "1m");
|
||||
protect = std::clamp(protect, block.Get<time_t>("minprotect", "10s"), block.Get<time_t>("maxprotect", "10m"));
|
||||
|
||||
u->SendMessage(NickServ, _(
|
||||
"This nickname is registered and has protection enabled. If it belongs to you, "
|
||||
"type \002%s\032\037password\037\002 to identify to your account."
|
||||
),
|
||||
NickServ->GetQueryCommand("nickserv/identify", u->nick).c_str()
|
||||
);
|
||||
|
||||
if (protect)
|
||||
{
|
||||
u->SendMessage(NickServ, NICK_IS_SECURE, NickServ->GetQueryCommand("nickserv/identify").c_str());
|
||||
u->SendMessage(NickServ, _("If you do not change within %s, I will change your nick."),
|
||||
u->SendMessage(NickServ, _("Your nickname will be changed in %s if you do not identify."),
|
||||
Anope::Duration(protect, u->Account()).c_str());
|
||||
new NickServCollide(this, this, u, na, protect);
|
||||
}
|
||||
else
|
||||
{
|
||||
u->SendMessage(NickServ, FORCENICKCHANGE_NOW);
|
||||
this->Collide(u, na);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,6 +113,13 @@ class CommandNSAJoin final
|
||||
{
|
||||
ListFormatter list(source.GetAccount());
|
||||
list.AddColumn(_("Number")).AddColumn(_("Channel")).AddColumn(_("Key"));
|
||||
list.SetFlexible([](ListFormatter::ListEntry &row)
|
||||
{
|
||||
return row["Key"].empty()
|
||||
? _("{number}: \002{channel}\002")
|
||||
: _("{number}: \002{channel}\002 (key: {key})");
|
||||
});
|
||||
|
||||
for (unsigned i = 0; i < (*channels)->size(); ++i)
|
||||
{
|
||||
AJoinEntry *aj = (*channels)->at(i);
|
||||
@@ -124,12 +131,7 @@ class CommandNSAJoin final
|
||||
}
|
||||
|
||||
source.Reply(_("%s's auto join list:"), nc->display.c_str());
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
list.Process(replies);
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
list.SendTo(source);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,10 +38,16 @@ public:
|
||||
nc = na->nc;
|
||||
}
|
||||
|
||||
ListFormatter list(source.GetAccount());
|
||||
int chan_count = 0;
|
||||
|
||||
ListFormatter list(source.GetAccount());
|
||||
list.AddColumn(_("Number")).AddColumn(_("Channel")).AddColumn(_("Access")).AddColumn(_("Description"));
|
||||
list.SetFlexible([](ListFormatter::ListEntry &row)
|
||||
{
|
||||
return row["Description"].empty()
|
||||
? _("{number}: \002{channel}\002 = {access}")
|
||||
: _("{number}: \002{channel}\002 = {access} ({description})");
|
||||
});
|
||||
|
||||
std::deque<ChannelInfo *> queue;
|
||||
nc->GetChannelReferences(queue);
|
||||
@@ -87,9 +93,6 @@ public:
|
||||
list.AddEntry(entry);
|
||||
}
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
list.Process(replies);
|
||||
|
||||
if (!chan_count)
|
||||
{
|
||||
source.Reply(_("\002%s\002 has no access in any channels."), nc->display.c_str());
|
||||
@@ -97,10 +100,7 @@ public:
|
||||
else
|
||||
{
|
||||
source.Reply(_("Channels that \002%s\002 has access on:"), nc->display.c_str());
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
|
||||
list.SendTo(source);
|
||||
source.Reply(_("End of list - %d channels shown."), chan_count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,7 +430,7 @@ public:
|
||||
|
||||
auto *NickServ = Config->GetClient("NickServ");
|
||||
u->SendMessage(NickServ, _("SSL certificate fingerprint accepted, you are now identified to \002%s\002."), nc->display.c_str());
|
||||
Log(NickServ) << u->GetMask() << " automatically identified for account " << nc->display << " via SSL certificate fingerprint";
|
||||
Log(NickServ) << u->GetMask() << " automatically identified for account " << nc->display << " via SSL certificate fingerprint " << u->fingerprint;
|
||||
}
|
||||
|
||||
void OnNickRegister(User *u, NickAlias *na, const Anope::string &pass) override
|
||||
|
||||
+115
-18
@@ -55,16 +55,8 @@ public:
|
||||
|
||||
if (u != NULL)
|
||||
{
|
||||
na->last_usermask = u->GetIdent() + "@" + u->GetDisplayedHost();
|
||||
na->last_realname = u->realname;
|
||||
}
|
||||
else
|
||||
{
|
||||
na->last_realname = source.GetNick();
|
||||
}
|
||||
na->last_userhost = u->GetIdent() + "@" + u->GetDisplayedHost();
|
||||
|
||||
if (u != NULL)
|
||||
{
|
||||
IRCD->SendLogin(u, na); // protocol modules prevent this on unconfirmed accounts
|
||||
u->Login(target->nc);
|
||||
FOREACH_MOD(OnNickGroup, (u, target));
|
||||
@@ -340,7 +332,14 @@ public:
|
||||
nc = source.GetAccount();
|
||||
|
||||
ListFormatter list(source.GetAccount());
|
||||
list.AddColumn(_("Nick")).AddColumn(_("Expires"));
|
||||
list.AddColumn(_("Nick")).AddColumn(_("Registered")).AddColumn(_("Expires"));
|
||||
list.SetFlexible([](ListFormatter::ListEntry &row)
|
||||
{
|
||||
return row["Expires"].equals_cs(NO_EXPIRE)
|
||||
? _("\002{nick}\002: registered on {registered}")
|
||||
: _("\002{nick}\002: registered on {registered}; expires in {expires}");
|
||||
});
|
||||
|
||||
time_t nickserv_expire = Config->GetModule("nickserv").Get<time_t>("expire", "90d"),
|
||||
unconfirmed_expire = Config->GetModule("ns_register").Get<time_t>("unconfirmedexpire", "1d");
|
||||
for (auto *na2 : *nc->aliases)
|
||||
@@ -357,17 +356,13 @@ public:
|
||||
|
||||
ListFormatter::ListEntry entry;
|
||||
entry["Nick"] = na2->nick;
|
||||
entry["Registered"] = Anope::strftime(na2->registered);
|
||||
entry["Expires"] = expires;
|
||||
list.AddEntry(entry);
|
||||
}
|
||||
|
||||
source.Reply(!nick.empty() ? _("List of nicknames belonging to \002%s\002:") : _("List of nicknames belonging to your account:"), nc->display.c_str());
|
||||
std::vector<Anope::string> replies;
|
||||
list.Process(replies);
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
|
||||
list.SendTo(source);
|
||||
source.Reply(nc->aliases->size(), N_("%zu nickname in the account.", "%zu nicknames in the account."), nc->aliases->size());
|
||||
}
|
||||
|
||||
@@ -393,16 +388,118 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class CommandNSSetDisplay
|
||||
: public Command
|
||||
{
|
||||
public:
|
||||
CommandNSSetDisplay(Module *creator, const Anope::string &sname = "nickserv/set/display", size_t min = 1)
|
||||
: Command(creator, sname, min, min + 1)
|
||||
{
|
||||
this->SetDesc(_("Set the display nickname for your account"));
|
||||
this->SetSyntax(_("\037new-display\037"));
|
||||
}
|
||||
|
||||
void Run(CommandSource &source, const Anope::string &user, const Anope::string ¶m)
|
||||
{
|
||||
if (Anope::ReadOnly)
|
||||
{
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
auto *user_na = NickAlias::Find(user);
|
||||
if (!user_na)
|
||||
{
|
||||
source.Reply(NICK_X_NOT_REGISTERED, user.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
auto *na = NickAlias::Find(param);
|
||||
if (!na || *na->nc != *user_na->nc)
|
||||
{
|
||||
source.Reply(_("The new display nickname must belong to the %s account."), user_na->nc->display.c_str());
|
||||
return;
|
||||
}
|
||||
NickCore *user_nc = user_na->nc;
|
||||
|
||||
EventReturn MOD_RESULT;
|
||||
FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, user_nc, param));
|
||||
if (MOD_RESULT == EVENT_STOP)
|
||||
return;
|
||||
|
||||
Log(user_nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to change the display of " << user_nc->display << " to " << na->nick;
|
||||
user_nc->SetDisplay(na);
|
||||
|
||||
// Send updated account name to the IRCd.
|
||||
for (auto *u : user_nc->users)
|
||||
IRCD->SendLogin(u, user_na);
|
||||
|
||||
source.Reply(NICK_SET_DISPLAY_CHANGED, user_nc->display.c_str());
|
||||
}
|
||||
|
||||
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override
|
||||
{
|
||||
this->Run(source, source.nc->display, params[0]);
|
||||
}
|
||||
|
||||
bool OnHelp(CommandSource &source, const Anope::string &) override
|
||||
{
|
||||
this->SendSyntax(source);
|
||||
source.Reply(" ");
|
||||
source.Reply(_(
|
||||
"Changes the display nickname used to refer to your account. The new display "
|
||||
"nickname must already be associated with your account."
|
||||
));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class CommandNSSASetDisplay final
|
||||
: public CommandNSSetDisplay
|
||||
{
|
||||
public:
|
||||
CommandNSSASetDisplay(Module *creator)
|
||||
: CommandNSSetDisplay(creator, "nickserv/saset/display", 2)
|
||||
{
|
||||
this->ClearSyntax();
|
||||
this->SetSyntax(_("\037nickname\037 \037new-display\037"));
|
||||
}
|
||||
|
||||
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override
|
||||
{
|
||||
this->Run(source, params[0], params[1]);
|
||||
}
|
||||
|
||||
bool OnHelp(CommandSource &source, const Anope::string &) override
|
||||
{
|
||||
this->SendSyntax(source);
|
||||
source.Reply(" ");
|
||||
source.Reply(_(
|
||||
"Changes the display nickname used to refer to the account. The new display"
|
||||
"nickname must already be associated with the account."
|
||||
));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class NSGroup final
|
||||
: public Module
|
||||
{
|
||||
private:
|
||||
CommandNSGroup commandnsgroup;
|
||||
CommandNSUngroup commandnsungroup;
|
||||
CommandNSGList commandnsglist;
|
||||
CommandNSSetDisplay commandnssetdisplay;
|
||||
CommandNSSASetDisplay commandnssasetdisplay;
|
||||
|
||||
public:
|
||||
NSGroup(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
|
||||
commandnsgroup(this), commandnsungroup(this), commandnsglist(this)
|
||||
NSGroup(const Anope::string &modname, const Anope::string &creator)
|
||||
: Module(modname, creator, VENDOR)
|
||||
, commandnsgroup(this)
|
||||
, commandnsungroup(this)
|
||||
, commandnsglist(this)
|
||||
, commandnssetdisplay(this)
|
||||
, commandnssasetdisplay(this)
|
||||
{
|
||||
if (Config->GetModule("nickserv").Get<bool>("nonicknameownership"))
|
||||
throw ModuleException(modname + " can not be used with options:nonicknameownership enabled");
|
||||
|
||||
@@ -78,7 +78,7 @@ public:
|
||||
if (has_auspex || na->nc == source.GetAccount())
|
||||
show_hidden = true;
|
||||
|
||||
source.Reply(_("%s is %s"), na->nick.c_str(), na->last_realname.c_str());
|
||||
source.Reply(_("Information about nick \002%s\002:"), na->nick.c_str());
|
||||
|
||||
if (na->nc->HasExt("UNCONFIRMED"))
|
||||
source.Reply(_("%s is an unconfirmed nickname."), na->nick.c_str());
|
||||
@@ -88,18 +88,17 @@ public:
|
||||
|
||||
InfoFormatter info(source.nc);
|
||||
|
||||
info[_("Account")] = na->nc->display;
|
||||
info[_("Account id")] = Anope::ToString(na->nc->GetId());
|
||||
info[_("Account")] = Anope::Format("%s (%zu)", na->nc->display.c_str(), na->nc->GetId());
|
||||
if (nick_online)
|
||||
{
|
||||
bool shown = false;
|
||||
if (show_hidden && !na->last_realhost.empty())
|
||||
if (show_hidden && !na->last_userhost_real.empty())
|
||||
{
|
||||
info[_("Online from")] = na->last_realhost;
|
||||
info[_("Online from")] = na->last_userhost_real;
|
||||
shown = true;
|
||||
}
|
||||
if ((show_hidden || !na->nc->HasExt("HIDE_MASK")) && (!shown || na->last_usermask != na->last_realhost))
|
||||
info[_("Online from")] = na->last_usermask;
|
||||
if ((show_hidden || !na->nc->HasExt("HIDE_MASK")) && (!shown || na->last_userhost != na->last_userhost_real))
|
||||
info[_("Online from")] = na->last_userhost;
|
||||
else
|
||||
source.Reply(_("%s is currently online."), na->nick.c_str());
|
||||
}
|
||||
@@ -108,22 +107,24 @@ public:
|
||||
Anope::string shown;
|
||||
if (show_hidden || !na->nc->HasExt("HIDE_MASK"))
|
||||
{
|
||||
info[_("Last seen address")] = na->last_usermask;
|
||||
shown = na->last_usermask;
|
||||
info[_("Last seen mask")] = na->last_userhost;
|
||||
shown = na->last_userhost;
|
||||
}
|
||||
|
||||
if (show_hidden && !na->last_realhost.empty() && na->last_realhost != shown)
|
||||
info[_("Last seen address")] = na->last_realhost;
|
||||
if (show_hidden && !na->last_userhost_real.empty() && na->last_userhost_real != shown)
|
||||
info[_("Last seen mask")] = na->last_userhost_real;
|
||||
}
|
||||
|
||||
info[_("Account registered")] = Anope::strftime(na->nc->registered, source.GetAccount());
|
||||
info[_("Nick registered")] = Anope::strftime(na->registered, source.GetAccount());
|
||||
|
||||
if (!nick_online)
|
||||
{
|
||||
info[_("Last seen")] = Anope::strftime(na->last_seen, source.GetAccount());
|
||||
|
||||
if (!na->last_quit.empty() && (show_hidden || !na->nc->HasExt("HIDE_QUIT")))
|
||||
info[_("Last quit message")] = na->last_quit;
|
||||
if (!na->last_quit.empty() && (show_hidden || !na->nc->HasExt("HIDE_QUIT")))
|
||||
info[_("Last quit message")] = na->last_quit;
|
||||
}
|
||||
|
||||
if (!na->nc->email.empty() && (show_hidden || !na->nc->HasExt("HIDE_EMAIL")))
|
||||
info[_("Email address")] = na->nc->email;
|
||||
@@ -135,12 +136,7 @@ public:
|
||||
}
|
||||
|
||||
FOREACH_MOD(OnNickInfo, (source, na, info, show_hidden));
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
info.Process(replies);
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
info.SendTo(source);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +163,7 @@ public:
|
||||
CommandNSSetHide(Module *creator, const Anope::string &sname = "nickserv/set/hide", size_t min = 2) : Command(creator, sname, min, min + 1)
|
||||
{
|
||||
this->SetDesc(_("Hide certain pieces of nickname information"));
|
||||
this->SetSyntax("{EMAIL | STATUS | USERMASK | QUIT} {ON | OFF}");
|
||||
this->SetSyntax("{EMAIL | STATUS | MASK | QUIT} {ON | OFF}");
|
||||
}
|
||||
|
||||
void Run(CommandSource &source, const Anope::string &user, const Anope::string ¶m, const Anope::string &arg)
|
||||
@@ -199,7 +195,7 @@ public:
|
||||
onmsg = _("The email address of \002%s\002 will now be hidden from %s INFO displays.");
|
||||
offmsg = _("The email address of \002%s\002 will now be shown in %s INFO displays.");
|
||||
}
|
||||
else if (param.equals_ci("USERMASK"))
|
||||
else if (param.equals_ci("MASK"))
|
||||
{
|
||||
flag = "HIDE_MASK";
|
||||
onmsg = _("The last seen user@host mask of \002%s\002 will now be hidden from %s INFO displays.");
|
||||
@@ -252,7 +248,7 @@ public:
|
||||
"Allows you to prevent certain pieces of information from "
|
||||
"being displayed when someone does a %s\032\002INFO\002 on your "
|
||||
"nick. You can hide your email address\032(\002EMAIL\002), last seen "
|
||||
"user@host mask (\002USERMASK\002), your services access status "
|
||||
"user@host mask (\002MASK\002), your services access status "
|
||||
"(\002STATUS\002) and last quit message (\002QUIT\002). "
|
||||
"The second parameter specifies whether the information should "
|
||||
"be displayed (\002OFF\002) or hidden (\002ON\002)."
|
||||
@@ -268,7 +264,7 @@ class CommandNSSASetHide final
|
||||
public:
|
||||
CommandNSSASetHide(Module *creator) : CommandNSSetHide(creator, "nickserv/saset/hide", 3)
|
||||
{
|
||||
this->SetSyntax(_("\037nickname\037 {EMAIL | STATUS | USERMASK | QUIT} {ON | OFF}"));
|
||||
this->SetSyntax(_("\037nickname\037 {EMAIL | STATUS | MASK | QUIT} {ON | OFF}"));
|
||||
}
|
||||
|
||||
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override
|
||||
@@ -285,7 +281,7 @@ public:
|
||||
"Allows you to prevent certain pieces of information from "
|
||||
"being displayed when someone does a %s\032\002INFO\002 on the "
|
||||
"nick. You can hide the email address (\002EMAIL\002), last seen "
|
||||
"user@host mask (\002USERMASK\002), the services access status "
|
||||
"user@host mask (\002MASK\002), the services access status "
|
||||
"(\002STATUS\002) and last quit message (\002QUIT\002). "
|
||||
"The second parameter specifies whether the information should "
|
||||
"be displayed (\002OFF\002) or hidden (\002ON\002)."
|
||||
@@ -303,13 +299,18 @@ class NSInfo final
|
||||
CommandNSSetHide commandnssethide;
|
||||
CommandNSSASetHide commandnssasethide;
|
||||
|
||||
SerializableExtensibleItem<bool> hide_email, hide_usermask, hide_status, hide_quit;
|
||||
SerializableExtensibleItem<bool> hide_email, hide_mask, hide_status, hide_quit;
|
||||
|
||||
public:
|
||||
NSInfo(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
|
||||
commandnsinfo(this), commandnssethide(this), commandnssasethide(this),
|
||||
hide_email(this, "HIDE_EMAIL"), hide_usermask(this, "HIDE_MASK"), hide_status(this, "HIDE_STATUS"),
|
||||
hide_quit(this, "HIDE_QUIT")
|
||||
NSInfo(const Anope::string &modname, const Anope::string &creator)
|
||||
: Module(modname, creator, VENDOR)
|
||||
, commandnsinfo(this)
|
||||
, commandnssethide(this)
|
||||
, commandnssasethide(this)
|
||||
, hide_email(this, "HIDE_EMAIL")
|
||||
, hide_mask(this, "HIDE_MASK")
|
||||
, hide_status(this, "HIDE_STATUS")
|
||||
, hide_quit(this, "HIDE_QUIT")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -71,9 +71,15 @@ public:
|
||||
}
|
||||
|
||||
mync = source.nc;
|
||||
ListFormatter list(source.GetAccount());
|
||||
|
||||
list.AddColumn(_("Nick")).AddColumn(_("Last usermask"));
|
||||
ListFormatter list(source.GetAccount());
|
||||
list.AddColumn(_("Nick")).AddColumn(_("Last mask"));
|
||||
list.SetFlexible([](ListFormatter::ListEntry &row)
|
||||
{
|
||||
return row["Last mask"].empty()
|
||||
? _("\002{nick}\002")
|
||||
: _("\002{nick}\002 (last mask: {last_mask})");
|
||||
});
|
||||
|
||||
Anope::map<NickAlias *> ordered_map;
|
||||
for (const auto &[nick, na] : *NickAliasList)
|
||||
@@ -94,7 +100,7 @@ public:
|
||||
/* We no longer compare the pattern against the output buffer.
|
||||
* Instead we build a nice nick!user@host buffer to compare.
|
||||
* The output is then generated separately. -TheShadow */
|
||||
Anope::string buf = Anope::printf("%s!%s", na->nick.c_str(), !na->last_usermask.empty() ? na->last_usermask.c_str() : "*@*");
|
||||
Anope::string buf = Anope::Format("%s!%s", na->nick.c_str(), !na->last_userhost.empty() ? na->last_userhost.c_str() : "*@*");
|
||||
if (na->nick.equals_ci(pattern) || Anope::Match(buf, pattern, false, true))
|
||||
{
|
||||
if (((count + 1 >= from && count + 1 <= to) || (!from && !to)) && ++nnicks <= listmax)
|
||||
@@ -106,13 +112,13 @@ public:
|
||||
ListFormatter::ListEntry entry;
|
||||
entry["Nick"] = (isnoexpire ? "!" : "") + na->nick;
|
||||
if (na->nc->HasExt("HIDE_MASK") && !is_servadmin && na->nc != mync)
|
||||
entry["Last usermask"] = Language::Translate(source.GetAccount(), _("[Hostname hidden]"));
|
||||
entry["Last mask"] = Language::Translate(source.GetAccount(), _("[Hostname hidden]"));
|
||||
else if (na->nc->HasExt("NS_SUSPENDED"))
|
||||
entry["Last usermask"] = Language::Translate(source.GetAccount(), _("[Suspended]"));
|
||||
entry["Last mask"] = Language::Translate(source.GetAccount(), _("[Suspended]"));
|
||||
else if (na->nc->HasExt("UNCONFIRMED"))
|
||||
entry["Last usermask"] = Language::Translate(source.GetAccount(), _("[Unconfirmed]"));
|
||||
entry["Last mask"] = Language::Translate(source.GetAccount(), _("[Unconfirmed]"));
|
||||
else
|
||||
entry["Last usermask"] = na->last_usermask;
|
||||
entry["Last mask"] = is_servadmin ? na->last_userhost_real : na->last_userhost;
|
||||
list.AddEntry(entry);
|
||||
}
|
||||
++count;
|
||||
@@ -120,13 +126,7 @@ public:
|
||||
}
|
||||
|
||||
source.Reply(_("List of entries matching \002%s\002:"), pattern.c_str());
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
list.Process(replies);
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
|
||||
list.SendTo(source);
|
||||
source.Reply(_("End of list - %d/%d matches shown."), nnicks > listmax ? listmax : nnicks, nnicks);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -140,12 +140,7 @@ public:
|
||||
nc->pass = encpass;
|
||||
|
||||
if (u)
|
||||
{
|
||||
na->last_usermask = u->GetIdent() + "@" + u->GetDisplayedHost();
|
||||
na->last_realname = u->realname;
|
||||
}
|
||||
else
|
||||
na->last_realname = source.GetNick();
|
||||
na->last_userhost = u->GetIdent() + "@" + u->GetDisplayedHost();
|
||||
|
||||
Log(LOG_COMMAND, source, this) << "to register " << na->nick << " (email: " << (!na->nc->email.empty() ? na->nc->email : "none") << ")";
|
||||
|
||||
@@ -243,7 +238,7 @@ class CommandNSConfirmRegister final
|
||||
{
|
||||
public:
|
||||
CommandNSConfirmRegister(Module *creator)
|
||||
: Command(creator, "nickserv/confirm/register", 1, 2)
|
||||
: Command(creator, "nickserv/confirm/register", 1, 1)
|
||||
{
|
||||
this->SetDesc(_("Confirm a previous account registration"));
|
||||
this->SetSyntax(_("\037code\037"));
|
||||
@@ -252,19 +247,17 @@ public:
|
||||
|
||||
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override
|
||||
{
|
||||
auto has_priv = source.HasPriv("nickserv/confirm/register");
|
||||
|
||||
Anope::string code;
|
||||
NickAlias *na;
|
||||
if (params[0] == '@')
|
||||
if (params[0][0] == '@')
|
||||
{
|
||||
if (!has_priv)
|
||||
if (!source.HasPriv("nickserv/confirm/register"))
|
||||
{
|
||||
source.Reply(ACCESS_DENIED);
|
||||
return;
|
||||
}
|
||||
|
||||
auto nick = params[0].substr(0);
|
||||
auto nick = params[0].substr(1);
|
||||
na = NickAlias::Find(nick);
|
||||
if (!na)
|
||||
{
|
||||
@@ -292,14 +285,16 @@ public:
|
||||
na->nick.c_str());
|
||||
return;
|
||||
}
|
||||
if (has_priv || !code.equals_cs(*passcode))
|
||||
if (!code.empty() && !code.equals_cs(*passcode))
|
||||
{
|
||||
source.Reply(_("The registration confirmation code you specified for %s is incorrect."),
|
||||
na->nick.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
na->nc->Shrink<bool>("UNCONFIRMED");
|
||||
nc->Shrink<Anope::string>("passcode");
|
||||
nc->Shrink<bool>("UNCONFIRMED");
|
||||
|
||||
FOREACH_MOD(OnNickConfirm, (source.GetUser(), nc));
|
||||
|
||||
auto nonicknameownership = Config->GetModule("nickserv").Get<bool>("nonicknameownership");
|
||||
@@ -351,9 +346,12 @@ class CommandNSResend final
|
||||
: public Command
|
||||
{
|
||||
public:
|
||||
CommandNSResend(Module *creator) : Command(creator, "nickserv/resend", 0, 0)
|
||||
CommandNSResend(Module *creator)
|
||||
: Command(creator, "nickserv/resend", 0, 1)
|
||||
{
|
||||
this->SetDesc(_("Resend registration confirmation email"));
|
||||
this->SetSyntax(_("[\037nickname\037]"), [](auto &source) { return source.HasCommand("nickserv/resend"); });
|
||||
this->AllowUnregistered(true);
|
||||
}
|
||||
|
||||
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override
|
||||
@@ -364,27 +362,49 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
const NickAlias *na = NickAlias::Find(source.GetNick());
|
||||
|
||||
if (na == NULL)
|
||||
source.Reply(NICK_NOT_REGISTERED);
|
||||
else if (na->nc != source.GetAccount() || !source.nc->HasExt("UNCONFIRMED"))
|
||||
source.Reply(_("Your account is already confirmed."));
|
||||
else
|
||||
auto is_oper = false;
|
||||
Anope::string nick;
|
||||
if (!params.empty() && source.HasCommand("nickserv/resend"))
|
||||
{
|
||||
if (Anope::CurTime < source.nc->lastmail + Config->GetModule(this->owner).Get<time_t>("resenddelay"))
|
||||
source.Reply(_("Cannot send mail now; please retry a little later."));
|
||||
else if (SendRegmail(source.GetUser(), na, source.service))
|
||||
{
|
||||
na->nc->lastmail = Anope::CurTime;
|
||||
source.Reply(_("Your confirmation code has been re-sent to %s."), na->nc->email.c_str());
|
||||
Log(LOG_COMMAND, source, this) << "to resend registration confirmation code";
|
||||
}
|
||||
else
|
||||
Log(this->owner) << "Unable to resend registration confirmation code for " << source.GetNick();
|
||||
nick = params[0];
|
||||
is_oper = true;
|
||||
}
|
||||
else if (source.nc)
|
||||
nick = source.GetAccount()->display;
|
||||
else
|
||||
nick = source.GetNick();
|
||||
|
||||
const auto *na = NickAlias::Find(nick);
|
||||
if (!na)
|
||||
{
|
||||
source.Reply(NICK_X_NOT_REGISTERED, nick.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
NickCore *nc = na->nc;
|
||||
if (!nc->HasExt("UNCONFIRMED"))
|
||||
{
|
||||
source.Reply(_("Nick \002%s\002 is already confirmed."), na->nick.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_oper && Anope::CurTime < nc->lastmail + Config->GetModule(this->owner).Get<time_t>("resenddelay"))
|
||||
{
|
||||
source.Reply(_("Cannot send mail now; please retry a little later."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SendRegmail(source.GetUser(), na, source.service))
|
||||
{
|
||||
Log(this->owner) << "Unable to resend registration confirmation code for " << na->nick;
|
||||
return;
|
||||
}
|
||||
|
||||
nc->lastmail = Anope::CurTime;
|
||||
source.Reply(_("The confirmation code for \002%s\002 has been re-sent to %s."),
|
||||
na->nick.c_str(), nc->email.c_str());
|
||||
|
||||
Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to resend the registration confirmation code for " << na->nick;
|
||||
}
|
||||
|
||||
bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
|
||||
@@ -394,7 +414,16 @@ public:
|
||||
|
||||
this->SendSyntax(source);
|
||||
source.Reply(" ");
|
||||
source.Reply(_("This command will resend you the registration confirmation email."));
|
||||
source.Reply(_("This command will resend a registration confirmation email."));
|
||||
|
||||
if (source.HasCommand("nickserv/resend"))
|
||||
{
|
||||
source.Reply(" ");
|
||||
source.Reply(_(
|
||||
"Additionally, Services Operators with the \037nickserv/resend\037 permission "
|
||||
"can specify a nickname to resend a confirmation email for another account."
|
||||
));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+15
-242
@@ -7,231 +7,8 @@
|
||||
*/
|
||||
|
||||
#include "module.h"
|
||||
#include "modules/nickserv/cert.h"
|
||||
#include "modules/nickserv/sasl.h"
|
||||
|
||||
class SASLIdentifyRequest final
|
||||
: public IdentifyRequest
|
||||
{
|
||||
private:
|
||||
Anope::string uid;
|
||||
Anope::string hostname;
|
||||
|
||||
inline Anope::string GetUserInfo()
|
||||
{
|
||||
auto *u = User::Find(uid);
|
||||
if (u)
|
||||
return u->GetMask();
|
||||
if (!hostname.empty() && !GetAddress().empty())
|
||||
return Anope::printf("%s (%s)", hostname.c_str(), GetAddress().c_str());
|
||||
return "A user";
|
||||
};
|
||||
|
||||
public:
|
||||
SASLIdentifyRequest(Module *m, const Anope::string &id, const Anope::string &acc, const Anope::string &pass, const Anope::string &h, const Anope::string &i)
|
||||
: IdentifyRequest(m, acc, pass, i)
|
||||
, uid(id)
|
||||
, hostname(h)
|
||||
{
|
||||
}
|
||||
|
||||
void OnSuccess() override
|
||||
{
|
||||
if (!SASL::service)
|
||||
return;
|
||||
|
||||
auto *na = NickAlias::Find(GetAccount());
|
||||
if (!na || na->nc->HasExt("NS_SUSPENDED") || na->nc->HasExt("UNCONFIRMED"))
|
||||
return OnFail();
|
||||
|
||||
auto maxlogins = Config->GetModule("ns_identify").Get<unsigned int>("maxlogins");
|
||||
if (maxlogins && na->nc->users.size() >= maxlogins)
|
||||
return OnFail();
|
||||
|
||||
auto *s = SASL::service->GetSession(uid);
|
||||
if (s)
|
||||
{
|
||||
Log(this->GetOwner(), "sasl", Config->GetClient("NickServ")) << GetUserInfo() << " identified to account " << this->GetAccount() << " using SASL";
|
||||
SASL::service->Succeed(s, na->nc);
|
||||
delete s;
|
||||
}
|
||||
}
|
||||
|
||||
void OnFail() override
|
||||
{
|
||||
if (!SASL::service)
|
||||
return;
|
||||
|
||||
auto *s = SASL::service->GetSession(uid);
|
||||
if (s)
|
||||
{
|
||||
SASL::service->Fail(s);
|
||||
delete s;
|
||||
}
|
||||
|
||||
Anope::string accountstatus;
|
||||
auto *na = NickAlias::Find(GetAccount());
|
||||
if (!na)
|
||||
accountstatus = "nonexistent ";
|
||||
else if (na->nc->HasExt("NS_SUSPENDED"))
|
||||
accountstatus = "suspended ";
|
||||
else if (na->nc->HasExt("UNCONFIRMED"))
|
||||
accountstatus = "unconfirmed ";
|
||||
|
||||
Log(this->GetOwner(), "sasl", Config->GetClient("NickServ")) << GetUserInfo() << " failed to identify for " << accountstatus << "account " << this->GetAccount() << " using SASL";
|
||||
}
|
||||
};
|
||||
|
||||
class Plain final
|
||||
: public SASL::Mechanism
|
||||
{
|
||||
public:
|
||||
Plain(Module *o)
|
||||
: SASL::Mechanism(o, "PLAIN")
|
||||
{
|
||||
}
|
||||
|
||||
bool ProcessMessage(SASL::Session *sess, const SASL::Message &m) override
|
||||
{
|
||||
if (m.type == "S")
|
||||
{
|
||||
SASL::service->SendMessage(sess, "C", "+");
|
||||
}
|
||||
else if (m.type == "C")
|
||||
{
|
||||
// message = [authzid] UTF8NUL authcid UTF8NUL passwd
|
||||
Anope::string message;
|
||||
Anope::B64Decode(m.data[0], message);
|
||||
|
||||
size_t zcsep = message.find('\0');
|
||||
if (zcsep == Anope::string::npos)
|
||||
return false;
|
||||
|
||||
size_t cpsep = message.find('\0', zcsep + 1);
|
||||
if (cpsep == Anope::string::npos)
|
||||
return false;
|
||||
|
||||
Anope::string authzid = message.substr(0, zcsep);
|
||||
Anope::string authcid = message.substr(zcsep + 1, cpsep - zcsep - 1);
|
||||
|
||||
// We don't support having an authcid that is different to the authzid.
|
||||
if (!authzid.empty() && authzid != authcid)
|
||||
return false;
|
||||
|
||||
Anope::string passwd = message.substr(cpsep + 1);
|
||||
|
||||
if (authcid.empty() || passwd.empty() || !IRCD->IsNickValid(authcid) || passwd.find_first_of("\r\n\0") != Anope::string::npos)
|
||||
return false;
|
||||
|
||||
auto *req = new SASLIdentifyRequest(this->owner, m.source, authcid, passwd, sess->hostname, sess->ip);
|
||||
FOREACH_MOD(OnCheckAuthentication, (NULL, req));
|
||||
req->Dispatch();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class External final
|
||||
: public SASL::Mechanism
|
||||
{
|
||||
private:
|
||||
ServiceReference<CertService> certs;
|
||||
|
||||
struct Session final
|
||||
: SASL::Session
|
||||
{
|
||||
std::vector<Anope::string> certs;
|
||||
|
||||
Session(SASL::Mechanism *m, const Anope::string &u) : SASL::Session(m, u) { }
|
||||
};
|
||||
|
||||
public:
|
||||
External(Module *o)
|
||||
: SASL::Mechanism(o, "EXTERNAL")
|
||||
, certs("CertService", "certs")
|
||||
{
|
||||
if (!IRCD || !IRCD->CanCertFP)
|
||||
throw ModuleException("No CertFP");
|
||||
}
|
||||
|
||||
Session *CreateSession(const Anope::string &uid) override
|
||||
{
|
||||
return new Session(this, uid);
|
||||
}
|
||||
|
||||
bool ProcessMessage(SASL::Session *sess, const SASL::Message &m) override
|
||||
{
|
||||
Session *mysess = anope_dynamic_static_cast<Session *>(sess);
|
||||
|
||||
if (m.type == "S")
|
||||
{
|
||||
mysess->certs.assign(m.data.begin() + 1, m.data.end());
|
||||
|
||||
SASL::service->SendMessage(sess, "C", "+");
|
||||
}
|
||||
else if (m.type == "C")
|
||||
{
|
||||
if (!certs || mysess->certs.empty())
|
||||
return false;
|
||||
|
||||
for (auto it = mysess->certs.begin(); it != mysess->certs.end(); ++it)
|
||||
{
|
||||
auto *nc = certs->FindAccountFromCert(*it);
|
||||
if (nc && !nc->HasExt("NS_SUSPENDED") && !nc->HasExt("UNCONFIRMED"))
|
||||
{
|
||||
// If we are using a fallback cert then upgrade it.
|
||||
if (it != mysess->certs.begin())
|
||||
{
|
||||
auto *cl = nc->GetExt<NSCertList>("certificates");
|
||||
if (cl)
|
||||
cl->ReplaceCert(*it, mysess->certs[0]);
|
||||
}
|
||||
|
||||
Log(this->owner, "sasl", Config->GetClient("NickServ")) << sess->GetUserInfo() << " identified to account " << nc->display << " using SASL EXTERNAL";
|
||||
SASL::service->Succeed(sess, nc);
|
||||
delete sess;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Log(this->owner, "sasl", Config->GetClient("NickServ")) << sess->GetUserInfo() << " failed to identify using certificate " << mysess->certs.front() << " using SASL EXTERNAL";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class Anonymous final
|
||||
: public SASL::Mechanism
|
||||
{
|
||||
public:
|
||||
Anonymous(Module *o)
|
||||
: SASL::Mechanism(o, "ANONYMOUS")
|
||||
{
|
||||
}
|
||||
|
||||
bool ProcessMessage(SASL::Session *sess, const SASL::Message &m) override
|
||||
{
|
||||
if (m.type == "S")
|
||||
{
|
||||
SASL::service->SendMessage(sess, "C", "+");
|
||||
}
|
||||
else if (m.type == "C")
|
||||
{
|
||||
Anope::string decoded;
|
||||
Anope::B64Decode(m.data[0], decoded);
|
||||
|
||||
auto user = sess->GetUserInfo();
|
||||
if (!decoded.empty())
|
||||
user += " [" + decoded + "]";
|
||||
|
||||
Log(this->owner, "sasl", Config->GetClient("NickServ")) << user << " unidentified using SASL ANONYMOUS";
|
||||
SASL::service->Succeed(sess, nullptr);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class SASLService final
|
||||
: public SASL::Service
|
||||
, public Timer
|
||||
@@ -241,6 +18,9 @@ private:
|
||||
Anope::map<SASL::Session *> sessions;
|
||||
|
||||
public:
|
||||
unsigned badpasslimit;
|
||||
unsigned badpasstimeout;
|
||||
|
||||
SASLService(Module *o)
|
||||
: SASL::Service(o)
|
||||
, Timer(o, 60, true)
|
||||
@@ -405,7 +185,6 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
const auto badpasslimit = Config->GetBlock("options").Get<unsigned>("badpasslimit");
|
||||
if (!badpasslimit)
|
||||
return;
|
||||
|
||||
@@ -414,7 +193,6 @@ public:
|
||||
it = badpasswords.emplace(session->uid, std::make_pair(0, 0)).first;
|
||||
auto &[invalid_pw_time, invalid_pw_count] = it->second;
|
||||
|
||||
const auto badpasstimeout = Config->GetBlock("options").Get<time_t>("badpasstimeout");
|
||||
if (badpasstimeout > 0 && invalid_pw_time > 0 && invalid_pw_time < Anope::CurTime - badpasstimeout)
|
||||
invalid_pw_count = 0;
|
||||
|
||||
@@ -439,7 +217,6 @@ public:
|
||||
|
||||
void Tick() override
|
||||
{
|
||||
const auto badpasstimeout = Config->GetBlock("options").Get<time_t>("badpasstimeout");
|
||||
for (auto it = badpasswords.begin(); it != badpasswords.end(); )
|
||||
{
|
||||
if (it->second.first + badpasstimeout < Anope::CurTime)
|
||||
@@ -463,12 +240,8 @@ public:
|
||||
class ModuleSASL final
|
||||
: public Module
|
||||
{
|
||||
private:
|
||||
SASLService sasl;
|
||||
|
||||
Anonymous anonymous;
|
||||
Plain plain;
|
||||
External *external = nullptr;
|
||||
|
||||
std::vector<Anope::string> mechs;
|
||||
|
||||
void CheckMechs()
|
||||
@@ -488,23 +261,23 @@ public:
|
||||
ModuleSASL(const Anope::string &modname, const Anope::string &creator)
|
||||
: Module(modname, creator, VENDOR)
|
||||
, sasl(this)
|
||||
, anonymous(this)
|
||||
, plain(this)
|
||||
{
|
||||
if (!SASL::protocol_interface)
|
||||
throw ModuleException("Your IRCd does not support SASL");
|
||||
|
||||
try
|
||||
{
|
||||
external = new External(this);
|
||||
CheckMechs();
|
||||
}
|
||||
catch (ModuleException &) { }
|
||||
}
|
||||
|
||||
~ModuleSASL() override
|
||||
void OnReload(Configuration::Conf &conf) override
|
||||
{
|
||||
delete external;
|
||||
const auto &modconf = conf.GetModule(this);
|
||||
const auto &options = conf.GetBlock("options");
|
||||
|
||||
sasl.badpasslimit = modconf.Get<unsigned>("badpasslimit");
|
||||
if(!sasl.badpasslimit)
|
||||
sasl.badpasslimit = options.Get<unsigned>("badpasslimit");
|
||||
|
||||
sasl.badpasstimeout = modconf.Get<time_t>("badpasstimeout");
|
||||
if (!sasl.badpasstimeout)
|
||||
sasl.badpasstimeout = options.Get<unsigned>("badpasstimeout");
|
||||
}
|
||||
|
||||
void OnModuleLoad(User *, Module *) override
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
*
|
||||
* (C) 2014-2025 Anope Team
|
||||
* Contact us at team@anope.org
|
||||
*
|
||||
* Please read COPYING and README for further details.
|
||||
*/
|
||||
|
||||
#include "module.h"
|
||||
#include "modules/nickserv/sasl.h"
|
||||
|
||||
class Anonymous final
|
||||
: public SASL::Mechanism
|
||||
{
|
||||
public:
|
||||
Anonymous(Module *o)
|
||||
: SASL::Mechanism(o, "ANONYMOUS")
|
||||
{
|
||||
}
|
||||
|
||||
bool ProcessMessage(SASL::Session *sess, const SASL::Message &m) override
|
||||
{
|
||||
if (m.type == "S")
|
||||
{
|
||||
SASL::service->SendMessage(sess, "C", "+");
|
||||
}
|
||||
else if (m.type == "C")
|
||||
{
|
||||
auto decoded = Anope::B64Decode(m.data[0]);
|
||||
|
||||
auto user = sess->GetUserInfo();
|
||||
if (!decoded.empty())
|
||||
user += " [" + decoded + "]";
|
||||
|
||||
Log(this->owner, "sasl", Config->GetClient("NickServ")) << user << " unidentified using SASL ANONYMOUS";
|
||||
SASL::service->Succeed(sess, nullptr);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class ModuleSASLAnonymous final
|
||||
: public Module
|
||||
{
|
||||
private:
|
||||
Anonymous anonymous;
|
||||
|
||||
public:
|
||||
ModuleSASLAnonymous(const Anope::string &modname, const Anope::string &creator)
|
||||
: Module(modname, creator, VENDOR)
|
||||
, anonymous(this)
|
||||
{
|
||||
if (!SASL::protocol_interface)
|
||||
throw ModuleException("Your IRCd does not support SASL");
|
||||
}
|
||||
};
|
||||
|
||||
MODULE_INIT(ModuleSASLAnonymous)
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
*
|
||||
* (C) 2014-2025 Anope Team
|
||||
* Contact us at team@anope.org
|
||||
*
|
||||
* Please read COPYING and README for further details.
|
||||
*/
|
||||
|
||||
#include "module.h"
|
||||
#include "modules/nickserv/cert.h"
|
||||
#include "modules/nickserv/sasl.h"
|
||||
|
||||
class External final
|
||||
: public SASL::Mechanism
|
||||
{
|
||||
private:
|
||||
ServiceReference<CertService> certs;
|
||||
|
||||
struct Session final
|
||||
: SASL::Session
|
||||
{
|
||||
std::vector<Anope::string> certs;
|
||||
|
||||
Session(SASL::Mechanism *m, const Anope::string &u)
|
||||
: SASL::Session(m, u)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
External(Module *o)
|
||||
: SASL::Mechanism(o, "EXTERNAL")
|
||||
, certs("CertService", "certs")
|
||||
{
|
||||
}
|
||||
|
||||
Session *CreateSession(const Anope::string &uid) override
|
||||
{
|
||||
return new Session(this, uid);
|
||||
}
|
||||
|
||||
bool ProcessMessage(SASL::Session *sess, const SASL::Message &m) override
|
||||
{
|
||||
Session *mysess = anope_dynamic_static_cast<Session *>(sess);
|
||||
|
||||
if (m.type == "S")
|
||||
{
|
||||
if (m.data.size() < 2)
|
||||
return false; // No client certs.
|
||||
|
||||
mysess->certs.assign(m.data.begin() + 1, m.data.end());
|
||||
SASL::service->SendMessage(sess, "C", "+");
|
||||
}
|
||||
else if (m.type == "C")
|
||||
{
|
||||
if (!certs || mysess->certs.empty())
|
||||
return false;
|
||||
|
||||
for (auto it = mysess->certs.begin(); it != mysess->certs.end(); ++it)
|
||||
{
|
||||
auto *nc = certs->FindAccountFromCert(*it);
|
||||
if (nc && !nc->HasExt("NS_SUSPENDED") && !nc->HasExt("UNCONFIRMED"))
|
||||
{
|
||||
// If we are using a fallback cert then upgrade it.
|
||||
if (it != mysess->certs.begin())
|
||||
{
|
||||
auto *cl = nc->GetExt<NSCertList>("certificates");
|
||||
if (cl)
|
||||
cl->ReplaceCert(*it, mysess->certs[0]);
|
||||
}
|
||||
|
||||
Log(this->owner, "sasl", Config->GetClient("NickServ")) << sess->GetUserInfo() << " identified to account " << nc->display << " using SASL EXTERNAL";
|
||||
SASL::service->Succeed(sess, nc);
|
||||
delete sess;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Log(this->owner, "sasl", Config->GetClient("NickServ")) << sess->GetUserInfo() << " failed to identify using certificate " << mysess->certs.front() << " using SASL EXTERNAL";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class ModuleSASLExternal final
|
||||
: public Module
|
||||
{
|
||||
private:
|
||||
External external;
|
||||
|
||||
public:
|
||||
ModuleSASLExternal(const Anope::string &modname, const Anope::string &creator)
|
||||
: Module(modname, creator, VENDOR)
|
||||
, external(this)
|
||||
{
|
||||
if (!IRCD || !IRCD->CanCertFP)
|
||||
throw ModuleException("No CertFP");
|
||||
|
||||
if (!SASL::protocol_interface)
|
||||
throw ModuleException("Your IRCd does not support SASL");
|
||||
}
|
||||
};
|
||||
|
||||
MODULE_INIT(ModuleSASLExternal)
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
*
|
||||
* (C) 2014-2025 Anope Team
|
||||
* Contact us at team@anope.org
|
||||
*
|
||||
* Please read COPYING and README for further details.
|
||||
*/
|
||||
|
||||
#include "module.h"
|
||||
#include "modules/nickserv/sasl.h"
|
||||
|
||||
class SASLIdentifyRequest final
|
||||
: public IdentifyRequest
|
||||
{
|
||||
private:
|
||||
Anope::string uid;
|
||||
Anope::string hostname;
|
||||
|
||||
inline Anope::string GetUserInfo()
|
||||
{
|
||||
auto *u = User::Find(uid);
|
||||
if (u)
|
||||
return u->GetMask();
|
||||
if (!hostname.empty() && !GetAddress().empty())
|
||||
return Anope::Format("%s (%s)", hostname.c_str(), GetAddress().c_str());
|
||||
return "A user";
|
||||
};
|
||||
|
||||
public:
|
||||
SASLIdentifyRequest(Module *m, const Anope::string &id, const Anope::string &acc, const Anope::string &pass, const Anope::string &h, const Anope::string &i)
|
||||
: IdentifyRequest(m, acc, pass, i)
|
||||
, uid(id)
|
||||
, hostname(h)
|
||||
{
|
||||
}
|
||||
|
||||
void OnSuccess() override
|
||||
{
|
||||
if (!SASL::service)
|
||||
return;
|
||||
|
||||
auto *na = NickAlias::Find(GetAccount());
|
||||
if (!na || na->nc->HasExt("NS_SUSPENDED") || na->nc->HasExt("UNCONFIRMED"))
|
||||
return OnFail();
|
||||
|
||||
auto maxlogins = Config->GetModule("ns_identify").Get<unsigned int>("maxlogins");
|
||||
if (maxlogins && na->nc->users.size() >= maxlogins)
|
||||
return OnFail();
|
||||
|
||||
auto *s = SASL::service->GetSession(uid);
|
||||
if (s)
|
||||
{
|
||||
Log(this->GetOwner(), "sasl", Config->GetClient("NickServ")) << GetUserInfo() << " identified to account " << this->GetAccount() << " using SASL";
|
||||
SASL::service->Succeed(s, na->nc);
|
||||
delete s;
|
||||
}
|
||||
}
|
||||
|
||||
void OnFail() override
|
||||
{
|
||||
if (!SASL::service)
|
||||
return;
|
||||
|
||||
auto *s = SASL::service->GetSession(uid);
|
||||
if (s)
|
||||
{
|
||||
SASL::service->Fail(s);
|
||||
delete s;
|
||||
}
|
||||
|
||||
Anope::string accountstatus;
|
||||
auto *na = NickAlias::Find(GetAccount());
|
||||
if (!na)
|
||||
accountstatus = "nonexistent ";
|
||||
else if (na->nc->HasExt("NS_SUSPENDED"))
|
||||
accountstatus = "suspended ";
|
||||
else if (na->nc->HasExt("UNCONFIRMED"))
|
||||
accountstatus = "unconfirmed ";
|
||||
|
||||
Log(this->GetOwner(), "sasl", Config->GetClient("NickServ")) << GetUserInfo() << " failed to identify for " << accountstatus << "account " << this->GetAccount() << " using SASL";
|
||||
}
|
||||
};
|
||||
|
||||
class Plain final
|
||||
: public SASL::Mechanism
|
||||
{
|
||||
public:
|
||||
Plain(Module *o)
|
||||
: SASL::Mechanism(o, "PLAIN")
|
||||
{
|
||||
}
|
||||
|
||||
bool ProcessMessage(SASL::Session *sess, const SASL::Message &m) override
|
||||
{
|
||||
if (m.type == "S")
|
||||
{
|
||||
SASL::service->SendMessage(sess, "C", "+");
|
||||
}
|
||||
else if (m.type == "C")
|
||||
{
|
||||
// message = [authzid] UTF8NUL authcid UTF8NUL passwd
|
||||
const auto message = Anope::B64Decode(m.data[0]);
|
||||
|
||||
const auto zcsep = message.find('\0');
|
||||
if (zcsep == Anope::string::npos)
|
||||
return false;
|
||||
|
||||
const auto cpsep = message.find('\0', zcsep + 1);
|
||||
if (cpsep == Anope::string::npos)
|
||||
return false;
|
||||
|
||||
const auto authzid = message.substr(0, zcsep);
|
||||
const auto authcid = message.substr(zcsep + 1, cpsep - zcsep - 1);
|
||||
|
||||
// We don't support having an authcid that is different to the authzid.
|
||||
if (!authzid.empty() && authzid != authcid)
|
||||
return false;
|
||||
|
||||
const auto passwd = message.substr(cpsep + 1);
|
||||
if (authcid.empty() || passwd.empty() || !IRCD->IsNickValid(authcid) || passwd.find_first_of("\r\n\0") != Anope::string::npos)
|
||||
return false;
|
||||
|
||||
auto *req = new SASLIdentifyRequest(this->owner, m.source, authcid, passwd, sess->hostname, sess->ip);
|
||||
FOREACH_MOD(OnCheckAuthentication, (NULL, req));
|
||||
req->Dispatch();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class ModuleSASLPlain final
|
||||
: public Module
|
||||
{
|
||||
private:
|
||||
Plain plain;
|
||||
|
||||
public:
|
||||
ModuleSASLPlain(const Anope::string &modname, const Anope::string &creator)
|
||||
: Module(modname, creator, VENDOR)
|
||||
, plain(this)
|
||||
{
|
||||
if (!SASL::protocol_interface)
|
||||
throw ModuleException("Your IRCd does not support SASL");
|
||||
}
|
||||
};
|
||||
|
||||
MODULE_INIT(ModuleSASLPlain)
|
||||
+2
-325
@@ -248,290 +248,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class CommandNSSetAutoOp
|
||||
: public Command
|
||||
{
|
||||
public:
|
||||
CommandNSSetAutoOp(Module *creator, const Anope::string &sname = "nickserv/set/autoop", size_t min = 1) : Command(creator, sname, min, min + 1)
|
||||
{
|
||||
this->SetDesc(_("Sets whether services should set channel status modes on you automatically."));
|
||||
this->SetSyntax("{ON | OFF}");
|
||||
}
|
||||
|
||||
void Run(CommandSource &source, const Anope::string &user, const Anope::string ¶m)
|
||||
{
|
||||
if (Anope::ReadOnly)
|
||||
{
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
const NickAlias *na = NickAlias::Find(user);
|
||||
if (na == NULL)
|
||||
{
|
||||
source.Reply(NICK_X_NOT_REGISTERED, user.c_str());
|
||||
return;
|
||||
}
|
||||
NickCore *nc = na->nc;
|
||||
|
||||
EventReturn MOD_RESULT;
|
||||
FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, nc, param));
|
||||
if (MOD_RESULT == EVENT_STOP)
|
||||
return;
|
||||
|
||||
if (param.equals_ci("ON"))
|
||||
{
|
||||
Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to enable autoop for " << na->nc->display;
|
||||
nc->Extend<bool>("AUTOOP");
|
||||
source.Reply(_("Services will from now on set status modes on %s in channels."), nc->display.c_str());
|
||||
}
|
||||
else if (param.equals_ci("OFF"))
|
||||
{
|
||||
Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable autoop for " << na->nc->display;
|
||||
nc->Shrink<bool>("AUTOOP");
|
||||
source.Reply(_("Services will no longer set status modes on %s in channels."), nc->display.c_str());
|
||||
}
|
||||
else
|
||||
this->OnSyntaxError(source, "AUTOOP");
|
||||
}
|
||||
|
||||
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override
|
||||
{
|
||||
this->Run(source, source.nc->display, params[0]);
|
||||
}
|
||||
|
||||
bool OnHelp(CommandSource &source, const Anope::string &) override
|
||||
{
|
||||
BotInfo *bi = Config->GetClient("ChanServ");
|
||||
this->SendSyntax(source);
|
||||
source.Reply(" ");
|
||||
source.Reply(_(
|
||||
"Sets whether you will be given your channel status modes automatically. "
|
||||
"Set to \002ON\002 to allow %s to set status modes on you automatically "
|
||||
"when entering channels. Note that depending on channel settings some modes "
|
||||
"may not get set automatically."
|
||||
),
|
||||
bi ? bi->nick.c_str() : "ChanServ");
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class CommandNSSASetAutoOp final
|
||||
: public CommandNSSetAutoOp
|
||||
{
|
||||
public:
|
||||
CommandNSSASetAutoOp(Module *creator) : CommandNSSetAutoOp(creator, "nickserv/saset/autoop", 2)
|
||||
{
|
||||
this->ClearSyntax();
|
||||
this->SetSyntax(_("\037nickname\037 {ON | OFF}"));
|
||||
}
|
||||
|
||||
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override
|
||||
{
|
||||
this->Run(source, params[0], params[1]);
|
||||
}
|
||||
|
||||
bool OnHelp(CommandSource &source, const Anope::string &) override
|
||||
{
|
||||
BotInfo *bi = Config->GetClient("ChanServ");
|
||||
this->SendSyntax(source);
|
||||
source.Reply(" ");
|
||||
source.Reply(_(
|
||||
"Sets whether the given nickname will be given its status modes "
|
||||
"in channels automatically. Set to \002ON\002 to allow %s "
|
||||
"to set status modes on the given nickname automatically when it "
|
||||
"is entering channels. Note that depending on channel settings "
|
||||
"some modes may not get set automatically."
|
||||
),
|
||||
bi ? bi->nick.c_str() : "ChanServ");
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class CommandNSSetNeverOp
|
||||
: public Command
|
||||
{
|
||||
public:
|
||||
CommandNSSetNeverOp(Module *creator, const Anope::string &sname = "nickserv/set/neverop", size_t min = 1) : Command(creator, sname, min, min + 1)
|
||||
{
|
||||
this->SetDesc(_("Sets whether you can be added to a channel access list."));
|
||||
this->SetSyntax("{ON | OFF}");
|
||||
}
|
||||
|
||||
void Run(CommandSource &source, const Anope::string &user, const Anope::string ¶m)
|
||||
{
|
||||
if (Anope::ReadOnly)
|
||||
{
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
const NickAlias *na = NickAlias::Find(user);
|
||||
if (na == NULL)
|
||||
{
|
||||
source.Reply(NICK_X_NOT_REGISTERED, user.c_str());
|
||||
return;
|
||||
}
|
||||
NickCore *nc = na->nc;
|
||||
|
||||
EventReturn MOD_RESULT;
|
||||
FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, nc, param));
|
||||
if (MOD_RESULT == EVENT_STOP)
|
||||
return;
|
||||
|
||||
if (param.equals_ci("ON"))
|
||||
{
|
||||
Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to enable neverop for " << na->nc->display;
|
||||
nc->Extend<bool>("NEVEROP");
|
||||
source.Reply(_("%s can no longer be added to channel access lists."), nc->display.c_str());
|
||||
}
|
||||
else if (param.equals_ci("OFF"))
|
||||
{
|
||||
Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable neverop for " << na->nc->display;
|
||||
nc->Shrink<bool>("NEVEROP");
|
||||
source.Reply(_("%s can now be added to channel access lists."), nc->display.c_str());
|
||||
}
|
||||
else
|
||||
this->OnSyntaxError(source, "NEVEROP");
|
||||
}
|
||||
|
||||
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override
|
||||
{
|
||||
this->Run(source, source.nc->display, params[0]);
|
||||
}
|
||||
|
||||
bool OnHelp(CommandSource &source, const Anope::string &) override
|
||||
{
|
||||
this->SendSyntax(source);
|
||||
source.Reply(" ");
|
||||
source.Reply(_("Sets whether you can be added to a channel access list."));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class CommandNSSASetNeverOp final
|
||||
: public CommandNSSetNeverOp
|
||||
{
|
||||
public:
|
||||
CommandNSSASetNeverOp(Module *creator) : CommandNSSetNeverOp(creator, "nickserv/saset/neverop", 2)
|
||||
{
|
||||
this->ClearSyntax();
|
||||
this->SetSyntax(_("\037nickname\037 {ON | OFF}"));
|
||||
}
|
||||
|
||||
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override
|
||||
{
|
||||
this->Run(source, params[0], params[1]);
|
||||
}
|
||||
|
||||
bool OnHelp(CommandSource &source, const Anope::string &) override
|
||||
{
|
||||
this->SendSyntax(source);
|
||||
source.Reply(" ");
|
||||
source.Reply(_("Sets whether the given nickname can be added to a channel access list."));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class CommandNSSetDisplay
|
||||
: public Command
|
||||
{
|
||||
public:
|
||||
CommandNSSetDisplay(Module *creator, const Anope::string &sname = "nickserv/set/display", size_t min = 1) : Command(creator, sname, min, min + 1)
|
||||
{
|
||||
this->SetDesc(_("Set the display nickname for your account"));
|
||||
this->SetSyntax(_("\037new-display\037"));
|
||||
}
|
||||
|
||||
void Run(CommandSource &source, const Anope::string &user, const Anope::string ¶m)
|
||||
{
|
||||
if (Anope::ReadOnly)
|
||||
{
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
NickAlias *user_na = NickAlias::Find(user), *na = NickAlias::Find(param);
|
||||
|
||||
if (Config->GetModule("nickserv").Get<bool>("nonicknameownership"))
|
||||
{
|
||||
source.Reply(_("This command may not be used on this network because nickname ownership is disabled."));
|
||||
return;
|
||||
}
|
||||
if (user_na == NULL)
|
||||
{
|
||||
source.Reply(NICK_X_NOT_REGISTERED, user.c_str());
|
||||
return;
|
||||
}
|
||||
else if (!na || *na->nc != *user_na->nc)
|
||||
{
|
||||
source.Reply(_("The new display nickname must belong to the %s account."), user_na->nc->display.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
EventReturn MOD_RESULT;
|
||||
FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, user_na->nc, param));
|
||||
if (MOD_RESULT == EVENT_STOP)
|
||||
return;
|
||||
|
||||
Log(user_na->nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to change the display of " << user_na->nc->display << " to " << na->nick;
|
||||
|
||||
user_na->nc->SetDisplay(na);
|
||||
|
||||
/* Send updated account name */
|
||||
for (std::list<User *>::iterator it = user_na->nc->users.begin(); it != user_na->nc->users.end(); ++it)
|
||||
{
|
||||
User *u = *it;
|
||||
IRCD->SendLogin(u, user_na);
|
||||
}
|
||||
|
||||
source.Reply(NICK_SET_DISPLAY_CHANGED, user_na->nc->display.c_str());
|
||||
}
|
||||
|
||||
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override
|
||||
{
|
||||
this->Run(source, source.nc->display, params[0]);
|
||||
}
|
||||
|
||||
bool OnHelp(CommandSource &source, const Anope::string &) override
|
||||
{
|
||||
this->SendSyntax(source);
|
||||
source.Reply(" ");
|
||||
source.Reply(_(
|
||||
"Changes the display nickname used to refer to your account. The new display "
|
||||
"nickname must already be associated with your account."
|
||||
));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class CommandNSSASetDisplay final
|
||||
: public CommandNSSetDisplay
|
||||
{
|
||||
public:
|
||||
CommandNSSASetDisplay(Module *creator) : CommandNSSetDisplay(creator, "nickserv/saset/display", 2)
|
||||
{
|
||||
this->ClearSyntax();
|
||||
this->SetSyntax(_("\037nickname\037 \037new-display\037"));
|
||||
}
|
||||
|
||||
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override
|
||||
{
|
||||
this->Run(source, params[0], params[1]);
|
||||
}
|
||||
|
||||
bool OnHelp(CommandSource &source, const Anope::string &) override
|
||||
{
|
||||
this->SendSyntax(source);
|
||||
source.Reply(" ");
|
||||
source.Reply(_(
|
||||
"Changes the display nickname used to refer to the account. The new display"
|
||||
"nickname must already be associated with the account."
|
||||
));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class CommandNSSASetNoexpire final
|
||||
: public Command
|
||||
{
|
||||
@@ -593,45 +309,22 @@ class NSSet final
|
||||
CommandNSSet commandnsset;
|
||||
CommandNSSASet commandnssaset;
|
||||
|
||||
CommandNSSetAutoOp commandnssetautoop;
|
||||
CommandNSSASetAutoOp commandnssasetautoop;
|
||||
|
||||
CommandNSSetNeverOp commandnssetneverop;
|
||||
CommandNSSASetNeverOp commandnssasetneverop;
|
||||
|
||||
CommandNSSetDisplay commandnssetdisplay;
|
||||
CommandNSSASetDisplay commandnssasetdisplay;
|
||||
|
||||
CommandNSSetPassword commandnssetpassword;
|
||||
CommandNSSASetPassword commandnssasetpassword;
|
||||
|
||||
CommandNSSASetNoexpire commandnssasetnoexpire;
|
||||
|
||||
SerializableExtensibleItem<bool> autoop, neverop, noexpire;
|
||||
SerializableExtensibleItem<bool> noexpire;
|
||||
|
||||
public:
|
||||
NSSet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
|
||||
commandnsset(this), commandnssaset(this),
|
||||
commandnssetautoop(this), commandnssasetautoop(this),
|
||||
commandnssetneverop(this), commandnssasetneverop(this),
|
||||
commandnssetdisplay(this), commandnssasetdisplay(this),
|
||||
commandnssetpassword(this), commandnssasetpassword(this),
|
||||
commandnssasetnoexpire(this),
|
||||
|
||||
autoop(this, "AUTOOP"), neverop(this, "NEVEROP"),
|
||||
noexpire(this, "NS_NO_EXPIRE")
|
||||
{
|
||||
}
|
||||
|
||||
void OnSetCorrectModes(User *user, Channel *chan, AccessGroup &access, bool &give_modes, bool &take_modes) override
|
||||
{
|
||||
if (chan->ci)
|
||||
{
|
||||
/* Only give modes if autoop is set */
|
||||
give_modes &= !user->IsIdentified() || autoop.HasExt(user->Account());
|
||||
}
|
||||
}
|
||||
|
||||
void OnPreNickExpire(NickAlias *na, bool &expire) override
|
||||
{
|
||||
if (noexpire.HasExt(na))
|
||||
@@ -643,24 +336,8 @@ public:
|
||||
if (!show_hidden)
|
||||
return;
|
||||
|
||||
if (autoop.HasExt(na->nc))
|
||||
info.AddOption(_("Auto-op"));
|
||||
if (neverop.HasExt(na->nc))
|
||||
info.AddOption(_("Never-op"));
|
||||
if (noexpire.HasExt(na))
|
||||
info.AddOption(_("No expire"));
|
||||
}
|
||||
|
||||
void OnUserModeSet(const MessageSource &setter, User *u, const Anope::string &mname) override
|
||||
{
|
||||
if (u->IsIdentified() && setter.GetUser() == u)
|
||||
u->Account()->last_modes = u->GetModeList();
|
||||
}
|
||||
|
||||
void OnUserModeUnset(const MessageSource &setter, User *u, const Anope::string &mname) override
|
||||
{
|
||||
if (u->IsIdentified() && setter.GetUser() == u)
|
||||
u->Account()->last_modes = u->GetModeList();
|
||||
info.AddOption(_("No expiry"));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@ public:
|
||||
for (const auto &[last_mode, last_data] : modes)
|
||||
{
|
||||
auto *um = ModeManager::FindUserModeByName(last_mode);
|
||||
if (um && um->CanSet(nullptr) && norestore.find(um->mchar) == Anope::string::npos)
|
||||
if (um && um->CanSet(nullptr, u) && norestore.find(um->mchar) == Anope::string::npos)
|
||||
u->SetMode(nullptr, last_mode, last_data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
/* NickServ core functions
|
||||
*
|
||||
* (C) 2003-2025 Anope Team
|
||||
* Contact us at team@anope.org
|
||||
*
|
||||
* Please read COPYING and README for further details.
|
||||
*
|
||||
* Based on the original code of Epona by Lara.
|
||||
* Based on the original code of Services by Andy Church.
|
||||
*/
|
||||
|
||||
#include "module.h"
|
||||
|
||||
class CommandNSSetLayout
|
||||
: public Command
|
||||
{
|
||||
public:
|
||||
CommandNSSetLayout(Module *creator, const Anope::string &sname = "nickserv/set/layout", size_t min = 1)
|
||||
: Command(creator, sname, min, min + 1)
|
||||
{
|
||||
this->SetDesc(_("Configures the layout used for services messages"));
|
||||
this->SetSyntax("{FIXED | FLEXIBLE | MONOSPACE}");
|
||||
}
|
||||
|
||||
void Run(CommandSource &source, const Anope::string &user, const Anope::string ¶m)
|
||||
{
|
||||
if (Anope::ReadOnly)
|
||||
{
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto *na = NickAlias::Find(user);
|
||||
if (!na)
|
||||
{
|
||||
source.Reply(NICK_X_NOT_REGISTERED, user.c_str());
|
||||
return;
|
||||
}
|
||||
NickCore *nc = na->nc;
|
||||
|
||||
EventReturn MOD_RESULT;
|
||||
FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, nc, param));
|
||||
if (MOD_RESULT == EVENT_STOP)
|
||||
return;
|
||||
|
||||
if (param.equals_ci("FIXED"))
|
||||
{
|
||||
nc->Shrink<bool>("NS_FLEXIBLE");
|
||||
nc->Shrink<bool>("NS_MONOSPACE");
|
||||
Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to change the layout to fixed for " << nc->display;
|
||||
source.Reply(_("Layout is now \002fixed\002 for \002%s\002."), nc->display.c_str());
|
||||
}
|
||||
else if (param.equals_ci("FLEXIBLE"))
|
||||
{
|
||||
nc->Extend<bool>("NS_FLEXIBLE");
|
||||
nc->Shrink<bool>("NS_MONOSPACE");
|
||||
Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to change the layout to flexible for " << nc->display;
|
||||
source.Reply(_("Layout is now \002flexible\002 for \002%s\002."), nc->display.c_str());
|
||||
}
|
||||
else if (param.equals_ci("MONOSPACE"))
|
||||
{
|
||||
nc->Shrink<bool>("NS_FLEXIBLE");
|
||||
nc->Extend<bool>("NS_MONOSPACE");
|
||||
Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to change the layout to monospace for " << nc->display;
|
||||
source.Reply(_("Layout is now \002monospace\002 for \002%s\002."), nc->display.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
this->OnSyntaxError(source, "LAYOUT");
|
||||
}
|
||||
}
|
||||
|
||||
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override
|
||||
{
|
||||
this->Run(source, source.nc->display, params[0]);
|
||||
}
|
||||
|
||||
bool OnHelp(CommandSource &source, const Anope::string &) override
|
||||
{
|
||||
this->SendSyntax(source);
|
||||
source.Reply(" ");
|
||||
source.Reply(_(
|
||||
"Configures the layout used for services messages."
|
||||
"\n\n"
|
||||
"When the layout is set to \037FIXED\037 services will use tables and position "
|
||||
"text such that it looks good in a client that uses a fixed-width font."
|
||||
"\n\n"
|
||||
"When the layout is set to \037FLEXIBLE\037 services will use an alternate "
|
||||
"format for messages and avoid any positioning that might be broken by a "
|
||||
"variable-width font."
|
||||
"\n\n"
|
||||
"When the layout is set to \037MONOSPACE\037 services will format messages "
|
||||
"similar to \037FIXED\037 but will prefix all messages with a monospace "
|
||||
"formatting character to force it to display correctly. This requires client "
|
||||
"support for monospace text formatting."
|
||||
));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class CommandNSSASetLayout final
|
||||
: public CommandNSSetLayout
|
||||
{
|
||||
public:
|
||||
CommandNSSASetLayout(Module *creator)
|
||||
: CommandNSSetLayout(creator, "nickserv/saset/layout", 2)
|
||||
{
|
||||
this->ClearSyntax();
|
||||
this->SetSyntax(_("\037nickname\037 {FIXED | FLEXIBLE | MONOSPACE}"));
|
||||
}
|
||||
|
||||
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override
|
||||
{
|
||||
this->Run(source, params[0], params[1]);
|
||||
}
|
||||
|
||||
bool OnHelp(CommandSource &source, const Anope::string &) override
|
||||
{
|
||||
this->SendSyntax(source);
|
||||
source.Reply(" ");
|
||||
source.Reply(_(
|
||||
"Configures the layout used by the account for services messages."
|
||||
"\n\n"
|
||||
"When the layout is set to \037FIXED\037 services will use tables and position "
|
||||
"text such that it looks good in a client that uses a fixed-width font."
|
||||
"\n\n"
|
||||
"When the layout is set to \037FLEXIBLE\037 services will use an alternate "
|
||||
"format for messages and avoid any positioning that might be broken by a "
|
||||
"variable-width font."
|
||||
"\n\n"
|
||||
"When the layout is set to \037MONOSPACE\037 services will format messages "
|
||||
"similar to \037FIXED\037 but will prefix all messages with a monospace "
|
||||
"formatting character to force it to display correctly. This requires client "
|
||||
"support for monospace text formatting."
|
||||
));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class NSSetLayout final
|
||||
: public Module
|
||||
{
|
||||
private:
|
||||
CommandNSSetLayout commandnssetlayout;
|
||||
CommandNSSASetLayout commandnssasetlayout;
|
||||
|
||||
SerializableExtensibleItem<bool> nsflexible;
|
||||
SerializableExtensibleItem<bool> nsmonospace;
|
||||
|
||||
public:
|
||||
NSSetLayout(const Anope::string &modname, const Anope::string &creator)
|
||||
: Module(modname, creator, VENDOR)
|
||||
, commandnssetlayout(this)
|
||||
, commandnssasetlayout(this)
|
||||
, nsflexible(this, "NS_FLEXIBLE")
|
||||
, nsmonospace(this, "NS_MONOSPACE")
|
||||
{
|
||||
}
|
||||
|
||||
void OnNickInfo(CommandSource &source, NickAlias *na, InfoFormatter &info, bool show_hidden) override
|
||||
{
|
||||
if (!show_hidden)
|
||||
return;
|
||||
|
||||
if (nsflexible.HasExt(na->nc))
|
||||
info.AddOption(_("Flexible layout"));
|
||||
else if (nsmonospace.HasExt(na->nc))
|
||||
info.AddOption(_("Monospace layout"));
|
||||
else
|
||||
info.AddOption(_("Fixed layout"));
|
||||
}
|
||||
};
|
||||
|
||||
MODULE_INIT(NSSetLayout)
|
||||
@@ -0,0 +1,250 @@
|
||||
/* NickServ core functions
|
||||
*
|
||||
* (C) 2003-2025 Anope Team
|
||||
* Contact us at team@anope.org
|
||||
*
|
||||
* Please read COPYING and README for further details.
|
||||
*
|
||||
* Based on the original code of Epona by Lara.
|
||||
* Based on the original code of Services by Andy Church.
|
||||
*/
|
||||
|
||||
#include "module.h"
|
||||
|
||||
class CommandNSSetOpAutoOp
|
||||
: public Command
|
||||
{
|
||||
public:
|
||||
CommandNSSetOpAutoOp(Module *creator, const Anope::string &sname = "nickserv/set/autoop", size_t min = 1)
|
||||
: Command(creator, sname, min, min + 1)
|
||||
{
|
||||
this->SetDesc(_("Sets whether services should set channel status modes on you automatically."));
|
||||
this->SetSyntax("{ON | OFF}");
|
||||
}
|
||||
|
||||
void Run(CommandSource &source, const Anope::string &user, const Anope::string ¶m)
|
||||
{
|
||||
if (Anope::ReadOnly)
|
||||
{
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
const NickAlias *na = NickAlias::Find(user);
|
||||
if (na == NULL)
|
||||
{
|
||||
source.Reply(NICK_X_NOT_REGISTERED, user.c_str());
|
||||
return;
|
||||
}
|
||||
NickCore *nc = na->nc;
|
||||
|
||||
EventReturn MOD_RESULT;
|
||||
FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, nc, param));
|
||||
if (MOD_RESULT == EVENT_STOP)
|
||||
return;
|
||||
|
||||
if (param.equals_ci("ON"))
|
||||
{
|
||||
Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to enable autoop for " << na->nc->display;
|
||||
nc->Extend<bool>("AUTOOP");
|
||||
source.Reply(_("Services will from now on set status modes on %s in channels."), nc->display.c_str());
|
||||
}
|
||||
else if (param.equals_ci("OFF"))
|
||||
{
|
||||
Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable autoop for " << na->nc->display;
|
||||
nc->Shrink<bool>("AUTOOP");
|
||||
source.Reply(_("Services will no longer set status modes on %s in channels."), nc->display.c_str());
|
||||
}
|
||||
else
|
||||
this->OnSyntaxError(source, "AUTOOP");
|
||||
}
|
||||
|
||||
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override
|
||||
{
|
||||
this->Run(source, source.nc->display, params[0]);
|
||||
}
|
||||
|
||||
bool OnHelp(CommandSource &source, const Anope::string &) override
|
||||
{
|
||||
BotInfo *bi = Config->GetClient("ChanServ");
|
||||
this->SendSyntax(source);
|
||||
source.Reply(" ");
|
||||
source.Reply(_(
|
||||
"Sets whether you will be given your channel status modes automatically. "
|
||||
"Set to \002ON\002 to allow %s to set status modes on you automatically "
|
||||
"when entering channels. Note that depending on channel settings some modes "
|
||||
"may not get set automatically."
|
||||
),
|
||||
bi ? bi->nick.c_str() : "ChanServ");
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class CommandNSSASetAutoOp final
|
||||
: public CommandNSSetOpAutoOp
|
||||
{
|
||||
public:
|
||||
CommandNSSASetAutoOp(Module *creator)
|
||||
: CommandNSSetOpAutoOp(creator, "nickserv/saset/autoop", 2)
|
||||
{
|
||||
this->ClearSyntax();
|
||||
this->SetSyntax(_("\037nickname\037 {ON | OFF}"));
|
||||
}
|
||||
|
||||
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override
|
||||
{
|
||||
this->Run(source, params[0], params[1]);
|
||||
}
|
||||
|
||||
bool OnHelp(CommandSource &source, const Anope::string &) override
|
||||
{
|
||||
BotInfo *bi = Config->GetClient("ChanServ");
|
||||
this->SendSyntax(source);
|
||||
source.Reply(" ");
|
||||
source.Reply(_(
|
||||
"Sets whether the given nickname will be given its status modes "
|
||||
"in channels automatically. Set to \002ON\002 to allow %s "
|
||||
"to set status modes on the given nickname automatically when it "
|
||||
"is entering channels. Note that depending on channel settings "
|
||||
"some modes may not get set automatically."
|
||||
),
|
||||
bi ? bi->nick.c_str() : "ChanServ");
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class CommandNSSetOpNeverOp
|
||||
: public Command
|
||||
{
|
||||
public:
|
||||
CommandNSSetOpNeverOp(Module *creator, const Anope::string &sname = "nickserv/set/neverop", size_t min = 1)
|
||||
: Command(creator, sname, min, min + 1)
|
||||
{
|
||||
this->SetDesc(_("Sets whether you can be added to a channel access list."));
|
||||
this->SetSyntax("{ON | OFF}");
|
||||
}
|
||||
|
||||
void Run(CommandSource &source, const Anope::string &user, const Anope::string ¶m)
|
||||
{
|
||||
if (Anope::ReadOnly)
|
||||
{
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
const NickAlias *na = NickAlias::Find(user);
|
||||
if (na == NULL)
|
||||
{
|
||||
source.Reply(NICK_X_NOT_REGISTERED, user.c_str());
|
||||
return;
|
||||
}
|
||||
NickCore *nc = na->nc;
|
||||
|
||||
EventReturn MOD_RESULT;
|
||||
FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, nc, param));
|
||||
if (MOD_RESULT == EVENT_STOP)
|
||||
return;
|
||||
|
||||
if (param.equals_ci("ON"))
|
||||
{
|
||||
Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to enable neverop for " << na->nc->display;
|
||||
nc->Extend<bool>("NEVEROP");
|
||||
source.Reply(_("%s can no longer be added to channel access lists."), nc->display.c_str());
|
||||
}
|
||||
else if (param.equals_ci("OFF"))
|
||||
{
|
||||
Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable neverop for " << na->nc->display;
|
||||
nc->Shrink<bool>("NEVEROP");
|
||||
source.Reply(_("%s can now be added to channel access lists."), nc->display.c_str());
|
||||
}
|
||||
else
|
||||
this->OnSyntaxError(source, "NEVEROP");
|
||||
}
|
||||
|
||||
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override
|
||||
{
|
||||
this->Run(source, source.nc->display, params[0]);
|
||||
}
|
||||
|
||||
bool OnHelp(CommandSource &source, const Anope::string &) override
|
||||
{
|
||||
this->SendSyntax(source);
|
||||
source.Reply(" ");
|
||||
source.Reply(_("Sets whether you can be added to a channel access list."));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class CommandNSSASetNeverOp final
|
||||
: public CommandNSSetOpNeverOp
|
||||
{
|
||||
public:
|
||||
CommandNSSASetNeverOp(Module *creator)
|
||||
: CommandNSSetOpNeverOp(creator, "nickserv/saset/neverop", 2)
|
||||
{
|
||||
this->ClearSyntax();
|
||||
this->SetSyntax(_("\037nickname\037 {ON | OFF}"));
|
||||
}
|
||||
|
||||
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override
|
||||
{
|
||||
this->Run(source, params[0], params[1]);
|
||||
}
|
||||
|
||||
bool OnHelp(CommandSource &source, const Anope::string &) override
|
||||
{
|
||||
this->SendSyntax(source);
|
||||
source.Reply(" ");
|
||||
source.Reply(_("Sets whether the given nickname can be added to a channel access list."));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class NSSetOp final
|
||||
: public Module
|
||||
{
|
||||
private:
|
||||
CommandNSSetOpAutoOp commandnssetautoop;
|
||||
CommandNSSASetAutoOp commandnssasetautoop;
|
||||
|
||||
CommandNSSetOpNeverOp commandnssetneverop;
|
||||
CommandNSSASetNeverOp commandnssasetneverop;
|
||||
|
||||
SerializableExtensibleItem<bool> autoop;
|
||||
SerializableExtensibleItem<bool> neverop;
|
||||
|
||||
public:
|
||||
NSSetOp(const Anope::string &modname, const Anope::string &creator)
|
||||
: Module(modname, creator, VENDOR)
|
||||
, commandnssetautoop(this)
|
||||
, commandnssasetautoop(this)
|
||||
, commandnssetneverop(this)
|
||||
, commandnssasetneverop(this)
|
||||
, autoop(this, "AUTOOP")
|
||||
, neverop(this, "NEVEROP")
|
||||
{
|
||||
}
|
||||
|
||||
void OnSetCorrectModes(User *user, Channel *chan, AccessGroup &access, bool &give_modes, bool &take_modes) override
|
||||
{
|
||||
if (!chan->ci)
|
||||
return;
|
||||
|
||||
// Only give modes if autoop is set.
|
||||
give_modes &= !user->IsIdentified() || autoop.HasExt(user->Account());
|
||||
}
|
||||
|
||||
void OnNickInfo(CommandSource &source, NickAlias *na, InfoFormatter &info, bool show_hidden) override
|
||||
{
|
||||
if (!show_hidden)
|
||||
return;
|
||||
|
||||
if (autoop.HasExt(na->nc))
|
||||
info.AddOption(_("Auto op"));
|
||||
|
||||
if (neverop.HasExt(na->nc))
|
||||
info.AddOption(_("Never op"));
|
||||
}
|
||||
};
|
||||
|
||||
MODULE_INIT(NSSetOp)
|
||||
@@ -27,10 +27,7 @@ public:
|
||||
NickAlias *na = NickAlias::Find(u->nick);
|
||||
|
||||
if (na && na->nc == source.GetAccount())
|
||||
{
|
||||
na->last_realname = u->realname;
|
||||
na->last_seen = Anope::CurTime;
|
||||
}
|
||||
|
||||
FOREACH_MOD(OnNickUpdate, (u));
|
||||
|
||||
|
||||
@@ -337,13 +337,7 @@ private:
|
||||
else
|
||||
{
|
||||
source.Reply(_("Current AKILL list:"));
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
list.Process(replies);
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
|
||||
list.SendTo(source);
|
||||
source.Reply(_("End of AKILL list."));
|
||||
}
|
||||
}
|
||||
@@ -358,6 +352,7 @@ private:
|
||||
|
||||
ListFormatter list(source.GetAccount());
|
||||
list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("Reason"));
|
||||
list.SetFlexible(_("{number}: \002{mask}\002 ({reason})"));
|
||||
|
||||
this->ProcessList(source, params, list);
|
||||
}
|
||||
@@ -370,11 +365,19 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
const auto akillids = Config->GetModule("operserv").Get<bool>("akillids");
|
||||
|
||||
ListFormatter list(source.GetAccount());
|
||||
list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("Creator")).AddColumn(_("Created")).AddColumn(_("Expires"));
|
||||
if (Config->GetModule("operserv").Get<bool>("akillids"))
|
||||
if (akillids)
|
||||
list.AddColumn(_("ID"));
|
||||
list.AddColumn(_("Reason"));
|
||||
list.SetFlexible([akillids](ListFormatter::ListEntry &row)
|
||||
{
|
||||
return akillids
|
||||
? _("{number}: [{id}] \002{mask}\002 -- created by {creator} on {created}; {expires} ({reason})")
|
||||
: _("{number}: \002{mask}\002 -- created by {creator} on {created}; {expires} ({reason})");
|
||||
});
|
||||
|
||||
this->ProcessList(source, params, list);
|
||||
}
|
||||
@@ -450,7 +453,7 @@ public:
|
||||
"\0371h30m\037) are not permitted. If a unit specifier is not "
|
||||
"included, the default is days (so \037+30\037 by itself means 30 "
|
||||
"days). To add an AKILL which does not expire, use \037+0\037. If the "
|
||||
"usermask to be added starts with a \037+\037, an expiry time must "
|
||||
"mask to be added starts with a \037+\037, an expiry time must "
|
||||
"be given, even if it is the same as the default. The "
|
||||
"current AKILL default expiry time can be found with the "
|
||||
"\002STATS\032AKILL\002 command."
|
||||
|
||||
@@ -62,6 +62,7 @@ public:
|
||||
|
||||
ListFormatter lflist(source.GetAccount());
|
||||
lflist.AddColumn(_("Name")).AddColumn(_("Value"));
|
||||
lflist.SetFlexible(_("\002{name}\002 = {value}"));
|
||||
|
||||
for (const auto &[name, value] : items)
|
||||
{
|
||||
@@ -71,19 +72,14 @@ public:
|
||||
lflist.AddEntry(entry);
|
||||
}
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
lflist.Process(replies);
|
||||
|
||||
source.Reply(_("%s settings:"), block.GetName().c_str());
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
|
||||
lflist.SendTo(source);
|
||||
source.Reply(" ");
|
||||
}
|
||||
|
||||
ListFormatter lflist(source.GetAccount());
|
||||
lflist.AddColumn(_("Module Name")).AddColumn(_("Name")).AddColumn(_("Value"));
|
||||
lflist.SetFlexible(_("\002{}{module_name}}:{name}\002 = {value}"));
|
||||
|
||||
for (int i = 0; i < Config->CountBlock("module"); ++i)
|
||||
{
|
||||
@@ -103,15 +99,8 @@ public:
|
||||
lflist.AddEntry(entry);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
lflist.Process(replies);
|
||||
|
||||
source.Reply(_("Module settings:"));
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
|
||||
lflist.SendTo(source);
|
||||
source.Reply(_("End of configuration."));
|
||||
}
|
||||
else
|
||||
|
||||
@@ -136,7 +136,7 @@ public:
|
||||
if (!DConfig.offmessage.empty())
|
||||
GlobalService->SendSingle(DConfig.offmessage);
|
||||
else
|
||||
GlobalService->SendSingle(Anope::printf(Language::Translate(_("The Defcon level is now at: \002%d\002")), DConfig.defaultlevel));
|
||||
GlobalService->SendSingle(Anope::Format(Language::Translate(_("The Defcon level is now at: \002%d\002")), DConfig.defaultlevel));
|
||||
|
||||
if (!DConfig.message.empty())
|
||||
GlobalService->SendSingle(DConfig.message);
|
||||
@@ -220,7 +220,7 @@ public:
|
||||
GlobalService->SendSingle(DConfig.offmessage);
|
||||
else if (DConfig.defaultlevel != 5)
|
||||
{
|
||||
GlobalService->SendSingle(Anope::printf(_("The Defcon level is now at: \002%d\002"), DConfig.defaultlevel));
|
||||
GlobalService->SendSingle(Anope::Format(_("The Defcon level is now at: \002%d\002"), DConfig.defaultlevel));
|
||||
if (!DConfig.message.empty())
|
||||
GlobalService->SendSingle(DConfig.message);
|
||||
}
|
||||
|
||||
@@ -241,6 +241,8 @@ class CommandOSDNS final
|
||||
|
||||
ListFormatter lf(source.GetAccount());
|
||||
lf.AddColumn(_("Server")).AddColumn(_("IP")).AddColumn(_("Limit")).AddColumn(_("State"));
|
||||
lf.SetFlexible(_("Server: \002{server}\002 = {ip} -- limit: {limit}; state: {state}"));
|
||||
|
||||
for (auto *s : *dns_servers)
|
||||
{
|
||||
Server *srv = Server::Find(s->GetName(), true);
|
||||
@@ -269,14 +271,18 @@ class CommandOSDNS final
|
||||
|
||||
lf.AddEntry(entry);
|
||||
}
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
lf.Process(replies);
|
||||
lf.SendTo(source);
|
||||
|
||||
if (!zones->empty())
|
||||
{
|
||||
ListFormatter lf2(source.GetAccount());
|
||||
lf2.AddColumn(_("Zone")).AddColumn(_("Servers"));
|
||||
lf2.SetFlexible([](ListFormatter::ListEntry &row)
|
||||
{
|
||||
return row["Servers"].equals_cs("None")
|
||||
? _("Zone: \002{zone}\002")
|
||||
: _("Zone: \002{zone}\002 = {servers}");
|
||||
});
|
||||
|
||||
for (auto *z : *zones)
|
||||
{
|
||||
@@ -295,12 +301,8 @@ class CommandOSDNS final
|
||||
|
||||
lf2.AddEntry(entry);
|
||||
}
|
||||
|
||||
lf2.Process(replies);
|
||||
lf2.SendTo(source);
|
||||
}
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
}
|
||||
|
||||
void AddZone(CommandSource &source, const std::vector<Anope::string> ¶ms)
|
||||
|
||||
@@ -347,7 +347,7 @@ public:
|
||||
if (u->server == Me || u->HasMode("OPER"))
|
||||
continue;
|
||||
|
||||
reason = Anope::printf(Language::Translate(u, _("This channel has been forbidden: %s")), d->reason.c_str());
|
||||
reason = Anope::Format(Language::Translate(u, _("This channel has been forbidden: %s")), d->reason.c_str());
|
||||
|
||||
c->Kick(source.service, u, reason);
|
||||
}
|
||||
@@ -402,6 +402,12 @@ public:
|
||||
{
|
||||
ListFormatter list(source.GetAccount());
|
||||
list.AddColumn(_("Mask")).AddColumn(_("Type")).AddColumn(_("Creator")).AddColumn(_("Expires")).AddColumn(_("Reason"));
|
||||
list.SetFlexible([](ListFormatter::ListEntry &row)
|
||||
{
|
||||
return row["Reason"].empty()
|
||||
? _("\002{mask}\002 on {type} -- created by {creator}; expires in {expires}")
|
||||
: _("\002{mask}\002 on {type} -- created by {creator}; expires in {expires} ({reason})");
|
||||
});
|
||||
|
||||
size_t shown = 0;
|
||||
for (auto *forbid : forbids)
|
||||
@@ -430,12 +436,7 @@ public:
|
||||
else
|
||||
{
|
||||
source.Reply(_("Forbid list:"));
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
list.Process(replies);
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
list.SendTo(source);
|
||||
|
||||
if (shown >= forbids.size())
|
||||
source.Reply(_("End of forbid list."));
|
||||
@@ -536,7 +537,7 @@ public:
|
||||
chanserv->Hold(c);
|
||||
}
|
||||
|
||||
reason = Anope::printf(Language::Translate(u, _("This channel has been forbidden: %s")), d->reason.c_str());
|
||||
reason = Anope::Format(Language::Translate(u, _("This channel has been forbidden: %s")), d->reason.c_str());
|
||||
|
||||
return EVENT_STOP;
|
||||
}
|
||||
|
||||
@@ -278,6 +278,12 @@ private:
|
||||
{
|
||||
ListFormatter list(source.GetAccount());
|
||||
list.AddColumn(_("Mask")).AddColumn(_("Creator")).AddColumn(_("Reason")).AddColumn(_("Expires"));
|
||||
list.SetFlexible([](ListFormatter::ListEntry &row)
|
||||
{
|
||||
return row["Reason"].empty()
|
||||
? _("\002{mask}\002 -- created by {creator}; {expires}")
|
||||
: _("\002{mask}\002 -- created by {creator}; {expires} ({reason})");
|
||||
});
|
||||
|
||||
for (unsigned i = ignores.size(); i > 0; --i)
|
||||
{
|
||||
@@ -292,12 +298,7 @@ private:
|
||||
}
|
||||
|
||||
source.Reply(_("Services ignore list:"));
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
list.Process(replies);
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
list.SendTo(source);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -281,7 +281,7 @@ class OSInfo final
|
||||
|
||||
for (auto *o : *(*oi))
|
||||
{
|
||||
info[_("Oper Info")] = Anope::printf(_("(by %s on %s) %s"), o->adder.c_str(), Anope::strftime(o->created, source.GetAccount(), true).c_str(), o->info.c_str());
|
||||
info[_("Oper Info")] = Anope::Format(_("(by %s on %s) %s"), o->adder.c_str(), Anope::strftime(o->created, source.GetAccount(), true).c_str(), o->info.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,13 @@ public:
|
||||
|
||||
ListFormatter list(source.GetAccount());
|
||||
list.AddColumn(_("Name")).AddColumn(_("Users")).AddColumn(_("Modes")).AddColumn(_("Topic"));
|
||||
list.SetFlexible([](ListFormatter::ListEntry &row)
|
||||
{
|
||||
return row["Topic"].empty()
|
||||
? _("\002{name}\002 -- {users} user(s); +{modes}")
|
||||
: _("\002{name}\002 -- {users} user(s); +{modes} ({topic})");
|
||||
});
|
||||
|
||||
|
||||
if (!pattern.empty() && (u2 = User::Find(pattern, true)))
|
||||
{
|
||||
@@ -96,12 +103,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
list.Process(replies);
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
|
||||
list.SendTo(source);
|
||||
source.Reply(_("End of channel list. \002%u\002 channels shown."), count);
|
||||
}
|
||||
|
||||
@@ -162,6 +164,7 @@ public:
|
||||
|
||||
ListFormatter list(source.GetAccount());
|
||||
list.AddColumn(_("Name")).AddColumn(_("Mask")).AddColumn(_("Realname"));
|
||||
list.SetFlexible(_("\002{name}\002 ({mask}) [{realname}]"));
|
||||
|
||||
if (!pattern.empty() && (c = Channel::Find(pattern)))
|
||||
{
|
||||
@@ -243,12 +246,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
list.Process(replies);
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
|
||||
list.SendTo(source);
|
||||
source.Reply(_("End of users list. \002%u\002 users shown."), count);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ public:
|
||||
|
||||
const Anope::string GetDesc(CommandSource &source) const override
|
||||
{
|
||||
return Anope::printf(Language::Translate(source.GetAccount(), _("Login to %s")), source.service->nick.c_str());
|
||||
return Anope::Format(Language::Translate(source.GetAccount(), _("Login to %s")), source.service->nick.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -120,7 +120,7 @@ public:
|
||||
|
||||
const Anope::string GetDesc(CommandSource &source) const override
|
||||
{
|
||||
return Anope::printf(Language::Translate(source.GetAccount(), _("Logout from %s")), source.service->nick.c_str());
|
||||
return Anope::Format(Language::Translate(source.GetAccount(), _("Logout from %s")), source.service->nick.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -205,6 +205,7 @@ protected:
|
||||
{
|
||||
ListFormatter lflist(source.GetAccount());
|
||||
lflist.AddColumn(_("Number")).AddColumn(_("Creator")).AddColumn(_("Created")).AddColumn(_("Text"));
|
||||
lflist.SetFlexible(_("{number}: {text} -- created by {creator} on {created}"));
|
||||
|
||||
for (unsigned i = 0, end = list.size(); i < end; ++i)
|
||||
{
|
||||
@@ -217,13 +218,7 @@ protected:
|
||||
}
|
||||
|
||||
source.Reply(msgs[MSG_LIST_HEADER]);
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
lflist.Process(replies);
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
|
||||
lflist.SendTo(source);
|
||||
source.Reply(_("End of news list."));
|
||||
}
|
||||
|
||||
|
||||
@@ -236,6 +236,7 @@ private:
|
||||
{
|
||||
ListFormatter list(source.GetAccount());
|
||||
list.AddColumn(_("Session")).AddColumn(_("Host"));
|
||||
list.SetFlexible(_("\002{host}\002: {session} sessions"));
|
||||
|
||||
for (const auto &[_, session] : session_service->GetSessions())
|
||||
{
|
||||
@@ -249,13 +250,7 @@ private:
|
||||
}
|
||||
|
||||
source.Reply(_("Hosts with at least \002%d\002 sessions:"), mincount);
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
list.Process(replies);
|
||||
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
list.SendTo(source);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -498,7 +493,7 @@ private:
|
||||
ListFormatter::ListEntry entry;
|
||||
entry["Number"] = Anope::ToString(Number);
|
||||
entry["Mask"] = e->mask;
|
||||
entry["By"] = e->who;
|
||||
entry["Creator"] = e->who;
|
||||
entry["Created"] = Anope::strftime(e->time, NULL, true);
|
||||
entry["Expires"] = Anope::Expires(e->expires, source.GetAccount());
|
||||
entry["Limit"] = Anope::ToString(e->limit);
|
||||
@@ -519,7 +514,7 @@ private:
|
||||
ListFormatter::ListEntry entry;
|
||||
entry["Number"] = Anope::ToString(i + 1);
|
||||
entry["Mask"] = e->mask;
|
||||
entry["By"] = e->who;
|
||||
entry["Creator"] = e->who;
|
||||
entry["Created"] = Anope::strftime(e->time, NULL, true);
|
||||
entry["Expires"] = Anope::Expires(e->expires, source.GetAccount());
|
||||
entry["Limit"] = Anope::ToString(e->limit);
|
||||
@@ -534,19 +529,15 @@ private:
|
||||
else
|
||||
{
|
||||
source.Reply(_("Current Session Limit Exception list:"));
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
list.Process(replies);
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
list.SendTo(source);
|
||||
}
|
||||
}
|
||||
|
||||
void DoList(CommandSource &source, const std::vector<Anope::string> ¶ms)
|
||||
{
|
||||
ListFormatter list(source.GetAccount());
|
||||
list.AddColumn(_("Number")).AddColumn(_("Limit")).AddColumn(_("Mask"));
|
||||
list.AddColumn(_("Number")).AddColumn(_("Limit")).AddColumn(_("Mask")).AddColumn(_("Reason"));
|
||||
list.SetFlexible(_("{number}: \002{mask}\002 -- {limit} sessions ({reason})"));
|
||||
|
||||
this->ProcessList(source, params, list);
|
||||
}
|
||||
@@ -554,7 +545,8 @@ private:
|
||||
void DoView(CommandSource &source, const std::vector<Anope::string> ¶ms)
|
||||
{
|
||||
ListFormatter list(source.GetAccount());
|
||||
list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("By")).AddColumn(_("Created")).AddColumn(_("Expires")).AddColumn(_("Limit")).AddColumn(_("Reason"));
|
||||
list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("Creator")).AddColumn(_("Created")).AddColumn(_("Expires")).AddColumn(_("Limit")).AddColumn(_("Reason"));
|
||||
list.SetFlexible(_("{number}: \002{mask}\002 -- {limit} sessions; created by {creator} on {created}; {expires} ({reason})"));
|
||||
|
||||
this->ProcessList(source, params, list);
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ private:
|
||||
ListFormatter::ListEntry entry;
|
||||
entry["Number"] = Anope::ToString(number);
|
||||
entry["Mask"] = x->mask;
|
||||
entry["By"] = x->by;
|
||||
entry["Creator"] = x->by;
|
||||
entry["Created"] = Anope::strftime(x->created, NULL, true);
|
||||
entry["Expires"] = Anope::Expires(x->expires, source.nc);
|
||||
entry["ID"] = x->id;
|
||||
@@ -176,7 +176,7 @@ private:
|
||||
ListFormatter::ListEntry entry;
|
||||
entry["Number"] = Anope::ToString(i + 1);
|
||||
entry["Mask"] = x->mask;
|
||||
entry["By"] = x->by;
|
||||
entry["Creator"] = x->by;
|
||||
entry["Created"] = Anope::strftime(x->created, NULL, true);
|
||||
entry["Expires"] = Anope::Expires(x->expires, source.nc);
|
||||
entry["ID"] = x->id;
|
||||
@@ -191,12 +191,7 @@ private:
|
||||
else
|
||||
{
|
||||
source.Reply(_("Current %s list:"), source.command.nobreak().c_str());
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
list.Process(replies);
|
||||
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
list.SendTo(source);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,17 +199,27 @@ private:
|
||||
{
|
||||
ListFormatter list(source.GetAccount());
|
||||
list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("Reason"));
|
||||
list.SetFlexible(_("{number}: \002{mask}\002 ({reason})"));
|
||||
|
||||
this->ProcessList(source, params, list);
|
||||
}
|
||||
|
||||
void OnView(CommandSource &source, const std::vector<Anope::string> ¶ms)
|
||||
{
|
||||
const auto akillids = Config->GetModule("operserv").Get<bool>("akillids");
|
||||
|
||||
ListFormatter list(source.GetAccount());
|
||||
list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("By")).AddColumn(_("Created")).AddColumn(_("Expires"));
|
||||
list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("Creator")).AddColumn(_("Created")).AddColumn(_("Expires"));
|
||||
if (Config->GetModule("operserv").Get<bool>("akillids"))
|
||||
list.AddColumn(_("ID"));
|
||||
list.AddColumn(_("Reason"));
|
||||
list.SetFlexible([akillids](ListFormatter::ListEntry &row)
|
||||
{
|
||||
return akillids
|
||||
? _("{number}: [{id}] \002{mask}\002 -- created by {creator} on {created}; {expires} ({reason})")
|
||||
: _("{number}: \002{mask}\002 -- created by {creator} on {created}; {expires} ({reason})");
|
||||
});
|
||||
|
||||
|
||||
this->ProcessList(source, params, list);
|
||||
}
|
||||
@@ -243,7 +248,7 @@ public:
|
||||
|
||||
const Anope::string GetDesc(CommandSource &source) const override
|
||||
{
|
||||
return Anope::printf(Language::Translate(source.GetAccount(), _("Manipulate the %s list")), source.command.nobreak().c_str());
|
||||
return Anope::Format(Language::Translate(source.GetAccount(), _("Manipulate the %s list")), source.command.nobreak().c_str());
|
||||
}
|
||||
|
||||
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override
|
||||
|
||||
@@ -192,6 +192,11 @@ public:
|
||||
MaxLine = 0;
|
||||
}
|
||||
|
||||
void GetLinkAdvice(std::vector<Anope::string> &advice) override
|
||||
{
|
||||
advice.push_back("You do not have the account, hidechans, services, and spanningtree modules loaded on InspIRCd.");
|
||||
}
|
||||
|
||||
size_t GetMaxListFor(Channel *c, ChannelMode *cm) override
|
||||
{
|
||||
ListLimits *limits = maxlist.Get(c);
|
||||
@@ -622,13 +627,13 @@ public:
|
||||
|
||||
if (spanningtree_proto_ver >= 1206)
|
||||
{
|
||||
Uplink::Send("SINFO", "customversion", Anope::printf("%s -- (%s) -- %s", IRCD->GetProtocolName().c_str(), enc ? enc->name.c_str() : "none", Anope::VersionBuildString().c_str()));
|
||||
Uplink::Send("SINFO", "customversion", Anope::Format("%s -- (%s) -- %s", IRCD->GetProtocolName().c_str(), enc ? enc->name.c_str() : "none", Anope::VersionBuildString().c_str()));
|
||||
Uplink::Send("SINFO", "rawbranch", "Anope-" + Anope::VersionShort());
|
||||
}
|
||||
else
|
||||
{
|
||||
Uplink::Send("SINFO", "version", Anope::printf("Anope-%s %s :%s -- (%s) -- %s", Anope::Version().c_str(), Me->GetName().c_str(), IRCD->GetProtocolName().c_str(), enc ? enc->name.c_str() : "none", Anope::VersionBuildString().c_str()));
|
||||
Uplink::Send("SINFO", "fullversion", Anope::printf("Anope-%s %s :[%s] %s -- (%s) -- %s", Anope::Version().c_str(), Me->GetName().c_str(), Me->GetSID().c_str(), IRCD->GetProtocolName().c_str(), enc ? enc->name.c_str() : "none", Anope::VersionBuildString().c_str()));
|
||||
Uplink::Send("SINFO", "version", Anope::Format("Anope-%s %s :%s -- (%s) -- %s", Anope::Version().c_str(), Me->GetName().c_str(), IRCD->GetProtocolName().c_str(), enc ? enc->name.c_str() : "none", Anope::VersionBuildString().c_str()));
|
||||
Uplink::Send("SINFO", "fullversion", Anope::Format("Anope-%s %s :[%s] %s -- (%s) -- %s", Anope::Version().c_str(), Me->GetName().c_str(), Me->GetSID().c_str(), IRCD->GetProtocolName().c_str(), enc ? enc->name.c_str() : "none", Anope::VersionBuildString().c_str()));
|
||||
}
|
||||
Uplink::Send("SINFO", "rawversion", "Anope-" + Anope::VersionShort());
|
||||
}
|
||||
@@ -1116,8 +1121,7 @@ struct IRCDMessageCapab final
|
||||
if (challenge.empty() || !sha256)
|
||||
return Config->Uplinks[Anope::CurrentUplink].password;
|
||||
|
||||
Anope::string b64challenge;
|
||||
Anope::B64Encode(sha256->HMAC(Config->Uplinks[Anope::CurrentUplink].password, challenge), b64challenge);
|
||||
auto b64challenge = Anope::B64Encode(sha256->HMAC(Config->Uplinks[Anope::CurrentUplink].password, challenge));
|
||||
challenge.clear();
|
||||
|
||||
return "AUTH:" + b64challenge.rtrim('=');
|
||||
|
||||
@@ -23,7 +23,7 @@ class ChannelModeLargeBan final
|
||||
public:
|
||||
ChannelModeLargeBan(const Anope::string &mname, char modeChar) : ChannelMode(mname, modeChar) { }
|
||||
|
||||
bool CanSet(User *u) const override
|
||||
bool CanSet(User *u, Channel *c) const override
|
||||
{
|
||||
return u && u->HasMode("OPER");
|
||||
}
|
||||
|
||||
@@ -463,7 +463,7 @@ private:
|
||||
if (!cm || !cm->mchar)
|
||||
return;
|
||||
|
||||
Uplink::Send(user, "SVS2MODE", c->name, Anope::printf("-%c", cm->mchar), u->GetUID());
|
||||
Uplink::Send(user, "SVS2MODE", c->name, Anope::Format("-%c", cm->mchar), u->GetUID());
|
||||
}
|
||||
|
||||
bool IsTagValid(const Anope::string &tname, const Anope::string &tvalue) override
|
||||
@@ -492,7 +492,7 @@ namespace UnrealExtBan
|
||||
ChannelMode *Wrap(Anope::string ¶m) override
|
||||
{
|
||||
auto prefix = Servers::Capab.count("NEXTBANS") ? xbname : Anope::string(xbchar);
|
||||
param = Anope::printf("~%s:%s", prefix.c_str(), param.c_str());
|
||||
param = Anope::Format("~%s:%s", prefix.c_str(), param.c_str());
|
||||
return ChannelModeVirtual<ChannelModeList>::Wrap(param);
|
||||
}
|
||||
|
||||
@@ -749,7 +749,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
bool CanSet(User *u) const override
|
||||
bool CanSet(User *u, Channel *c) const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -1233,8 +1233,7 @@ struct IRCDMessageNick final
|
||||
Anope::string ip;
|
||||
if (params[9] != "*")
|
||||
{
|
||||
Anope::string decoded_ip;
|
||||
Anope::B64Decode(params[9], decoded_ip);
|
||||
auto decoded_ip = Anope::B64Decode(params[9]);
|
||||
|
||||
sockaddrs ip_addr;
|
||||
ip_addr.ntop(params[9].length() == 8 ? AF_INET : AF_INET6, decoded_ip.c_str());
|
||||
@@ -1646,8 +1645,7 @@ struct IRCDMessageUID final
|
||||
|
||||
if (ip != "*")
|
||||
{
|
||||
Anope::string decoded_ip;
|
||||
Anope::B64Decode(ip, decoded_ip);
|
||||
auto decoded_ip = Anope::B64Decode(ip);
|
||||
|
||||
sockaddrs ip_addr;
|
||||
ip_addr.ntop(ip.length() == 8 ? AF_INET : AF_INET6, decoded_ip.c_str());
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user