mirror of
https://github.com/anope/anope.git
synced 2026-06-14 11:14:46 +02:00
Compare commits
109 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 507eefc820 | |||
| 72af764861 | |||
| 1b631e96bd | |||
| 3b4a4da52d | |||
| 2e59aac025 | |||
| 3de6da80c6 | |||
| 2829c83118 | |||
| f58012ea0e | |||
| f8605ec188 | |||
| 5a12beff15 | |||
| 6b1f8de885 | |||
| 33be5de66e | |||
| 4399eeaa58 | |||
| 38cfb0603a | |||
| 1a3844e74e | |||
| bfed2e1bf5 | |||
| e488f294a1 | |||
| fde3438ef2 | |||
| 1575dea5b9 | |||
| a1972ba9b6 | |||
| 5a72d8783e | |||
| e6770bc2fd | |||
| 413fed474e | |||
| 60083834f8 | |||
| 9ac4da0489 | |||
| cb3848b7db | |||
| 753119c4a1 | |||
| d996c3aa8f | |||
| ac6e8c617d | |||
| 3b09f4aa54 | |||
| 10f4724b6b | |||
| ad86535024 | |||
| 628f4d2a7e | |||
| 8d5be77c93 | |||
| 13a556df89 | |||
| 4e9d183523 | |||
| 074dfb63a7 | |||
| beaf09de7b | |||
| c8d8978cd0 | |||
| 11edba04fe | |||
| 22ba54b00d | |||
| 6fd7ca39e2 | |||
| b8cd00b412 | |||
| f4bd43e898 | |||
| 7d7664444a | |||
| 1a8bbd6004 | |||
| e725c880a9 | |||
| f5abcd1c4c | |||
| 1986aa6581 | |||
| b52e1b2b02 | |||
| 54719fbfc6 | |||
| 06add0e5fc | |||
| 63d682314b | |||
| 1a6060ac5b | |||
| a302f8f1be | |||
| 04e1a4f5c8 | |||
| 1538909ac0 | |||
| a15f165a1d | |||
| 77435dd0d9 | |||
| 6f57907416 | |||
| 7c1cfd2849 | |||
| 259b10b83a | |||
| 02355546ff | |||
| 6ad3430ac4 | |||
| 29e7674e56 | |||
| b09632d1c3 | |||
| e2df7d4d01 | |||
| 9a984a8148 | |||
| fb9c8950ed | |||
| a849a81ac3 | |||
| f919bb0748 | |||
| 3b85a8071f | |||
| 9a8cac060d | |||
| 0353338436 | |||
| defb8348a7 | |||
| 1b86665d81 | |||
| 2f52fa723c | |||
| 6e0f0b8896 | |||
| 2c5b2c649e | |||
| a6bc4cab9d | |||
| ef37daf44a | |||
| 39d288f99f | |||
| 9ead3f15c3 | |||
| 272104af95 | |||
| 51e95d72e3 | |||
| 12486f6cee | |||
| 891375baca | |||
| 41a295202e | |||
| 722c5b7d61 | |||
| 52216b56a1 | |||
| cd614831de | |||
| 1d0a836a2e | |||
| 46b7064834 | |||
| bb8df01e25 | |||
| 16700b0e30 | |||
| 4e04a11995 | |||
| 76e5480be9 | |||
| 464e6b8010 | |||
| 3272c1bbc6 | |||
| 307ffea951 | |||
| bb94f286f5 | |||
| 86e3556438 | |||
| 12214bee72 | |||
| 08a35231ef | |||
| 9649dc78a1 | |||
| 9970b4e37b | |||
| a5a615d5c7 | |||
| ce97fdd1c6 | |||
| 1a8ae513b4 |
@@ -4,3 +4,4 @@ updates:
|
||||
directory: /
|
||||
schedule:
|
||||
interval: monthly
|
||||
target-branch: "2.1"
|
||||
|
||||
@@ -20,6 +20,7 @@ jobs:
|
||||
echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories
|
||||
apk update
|
||||
apk add \
|
||||
argon2-dev \
|
||||
clang \
|
||||
cmake \
|
||||
g++ \
|
||||
@@ -36,7 +37,7 @@ jobs:
|
||||
|
||||
- name: Enable extras
|
||||
run: |
|
||||
for MODULE in ldap ldap_authentication ldap_oper mysql regex_pcre2 regex_posix regex_tre sql_authentication sql_log sql_oper sqlite ssl_gnutls ssl_openssl
|
||||
for MODULE in enc_argon2 enc_posix ldap mysql regex_pcre2 regex_posix regex_tre sqlite ssl_gnutls ssl_openssl
|
||||
do
|
||||
ln -s $PWD/modules/extra/$MODULE.cpp $PWD/modules
|
||||
done
|
||||
|
||||
@@ -20,6 +20,7 @@ jobs:
|
||||
g++ \
|
||||
gettext \
|
||||
git \
|
||||
libargon2-dev \
|
||||
libgnutls28-dev \
|
||||
libldap2-dev \
|
||||
libmysqlclient-dev \
|
||||
@@ -31,7 +32,7 @@ jobs:
|
||||
|
||||
- name: Enable extras
|
||||
run: |
|
||||
for MODULE in ldap ldap_authentication ldap_oper mysql regex_pcre2 regex_posix regex_tre sql_authentication sql_log sql_oper sqlite ssl_gnutls ssl_openssl
|
||||
for MODULE in enc_argon2 enc_posix ldap mysql regex_pcre2 regex_posix regex_tre sqlite ssl_gnutls ssl_openssl
|
||||
do
|
||||
ln -s ${{ github.workspace }}/modules/extra/$MODULE.cpp ${{ github.workspace }}/modules
|
||||
done
|
||||
|
||||
@@ -9,7 +9,7 @@ jobs:
|
||||
if: "!contains(github.event.head_commit.message, '[skip windows ci]')"
|
||||
runs-on: windows-2019
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup MSBuild
|
||||
uses: microsoft/setup-msbuild@v1.3
|
||||
|
||||
+2
-5
@@ -1,16 +1,13 @@
|
||||
build/
|
||||
config.cache
|
||||
include/sysconf.h
|
||||
modules/enc_argon2.cpp
|
||||
modules/enc_posix.cpp
|
||||
modules/ldap.cpp
|
||||
modules/ldap_authentication.cpp
|
||||
modules/ldap_oper.cpp
|
||||
modules/mysql.cpp
|
||||
modules/regex_pcre2.cpp
|
||||
modules/regex_posix.cpp
|
||||
modules/regex_tre.cpp
|
||||
modules/sql_authentication.cpp
|
||||
modules/sql_log.cpp
|
||||
modules/sql_oper.cpp
|
||||
modules/sqlite.cpp
|
||||
modules/ssl_gnutls.cpp
|
||||
modules/ssl_openssl.cpp
|
||||
|
||||
@@ -224,6 +224,7 @@ if(CMAKE_BUILD_TYPE STREQUAL "DEBUG" OR CMAKE_BUILD_TYPE STREQUAL "RELWITHDEBINF
|
||||
endif()
|
||||
|
||||
# Check for the existence of the following functions
|
||||
check_function_exists(clock_gettime HAVE_CLOCK_GETTIME)
|
||||
check_function_exists(umask HAVE_UMASK)
|
||||
check_function_exists(epoll_wait HAVE_EPOLL)
|
||||
check_function_exists(poll HAVE_POLL)
|
||||
|
||||
+138
-63
@@ -238,15 +238,15 @@ serverinfo
|
||||
|
||||
/*
|
||||
* The filename containing the Anope process ID. The path is relative to the
|
||||
* services root directory.
|
||||
* data directory.
|
||||
*/
|
||||
pid = "data/anope.pid"
|
||||
pid = "anope.pid"
|
||||
|
||||
/*
|
||||
* The filename containing the Message of the Day. The path is relative to the
|
||||
* services root directory.
|
||||
* config directory.
|
||||
*/
|
||||
motd = "conf/motd.txt"
|
||||
motd = "motd.txt"
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -395,19 +395,6 @@ options
|
||||
*/
|
||||
casemap = "ascii"
|
||||
|
||||
/*
|
||||
* This key is used to initiate the random number generator. This number
|
||||
* MUST be random as you want your passcodes to be random. Don't give this
|
||||
* key to anyone! Keep it private!
|
||||
*
|
||||
* NOTE: If you don't uncomment this or keep the default values, any talented
|
||||
* programmer would be able to easily "guess" random strings used to mask
|
||||
* information. Be safe, and come up with a 7-digit number.
|
||||
*
|
||||
* This directive is optional, but highly recommended.
|
||||
*/
|
||||
#seed = 9866235
|
||||
|
||||
/*
|
||||
* Sets the number of invalid password tries before services removes a user
|
||||
* from the network. If a user enters a number of invalid passwords equal to
|
||||
@@ -802,7 +789,7 @@ log
|
||||
*
|
||||
* hostserv/set hostserv/del hostserv/list
|
||||
*
|
||||
* global/global
|
||||
* global/global global/queue global/server
|
||||
*
|
||||
* operserv/news operserv/stats operserv/kick operserv/exception operserv/seen
|
||||
* operserv/mode operserv/session operserv/modinfo operserv/ignore operserv/chanlist
|
||||
@@ -931,7 +918,7 @@ opertype
|
||||
/*
|
||||
* [OPTIONAL] Mail Config
|
||||
*
|
||||
* This section contains settings related to the use of e-mail from services.
|
||||
* This section contains settings related to the use of email from services.
|
||||
* If the usemail directive is set to yes, unless specified otherwise, all other
|
||||
* directives are required.
|
||||
*
|
||||
@@ -951,7 +938,7 @@ mail
|
||||
|
||||
/*
|
||||
* This is the command-line that will be used to call the mailer to send an
|
||||
* e-mail. It must be called with all the parameters needed to make it
|
||||
* email. It must be called with all the parameters needed to make it
|
||||
* scan the mail input to find the mail recipient; consult your mailer
|
||||
* documentation.
|
||||
*
|
||||
@@ -963,18 +950,18 @@ mail
|
||||
* If you are running on Windows you should use a Windows sendmail port
|
||||
* like https://www.glob.com.au/sendmail/ for sending emails.
|
||||
*/
|
||||
sendmailpath = "/usr/sbin/sendmail -t"
|
||||
#sendmailpath = "/usr/sbin/sendmail -it"
|
||||
|
||||
/*
|
||||
* This is the e-mail address from which all the e-mails are to be sent from.
|
||||
* This is the email address from which all the emails are to be sent from.
|
||||
* It should really exist.
|
||||
*/
|
||||
sendfrom = "services@example.com"
|
||||
|
||||
/*
|
||||
* This controls the minimum amount of time a user must wait before sending
|
||||
* another e-mail after they have sent one. It also controls the minimum time
|
||||
* a user must wait before they can receive another e-mail.
|
||||
* another email after they have sent one. It also controls the minimum time
|
||||
* a user must wait before they can receive another email.
|
||||
*
|
||||
* This feature prevents users from being mail bombed using services and
|
||||
* it is highly recommended that it be used.
|
||||
@@ -985,10 +972,10 @@ mail
|
||||
|
||||
/*
|
||||
* If set, Anope will not put quotes around the TO: fields
|
||||
* in e-mails.
|
||||
* in emails.
|
||||
*
|
||||
* This directive is optional, and as far as we know, it's only needed
|
||||
* if you are using ESMTP or QMail to send out e-mails.
|
||||
* if you are using ESMTP or QMail to send out emails.
|
||||
*/
|
||||
#dontquoteaddresses = yes
|
||||
|
||||
@@ -1105,6 +1092,22 @@ mail
|
||||
#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.
|
||||
*/
|
||||
#module
|
||||
{
|
||||
name = "db_atheme"
|
||||
|
||||
/*
|
||||
* The database name db_atheme should use.
|
||||
*/
|
||||
database = "atheme.db"
|
||||
}
|
||||
|
||||
/*
|
||||
* [RECOMMENDED] db_flatfile
|
||||
*
|
||||
@@ -1225,49 +1228,121 @@ module
|
||||
/*
|
||||
* [RECOMMENDED] Encryption modules.
|
||||
*
|
||||
* The encryption modules are used when dealing with passwords. This determines how
|
||||
* the passwords are stored in the databases, and does not add any security as
|
||||
* far as transmitting passwords over the network goes.
|
||||
*
|
||||
* Without any encryption modules loaded users will not be able to authenticate unless
|
||||
* there is another module loaded that provides authentication checking, such as
|
||||
* ldap_authentication or sql_authentication.
|
||||
*
|
||||
* With enc_none, passwords will be stored in plain text, allowing for passwords
|
||||
* to be recovered later but it isn't secure and therefore is not recommended.
|
||||
*
|
||||
* The other encryption modules use one-way encryption, so the passwords can not
|
||||
* be recovered later if those are used.
|
||||
*
|
||||
* The first encryption module loaded is the primary encryption module. All new passwords are
|
||||
* encrypted by this module. Old passwords stored in another encryption method are
|
||||
* automatically re-encrypted by the primary encryption module on next identify.
|
||||
*
|
||||
* enc_md5, enc_sha1, and enc_old are deprecated, and are provided for users
|
||||
* to upgrade to a newer encryption module. Do not use them as the primary
|
||||
* encryption module. They will be removed in a future release.
|
||||
* The encryption modules are used when dealing with passwords. This determines
|
||||
* how the passwords are stored in the databases.
|
||||
*
|
||||
* The first encryption module loaded is the primary encryption module. All new
|
||||
* passwords are encrypted by this module. Old passwords encrypted with another
|
||||
* encryption method are automatically re-encrypted with the primary encryption
|
||||
* module the next time the user identifies.
|
||||
*/
|
||||
|
||||
#module { name = "enc_bcrypt" }
|
||||
module { name = "enc_sha256" }
|
||||
|
||||
/*
|
||||
* [DEPRECATED] Deprecated encryption modules. You can only use these for compatibility with
|
||||
* old databases and will need to load one of the above modules as your primary encryption
|
||||
* module.
|
||||
*/
|
||||
#module { name = "enc_md5" }
|
||||
#module { name = "enc_none" }
|
||||
#module { name = "enc_sha1" }
|
||||
|
||||
/*
|
||||
* enc_old is Anope's previous (broken) MD5 implementation used from 1.4.x to 1.7.16.
|
||||
* If your databases were made using that module, load it here to allow conversion to the primary
|
||||
* encryption method.
|
||||
* enc_sha2
|
||||
*
|
||||
* Provides support for encrypting passwords using the HMAC-SHA-2 algorithm. See
|
||||
* https://en.wikipedia.org/wiki/SHA-2 and https://en.wikipedia.org/wiki/HMAC
|
||||
* for more information.
|
||||
*/
|
||||
#module { name = "enc_old" }
|
||||
module
|
||||
{
|
||||
name = "enc_sha2"
|
||||
|
||||
/** The sub-algorithm to use. Can be set to sha224 for SHA-224, sha256 for
|
||||
* SHA-256, sha284 for SHA-384 or sha512 to SHA-512. Defaults to sha256.
|
||||
*/
|
||||
#algorithm = "sha256"
|
||||
}
|
||||
|
||||
/*
|
||||
* [EXTRA] enc_argon2
|
||||
*
|
||||
* Provides support for encrypting passwords using the Argon2 algorithm. See
|
||||
* https://en.wikipedia.org/wiki/Argon2 for more information.
|
||||
*/
|
||||
#module
|
||||
{
|
||||
name = "enc_argon2"
|
||||
|
||||
/** The sub-algorithm to use. Can be set to argon2d for Argon2d, argon2i for
|
||||
* Argon2i, or argon2id for Argon2id. Defaults to argon2id.
|
||||
*/
|
||||
#algorithm = "argon2id"
|
||||
|
||||
/** The memory hardness in kibibytes of the Argon2 algorithm. Defaults to
|
||||
* 128 mebibytes.
|
||||
*/
|
||||
#memory_cost = 121072
|
||||
|
||||
/** The time hardness (iterations) of the Argon2 algorithm. Defaults to 3.
|
||||
*/
|
||||
#time_cost = 3
|
||||
|
||||
/** The amount of parallel threads to use when encrypting passwords.
|
||||
* Defaults to 1.
|
||||
*/
|
||||
#parallelism = 1
|
||||
|
||||
/** The length in bytes of an Argon2 hash. Defaults to 32. */
|
||||
#hash_length = 32
|
||||
|
||||
/** The length in bytes of an Argon2 salt. Defaults to 32. */
|
||||
#salt_length = 32
|
||||
}
|
||||
|
||||
/*
|
||||
* enc_bcrypt
|
||||
*
|
||||
* Provides support for encrypting passwords using the Bcrypt algorithm. See
|
||||
* https://en.wikipedia.org/wiki/Bcrypt for more information.
|
||||
*/
|
||||
#module
|
||||
{
|
||||
name = "enc_bcrypt"
|
||||
|
||||
/** The number of Bcrypt rounds to perform on passwords. Can be set to any
|
||||
* number between 10 and 32 but higher numbers are more CPU intensive and
|
||||
* may impact performance.
|
||||
*/
|
||||
#rounds = 10
|
||||
}
|
||||
|
||||
/*
|
||||
* [EXTRA] enc_posix
|
||||
*
|
||||
* Provides verify-only support for passwords encrypted using the POSIX crypt()
|
||||
* function. Load this if you are migratign from another services packages such
|
||||
* as Atheme. See https://en.wikipedia.org/wiki/Crypt_(C) for more information.
|
||||
*
|
||||
* You must load another encryption method before this to re-encrypt passwords
|
||||
* with when a user logs in.
|
||||
*/
|
||||
#module { name = "enc_posix" }
|
||||
|
||||
/*
|
||||
* [DEPRECATED] enc_md5, enc_none, enc_old, 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
|
||||
* and will be removed in a future version of Anope. Only load them if you are
|
||||
* upgrading from a previous version of Anope that used them.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You must load another encryption method before this to re-encrypt passwords
|
||||
* with when a user logs in.
|
||||
*/
|
||||
#module { name = "enc_md5" }
|
||||
#module { name = "enc_none" }
|
||||
#module { name = "enc_old" }
|
||||
#module { name = "enc_sha1" }
|
||||
#module { name = "enc_sha256" }
|
||||
|
||||
/* Extra (optional) modules. */
|
||||
include
|
||||
|
||||
@@ -183,7 +183,7 @@ module
|
||||
/*
|
||||
* The maximum number of entries a single bad words list can have.
|
||||
*/
|
||||
badwordsmax = 32
|
||||
badwordsmax = 50
|
||||
|
||||
/*
|
||||
* If set, BotServ will use case sensitive checking for badwords.
|
||||
|
||||
@@ -114,15 +114,15 @@ module
|
||||
|
||||
/*
|
||||
* The maximum number of entries on a channel's access list.
|
||||
* If not set, the default is 1024. This can be set to 0 for unlimited.
|
||||
* If not set, the default is 1000. This can be set to 0 for unlimited.
|
||||
*/
|
||||
accessmax = 1024
|
||||
accessmax = 1000
|
||||
|
||||
/*
|
||||
* The length of time ChanServ stays in a channel after kicking a user from a channel they are not
|
||||
* permitted to be in. This only occurs when the user is the only one in the channel.
|
||||
*/
|
||||
inhabit = 15s
|
||||
inhabit = 1m
|
||||
|
||||
/*
|
||||
* Allow only IRC Operators to use ChanServ.
|
||||
@@ -913,7 +913,7 @@ module
|
||||
/*
|
||||
* The maximum number of entries on a channel's autokick list.
|
||||
*/
|
||||
autokickmax = 32
|
||||
autokickmax = 50
|
||||
|
||||
/*
|
||||
* The default reason for an autokick if none is given.
|
||||
@@ -1126,7 +1126,7 @@ module
|
||||
*
|
||||
* This directive is optional.
|
||||
*/
|
||||
max = 32
|
||||
max = 50
|
||||
}
|
||||
command { service = "ChanServ"; name = "MODE"; command = "chanserv/mode"; group = "chanserv/management"; }
|
||||
|
||||
@@ -1247,7 +1247,7 @@ command { service = "ChanServ"; name = "SET NOEXPIRE"; command = "chanserv/saset
|
||||
*/
|
||||
module { name = "cs_set_misc" }
|
||||
command { service = "ChanServ"; name = "SET URL"; command = "chanserv/set/misc"; misc_description = _("Associate a URL with the channel"); }
|
||||
command { service = "ChanServ"; name = "SET EMAIL"; command = "chanserv/set/misc"; misc_description = _("Associate an E-mail address with the channel"); }
|
||||
command { service = "ChanServ"; name = "SET EMAIL"; command = "chanserv/set/misc"; misc_description = _("Associate an email address with the channel"); }
|
||||
|
||||
/*
|
||||
* cs_status
|
||||
|
||||
@@ -113,3 +113,29 @@ command { service = "Global"; name = "HELP"; command = "generic/help"; }
|
||||
*/
|
||||
module { name = "gl_global" }
|
||||
command { service = "Global"; name = "GLOBAL"; command = "global/global"; permission = "global/global"; }
|
||||
|
||||
/*
|
||||
* gl_queue
|
||||
*
|
||||
* Provides the command global/queue.
|
||||
*
|
||||
* Used for queuing messages to send to every online user.
|
||||
*/
|
||||
module
|
||||
{
|
||||
name = "gl_queue"
|
||||
|
||||
/* The maximum number of messages in a message queue. Defaults to 10. */
|
||||
maxqueue = 10
|
||||
}
|
||||
command { service = "Global"; name = "QUEUE"; command = "global/queue"; permission = "global/queue"; }
|
||||
|
||||
/*
|
||||
* gl_server
|
||||
*
|
||||
* Provides the command global/server.
|
||||
*
|
||||
* Used for sending a message to every online user on a server.
|
||||
*/
|
||||
module { name = "gl_server" }
|
||||
command { service = "Global"; name = "SERVER"; command = "global/server"; permission = "global/server"; }
|
||||
|
||||
@@ -83,7 +83,7 @@ module
|
||||
*
|
||||
* This directive is optional, but recommended.
|
||||
*/
|
||||
senddelay = 3s
|
||||
senddelay = 30s
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -146,7 +146,7 @@ module
|
||||
*
|
||||
* This directive is optional.
|
||||
*/
|
||||
max = 32
|
||||
max = 50
|
||||
}
|
||||
command { service = "MemoServ"; name = "IGNORE"; command = "memoserv/ignore"; }
|
||||
|
||||
|
||||
+19
-14
@@ -59,7 +59,7 @@ module { name = "help" }
|
||||
* SOA record information.
|
||||
*/
|
||||
|
||||
/* E-mail address of the DNS administrator. */
|
||||
/* Email address of the DNS administrator. */
|
||||
admin = "admin@example.com"
|
||||
|
||||
/* This should be the names of the public facing nameservers serving the records. */
|
||||
@@ -254,7 +254,7 @@ module { name = "help" }
|
||||
}
|
||||
|
||||
/*
|
||||
* ldap_authentication [EXTRA]
|
||||
* ldap_authentication
|
||||
*
|
||||
* This module allows many commands such as IDENTIFY, RELEASE, RECOVER, GHOST, etc. use
|
||||
* LDAP to authenticate users. Requires ldap.
|
||||
@@ -314,7 +314,7 @@ module { name = "help" }
|
||||
}
|
||||
|
||||
/*
|
||||
* ldap_oper [EXTRA]
|
||||
* ldap_oper
|
||||
*
|
||||
* This module dynamically ties users to Anope opertypes when they identify
|
||||
* via LDAP group membership. Requires ldap.
|
||||
@@ -580,7 +580,8 @@ module { name = "sasl" }
|
||||
name = "ssl_gnutls"
|
||||
|
||||
/*
|
||||
* An optional certificate and key for ssl_gnutls to give to the uplink.
|
||||
* An optional certificate and key for ssl_gnutls to give to the uplink. All
|
||||
* paths are relative to the config directory.
|
||||
*
|
||||
* You can generate your own certificate and key pair by using:
|
||||
*
|
||||
@@ -588,8 +589,8 @@ module { name = "sasl" }
|
||||
* certtool --generate-self-signed --load-privkey privkey.pem --outfile fullchain.pem
|
||||
*
|
||||
*/
|
||||
cert = "data/fullchain.pem"
|
||||
key = "data/privkey.pem"
|
||||
cert = "fullchain.pem"
|
||||
key = "privkey.pem"
|
||||
|
||||
/*
|
||||
* Diffie-Hellman parameters to use when acting as a server. This is only
|
||||
@@ -602,7 +603,7 @@ module { name = "sasl" }
|
||||
* certtool --generate-dh-params --bits 2048 --outfile dhparams.pem
|
||||
*
|
||||
*/
|
||||
# dhparams = "data/dhparams.pem"
|
||||
#dhparams = "dhparams.pem"
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -620,14 +621,15 @@ module { name = "sasl" }
|
||||
|
||||
/*
|
||||
* An optional certificate and key for ssl_openssl to give to the uplink.
|
||||
* All paths are relative to the config directory.
|
||||
*
|
||||
* You can generate your own certificate and key pair by using:
|
||||
*
|
||||
* openssl genrsa -out privkey.pem 2048
|
||||
* openssl req -new -x509 -key privkey.pem -out fullchain.pem -days 1095
|
||||
*/
|
||||
cert = "data/fullchain.pem"
|
||||
key = "data/privkey.pem"
|
||||
cert = "fullchain.pem"
|
||||
key = "privkey.pem"
|
||||
|
||||
/*
|
||||
* If you wish to increase security you can disable support for older
|
||||
@@ -640,7 +642,7 @@ module { name = "sasl" }
|
||||
}
|
||||
|
||||
/*
|
||||
* sql_authentication [EXTRA]
|
||||
* sql_authentication
|
||||
*
|
||||
* This module allows authenticating users against an external SQL database using a custom
|
||||
* query.
|
||||
@@ -691,7 +693,7 @@ module { name = "sasl" }
|
||||
}
|
||||
|
||||
/*
|
||||
* sql_log [EXTRA]
|
||||
* sql_log
|
||||
*
|
||||
* This module adds an additional target option to log{} blocks
|
||||
* that allows logging Service's logs to SQL. To log to SQL, add
|
||||
@@ -711,7 +713,7 @@ module { name = "sasl" }
|
||||
#module { name = "sql_log" }
|
||||
|
||||
/*
|
||||
* sql_oper [EXTRA]
|
||||
* sql_oper
|
||||
*
|
||||
* This module allows granting users services operator privileges and possibly IRC Operator
|
||||
* privileges based on an external SQL database using a custom query.
|
||||
@@ -772,8 +774,11 @@ module { name = "sasl" }
|
||||
/* Web server to use. */
|
||||
server = "httpd/main";
|
||||
|
||||
/* Template to use. */
|
||||
template = "default";
|
||||
/*
|
||||
* The directory containing the webcpanel templates. This is relative to the
|
||||
* data directory.
|
||||
*/
|
||||
template_dir = "webcpanel/templates/default";
|
||||
|
||||
/* Page title. */
|
||||
title = "Anope IRC Services";
|
||||
|
||||
@@ -66,9 +66,9 @@ module
|
||||
client = "NickServ"
|
||||
|
||||
/*
|
||||
* Force users to give an e-mail address when they register a nick.
|
||||
* Requires users to give an email address when they register a nick.
|
||||
*
|
||||
* This directive defaults to "yes" and is recommended to be enabled. This is required if e-mail registration is enabled.
|
||||
* This directive defaults to "yes" and is recommended to be enabled.
|
||||
*/
|
||||
forceemail = yes
|
||||
|
||||
@@ -97,7 +97,7 @@ module
|
||||
* - kill_immed: Kill nick immediately if not identified, this one overrides both the killprotect
|
||||
* and kill_quick options and the killprotect option must be specified with this one
|
||||
* - ns_private: Hide the nick from NickServ's LIST command
|
||||
* - hide_email: Hide the nick's e-mail address from NickServ's INFO command
|
||||
* - hide_email: Hide the nick's email address from NickServ's INFO command
|
||||
* - hide_mask: Hide the nick's last or current user@host from NickServ's INFO command
|
||||
* - hide_status: Hide the nick's services operator access status from NickServ's INFO command
|
||||
* - hide_quit: Hide the nick's last quit message from NickServ's INFO command
|
||||
@@ -119,7 +119,7 @@ module
|
||||
* The minimum length of time between consecutive uses of NickServ's REGISTER command. This
|
||||
* directive is optional, but recommended. If not set, this restriction will be disabled.
|
||||
*/
|
||||
regdelay = 30s
|
||||
regdelay = 5m
|
||||
|
||||
/*
|
||||
* The length of time before a nick's registration expires.
|
||||
@@ -273,6 +273,7 @@ command { service = "NickServ"; name = "AJOIN"; command = "nickserv/ajoin"; }
|
||||
*/
|
||||
module { name = "ns_alist" }
|
||||
command { service = "NickServ"; name = "ALIST"; command = "nickserv/alist"; }
|
||||
command { service = "NickServ"; name = "ACCESS"; command = "nickserv/alist"; hide = true; }
|
||||
|
||||
/*
|
||||
* ns_cert
|
||||
@@ -329,7 +330,7 @@ module
|
||||
*
|
||||
* This directive is optional, but recommended. If not set or set to 0, no limits will be applied.
|
||||
*/
|
||||
maxaliases = 16
|
||||
maxaliases = 10
|
||||
|
||||
/*
|
||||
* If set, the NickServ GROUP command won't allow any group changes. This is recommended to
|
||||
@@ -467,7 +468,7 @@ module
|
||||
*
|
||||
* This directive is optional.
|
||||
*/
|
||||
#nickregdelay = 30s
|
||||
nickregdelay = 15s
|
||||
|
||||
/*
|
||||
* The length of time a user using an unconfirmed account has
|
||||
@@ -622,7 +623,7 @@ command { service = "NickServ"; name = "UPDATE"; command = "nickserv/update"; }
|
||||
name = "ns_maxemail"
|
||||
|
||||
/*
|
||||
* The limit to how many registered nicks can use the same e-mail address. If set to 0 or left
|
||||
* The limit to how many registered nicks can use the same email address. If set to 0 or left
|
||||
* commented, there will be no limit enforced when registering new accounts or using
|
||||
* /msg NickServ SET EMAIL.
|
||||
*/
|
||||
|
||||
@@ -235,15 +235,15 @@ serverinfo
|
||||
|
||||
/*
|
||||
* The filename containing the Anope process ID. The path is relative to the
|
||||
* services root directory.
|
||||
* data directory.
|
||||
*/
|
||||
pid = "data/anope.pid"
|
||||
pid = "anope.pid"
|
||||
|
||||
/*
|
||||
* The filename containing the Message of the Day. The path is relative to the
|
||||
* services root directory.
|
||||
* config directory.
|
||||
*/
|
||||
motd = "conf/motd.txt"
|
||||
motd = "motd.txt"
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+39
-3
@@ -1,5 +1,41 @@
|
||||
Anope Version 2.1.3-git
|
||||
-----------------------
|
||||
Anope Version 2.1.4
|
||||
-------------------
|
||||
Added a check for a non-deprecated encryption module on start.
|
||||
Added a way for protocol modules to report an error to the uplink.
|
||||
Added more account settings to the webcpanel.
|
||||
Added self-test functionality for all encryption modules.
|
||||
Added support for challenge authentication on InspIRCd.
|
||||
Added support for importing databases from Atheme.
|
||||
Added support for sending client tags on UnrealIRCd.
|
||||
Added support for the InspIRCd 1206 (v4) protocol.
|
||||
Added the --nopid option to disable writing a pid file.
|
||||
Added the enc_argon2 module to encrypt passwords with Argon2.
|
||||
Added the enc_sha2 module to encrypt passwords with HMAC-SHA-2.
|
||||
Added the global/queue command for queueing multi-line messages.
|
||||
Added the global/server command for sending messages to an individual server.
|
||||
Added the PASSWORD category to operserv/stats to view password encryption methods.
|
||||
Added the verify-only enc_posix module to validate passwords from Atheme that were encrypted with Argon2.
|
||||
Changed nickserv/drop to use confirmation codes to confirm a nickname drop.
|
||||
Changed various paths to be relative to the data and config directories.
|
||||
Converted some IRCDProto member functions to variables.
|
||||
Converted the enc_md5, enc_none, enc_old, enc_sha1, and enc_sha256 modules to be verify-only.
|
||||
Deduplicated page headers and footers in the webcpanel templates.
|
||||
Deprecated the enc_sha256 module.
|
||||
Fixed inconsistent spelling/casing of email, vhost, and vident.
|
||||
Fixed various bugs in the inspircd module.
|
||||
Improved portability of email sending.
|
||||
Improved protocol debug messages.
|
||||
Improved the performance and reliability of internal conversion logic.
|
||||
Improved the randomness of randomly generated numbers.
|
||||
Refactored the enc_bcrypt module and exposed it as an encryption context.
|
||||
Removed several duplicate translation strings.
|
||||
Replaced the custom MD5 implementation in enc_md5 with a vendored one.
|
||||
Replaced the custom SHA256 implementation in enc_sha256 with a vendored one.
|
||||
The ldap_authentication, ldap_oper, sql_authentication, sql_log, and sql_oper modules are now always enabled.
|
||||
|
||||
Anope Version 2.1.3
|
||||
-------------------
|
||||
Added alternate command suggestions when a user runs an invalid command.
|
||||
Added support for the IRCv3 +draft/channel-context tag.
|
||||
Added support for the IRCv3 +draft/reply tag.
|
||||
Allow using more than one fingerprint in an oper block.
|
||||
@@ -17,7 +53,7 @@ Reworked how messages are sent in protocol modules to allow sending message tags
|
||||
Anope Version 2.1.2
|
||||
-------------------
|
||||
Bumped the minimum OpenSSL version to 1.1.0.
|
||||
Bumped the minumum GnuTLS version to 3.0.0.
|
||||
Bumped the minimum GnuTLS version to 3.0.0.
|
||||
Disabled SSLv3 support in the m_ssl_openssl module.
|
||||
Modernized mutex and thread code to use Modern C++.
|
||||
Normalised the program exit codes.
|
||||
|
||||
+24
-2
@@ -1,5 +1,27 @@
|
||||
Anope Version 2.1.3-git
|
||||
-----------------------
|
||||
Anope Version 2.1.4
|
||||
-------------------
|
||||
Added the db_atheme module.
|
||||
Added the enc_argon2 module.
|
||||
Added the enc_posix module.
|
||||
Added the enc_sha2 module.
|
||||
Added the gl_queue module.
|
||||
Added the gl_server module.
|
||||
Added the global/queue operator privilege.
|
||||
Added the global/server operator privilege.
|
||||
Changed serverinfo:motd to be relative to the config directory.
|
||||
Changed serverinfo:pid to be relative to the data directory.
|
||||
Changed the default value of mail:sendmailpath to "/usr/sbin/sendmail -it".
|
||||
Changed the default value of module:accessmax for the chanserv module to 1000.
|
||||
Changed the default value of module:inhabit for the chanserv module to 1 minute.
|
||||
Changed the default value of module:max for the cs_mode module to 50.
|
||||
Changed the default value of module:max for the ms_ignore module to 50.
|
||||
Removed options:seed.
|
||||
Replaced module:template for the webcpanel module with module:template_dir.
|
||||
|
||||
|
||||
Anope Version 2.1.3
|
||||
-------------------
|
||||
Added options:didyoumeandifference.
|
||||
Added support for multiple SSL fingerprints in oper:certfp.
|
||||
Added the chanserv/cert oper privilege for modifying other user's certificate lists.
|
||||
Changed networkinfo:chanlen to default to 32 if not set.
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ Anope Multi Language Support
|
||||
po files (especially on Windows).
|
||||
|
||||
If you have finished a language file translation and you want others to use it, please send it to team@anope.org
|
||||
(don't forget to mention clearly your (nick)name, your e-mail and the language name). You'll of course get full credit for it.
|
||||
(don't forget to mention clearly your (nick)name, your email and the language name). You'll of course get full credit for it.
|
||||
|
||||
NOTE: There is no guarantee we will use your work so please do not be offended if we say no thanks.
|
||||
|
||||
|
||||
+22
-16
@@ -17,8 +17,8 @@
|
||||
#include "memo.h"
|
||||
#include "base.h"
|
||||
|
||||
typedef Anope::hash_map<NickAlias *> nickalias_map;
|
||||
typedef Anope::hash_map<NickCore *> nickcore_map;
|
||||
typedef Anope::unordered_map<NickAlias *> nickalias_map;
|
||||
typedef Anope::unordered_map<NickCore *> nickcore_map;
|
||||
typedef std::unordered_map<uint64_t, NickCore *> nickcoreid_map;
|
||||
|
||||
extern CoreExport Serialize::Checker<nickalias_map> NickAliasList;
|
||||
@@ -33,7 +33,7 @@ class CoreExport NickAlias final
|
||||
, public Extensible
|
||||
{
|
||||
Anope::string vhost_ident, vhost_host, vhost_creator;
|
||||
time_t vhost_created;
|
||||
time_t vhost_created = 0;
|
||||
|
||||
public:
|
||||
Anope::string nick;
|
||||
@@ -43,8 +43,9 @@ public:
|
||||
Anope::string last_usermask;
|
||||
/* Last uncloaked usermask, requires nickserv/auspex to see */
|
||||
Anope::string last_realhost;
|
||||
time_t time_registered;
|
||||
time_t last_seen;
|
||||
time_t time_registered = Anope::CurTime;
|
||||
time_t last_seen = Anope::CurTime;
|
||||
|
||||
/* Account this nick is tied to. Multiple nicks can be tied to a single account. */
|
||||
Serialize::Reference<NickCore> nc;
|
||||
|
||||
@@ -64,36 +65,41 @@ public:
|
||||
* @param creator Who created the vhost
|
||||
* @param time When the vhost was created
|
||||
*/
|
||||
void SetVhost(const Anope::string &ident, const Anope::string &host, const Anope::string &creator, time_t created = Anope::CurTime);
|
||||
void SetVHost(const Anope::string &ident, const Anope::string &host, const Anope::string &creator, time_t created = Anope::CurTime);
|
||||
|
||||
/** Remove a users vhost
|
||||
**/
|
||||
void RemoveVhost();
|
||||
void RemoveVHost();
|
||||
|
||||
/** Check if the user has a vhost
|
||||
* @return true or false
|
||||
*/
|
||||
bool HasVhost() const;
|
||||
bool HasVHost() const;
|
||||
|
||||
/** Retrieve the vhost ident
|
||||
* @return the ident
|
||||
*/
|
||||
const Anope::string &GetVhostIdent() const;
|
||||
const Anope::string &GetVHostIdent() const;
|
||||
|
||||
/** Retrieve the vhost host
|
||||
* @return the host
|
||||
*/
|
||||
const Anope::string &GetVhostHost() const;
|
||||
const Anope::string &GetVHostHost() const;
|
||||
|
||||
/** Retrieve the vhost mask
|
||||
* @param the mask
|
||||
*/
|
||||
Anope::string GetVHostMask() const;
|
||||
|
||||
/** Retrieve the vhost creator
|
||||
* @return the creator
|
||||
*/
|
||||
const Anope::string &GetVhostCreator() const;
|
||||
const Anope::string &GetVHostCreator() const;
|
||||
|
||||
/** Retrieve when the vhost was created
|
||||
* @return the time it was created
|
||||
*/
|
||||
time_t GetVhostCreated() const;
|
||||
time_t GetVHostCreated() const;
|
||||
|
||||
/** Finds a registered nick
|
||||
* @param nick The nick to lookup
|
||||
@@ -122,6 +128,8 @@ public:
|
||||
Anope::string email;
|
||||
/* Locale name of the language of the user. Empty means default language */
|
||||
Anope::string language;
|
||||
/* Last time an email was sent to this user */
|
||||
time_t lastmail = 0;
|
||||
MemoInfo memos;
|
||||
std::map<Anope::string, Anope::string> last_modes;
|
||||
|
||||
@@ -131,14 +139,12 @@ public:
|
||||
Serialize::Checker<std::vector<NickAlias *> > aliases;
|
||||
|
||||
/* Set if this user is a services operator. o->ot must exist. */
|
||||
Oper *o;
|
||||
Oper *o = nullptr;
|
||||
|
||||
/* Unsaved data */
|
||||
|
||||
/* Number of channels registered by this account */
|
||||
uint16_t channelcount;
|
||||
/* Last time an email was sent to this user */
|
||||
time_t lastmail;
|
||||
uint16_t channelcount = 0;
|
||||
/* Users online now logged into this account */
|
||||
std::list<User *> users;
|
||||
|
||||
|
||||
+49
-72
@@ -39,6 +39,7 @@ namespace Anope
|
||||
typedef std::string::reverse_iterator reverse_iterator;
|
||||
typedef std::string::const_reverse_iterator const_reverse_iterator;
|
||||
typedef std::string::size_type size_type;
|
||||
typedef std::string::value_type value_type;
|
||||
static const size_type npos = static_cast<size_type>(-1);
|
||||
|
||||
/**
|
||||
@@ -343,7 +344,7 @@ namespace Anope
|
||||
using multimap = std::multimap<string, T, ci::less>;
|
||||
|
||||
template<typename T>
|
||||
using hash_map = std::unordered_map<string, T, hash_ci, compare>;
|
||||
using unordered_map = std::unordered_map<string, T, hash_ci, compare>;
|
||||
|
||||
#ifndef REPRODUCIBLE_BUILD
|
||||
static const char *const compiled = __TIME__ " " __DATE__;
|
||||
@@ -366,6 +367,7 @@ namespace Anope
|
||||
* Use this unless you need very specific time checks
|
||||
*/
|
||||
extern CoreExport time_t CurTime;
|
||||
extern CoreExport long long CurTimeNs;
|
||||
|
||||
/** The debug level we are running at.
|
||||
*/
|
||||
@@ -373,7 +375,7 @@ namespace Anope
|
||||
|
||||
/** Other command line options.
|
||||
*/
|
||||
extern CoreExport bool ReadOnly, NoFork, NoThird, NoExpire, ProtocolDebug;
|
||||
extern CoreExport bool ReadOnly, NoFork, NoThird, NoPID, NoExpire, ProtocolDebug;
|
||||
|
||||
/** The root of the Anope installation. Usually ~/anope
|
||||
*/
|
||||
@@ -469,7 +471,7 @@ namespace Anope
|
||||
* @param src The source string to encrypt
|
||||
* @param dest The destination where the encrypted string is placed
|
||||
*/
|
||||
extern CoreExport void Encrypt(const Anope::string &src, Anope::string &dest);
|
||||
extern CoreExport bool Encrypt(const Anope::string &src, Anope::string &dest);
|
||||
|
||||
/** Hashes a buffer with SipHash-2-4
|
||||
* @param src The start of the buffer to hash
|
||||
@@ -534,10 +536,18 @@ namespace Anope
|
||||
*/
|
||||
extern CoreExport Anope::string NormalizeBuffer(const Anope::string &);
|
||||
|
||||
/** Main processing routine. Parses the message and takes the appropriate action.
|
||||
* @param Raw message from the uplink
|
||||
/** Parses a raw message from the uplink and calls its command handler.
|
||||
* @param message Raw message from the uplink
|
||||
*/
|
||||
extern void Process(const Anope::string &);
|
||||
extern void Process(const Anope::string &message);
|
||||
|
||||
/** Calls the command handler for an already parsed message.
|
||||
* @param source Source of the message.
|
||||
* @param command Command name.
|
||||
* @param params Any extra parameters.
|
||||
* @param tags IRCv3 message tags.
|
||||
*/
|
||||
extern CoreExport void ProcessInternal(MessageSource &src, const Anope::string &command, const std::vector<Anope::string> ¶ms, const Anope::map<Anope::string> & tags);
|
||||
|
||||
/** Does a blocking dns query and returns the first IP.
|
||||
* @param host host to look up
|
||||
@@ -558,11 +568,39 @@ namespace Anope
|
||||
*/
|
||||
extern CoreExport Anope::string Random(size_t len);
|
||||
|
||||
/** 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); }
|
||||
|
||||
}
|
||||
|
||||
/** sepstream allows for splitting token separated lists.
|
||||
@@ -590,6 +628,9 @@ public:
|
||||
*/
|
||||
sepstream(const Anope::string &source, char separator, bool allowempty = false);
|
||||
|
||||
/** Retrieves the underlying string. */
|
||||
const auto &GetString() const { return tokens; }
|
||||
|
||||
/** Fetch the next token from the stream
|
||||
* @param token The next token from the stream is placed here
|
||||
* @return True if tokens still remain, false if there are none left
|
||||
@@ -723,72 +764,6 @@ public:
|
||||
virtual ~ModuleException() noexcept = default;
|
||||
};
|
||||
|
||||
class CoreExport ConvertException final
|
||||
: public CoreException
|
||||
{
|
||||
public:
|
||||
ConvertException(const Anope::string &reason = "") : CoreException(reason) { }
|
||||
|
||||
virtual ~ConvertException() noexcept = default;
|
||||
};
|
||||
|
||||
/** Convert something to a string
|
||||
*/
|
||||
inline Anope::string stringify(const Anope::string &x)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
template<typename T> inline Anope::string stringify(const T &x)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
|
||||
if (!(stream << x))
|
||||
throw ConvertException("Stringify fail");
|
||||
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
template<typename T> inline void convert(const Anope::string &s, T &x, Anope::string &leftover, bool failIfLeftoverChars = true)
|
||||
{
|
||||
leftover.clear();
|
||||
std::istringstream i(s.str());
|
||||
char c;
|
||||
if (!(i >> x))
|
||||
throw ConvertException("Convert fail");
|
||||
if (failIfLeftoverChars)
|
||||
{
|
||||
if (i.get(c))
|
||||
throw ConvertException("Convert fail");
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string left;
|
||||
getline(i, left);
|
||||
leftover = left;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> inline void convert(const Anope::string &s, T &x, bool failIfLeftoverChars = true)
|
||||
{
|
||||
Anope::string Unused;
|
||||
convert(s, x, Unused, failIfLeftoverChars);
|
||||
}
|
||||
|
||||
template<typename T> inline T convertTo(const Anope::string &s, Anope::string &leftover, bool failIfLeftoverChars = true)
|
||||
{
|
||||
T x;
|
||||
convert(s, x, leftover, failIfLeftoverChars);
|
||||
return x;
|
||||
}
|
||||
|
||||
template<typename T> inline T convertTo(const Anope::string &s, bool failIfLeftoverChars = true)
|
||||
{
|
||||
T x;
|
||||
convert(s, x, failIfLeftoverChars);
|
||||
return x;
|
||||
}
|
||||
|
||||
/** Casts to be used instead of dynamic_cast, this uses dynamic_cast
|
||||
* for debug builds and static_cast on release builds
|
||||
* to speed up the program because dynamic_cast relies on RTTI.
|
||||
@@ -808,3 +783,5 @@ template<typename T, typename O> inline T anope_dynamic_static_cast(O ptr)
|
||||
return static_cast<T>(ptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "convert.h"
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
#include "modes.h"
|
||||
#include "serialize.h"
|
||||
|
||||
typedef Anope::hash_map<Channel *> channel_map;
|
||||
typedef Anope::unordered_map<Channel *> channel_map;
|
||||
|
||||
extern CoreExport channel_map ChannelList;
|
||||
|
||||
|
||||
+1
-9
@@ -15,7 +15,6 @@
|
||||
#include "regchannel.h"
|
||||
#include "users.h"
|
||||
#include "opertype.h"
|
||||
#include <stack>
|
||||
|
||||
namespace Configuration
|
||||
{
|
||||
@@ -47,14 +46,7 @@ namespace Configuration
|
||||
|
||||
template<typename T> T Get(const Anope::string &tag, const Anope::string &def = "") const
|
||||
{
|
||||
const Anope::string &value = this->Get<const Anope::string>(tag, def);
|
||||
if (!value.empty())
|
||||
try
|
||||
{
|
||||
return convertTo<T>(value);
|
||||
}
|
||||
catch (const ConvertException &) { }
|
||||
return T();
|
||||
return Anope::TryConvert<T>(this->Get<const Anope::string>(tag, def)).value_or(T());
|
||||
}
|
||||
|
||||
bool Set(const Anope::string &tag, const Anope::string &value);
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
*
|
||||
* (C) 2003-2024 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace Anope
|
||||
{
|
||||
/** Attempts to convert a string to any type.
|
||||
* @param in The value to convert.
|
||||
* @param leftover If non-nullptr then the location to store leftover data.
|
||||
*/
|
||||
template<typename T>
|
||||
inline std::optional<T> TryConvert(const Anope::string &in, Anope::string *leftover = nullptr)
|
||||
{
|
||||
std::istringstream tmp(in.str());
|
||||
T out;
|
||||
if (!(tmp >> out))
|
||||
return std::nullopt;
|
||||
|
||||
if (leftover)
|
||||
{
|
||||
leftover->clear();
|
||||
std::getline(tmp, leftover->str());
|
||||
}
|
||||
else
|
||||
{
|
||||
char extra;
|
||||
if (tmp >> extra)
|
||||
return std::nullopt;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/** Converts a string to any type.
|
||||
* @param in The value to convert.
|
||||
* @param def The default to use if the conversion failed.
|
||||
* @param leftover If non-nullptr then the location to store leftover data.
|
||||
*/
|
||||
template<typename T>
|
||||
inline T Convert(const Anope::string &in, T def, Anope::string *leftover = nullptr)
|
||||
{
|
||||
return TryConvert<T>(in, leftover).value_or(def);
|
||||
}
|
||||
|
||||
/** Attempts to convert any type to a string.
|
||||
* @param in The value to convert.
|
||||
*/
|
||||
template <class T>
|
||||
inline std::optional<Anope::string> TryString(const T &in)
|
||||
{
|
||||
std::ostringstream tmp;
|
||||
if (!(tmp << in))
|
||||
return std::nullopt;
|
||||
return tmp.str();
|
||||
}
|
||||
|
||||
/** No-op function that returns the string that was passed to it.
|
||||
* @param in The string to return.
|
||||
*/
|
||||
inline const string &ToString(const string &in)
|
||||
{
|
||||
return in;
|
||||
}
|
||||
|
||||
/** Converts a std::string to a string.
|
||||
* @param in The value to convert.
|
||||
*/
|
||||
inline string ToString(const std::string &in)
|
||||
{
|
||||
return in;
|
||||
}
|
||||
|
||||
/** Converts a char array to a string.
|
||||
* @param in The value to convert.
|
||||
*/
|
||||
inline string ToString(const char *in)
|
||||
{
|
||||
return string(in);
|
||||
}
|
||||
|
||||
/** Converts a char to a string.
|
||||
* @param in The value to convert.
|
||||
*/
|
||||
inline string ToString(char in)
|
||||
{
|
||||
return string(1, static_cast<string::value_type>(in));
|
||||
}
|
||||
|
||||
/** Converts an unsigned char to a string.
|
||||
* @param in The value to convert.
|
||||
*/
|
||||
inline string ToString(unsigned char in)
|
||||
{
|
||||
return string(1, static_cast<string::value_type>(in));
|
||||
}
|
||||
|
||||
/** Converts a bool to a string.
|
||||
* @param in The value to convert.
|
||||
*/
|
||||
inline string ToString(bool in)
|
||||
{
|
||||
return (in ? "1" : "0");
|
||||
}
|
||||
|
||||
/** Converts a type that std::to_string is implemented for to a string.
|
||||
* @param in The value to convert.
|
||||
*/
|
||||
template<typename Stringable>
|
||||
inline std::enable_if_t<std::is_arithmetic_v<Stringable>, string> ToString(const Stringable &in)
|
||||
{
|
||||
return std::to_string(in);
|
||||
}
|
||||
|
||||
/** Converts any type to a string.
|
||||
* @param in The value to convert.
|
||||
*/
|
||||
template <class T>
|
||||
inline std::enable_if_t<!std::is_arithmetic_v<T>, string> ToString(const T &in)
|
||||
{
|
||||
return TryString(in).value_or(Anope::string());
|
||||
}
|
||||
}
|
||||
+11
-10
@@ -65,11 +65,13 @@ namespace Language
|
||||
} // namespace Language
|
||||
|
||||
/* Commonly used language strings */
|
||||
#define CONFIRM_DROP _("Please confirm that you want to drop \002%s\002 with \002%s%s DROP %s %s\002")
|
||||
#define SERVICE_UNAVAILABLE _("Sorry, %s is temporarily unavailable.")
|
||||
#define MORE_INFO _("\002%s%s HELP %s\002 for more information.")
|
||||
#define BAD_USERHOST_MASK _("Mask must be in the form \037user\037@\037host\037.")
|
||||
#define BAD_EXPIRY_TIME _("Invalid expiry time.")
|
||||
#define USERHOST_MASK_TOO_WIDE _("%s coverage is too wide; Please use a more specific mask.")
|
||||
#define READ_ONLY_MODE _("Services are in read-only mode!")
|
||||
#define READ_ONLY_MODE _("Services are temporarily in read-only mode.")
|
||||
#define PASSWORD_INCORRECT _("Password incorrect.")
|
||||
#define ACCESS_DENIED _("Access denied.")
|
||||
#define MORE_OBSCURE_PASSWORD _("Please try again with a more obscure password. Passwords should not be\n" \
|
||||
@@ -86,7 +88,7 @@ namespace Language
|
||||
#define CHAN_X_NOT_REGISTERED _("Channel \002%s\002 isn't registered.")
|
||||
#define CHAN_X_NOT_IN_USE _("Channel \002%s\002 doesn't exist.")
|
||||
#define NICK_IDENTIFY_REQUIRED _("You must be logged into an account to use that command.")
|
||||
#define MAIL_X_INVALID _("\002%s\002 is not a valid e-mail address.")
|
||||
#define MAIL_X_INVALID _("\002%s\002 is not a valid email address.")
|
||||
#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.")
|
||||
@@ -97,7 +99,7 @@ namespace Language
|
||||
#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.")
|
||||
#define NICK_CONFIRM_INVALID _("Invalid passcode has been entered, please check the e-mail again, and retry.")
|
||||
#define NICK_CONFIRM_INVALID _("Invalid passcode has been entered, please check the email again, and retry.")
|
||||
#define CHAN_NOT_ALLOWED_TO_JOIN _("You are not permitted to be on this channel.")
|
||||
#define CHAN_X_INVALID _("Channel %s is not a valid channel.")
|
||||
#define CHAN_REACHED_CHANNEL_LIMIT _("Sorry, you have already reached your limit of \002%d\002 channels.")
|
||||
@@ -114,15 +116,14 @@ namespace Language
|
||||
"Type \002%s%s READ %zu\002 to read it.")
|
||||
#define MEMO_HAVE_NO_MEMOS _("You have no memos.")
|
||||
#define MEMO_X_HAS_NO_MEMOS _("%s has no memos.")
|
||||
#define MEMO_SEND_DISABLED _("Sorry, memo sending is temporarily disabled.")
|
||||
#define MEMO_HAVE_NO_NEW_MEMOS _("You have no new memos.")
|
||||
#define MEMO_X_HAS_NO_NEW_MEMOS _("%s has no new memos.")
|
||||
#define BOT_DOES_NOT_EXIST _("Bot \002%s\002 does not exist.")
|
||||
#define BOT_NOT_ASSIGNED _("You must assign a bot to the channel before using this command.")
|
||||
#define BOT_NOT_ON_CHANNEL _("Bot is not on channel \002%s\002.")
|
||||
#define HOST_SET_ERROR _("A vHost must be in the format of a valid hostname.")
|
||||
#define HOST_SET_IDENT_ERROR _("A vHost ident must be in the format of a valid ident.")
|
||||
#define HOST_SET_TOOLONG _("Error! The vHost is too long, please use a hostname shorter than %zu characters.")
|
||||
#define HOST_SET_IDENTTOOLONG _("Error! The vHost ident is too long, please use an ident shorter than %zu characters.")
|
||||
#define HOST_NOT_ASSIGNED _("Please contact an Operator to get a vHost assigned to this nick.")
|
||||
#define HOST_NO_VIDENT _("Your IRCd does not support vIdent's, if this is incorrect, please report this as a possible bug")
|
||||
#define HOST_SET_ERROR _("A vhost must be in the format of a valid hostname.")
|
||||
#define HOST_SET_IDENT_ERROR _("A vident must be in the format of a valid ident.")
|
||||
#define HOST_SET_TOOLONG _("Error! The vhost is too long, please use a hostname shorter than %zu characters.")
|
||||
#define HOST_SET_IDENTTOOLONG _("Error! The vident is too long, please use an ident shorter than %zu characters.")
|
||||
#define HOST_NOT_ASSIGNED _("Please contact an Operator to get a vhost assigned to this nick.")
|
||||
#define HOST_NO_VIDENT _("Your IRCd does not support vidents. If this is incorrect please report this as a possible bug.")
|
||||
|
||||
+1
-1
@@ -26,6 +26,7 @@ namespace Mail
|
||||
: public Thread
|
||||
{
|
||||
private:
|
||||
Anope::string error;
|
||||
Anope::string sendmail_path;
|
||||
Anope::string send_from;
|
||||
Anope::string mail_to;
|
||||
@@ -35,7 +36,6 @@ namespace Mail
|
||||
Anope::string content_type;
|
||||
bool dont_quote_addresses;
|
||||
|
||||
bool success = false;
|
||||
public:
|
||||
/** Construct this message. Once constructed call Thread::Start to launch the mail sending.
|
||||
* @param sf Config->SendFrom
|
||||
|
||||
+3
-3
@@ -838,12 +838,12 @@ public:
|
||||
/** Called when a vhost is deleted
|
||||
* @param na The nickalias of the vhost
|
||||
*/
|
||||
virtual void OnDeleteVhost(NickAlias *na) { throw NotImplementedException(); }
|
||||
virtual void OnDeleteVHost(NickAlias *na) { throw NotImplementedException(); }
|
||||
|
||||
/** Called when a vhost is set
|
||||
* @param na The nickalias of the vhost
|
||||
*/
|
||||
virtual void OnSetVhost(NickAlias *na) { throw NotImplementedException(); }
|
||||
virtual void OnSetVHost(NickAlias *na) { throw NotImplementedException(); }
|
||||
|
||||
/** Called when a users host changes
|
||||
* @param u The user
|
||||
@@ -1081,7 +1081,7 @@ enum Implementation
|
||||
I_OnUserLogin, I_OnNickLogout, I_OnNickRegister, I_OnNickConfirm, I_OnNickSuspend, I_OnNickUnsuspended, I_OnDelNick, I_OnNickCoreCreate,
|
||||
I_OnDelCore, I_OnChangeCoreDisplay, I_OnNickClearCert,
|
||||
I_OnNickAddCert, I_OnNickEraseCert, I_OnNickInfo, I_OnBotInfo, I_OnCheckAuthentication, I_OnNickUpdate,
|
||||
I_OnFingerprint, I_OnUserAway, I_OnInvite, I_OnDeleteVhost, I_OnSetVhost, I_OnSetDisplayedHost, I_OnMemoSend, I_OnMemoDel,
|
||||
I_OnFingerprint, I_OnUserAway, I_OnInvite, I_OnDeleteVHost, I_OnSetVHost, I_OnSetDisplayedHost, I_OnMemoSend, I_OnMemoDel,
|
||||
I_OnChannelModeSet, I_OnChannelModeUnset, I_OnUserModeSet, I_OnUserModeUnset, I_OnChannelModeAdd, I_OnUserModeAdd,
|
||||
I_OnMLock, I_OnUnMLock, I_OnModuleLoad, I_OnModuleUnload, I_OnServerSync, I_OnUplinkSync, I_OnBotPrivmsg, I_OnBotNotice,
|
||||
I_OnPrivmsg, I_OnLog, I_OnLogMessage, I_OnDnsRequest, I_OnCheckModes, I_OnChannelSync, I_OnSetCorrectModes,
|
||||
|
||||
@@ -13,26 +13,135 @@
|
||||
|
||||
namespace Encryption
|
||||
{
|
||||
typedef std::pair<const unsigned char *, size_t> Hash;
|
||||
typedef std::pair<const uint32_t *, size_t> IV;
|
||||
|
||||
/** Base class for encryption contexts. */
|
||||
class Context
|
||||
{
|
||||
public:
|
||||
virtual ~Context() = default;
|
||||
|
||||
/** Updates the encryption context with the specified data.
|
||||
* @param str The data to update the context with.
|
||||
*/
|
||||
inline void Update(const Anope::string &str)
|
||||
{
|
||||
Update(reinterpret_cast<const unsigned char *>(str.c_str()), str.length());
|
||||
}
|
||||
|
||||
/** Updates the encryption context with the specified data.
|
||||
* @param data The data to update the context with.
|
||||
* @param len The length of the data.
|
||||
*/
|
||||
virtual void Update(const unsigned char *data, size_t len) = 0;
|
||||
virtual void Finalize() = 0;
|
||||
virtual Hash GetFinalizedHash() = 0;
|
||||
|
||||
/** Finalises the encryption context and returns the digest. */
|
||||
virtual Anope::string Finalize() = 0;
|
||||
};
|
||||
|
||||
/** Provider of encryption contexts. */
|
||||
class Provider
|
||||
: public Service
|
||||
{
|
||||
public:
|
||||
Provider(Module *creator, const Anope::string &sname) : Service(creator, "Encryption::Provider", sname) { }
|
||||
/** The byte size of the block cipher. */
|
||||
const size_t block_size;
|
||||
|
||||
/** The byte size of the resulting digest. */
|
||||
const size_t digest_size;
|
||||
|
||||
/** Creates a provider of encryption contexts.
|
||||
* @param creator The module that created this provider.
|
||||
* @param algorithm The name of the encryption algorithm.
|
||||
* @param bs The byte size of the block cipher.
|
||||
* @param ds The byte size of the resulting digest.
|
||||
*/
|
||||
Provider(Module *creator, const Anope::string &algorithm, size_t bs, size_t ds)
|
||||
: Service(creator, "Encryption::Provider", algorithm)
|
||||
, block_size(bs)
|
||||
, digest_size(ds)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~Provider() = default;
|
||||
|
||||
virtual Context *CreateContext(IV * = NULL) = 0;
|
||||
virtual IV GetDefaultIV() = 0;
|
||||
/** Checks whether a plain text value matches a hash created by this provider. */
|
||||
virtual bool Compare(const Anope::string &hash, const Anope::string &plain)
|
||||
{
|
||||
return !hash.empty() && hash.equals_cs(ToPrintable(Encrypt(plain)));
|
||||
}
|
||||
|
||||
/** Called on initialising a encryption provider to check it works properly. */
|
||||
void Check(const Anope::map<Anope::string> &checks)
|
||||
{
|
||||
for (const auto &[hash, plain] : checks)
|
||||
{
|
||||
if (!Compare(hash, plain))
|
||||
throw ModuleException("BUG: unable to generate " + this->name + " hashes safely! Please report this!");
|
||||
}
|
||||
Log(LOG_DEBUG) << "The " << this->name << " encryption provider appears to be working correctly.";
|
||||
}
|
||||
|
||||
/** Creates a new encryption context. */
|
||||
virtual std::unique_ptr<Context> CreateContext() = 0;
|
||||
|
||||
/** Quickly encrypts the specified values and returns the digest. */
|
||||
template<typename... Args>
|
||||
Anope::string Encrypt(Args &&...args)
|
||||
{
|
||||
auto context = CreateContext();
|
||||
context->Update(std::forward<Args>(args)...);
|
||||
return context->Finalize();
|
||||
}
|
||||
|
||||
/** Calculates the RFC 2104 hash-based message authentication code for the specified data. */
|
||||
inline Anope::string HMAC(const Anope::string &key, const Anope::string &data)
|
||||
{
|
||||
if (!block_size)
|
||||
return {};
|
||||
|
||||
auto keybuf = key.length() > block_size ? Encrypt(key) : key;
|
||||
keybuf.resize(block_size);
|
||||
|
||||
Anope::string hmac1;
|
||||
Anope::string hmac2;
|
||||
for (size_t i = 0; i < block_size; ++i)
|
||||
{
|
||||
hmac1.push_back(static_cast<char>(keybuf[i] ^ 0x5C));
|
||||
hmac2.push_back(static_cast<char>(keybuf[i] ^ 0x36));
|
||||
}
|
||||
hmac2.append(data);
|
||||
hmac1.append(Encrypt(hmac2));
|
||||
|
||||
return Encrypt(hmac1);
|
||||
}
|
||||
|
||||
/** Converts a hash to its printable form. */
|
||||
virtual Anope::string ToPrintable(const Anope::string &hash)
|
||||
{
|
||||
return Anope::Hex(hash);
|
||||
}
|
||||
};
|
||||
|
||||
/** Helper template for creating simple providers of encryption contexts. */
|
||||
template <typename T>
|
||||
class SimpleProvider final
|
||||
: public Provider
|
||||
{
|
||||
public:
|
||||
/** Creates a simple provider of encryption contexts.
|
||||
* @param creator The module that created this provider.
|
||||
* @param algorithm The name of the encryption algorithm.
|
||||
* @param bs The byte size of the block cipher.
|
||||
* @param ds The byte size of the resulting digest.
|
||||
*/
|
||||
SimpleProvider(Module *creator, const Anope::string &algorithm, size_t bs, size_t ds)
|
||||
: Provider(creator, algorithm, bs, ds)
|
||||
{
|
||||
}
|
||||
|
||||
/** @copydoc Encryption::Provider::CreateContext. */
|
||||
std::unique_ptr<Context> CreateContext() override
|
||||
{
|
||||
return std::make_unique<T>();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
*
|
||||
* (C) 2003-2024 Anope Team
|
||||
* Contact us at team@anope.org
|
||||
*
|
||||
* Please read COPYING and README for further details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
class HostRequest
|
||||
{
|
||||
protected:
|
||||
HostRequest() = default;
|
||||
|
||||
public:
|
||||
Anope::string nick;
|
||||
Anope::string ident;
|
||||
Anope::string host;
|
||||
time_t time = 0;
|
||||
|
||||
virtual ~HostRequest() = default;
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
*
|
||||
* (C) 2003-2024 Anope Team
|
||||
* Contact us at team@anope.org
|
||||
*
|
||||
* Please read COPYING and README for further details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
class OperInfo
|
||||
{
|
||||
protected:
|
||||
OperInfo() = default;
|
||||
|
||||
OperInfo(const Anope::string &t, const Anope::string &i, const Anope::string &a, time_t c)
|
||||
: target(t)
|
||||
, info(i)
|
||||
, adder(a)
|
||||
, created(c)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
Anope::string target;
|
||||
Anope::string info;
|
||||
Anope::string adder;
|
||||
time_t created = 0;
|
||||
|
||||
virtual ~OperInfo() = default;
|
||||
};
|
||||
|
||||
class OperInfoList
|
||||
: public Serialize::Checker<std::vector<OperInfo *>>
|
||||
{
|
||||
public:
|
||||
OperInfoList()
|
||||
: Serialize::Checker<std::vector<OperInfo *>>("OperInfo")
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~OperInfoList()
|
||||
{
|
||||
for (auto *info : *(*this))
|
||||
delete info;
|
||||
}
|
||||
|
||||
virtual OperInfo *Create() = 0;
|
||||
};
|
||||
@@ -8,21 +8,68 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define GLOBAL_NO_MESSAGE _("You do not have any messages queued and did not specify a message to send.")
|
||||
#define GLOBAL_QUEUE_CONFLICT _("You can not send a single message while you have messages queued.")
|
||||
|
||||
class GlobalService
|
||||
: public Service
|
||||
{
|
||||
public:
|
||||
GlobalService(Module *m) : Service(m, "GlobalService", "Global")
|
||||
GlobalService(Module *m)
|
||||
: Service(m, "GlobalService", "Global")
|
||||
{
|
||||
}
|
||||
|
||||
/** Retrieves the bot which sends global messages unless otherwise specified. */
|
||||
virtual Reference<BotInfo> GetDefaultSender() = 0;
|
||||
virtual Reference<BotInfo> GetDefaultSender() const = 0;
|
||||
|
||||
/** Send out a global message to all users
|
||||
* @param sender Our client which should send the global
|
||||
* @param source The sender of the global
|
||||
* @param message The message
|
||||
/** Clears any queued messages for the specified account.
|
||||
* @param nc The account to clear queued messages for.
|
||||
*/
|
||||
virtual void SendGlobal(BotInfo *sender, const Anope::string &source, const Anope::string &message) = 0;
|
||||
virtual void ClearQueue(NickCore *nc) = 0;
|
||||
|
||||
/** Retrieves the size of the messages queue for the specified user.
|
||||
* @param nc The account to count queued messages for.
|
||||
*/
|
||||
inline size_t CountQueue(NickCore* nc) const
|
||||
{
|
||||
auto *q = GetQueue(nc);
|
||||
return q ? q->size() : 0;
|
||||
}
|
||||
|
||||
/** Retrieves the messages queue for the specified user.
|
||||
* @param nc The account to retrieve queued messages for.
|
||||
*/
|
||||
virtual const std::vector<Anope::string> *GetQueue(NickCore* nc) const = 0;
|
||||
|
||||
/** Queues a message to be sent later.
|
||||
* @param nc The account to queue the message for.
|
||||
* @param message The message to queue.
|
||||
* @return The new number of messages in the queue.
|
||||
*/
|
||||
virtual size_t Queue(NickCore *nc, const Anope::string &message) = 0;
|
||||
|
||||
/** Sends a single message to all users on the network.
|
||||
* @param message The message to send.
|
||||
* @param source If non-nullptr then the source of the message.
|
||||
* @param sender If non-nullptr then the bot to send the message from.
|
||||
* @param server If non-nullptr then the server to send messages to.
|
||||
* @return If the message was sent then true; otherwise, false.
|
||||
*/
|
||||
virtual bool SendSingle(const Anope::string &message, CommandSource *source = nullptr, BotInfo *sender = nullptr, Server *server = nullptr) = 0;
|
||||
|
||||
/** Sends a message queue to all users on the network.
|
||||
* @param source The source of the message.
|
||||
* @param sender If non-nullptr then the bot to send the message from.
|
||||
* @param server If non-nullptr then the server to send messages to.
|
||||
* @return If the message queue was sent then true; otherwise, false.
|
||||
*/
|
||||
virtual bool SendQueue(CommandSource &source, BotInfo *sender = nullptr, Server *server = nullptr) = 0;
|
||||
|
||||
/** Unqueues a message from the message queue.
|
||||
* @param nc The account to unqueue the message from.
|
||||
* @param idx The index of the item to remove.
|
||||
* @return Whether the message was removed from the queue.
|
||||
*/
|
||||
virtual bool Unqueue(NickCore *nc, size_t idx) = 0;
|
||||
};
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace SQL
|
||||
{
|
||||
|
||||
@@ -126,13 +128,12 @@ namespace SQL
|
||||
|
||||
template<typename T> void SetValue(const Anope::string &key, const T &value, bool escape = true)
|
||||
{
|
||||
try
|
||||
{
|
||||
Anope::string string_value = stringify(value);
|
||||
this->parameters[key].data = string_value;
|
||||
this->parameters[key].escape = escape;
|
||||
}
|
||||
catch (const ConvertException &ex) { }
|
||||
auto str = Anope::TryString(value);
|
||||
if (!str.has_value())
|
||||
return;
|
||||
|
||||
this->parameters[key].data = str.value();
|
||||
this->parameters[key].escape = escape;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+40
-19
@@ -16,6 +16,17 @@
|
||||
#include "service.h"
|
||||
#include "modes.h"
|
||||
|
||||
/** Thrown when a protocol error happens. */
|
||||
class CoreExport ProtocolException final
|
||||
: public ModuleException
|
||||
{
|
||||
public:
|
||||
ProtocolException(const Anope::string &message)
|
||||
: ModuleException(message)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/* Encapsulates the IRCd protocol we are speaking. */
|
||||
class CoreExport IRCDProto
|
||||
: public Service
|
||||
@@ -99,16 +110,37 @@ public:
|
||||
/** Can we ask the server to unban a user? */
|
||||
bool CanClearBans = false;
|
||||
|
||||
/* The maximum number of modes we are allowed to set with one MODE command */
|
||||
unsigned MaxModes = 3;
|
||||
/* The maximum length of a channel name. */
|
||||
size_t MaxChannel = 0;
|
||||
|
||||
/* The maximum length of a hostname. */
|
||||
size_t MaxHost = 0;
|
||||
|
||||
/* The maximum number of bytes a line may have */
|
||||
unsigned MaxLine = 512;
|
||||
size_t MaxLine = 512;
|
||||
|
||||
/* The maximum number of modes we are allowed to set with one MODE command */
|
||||
size_t MaxModes = 3;
|
||||
|
||||
/* The maximum length of a nickname. */
|
||||
size_t MaxNick = 0;
|
||||
|
||||
/* 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();
|
||||
|
||||
/** Extracts a timestamp from a string. */
|
||||
virtual time_t ExtractTimestamp(const Anope::string &str);
|
||||
|
||||
/** Sends an error to the uplink before disconnecting.
|
||||
* @param reason The error message.
|
||||
*/
|
||||
virtual void SendError(const Anope::string &reason);
|
||||
|
||||
/** Sets the server in NOOP mode. If NOOP mode is enabled, no users
|
||||
* will be able to oper on the server.
|
||||
* @param s The server
|
||||
@@ -127,8 +159,8 @@ public:
|
||||
* @param vident The ident to set
|
||||
* @param vhost The vhost to set
|
||||
*/
|
||||
virtual void SendVhost(User *u, const Anope::string &vident, const Anope::string &vhost) { }
|
||||
virtual void SendVhostDel(User *) { }
|
||||
virtual void SendVHost(User *u, const Anope::string &vident, const Anope::string &vhost) { }
|
||||
virtual void SendVHostDel(User *) { }
|
||||
|
||||
/** Sets an akill. This is a recursive function that can be called multiple times
|
||||
* for the same xline, but for different users, if the xline is not one that can be
|
||||
@@ -164,14 +196,14 @@ public:
|
||||
template <typename... Args>
|
||||
void SendMode(const MessageSource &source, Channel *chan, const Anope::string &modes, Args &&...args)
|
||||
{
|
||||
SendModeInternal(source, chan, modes, { stringify(args)... });
|
||||
SendModeInternal(source, chan, modes, { Anope::ToString(args)... });
|
||||
}
|
||||
|
||||
virtual void SendModeInternal(const MessageSource &source, User *u, const Anope::string &modes, const std::vector<Anope::string> &values);
|
||||
template <typename... Args>
|
||||
void SendMode(const MessageSource &source, User *u, const Anope::string &modes, Args &&...args)
|
||||
{
|
||||
SendModeInternal(source, u, modes, { stringify(args)... });
|
||||
SendModeInternal(source, u, modes, { Anope::ToString(args)... });
|
||||
}
|
||||
|
||||
/** Introduces a client to the rest of the network
|
||||
@@ -258,7 +290,7 @@ public:
|
||||
template <typename... Args>
|
||||
void SendNumeric(int numeric, const Anope::string &dest, Args &&...args)
|
||||
{
|
||||
SendNumericInternal(numeric, dest, { stringify(args)... });
|
||||
SendNumericInternal(numeric, dest, { Anope::ToString(args)... });
|
||||
}
|
||||
|
||||
virtual void SendLogin(User *u, NickAlias *na) = 0;
|
||||
@@ -294,17 +326,6 @@ public:
|
||||
|
||||
virtual Anope::string NormalizeMask(const Anope::string &mask);
|
||||
|
||||
/** Retrieves the maximum length of a channel name. */
|
||||
virtual size_t GetMaxChannel();
|
||||
|
||||
/** Retrieves the maximum length of a hostname. */
|
||||
virtual size_t GetMaxHost();
|
||||
|
||||
/** Retrieves the maximum length of a nickname. */
|
||||
virtual size_t GetMaxNick();
|
||||
|
||||
/** Retrieves the maximum length of a username. */
|
||||
virtual size_t GetMaxUser();
|
||||
};
|
||||
|
||||
class CoreExport MessageSource final
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#include "serialize.h"
|
||||
#include "bots.h"
|
||||
|
||||
typedef Anope::hash_map<ChannelInfo *> registered_channel_map;
|
||||
typedef Anope::unordered_map<ChannelInfo *> registered_channel_map;
|
||||
|
||||
extern CoreExport Serialize::Checker<registered_channel_map> RegisteredChannelList;
|
||||
|
||||
|
||||
+23
-29
@@ -11,39 +11,33 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdarg>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <algorithm>
|
||||
#include <bitset>
|
||||
#include <deque>
|
||||
#include <exception>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#ifndef _WIN32
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "defs.h"
|
||||
#include "sysconf.h"
|
||||
|
||||
#define BUFSIZE 1024
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstdarg>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
/* Pull in the various bits of STL */
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
#include <exception>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <bitset>
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
#include "defs.h"
|
||||
|
||||
#define _(x) x
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
// Whether Anope was built in debug mode.
|
||||
#cmakedefine01 DEBUG_BUILD
|
||||
|
||||
// Whether the clock_gettime() function is available.
|
||||
#cmakedefine01 HAVE_CLOCK_GETTIME
|
||||
|
||||
// Whether Anope was built with localization support.
|
||||
#cmakedefine01 HAVE_LOCALIZATION
|
||||
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ class CoreExport Timer
|
||||
private:
|
||||
/** The owner of the timer, if any
|
||||
*/
|
||||
Module *owner;
|
||||
Module *owner = nullptr;
|
||||
|
||||
/** The triggering time
|
||||
*/
|
||||
|
||||
+4
-4
@@ -23,25 +23,25 @@ namespace Uplink
|
||||
template<typename... Args>
|
||||
void Send(const Anope::map<Anope::string> &tags, const MessageSource &source, const Anope::string &command, Args &&...args)
|
||||
{
|
||||
SendInternal(tags, source, command, { stringify(args)... });
|
||||
SendInternal(tags, source, command, { Anope::ToString(args)... });
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void Send(const Anope::map<Anope::string> &tags, const Anope::string &command, Args &&...args)
|
||||
{
|
||||
SendInternal(tags, Me, command, { stringify(args)... });
|
||||
SendInternal(tags, Me, command, { Anope::ToString(args)... });
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void Send(const MessageSource &source, const Anope::string &command, Args &&...args)
|
||||
{
|
||||
SendInternal({}, source, command, { stringify(args)... });
|
||||
SendInternal({}, source, command, { Anope::ToString(args)... });
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void Send(const Anope::string &command, Args &&...args)
|
||||
{
|
||||
SendInternal({}, Me, command, { stringify(args)... });
|
||||
SendInternal({}, Me, command, { Anope::ToString(args)... });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@
|
||||
#include "account.h"
|
||||
#include "sockets.h"
|
||||
|
||||
typedef Anope::hash_map<User *> user_map;
|
||||
typedef Anope::unordered_map<User *> user_map;
|
||||
|
||||
extern CoreExport user_map UserListByNick, UserListByUID;
|
||||
|
||||
|
||||
+456
-413
File diff suppressed because it is too large
Load Diff
@@ -19,18 +19,13 @@ if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../conanbuildinfo.cmake")
|
||||
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/extra/${NAME}.cpp" DESTINATION "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
endfunction()
|
||||
|
||||
enable_extra("enc_argon2" "ARGON2")
|
||||
enable_extra("mysql" "LIBMYSQLCLIENT")
|
||||
enable_extra("regex_pcre2" "PCRE2")
|
||||
enable_extra("sqlite" "SQLITE3")
|
||||
enable_extra("ssl_openssl" "OPENSSL")
|
||||
# this uses Wldap so should always be available
|
||||
copy_extra("ldap")
|
||||
# these don't actually have extra dependencies, but require a module which does
|
||||
copy_extra("sql_authentication")
|
||||
copy_extra("sql_log")
|
||||
copy_extra("sql_oper")
|
||||
copy_extra("ldap_authentication")
|
||||
copy_extra("ldap_oper")
|
||||
|
||||
# Package extra dlls
|
||||
file(GLOB EXTRA_DLLS "${Anope_SOURCE_DIR}/extradll/bin/*.dll" "${Anope_SOURCE_DIR}/extradll/lib/*.dll")
|
||||
|
||||
@@ -67,12 +67,8 @@ public:
|
||||
|
||||
Anope::string Limit;
|
||||
unsigned limit = 0;
|
||||
try
|
||||
{
|
||||
if (c->GetParam("LIMIT", Limit))
|
||||
limit = convertTo<unsigned>(Limit);
|
||||
}
|
||||
catch (const ConvertException &) { }
|
||||
if (c->GetParam("LIMIT", Limit))
|
||||
limit = Anope::Convert<unsigned>(Limit, limit);
|
||||
|
||||
/* Should we be invited? */
|
||||
if (c->HasMode("INVITE") || (limit && c->users.size() >= limit))
|
||||
|
||||
@@ -28,7 +28,7 @@ public:
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
{
|
||||
source.Reply(_("Sorry, bot assignment is temporarily disabled."));
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ public:
|
||||
{
|
||||
if (Anope::ReadOnly)
|
||||
{
|
||||
source.Reply(_("Sorry, bot assignment is temporarily disabled."));
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ public:
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
{
|
||||
source.Reply(_("Sorry, bot modification is temporarily disabled."));
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -222,7 +222,7 @@ private:
|
||||
|
||||
const BadWord *b = bw->GetBadWord(Number - 1);
|
||||
ListFormatter::ListEntry entry;
|
||||
entry["Number"] = stringify(Number);
|
||||
entry["Number"] = Anope::ToString(Number);
|
||||
entry["Word"] = b->word;
|
||||
entry["Type"] = b->type == BW_SINGLE ? "(SINGLE)" : (b->type == BW_START ? "(START)" : (b->type == BW_END ? "(END)" : ""));
|
||||
this->list.AddEntry(entry);
|
||||
@@ -241,7 +241,7 @@ private:
|
||||
continue;
|
||||
|
||||
ListFormatter::ListEntry entry;
|
||||
entry["Number"] = stringify(i + 1);
|
||||
entry["Number"] = Anope::ToString(i + 1);
|
||||
entry["Word"] = b->word;
|
||||
entry["Type"] = b->type == BW_SINGLE ? "(SINGLE)" : (b->type == BW_START ? "(START)" : (b->type == BW_END ? "(END)" : ""));
|
||||
list.AddEntry(entry);
|
||||
@@ -407,7 +407,7 @@ public:
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
{
|
||||
source.Reply(_("Sorry, bad words list modification is temporarily disabled."));
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+13
-13
@@ -28,21 +28,21 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
if (nick.length() > IRCD->GetMaxNick())
|
||||
if (nick.length() > IRCD->MaxNick)
|
||||
{
|
||||
source.Reply(_("Bot nicks may only be %zu characters long."), IRCD->GetMaxNick());
|
||||
source.Reply(_("Bot nicks may only be %zu characters long."), IRCD->MaxNick);
|
||||
return;
|
||||
}
|
||||
|
||||
if (user.length() > IRCD->GetMaxUser())
|
||||
if (user.length() > IRCD->MaxUser)
|
||||
{
|
||||
source.Reply(_("Bot idents may only be %zu characters long."), IRCD->GetMaxUser());
|
||||
source.Reply(_("Bot idents may only be %zu characters long."), IRCD->MaxUser);
|
||||
return;
|
||||
}
|
||||
|
||||
if (host.length() > IRCD->GetMaxHost())
|
||||
if (host.length() > IRCD->MaxHost)
|
||||
{
|
||||
source.Reply(_("Bot hosts may only be %zu characters long."), IRCD->GetMaxHost());
|
||||
source.Reply(_("Bot hosts may only be %zu characters long."), IRCD->MaxHost);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -118,21 +118,21 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
if (nick.length() > IRCD->GetMaxNick())
|
||||
if (nick.length() > IRCD->MaxNick)
|
||||
{
|
||||
source.Reply(_("Bot nicks may only be %zu characters long."), IRCD->GetMaxNick());
|
||||
source.Reply(_("Bot nicks may only be %zu characters long."), IRCD->MaxNick);
|
||||
return;
|
||||
}
|
||||
|
||||
if (user.length() > IRCD->GetMaxUser())
|
||||
if (user.length() > IRCD->MaxUser)
|
||||
{
|
||||
source.Reply(_("Bot idents may only be %zu characters long."), IRCD->GetMaxUser());
|
||||
source.Reply(_("Bot idents may only be %zu characters long."), IRCD->MaxUser);
|
||||
return;
|
||||
}
|
||||
|
||||
if (host.length() > IRCD->GetMaxHost())
|
||||
if (host.length() > IRCD->MaxHost)
|
||||
{
|
||||
source.Reply(_("Bot hosts may only be %zu characters long."), IRCD->GetMaxHost()
|
||||
source.Reply(_("Bot hosts may only be %zu characters long."), IRCD->MaxHost
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -276,7 +276,7 @@ public:
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
{
|
||||
source.Reply(_("Sorry, bot modification is temporarily disabled."));
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ public:
|
||||
info[_("Real name")] = bi->realname;
|
||||
info[_("Created")] = Anope::strftime(bi->created, source.GetAccount());
|
||||
info[_("Options")] = bi->oper_only ? _("Private") : _("None");
|
||||
info[_("Used on")] = stringify(bi->GetChannelCount()) + " channel(s)";
|
||||
info[_("Used on")] = Anope::ToString(bi->GetChannelCount()) + " channel(s)";
|
||||
|
||||
FOREACH_MOD(OnBotInfo, (source, bi, ci, info));
|
||||
|
||||
|
||||
+24
-78
@@ -106,11 +106,10 @@ struct KickerDataImpl final
|
||||
data["ttb"] >> ttb;
|
||||
spacesepstream sep(ttb);
|
||||
for (int i = 0; sep.GetToken(tok) && i < TTB_SIZE; ++i)
|
||||
try
|
||||
{
|
||||
kd->ttb[i] = convertTo<int16_t>(tok);
|
||||
}
|
||||
catch (const ConvertException &) { }
|
||||
{
|
||||
if (auto n = Anope::TryConvert<int16_t>(tok))
|
||||
kd->ttb[i] = n.value();
|
||||
}
|
||||
|
||||
kd->Check(ci);
|
||||
}
|
||||
@@ -183,7 +182,7 @@ protected:
|
||||
ci = ChannelInfo::Find(chan);
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
source.Reply(_("Sorry, kicker configuration is temporarily disabled."));
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
else if (ci == NULL)
|
||||
source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
|
||||
else if (option.empty())
|
||||
@@ -206,21 +205,13 @@ protected:
|
||||
{
|
||||
if (!ttb.empty())
|
||||
{
|
||||
int16_t i;
|
||||
|
||||
try
|
||||
{
|
||||
i = convertTo<int16_t>(ttb);
|
||||
if (i < 0)
|
||||
throw ConvertException();
|
||||
}
|
||||
catch (const ConvertException &)
|
||||
kd->ttb[ttb_idx] = Anope::Convert<int16_t>(ttb, -1);
|
||||
if (kd->ttb[ttb_idx] < 0)
|
||||
{
|
||||
kd->ttb[ttb_idx] = 0;
|
||||
source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
kd->ttb[ttb_idx] = i;
|
||||
}
|
||||
else
|
||||
kd->ttb[ttb_idx] = 0;
|
||||
@@ -386,13 +377,8 @@ public:
|
||||
|
||||
if (!ttb.empty())
|
||||
{
|
||||
try
|
||||
{
|
||||
kd->ttb[TTB_CAPS] = convertTo<int16_t>(ttb);
|
||||
if (kd->ttb[TTB_CAPS] < 0)
|
||||
throw ConvertException();
|
||||
}
|
||||
catch (const ConvertException &)
|
||||
kd->ttb[TTB_CAPS] = Anope::Convert<int16_t>(ttb, -1);
|
||||
if (kd->ttb[TTB_CAPS] < 0)
|
||||
{
|
||||
kd->ttb[TTB_CAPS] = 0;
|
||||
source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str());
|
||||
@@ -402,21 +388,11 @@ public:
|
||||
else
|
||||
kd->ttb[TTB_CAPS] = 0;
|
||||
|
||||
kd->capsmin = 10;
|
||||
try
|
||||
{
|
||||
kd->capsmin = convertTo<int16_t>(min);
|
||||
}
|
||||
catch (const ConvertException &) { }
|
||||
kd->capsmin = Anope::Convert(min, 0);
|
||||
if (kd->capsmin < 1)
|
||||
kd->capsmin = 10;
|
||||
|
||||
kd->capspercent = 25;
|
||||
try
|
||||
{
|
||||
kd->capspercent = convertTo<int16_t>(percent);
|
||||
}
|
||||
catch (const ConvertException &) { }
|
||||
kd->capspercent = Anope::Convert(percent, 0);
|
||||
if (kd->capspercent < 1 || kd->capspercent > 100)
|
||||
kd->capspercent = 25;
|
||||
|
||||
@@ -518,42 +494,25 @@ public:
|
||||
|
||||
if (!ttb.empty())
|
||||
{
|
||||
int16_t i;
|
||||
|
||||
try
|
||||
{
|
||||
i = convertTo<int16_t>(ttb);
|
||||
if (i < 0)
|
||||
throw ConvertException();
|
||||
}
|
||||
catch (const ConvertException &)
|
||||
kd->ttb[TTB_FLOOD] = Anope::Convert<int16_t>(ttb, -1);
|
||||
if (kd->ttb[TTB_FLOOD] < 0)
|
||||
{
|
||||
kd->ttb[TTB_FLOOD] = 0;
|
||||
source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
kd->ttb[TTB_FLOOD] = i;
|
||||
}
|
||||
else
|
||||
kd->ttb[TTB_FLOOD] = 0;
|
||||
|
||||
kd->floodlines = 6;
|
||||
try
|
||||
{
|
||||
kd->floodlines = convertTo<int16_t>(lines);
|
||||
}
|
||||
catch (const ConvertException &) { }
|
||||
kd->floodlines = Anope::Convert(lines, -1);
|
||||
if (kd->floodlines < 2)
|
||||
kd->floodlines = 6;
|
||||
|
||||
kd->floodsecs = 10;
|
||||
try
|
||||
{
|
||||
kd->floodsecs = convertTo<int16_t>(secs);
|
||||
}
|
||||
catch (const ConvertException &) { }
|
||||
kd->floodsecs = Anope::Convert(secs, -1);
|
||||
if (kd->floodsecs < 1)
|
||||
kd->floodsecs = 10;
|
||||
|
||||
if (kd->floodsecs > Config->GetModule(me)->Get<time_t>("keepdata"))
|
||||
kd->floodsecs = Config->GetModule(me)->Get<time_t>("keepdata");
|
||||
|
||||
@@ -651,31 +610,18 @@ public:
|
||||
|
||||
if (!ttb.empty())
|
||||
{
|
||||
int16_t i;
|
||||
|
||||
try
|
||||
{
|
||||
i = convertTo<int16_t>(ttb);
|
||||
if (i < 0)
|
||||
throw ConvertException();
|
||||
}
|
||||
catch (const ConvertException &)
|
||||
kd->ttb[TTB_REPEAT] = Anope::Convert(ttb, -1);
|
||||
if (kd->ttb[TTB_REPEAT] < 0)
|
||||
{
|
||||
kd->ttb[TTB_REPEAT] = 0;
|
||||
source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
kd->ttb[TTB_REPEAT] = i;
|
||||
}
|
||||
else
|
||||
kd->ttb[TTB_REPEAT] = 0;
|
||||
|
||||
kd->repeattimes = 3;
|
||||
try
|
||||
{
|
||||
kd->repeattimes = convertTo<int16_t>(times);
|
||||
}
|
||||
catch (const ConvertException &) { }
|
||||
kd->repeattimes = Anope::Convert<int16_t>(times, -1);
|
||||
if (kd->repeattimes < 1)
|
||||
kd->repeattimes = 3;
|
||||
|
||||
@@ -826,7 +772,7 @@ public:
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
{
|
||||
source.Reply(_("Sorry, bot option setting is temporarily disabled."));
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -892,7 +838,7 @@ public:
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
{
|
||||
source.Reply(_("Sorry, bot option setting is temporarily disabled."));
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ public:
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
{
|
||||
source.Reply(_("Sorry, changing bot options is temporarily disabled."));
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ public:
|
||||
* @param chan The channel
|
||||
*/
|
||||
ChanServTimer(Reference<BotInfo> &cs, ExtensibleItem<bool> &i, Module *m, Channel *chan)
|
||||
: Timer(m, Config->GetModule(m)->Get<time_t>("inhabit", "15s"))
|
||||
: Timer(m, Config->GetModule(m)->Get<time_t>("inhabit", "1m"))
|
||||
, ChanServ(cs)
|
||||
, inhabit(i)
|
||||
, c(chan)
|
||||
|
||||
@@ -37,18 +37,13 @@ public:
|
||||
|
||||
Anope::string AccessSerialize() const override
|
||||
{
|
||||
return stringify(this->level);
|
||||
return Anope::ToString(this->level);
|
||||
}
|
||||
|
||||
void AccessUnserialize(const Anope::string &data) override
|
||||
{
|
||||
try
|
||||
{
|
||||
this->level = convertTo<int>(data);
|
||||
}
|
||||
catch (const ConvertException &)
|
||||
{
|
||||
}
|
||||
if (auto l = Anope::TryConvert<int>(data))
|
||||
this->level = l.value();
|
||||
}
|
||||
|
||||
bool operator>(const ChanAccess &other) const override
|
||||
@@ -95,11 +90,9 @@ class CommandCSAccess final
|
||||
Privilege *p = NULL;
|
||||
int level = ACCESS_INVALID;
|
||||
|
||||
try
|
||||
{
|
||||
level = convertTo<int>(params[3]);
|
||||
}
|
||||
catch (const ConvertException &)
|
||||
if (auto lvl = Anope::TryConvert<int>(params[3]))
|
||||
level = lvl.value();
|
||||
else
|
||||
{
|
||||
p = PrivilegeManager::FindPrivilege(params[3]);
|
||||
if (p != NULL && defaultLevels[p->name])
|
||||
@@ -207,7 +200,7 @@ class CommandCSAccess final
|
||||
}
|
||||
}
|
||||
|
||||
unsigned access_max = Config->GetModule("chanserv")->Get<unsigned>("accessmax", "1024");
|
||||
unsigned access_max = Config->GetModule("chanserv")->Get<unsigned>("accessmax", "1000");
|
||||
if (access_max && ci->GetDeepAccessCount() >= access_max)
|
||||
{
|
||||
source.Reply(_("Sorry, you can only have %d access entries on a channel, including access entries from other channels."), access_max);
|
||||
@@ -402,7 +395,7 @@ class CommandCSAccess final
|
||||
}
|
||||
|
||||
ListFormatter::ListEntry entry;
|
||||
entry["Number"] = stringify(number);
|
||||
entry["Number"] = Anope::ToString(number);
|
||||
entry["Level"] = access->AccessSerialize();
|
||||
entry["Mask"] = access->Mask();
|
||||
entry["By"] = access->creator;
|
||||
@@ -442,7 +435,7 @@ class CommandCSAccess final
|
||||
}
|
||||
|
||||
ListFormatter::ListEntry entry;
|
||||
entry["Number"] = stringify(i + 1);
|
||||
entry["Number"] = Anope::ToString(i + 1);
|
||||
entry["Level"] = access->AccessSerialize();
|
||||
entry["Mask"] = access->Mask();
|
||||
entry["By"] = access->creator;
|
||||
@@ -567,7 +560,7 @@ public:
|
||||
else if (!has_access)
|
||||
source.Reply(ACCESS_DENIED);
|
||||
else if (Anope::ReadOnly && !is_list)
|
||||
source.Reply(_("Sorry, channel access list modification is temporarily disabled."));
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
else if (cmd.equals_ci("ADD"))
|
||||
this->DoAdd(source, ci, params);
|
||||
else if (cmd.equals_ci("DEL"))
|
||||
@@ -652,11 +645,9 @@ class CommandCSLevels final
|
||||
level = ACCESS_FOUNDER;
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
level = convertTo<int>(lev);
|
||||
}
|
||||
catch (const ConvertException &)
|
||||
if (auto lvl = Anope::TryConvert<int>(lev))
|
||||
level = lvl.value();
|
||||
else
|
||||
{
|
||||
this->OnSyntaxError(source, "SET");
|
||||
return;
|
||||
@@ -734,7 +725,7 @@ class CommandCSLevels final
|
||||
else if (j == ACCESS_FOUNDER)
|
||||
entry["Level"] = Language::Translate(source.GetAccount(), _("(founder only)"));
|
||||
else
|
||||
entry["Level"] = stringify(j);
|
||||
entry["Level"] = Anope::ToString(j);
|
||||
|
||||
list.AddEntry(entry);
|
||||
}
|
||||
|
||||
@@ -311,7 +311,7 @@ class CommandCSAKick final
|
||||
lastused = UNKNOWN;
|
||||
|
||||
ListFormatter::ListEntry entry;
|
||||
entry["Number"] = stringify(number);
|
||||
entry["Number"] = Anope::ToString(number);
|
||||
if (akick->nc)
|
||||
entry["Mask"] = akick->nc->display;
|
||||
else
|
||||
@@ -351,7 +351,7 @@ class CommandCSAKick final
|
||||
lastused = UNKNOWN;
|
||||
|
||||
ListFormatter::ListEntry entry;
|
||||
entry["Number"] = stringify(i + 1);
|
||||
entry["Number"] = Anope::ToString(i + 1);
|
||||
if (akick->nc)
|
||||
entry["Mask"] = akick->nc->display;
|
||||
else
|
||||
@@ -466,7 +466,7 @@ public:
|
||||
else if (!has_access)
|
||||
source.Reply(ACCESS_DENIED);
|
||||
else if (!cmd.equals_ci("LIST") && !cmd.equals_ci("VIEW") && !cmd.equals_ci("ENFORCE") && Anope::ReadOnly)
|
||||
source.Reply(_("Sorry, channel autokick list modification is temporarily disabled."));
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
else if (cmd.equals_ci("ADD"))
|
||||
this->DoAdd(source, ci, params);
|
||||
else if (cmd.equals_ci("DEL"))
|
||||
|
||||
@@ -24,7 +24,7 @@ class CommandCSClone final
|
||||
static void CopyAccess(CommandSource &source, ChannelInfo *ci, ChannelInfo *target_ci)
|
||||
{
|
||||
std::set<Anope::string> masks;
|
||||
unsigned access_max = Config->GetModule("chanserv")->Get<unsigned>("accessmax", "1024");
|
||||
unsigned access_max = Config->GetModule("chanserv")->Get<unsigned>("accessmax", "1000");
|
||||
unsigned count = 0;
|
||||
|
||||
for (unsigned i = 0; i < target_ci->GetAccessCount(); ++i)
|
||||
|
||||
@@ -20,7 +20,7 @@ private:
|
||||
public:
|
||||
CommandCSDrop(Module *creator)
|
||||
: Command(creator, "chanserv/drop", 1, 2)
|
||||
, dropcode(creator, "dropcode")
|
||||
, dropcode(creator, "channel-dropcode")
|
||||
{
|
||||
this->SetDesc(_("Cancel the registration of a channel"));
|
||||
this->SetSyntax(_("\037channel\037 [\037code\037]"));
|
||||
@@ -32,7 +32,7 @@ public:
|
||||
|
||||
if (Anope::ReadOnly && !source.HasPriv("chanserv/administration"))
|
||||
{
|
||||
source.Reply(_("Sorry, channel de-registration is temporarily disabled.")); // XXX: READ_ONLY_MODE?
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -54,12 +54,12 @@ public:
|
||||
{
|
||||
if (!code)
|
||||
{
|
||||
code = ci->Extend<Anope::string>("dropcode");
|
||||
code = ci->Extend<Anope::string>("channel-dropcode");
|
||||
*code = Anope::Random(15);
|
||||
}
|
||||
|
||||
source.Reply(_("Please confirm that you want to drop \002%s\002 with \002DROP %s %s\002"),
|
||||
chan.c_str(), chan.c_str(), code->c_str());
|
||||
source.Reply(CONFIRM_DROP, ci->name.c_str(), Config->StrictPrivmsg.c_str(),
|
||||
source.service->nick.c_str(), ci->name.c_str(), code->c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -166,14 +166,8 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
int l;
|
||||
try
|
||||
{
|
||||
l = convertTo<int>(l_str);
|
||||
if (l < 0)
|
||||
throw ConvertException();
|
||||
}
|
||||
catch (const ConvertException &)
|
||||
auto l = Anope::Convert<int>(l_str, -1);
|
||||
if (l < 0)
|
||||
{
|
||||
source.Reply(_("The limit on %s is not valid."), ci->name.c_str());
|
||||
return;
|
||||
|
||||
@@ -123,7 +123,7 @@ private:
|
||||
EntryMsg *msg = (*messages)->at(i);
|
||||
|
||||
ListFormatter::ListEntry entry;
|
||||
entry["Number"] = stringify(i + 1);
|
||||
entry["Number"] = Anope::ToString(i + 1);
|
||||
entry["Creator"] = msg->creator;
|
||||
entry["Created"] = Anope::strftime(msg->when, NULL, true);
|
||||
entry["Message"] = msg->message;
|
||||
@@ -162,21 +162,16 @@ private:
|
||||
source.Reply(_("Entry message list for \002%s\002 is empty."), ci->name.c_str());
|
||||
else
|
||||
{
|
||||
try
|
||||
auto i = Anope::Convert<unsigned>(message, 0);
|
||||
if (i > 0 && i <= (*messages)->size())
|
||||
{
|
||||
unsigned i = convertTo<unsigned>(message);
|
||||
if (i > 0 && i <= (*messages)->size())
|
||||
{
|
||||
delete (*messages)->at(i - 1);
|
||||
if ((*messages)->empty())
|
||||
ci->Shrink<EntryMessageList>("entrymsg");
|
||||
Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to remove a message";
|
||||
source.Reply(_("Entry message \002%i\002 for \002%s\002 deleted."), i, ci->name.c_str());
|
||||
}
|
||||
else
|
||||
throw ConvertException();
|
||||
delete (*messages)->at(i - 1);
|
||||
if ((*messages)->empty())
|
||||
ci->Shrink<EntryMessageList>("entrymsg");
|
||||
Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to remove a message";
|
||||
source.Reply(_("Entry message \002%i\002 for \002%s\002 deleted."), i, ci->name.c_str());
|
||||
}
|
||||
catch (const ConvertException &)
|
||||
else
|
||||
{
|
||||
source.Reply(_("Entry message \002%s\002 not found on channel \002%s\002."), message.c_str(), ci->name.c_str());
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ class CommandCSFlags final
|
||||
}
|
||||
}
|
||||
|
||||
unsigned access_max = Config->GetModule("chanserv")->Get<unsigned>("accessmax", "1024");
|
||||
unsigned access_max = Config->GetModule("chanserv")->Get<unsigned>("accessmax", "1000");
|
||||
if (access_max && ci->GetDeepAccessCount() >= access_max)
|
||||
{
|
||||
source.Reply(_("Sorry, you can only have %d access entries on a channel, including access entries from other channels."), access_max);
|
||||
@@ -331,7 +331,7 @@ class CommandCSFlags final
|
||||
|
||||
ListFormatter::ListEntry entry;
|
||||
++count;
|
||||
entry["Number"] = stringify(i + 1);
|
||||
entry["Number"] = Anope::ToString(i + 1);
|
||||
entry["Mask"] = access->Mask();
|
||||
entry["Flags"] = flags;
|
||||
entry["Creator"] = access->creator;
|
||||
@@ -411,7 +411,7 @@ public:
|
||||
if (!has_access)
|
||||
source.Reply(ACCESS_DENIED);
|
||||
else if (Anope::ReadOnly && !is_list)
|
||||
source.Reply(_("Sorry, channel access list modification is temporarily disabled."));
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
else if (is_list)
|
||||
this->DoList(source, ci, params);
|
||||
else if (cmd.equals_ci("CLEAR"))
|
||||
|
||||
@@ -58,7 +58,7 @@ public:
|
||||
|
||||
if (show_all)
|
||||
{
|
||||
info[_("Ban type")] = stringify(ci->bantype);
|
||||
info[_("Ban type")] = Anope::ToString(ci->bantype);
|
||||
}
|
||||
|
||||
FOREACH_MOD(OnChanInfo, (source, ci, info, show_all));
|
||||
|
||||
@@ -36,12 +36,10 @@ public:
|
||||
sepstream(pattern.substr(1), '-').GetToken(n1, 0);
|
||||
sepstream(pattern, '-').GetToken(n2, 1);
|
||||
|
||||
try
|
||||
{
|
||||
from = convertTo<int>(n1);
|
||||
to = convertTo<int>(n2);
|
||||
}
|
||||
catch (const ConvertException &)
|
||||
auto num1 = Anope::TryConvert<int>(n1);
|
||||
auto num2 = Anope::TryConvert<int>(n2);
|
||||
|
||||
if (!num1.has_value() || !num2.has_value())
|
||||
{
|
||||
source.Reply(LIST_INCORRECT_RANGE);
|
||||
source.Reply(_("To search for channels starting with #, search for the channel\n"
|
||||
@@ -49,6 +47,8 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
from = num1.value();
|
||||
to = num2.value();
|
||||
pattern = "*";
|
||||
}
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ public:
|
||||
const LogSetting *log = (*ls)->at(i);
|
||||
|
||||
ListFormatter::ListEntry entry;
|
||||
entry["Number"] = stringify(i + 1);
|
||||
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;
|
||||
|
||||
@@ -336,7 +336,7 @@ class CommandCSMode final
|
||||
continue;
|
||||
}
|
||||
|
||||
if (modelocks->GetMLock().size() >= Config->GetModule(this->owner)->Get<unsigned>("max", "32"))
|
||||
if (modelocks->GetMLock().size() >= Config->GetModule(this->owner)->Get<unsigned>("max", "50"))
|
||||
{
|
||||
source.Reply(_("The mode lock list of \002%s\002 is full."), ci->name.c_str());
|
||||
continue;
|
||||
@@ -716,7 +716,7 @@ class CommandCSMode final
|
||||
std::vector<Anope::string> new_params;
|
||||
new_params.push_back(params[0]);
|
||||
new_params.emplace_back("SET");
|
||||
new_params.push_back("-" + stringify(cm->mchar));
|
||||
new_params.push_back("-" + Anope::ToString(cm->mchar));
|
||||
new_params.emplace_back("*");
|
||||
this->DoSet(source, ci, new_params);
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
ChannelInfo *ci = ChannelInfo::Find(params[0]);
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
source.Reply(_("Sorry, channel registration is temporarily disabled."));
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
else if (nc->HasExt("UNCONFIRMED"))
|
||||
source.Reply(_("You must confirm your account before you can register a channel."));
|
||||
else if (chan[0] == '&')
|
||||
|
||||
@@ -19,7 +19,7 @@ enum TypeInfo
|
||||
static bool simple;
|
||||
struct SeenInfo;
|
||||
static SeenInfo *FindInfo(const Anope::string &nick);
|
||||
typedef Anope::hash_map<SeenInfo *> database_map;
|
||||
typedef Anope::unordered_map<SeenInfo *> database_map;
|
||||
database_map database;
|
||||
|
||||
struct SeenInfo final
|
||||
@@ -270,9 +270,9 @@ public:
|
||||
if (simple)
|
||||
return this->SimpleSeen(source, params);
|
||||
|
||||
if (target.length() > IRCD->GetMaxNick())
|
||||
if (target.length() > IRCD->MaxNick)
|
||||
{
|
||||
source.Reply(_("Nick too long, max length is %zu characters."), IRCD->GetMaxNick());
|
||||
source.Reply(_("Nick too long, max length is %zu characters."), IRCD->MaxNick);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -165,19 +165,16 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
int16_t new_type = convertTo<int16_t>(params[1]);
|
||||
if (new_type < 0 || new_type > 3)
|
||||
throw ConvertException("Invalid range");
|
||||
Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to change the ban type to " << new_type;
|
||||
ci->bantype = new_type;
|
||||
source.Reply(_("Ban type for channel %s is now #%d."), ci->name.c_str(), ci->bantype);
|
||||
}
|
||||
catch (const ConvertException &)
|
||||
auto new_type = Anope::Convert<int16_t>(params[1], -1);
|
||||
if (new_type < 0 || new_type > 3)
|
||||
{
|
||||
source.Reply(_("\002%s\002 is not a valid ban type."), params[1].c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to change the ban type to " << new_type;
|
||||
ci->bantype = new_type;
|
||||
source.Reply(_("Ban type for channel %s is now #%d."), ci->name.c_str(), ci->bantype);
|
||||
}
|
||||
|
||||
bool OnHelp(CommandSource &source, const Anope::string &) override
|
||||
|
||||
@@ -214,6 +214,13 @@ class CSSuspend final
|
||||
}
|
||||
};
|
||||
|
||||
void Expire(ChannelInfo *ci)
|
||||
{
|
||||
suspend.Unset(ci);
|
||||
Log(this) << "Expiring suspend for " << ci->name;
|
||||
}
|
||||
|
||||
|
||||
bool Show(CommandSource &source, const Anope::string &what) const
|
||||
{
|
||||
return source.IsOper() || std::find(show.begin(), show.end(), what) != show.end();
|
||||
@@ -259,23 +266,28 @@ public:
|
||||
|
||||
expire = false;
|
||||
|
||||
if (!si->expires)
|
||||
return;
|
||||
|
||||
if (si->expires < Anope::CurTime)
|
||||
if (!Anope::NoExpire && si->expires && si->expires < Anope::CurTime)
|
||||
{
|
||||
ci->last_used = Anope::CurTime;
|
||||
suspend.Unset(ci);
|
||||
|
||||
Log(this) << "Expiring suspend for " << ci->name;
|
||||
Expire(ci);
|
||||
}
|
||||
}
|
||||
|
||||
EventReturn OnCheckKick(User *u, Channel *c, Anope::string &mask, Anope::string &reason) override
|
||||
{
|
||||
if (u->HasMode("OPER") || !c->ci || !suspend.HasExt(c->ci))
|
||||
if (u->HasMode("OPER") || !c->ci)
|
||||
return EVENT_CONTINUE;
|
||||
|
||||
CSSuspendInfo *si = suspend.Get(c->ci);
|
||||
if (!si)
|
||||
return EVENT_CONTINUE;
|
||||
|
||||
if (!Anope::NoExpire && si->expires && si->expires < Anope::CurTime)
|
||||
{
|
||||
Expire(c->ci);
|
||||
return EVENT_CONTINUE;
|
||||
}
|
||||
|
||||
reason = Language::Translate(u, _("This channel may not be used."));
|
||||
return EVENT_STOP;
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ private:
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
{
|
||||
source.Reply(_("Sorry, channel %s list modification is temporarily disabled."), source.command.c_str());
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
unsigned access_max = Config->GetModule("chanserv")->Get<unsigned>("accessmax", "1024");
|
||||
unsigned access_max = Config->GetModule("chanserv")->Get<unsigned>("accessmax", "1000");
|
||||
if (access_max && ci->GetDeepAccessCount() >= access_max)
|
||||
{
|
||||
source.Reply(_("Sorry, you can only have %d access entries on a channel, including access entries from other channels."), access_max);
|
||||
@@ -246,7 +246,7 @@ private:
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
{
|
||||
source.Reply(_("Sorry, channel %s list modification is temporarily disabled."), source.command.c_str());
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -420,7 +420,7 @@ private:
|
||||
return;
|
||||
|
||||
ListFormatter::ListEntry entry;
|
||||
entry["Number"] = stringify(Number);
|
||||
entry["Number"] = Anope::ToString(Number);
|
||||
entry["Mask"] = a->Mask();
|
||||
entry["Description"] = a->description;
|
||||
this->list.AddEntry(entry);
|
||||
@@ -440,7 +440,7 @@ private:
|
||||
continue;
|
||||
|
||||
ListFormatter::ListEntry entry;
|
||||
entry["Number"] = stringify(i + 1);
|
||||
entry["Number"] = Anope::ToString(i + 1);
|
||||
entry["Mask"] = a->Mask();
|
||||
entry["Description"] = a->description;
|
||||
list.AddEntry(entry);
|
||||
@@ -464,7 +464,7 @@ private:
|
||||
{
|
||||
if (Anope::ReadOnly)
|
||||
{
|
||||
source.Reply(_("Sorry, channel %s list modification is temporarily disabled."), source.command.c_str());
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -52,12 +52,7 @@ public:
|
||||
{
|
||||
if (token.find("ID ") == 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
this->id = convertTo<unsigned int>(token.substr(3));
|
||||
}
|
||||
catch (const ConvertException &) { }
|
||||
|
||||
this->id = Anope::Convert(token.substr(3), 0);
|
||||
continue;
|
||||
}
|
||||
else if (token.find("DATA ") != 0)
|
||||
@@ -135,8 +130,8 @@ class DBFlatFile final
|
||||
|
||||
for (const auto &db : dbs)
|
||||
{
|
||||
const Anope::string &oldname = Anope::DataDir + "/" + db;
|
||||
Anope::string newname = Anope::DataDir + "/backups/" + db + "-" + stringify(tm->tm_year + 1900) + Anope::printf("-%02i-", tm->tm_mon + 1) + Anope::printf("%02i", tm->tm_mday);
|
||||
const auto oldname = Anope::ExpandData(db);
|
||||
const auto newname = Anope::ExpandData("backups/" + db + "-" + Anope::ToString(tm->tm_year + 1900) + Anope::printf("-%02i-", tm->tm_mon + 1) + Anope::printf("%02i", tm->tm_mday));
|
||||
|
||||
/* Backup already exists or no database to backup */
|
||||
if (Anope::IsFile(newname) || !Anope::IsFile(oldname))
|
||||
@@ -221,7 +216,7 @@ public:
|
||||
{
|
||||
std::set<Anope::string> tried_dbs;
|
||||
|
||||
const Anope::string &db_name = Anope::DataDir + "/" + Config->GetModule(this)->Get<const Anope::string>("database", "anope.db");
|
||||
const auto db_name = Anope::ExpandData(Config->GetModule(this)->Get<const Anope::string>("database", "anope.db"));
|
||||
|
||||
std::fstream fd(db_name.c_str(), std::ios_base::in | std::ios_base::binary);
|
||||
if (!fd.is_open())
|
||||
@@ -301,9 +296,9 @@ public:
|
||||
|
||||
Anope::string db_name;
|
||||
if (s_type->GetOwner())
|
||||
db_name = Anope::DataDir + "/module_" + s_type->GetOwner()->name + ".db";
|
||||
db_name = Anope::ExpandData("module_" + s_type->GetOwner()->name + ".db");
|
||||
else
|
||||
db_name = Anope::DataDir + "/" + Config->GetModule(this)->Get<const Anope::string>("database", "anope.db");
|
||||
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);
|
||||
|
||||
@@ -330,7 +325,7 @@ public:
|
||||
|
||||
for (auto &[mod, f] : databases)
|
||||
{
|
||||
const Anope::string &db_name = Anope::DataDir + "/" + (mod ? (mod->name + ".db") : Config->GetModule(this)->Get<const Anope::string>("database", "anope.db"));
|
||||
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())
|
||||
{
|
||||
@@ -372,9 +367,9 @@ public:
|
||||
|
||||
Anope::string db_name;
|
||||
if (stype->GetOwner())
|
||||
db_name = Anope::DataDir + "/module_" + stype->GetOwner()->name + ".db";
|
||||
db_name = Anope::ExpandData("module_" + stype->GetOwner()->name + ".db");
|
||||
else
|
||||
db_name = Anope::DataDir + "/" + Config->GetModule(this)->Get<const Anope::string>("database", "anope.db");
|
||||
db_name = Anope::ExpandData(Config->GetModule(this)->Get<const Anope::string>("database", "anope.db"));
|
||||
|
||||
std::fstream fd(db_name.c_str(), std::ios_base::in | std::ios_base::binary);
|
||||
if (!fd.is_open())
|
||||
|
||||
@@ -40,7 +40,7 @@ else \
|
||||
#define OLD_NI_MEMO_SIGNON 0x00000010 /* Notify of memos at signon and un-away */
|
||||
#define OLD_NI_MEMO_RECEIVE 0x00000020 /* Notify of new memos when sent */
|
||||
#define OLD_NI_PRIVATE 0x00000040 /* Don't show in LIST to non-servadmins */
|
||||
#define OLD_NI_HIDE_EMAIL 0x00000080 /* Don't show E-mail in INFO */
|
||||
#define OLD_NI_HIDE_EMAIL 0x00000080 /* Don't show email in INFO */
|
||||
#define OLD_NI_HIDE_MASK 0x00000100 /* Don't show last seen address in INFO */
|
||||
#define OLD_NI_HIDE_QUIT 0x00000200 /* Don't show last quit message in INFO */
|
||||
#define OLD_NI_KILL_QUICK 0x00000400 /* Kill in 20 seconds instead of 60 */
|
||||
@@ -152,7 +152,7 @@ static void process_mlock(ChannelInfo *ci, uint32_t lock, bool status, uint32_t
|
||||
if (cm && ml)
|
||||
{
|
||||
if (limit && mlock_info.c == 'l')
|
||||
ml->SetMLock(cm, status, stringify(*limit));
|
||||
ml->SetMLock(cm, status, Anope::ToString(*limit));
|
||||
else if (key && mlock_info.c == 'k')
|
||||
ml->SetMLock(cm, status, *key);
|
||||
else
|
||||
@@ -205,7 +205,7 @@ static Anope::string Hex(const char *data, size_t l)
|
||||
{
|
||||
const char hextable[] = "0123456789abcdef";
|
||||
|
||||
std::string rv;
|
||||
Anope::string rv;
|
||||
for (size_t i = 0; i < l; ++i)
|
||||
{
|
||||
unsigned char c = data[i];
|
||||
@@ -321,7 +321,7 @@ static dbFILE *open_db_read(const char *service, const char *filename, int versi
|
||||
int myversion;
|
||||
|
||||
f = new dbFILE;
|
||||
strscpy(f->filename, (Anope::DataDir + "/" + filename).c_str(), sizeof(f->filename));
|
||||
strscpy(f->filename, Anope::ExpandData(filename).c_str(), sizeof(f->filename));
|
||||
f->mode = 'r';
|
||||
fp = fopen(f->filename, "rb");
|
||||
if (!fp)
|
||||
@@ -690,7 +690,7 @@ static void LoadVHosts()
|
||||
continue;
|
||||
}
|
||||
|
||||
na->SetVhost(ident, host, creator, vtime);
|
||||
na->SetVHost(ident, host, creator, vtime);
|
||||
|
||||
Log() << "Loaded vhost for " << na->nick;
|
||||
}
|
||||
@@ -889,7 +889,7 @@ static void LoadChannels()
|
||||
}
|
||||
}
|
||||
else
|
||||
access->AccessUnserialize(stringify(level));
|
||||
access->AccessUnserialize(Anope::ToString(level));
|
||||
}
|
||||
|
||||
Anope::string mask;
|
||||
|
||||
@@ -162,7 +162,7 @@ public:
|
||||
|
||||
std::vector<Anope::string> args;
|
||||
args.emplace_back("HGETALL");
|
||||
args.push_back("hash:" + t->GetName() + ":" + stringify(obj->id));
|
||||
args.push_back("hash:" + t->GetName() + ":" + Anope::ToString(obj->id));
|
||||
|
||||
/* Get object attrs to clear before updating */
|
||||
redis->SendCommand(new Updater(this, t->GetName(), obj->id), args);
|
||||
@@ -248,7 +248,7 @@ public:
|
||||
|
||||
std::vector<Anope::string> args;
|
||||
args.emplace_back("HGETALL");
|
||||
args.push_back("hash:" + t->GetName() + ":" + stringify(obj->id));
|
||||
args.push_back("hash:" + t->GetName() + ":" + Anope::ToString(obj->id));
|
||||
|
||||
/* Get all of the attributes for this object */
|
||||
redis->SendCommand(new Deleter(this, t->GetName(), obj->id), args);
|
||||
@@ -278,19 +278,14 @@ void TypeLoader::OnResult(const Reply &r)
|
||||
if (reply->type != Reply::BULK)
|
||||
continue;
|
||||
|
||||
int64_t id;
|
||||
try
|
||||
{
|
||||
id = convertTo<int64_t>(reply->bulk);
|
||||
}
|
||||
catch (const ConvertException &)
|
||||
{
|
||||
auto i = Anope::TryConvert<int64_t>(reply->bulk);
|
||||
if (!i)
|
||||
continue;
|
||||
}
|
||||
|
||||
auto id = i.value();
|
||||
std::vector<Anope::string> args;
|
||||
args.emplace_back("HGETALL");
|
||||
args.push_back("hash:" + this->type + ":" + stringify(id));
|
||||
args.push_back("hash:" + this->type + ":" + Anope::ToString(id));
|
||||
|
||||
me->redis->SendCommand(new ObjectLoader(me, this->type, id), args);
|
||||
}
|
||||
@@ -364,7 +359,7 @@ void Deleter::OnResult(const Reply &r)
|
||||
|
||||
std::vector<Anope::string> args;
|
||||
args.emplace_back("DEL");
|
||||
args.push_back("hash:" + this->type + ":" + stringify(this->id));
|
||||
args.push_back("hash:" + this->type + ":" + Anope::ToString(this->id));
|
||||
|
||||
/* Delete hash object */
|
||||
me->redis->SendCommand(NULL, args);
|
||||
@@ -372,7 +367,7 @@ void Deleter::OnResult(const Reply &r)
|
||||
args.clear();
|
||||
args.emplace_back("SREM");
|
||||
args.push_back("ids:" + this->type);
|
||||
args.push_back(stringify(this->id));
|
||||
args.push_back(Anope::ToString(this->id));
|
||||
|
||||
/* Delete id from ids set */
|
||||
me->redis->SendCommand(NULL, args);
|
||||
@@ -385,7 +380,7 @@ void Deleter::OnResult(const Reply &r)
|
||||
args.clear();
|
||||
args.emplace_back("SREM");
|
||||
args.push_back("value:" + this->type + ":" + key->bulk + ":" + value->bulk);
|
||||
args.push_back(stringify(this->id));
|
||||
args.push_back(Anope::ToString(this->id));
|
||||
|
||||
/* Delete value -> object id */
|
||||
me->redis->SendCommand(NULL, args);
|
||||
@@ -428,7 +423,7 @@ void Updater::OnResult(const Reply &r)
|
||||
std::vector<Anope::string> args;
|
||||
args.emplace_back("SREM");
|
||||
args.push_back("value:" + this->type + ":" + key->bulk + ":" + value->bulk);
|
||||
args.push_back(stringify(this->id));
|
||||
args.push_back(Anope::ToString(this->id));
|
||||
|
||||
/* Delete value -> object id */
|
||||
me->redis->SendCommand(NULL, args);
|
||||
@@ -438,12 +433,12 @@ void Updater::OnResult(const Reply &r)
|
||||
std::vector<Anope::string> args;
|
||||
args.emplace_back("SADD");
|
||||
args.push_back("ids:" + this->type);
|
||||
args.push_back(stringify(obj->id));
|
||||
args.push_back(Anope::ToString(obj->id));
|
||||
me->redis->SendCommand(NULL, args);
|
||||
|
||||
args.clear();
|
||||
args.emplace_back("HMSET");
|
||||
args.push_back("hash:" + this->type + ":" + stringify(obj->id));
|
||||
args.push_back("hash:" + this->type + ":" + Anope::ToString(obj->id));
|
||||
|
||||
for (const auto &[key, value] : data.data)
|
||||
{
|
||||
@@ -454,7 +449,7 @@ void Updater::OnResult(const Reply &r)
|
||||
|
||||
args2.emplace_back("SADD");
|
||||
args2.push_back("value:" + this->type + ":" + key + ":" + value->str());
|
||||
args2.push_back(stringify(obj->id));
|
||||
args2.push_back(Anope::ToString(obj->id));
|
||||
|
||||
/* Add to value -> object id set */
|
||||
me->redis->SendCommand(NULL, args2);
|
||||
@@ -505,16 +500,11 @@ void SubscriptionListener::OnResult(const Reply &r)
|
||||
if (s_type == NULL)
|
||||
return;
|
||||
|
||||
uint64_t obj_id;
|
||||
try
|
||||
{
|
||||
obj_id = convertTo<uint64_t>(id);
|
||||
}
|
||||
catch (const ConvertException &)
|
||||
{
|
||||
auto oid = Anope::TryConvert<uint64_t>(id);
|
||||
if (!oid.has_value())
|
||||
return;
|
||||
}
|
||||
|
||||
auto obj_id = oid.value();
|
||||
if (op == "hset" || op == "hdel")
|
||||
{
|
||||
Serializable *s = s_type->objects[obj_id];
|
||||
@@ -564,7 +554,7 @@ void SubscriptionListener::OnResult(const Reply &r)
|
||||
std::vector<Anope::string> args;
|
||||
args.emplace_back("SREM");
|
||||
args.push_back("ids:" + type);
|
||||
args.push_back(stringify(s->id));
|
||||
args.push_back(Anope::ToString(s->id));
|
||||
|
||||
/* Delete object from id set */
|
||||
me->redis->SendCommand(NULL, args);
|
||||
@@ -604,7 +594,7 @@ void ModifiedObject::OnResult(const Reply &r)
|
||||
std::vector<Anope::string> args;
|
||||
args.emplace_back("SREM");
|
||||
args.push_back("value:" + st->GetName() + ":" + key + ":" + value->str());
|
||||
args.push_back(stringify(this->id));
|
||||
args.push_back(Anope::ToString(this->id));
|
||||
|
||||
/* Delete value -> object id */
|
||||
me->redis->SendCommand(NULL, args);
|
||||
@@ -633,7 +623,7 @@ void ModifiedObject::OnResult(const Reply &r)
|
||||
std::vector<Anope::string> args;
|
||||
args.emplace_back("SADD");
|
||||
args.push_back("value:" + st->GetName() + ":" + key + ":" + value->str());
|
||||
args.push_back(stringify(obj->id));
|
||||
args.push_back(Anope::ToString(obj->id));
|
||||
|
||||
/* Add to value -> object id set */
|
||||
me->redis->SendCommand(NULL, args);
|
||||
@@ -642,7 +632,7 @@ void ModifiedObject::OnResult(const Reply &r)
|
||||
std::vector<Anope::string> args;
|
||||
args.emplace_back("SADD");
|
||||
args.push_back("ids:" + st->GetName());
|
||||
args.push_back(stringify(obj->id));
|
||||
args.push_back(Anope::ToString(obj->id));
|
||||
|
||||
/* Add to type -> id set */
|
||||
me->redis->SendCommand(NULL, args);
|
||||
|
||||
@@ -209,7 +209,7 @@ public:
|
||||
return;
|
||||
Serialize::Type *s_type = obj->GetSerializableType();
|
||||
if (s_type && obj->id > 0)
|
||||
this->RunBackground("DELETE FROM `" + this->prefix + s_type->GetName() + "` WHERE `id` = " + stringify(obj->id));
|
||||
this->RunBackground("DELETE FROM `" + this->prefix + s_type->GetName() + "` WHERE `id` = " + Anope::ToString(obj->id));
|
||||
this->updated_items.erase(obj);
|
||||
}
|
||||
|
||||
@@ -240,18 +240,14 @@ public:
|
||||
data[key] << value;
|
||||
|
||||
Serializable *obj = sb->Unserialize(NULL, data);
|
||||
try
|
||||
{
|
||||
if (obj)
|
||||
obj->id = convertTo<unsigned int>(res.Get(j, "id"));
|
||||
}
|
||||
catch (const ConvertException &)
|
||||
{
|
||||
Log(this) << "Unable to convert id for object #" << j << " of type " << sb->GetName();
|
||||
}
|
||||
|
||||
if (obj)
|
||||
{
|
||||
auto oid = Anope::TryConvert<unsigned int>(res.Get(j, "id"));
|
||||
if (oid.has_value())
|
||||
obj->id = oid.value();
|
||||
else
|
||||
Log(this) << "Unable to convert id for object #" << j << " of type " << sb->GetName();
|
||||
|
||||
/* The Unserialize operation is destructive so rebuild the data for UpdateCache.
|
||||
* Also the old data may contain columns that we don't use, so we reserialize the
|
||||
* object to know for sure our cache is consistent
|
||||
|
||||
@@ -163,7 +163,7 @@ public:
|
||||
if (s_type)
|
||||
{
|
||||
if (obj->id > 0)
|
||||
this->RunQuery("DELETE FROM `" + this->prefix + s_type->GetName() + "` WHERE `id` = " + stringify(obj->id));
|
||||
this->RunQuery("DELETE FROM `" + this->prefix + s_type->GetName() + "` WHERE `id` = " + Anope::ToString(obj->id));
|
||||
s_type->objects.erase(obj->id);
|
||||
}
|
||||
this->updated_items.erase(obj);
|
||||
@@ -185,17 +185,16 @@ public:
|
||||
{
|
||||
const std::map<Anope::string, Anope::string> &row = res.Row(i);
|
||||
|
||||
unsigned int id;
|
||||
try
|
||||
{
|
||||
id = convertTo<unsigned int>(res.Get(i, "id"));
|
||||
}
|
||||
catch (const ConvertException &)
|
||||
|
||||
|
||||
auto oid = Anope::TryConvert<unsigned int>(res.Get(i, "id"));
|
||||
if (!oid.has_value())
|
||||
{
|
||||
Log(LOG_DEBUG) << "Unable to convert id from " << obj->GetName();
|
||||
continue;
|
||||
}
|
||||
|
||||
auto id = oid.value();
|
||||
if (res.Get(i, "timestamp").empty())
|
||||
{
|
||||
clear_null = true;
|
||||
@@ -237,7 +236,7 @@ public:
|
||||
else
|
||||
{
|
||||
if (!s)
|
||||
this->RunQuery("UPDATE `" + prefix + obj->GetName() + "` SET `timestamp` = " + this->SQL->FromUnixtime(obj->GetTimestamp()) + " WHERE `id` = " + stringify(id));
|
||||
this->RunQuery("UPDATE `" + prefix + obj->GetName() + "` SET `timestamp` = " + this->SQL->FromUnixtime(obj->GetTimestamp()) + " WHERE `id` = " + Anope::ToString(id));
|
||||
else
|
||||
delete s;
|
||||
}
|
||||
|
||||
+1
-1
@@ -679,7 +679,7 @@ public:
|
||||
: Manager(creator)
|
||||
, Timer(300, true)
|
||||
, serial(Anope::CurTime)
|
||||
, cur_id(rand())
|
||||
, cur_id(Anope::RandomNumber())
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -8,100 +8,154 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "module.h"
|
||||
#include "modules/encryption.h"
|
||||
|
||||
#include <climits>
|
||||
#include <random>
|
||||
|
||||
#include "bcrypt/crypt_blowfish.c"
|
||||
|
||||
class EBCRYPT final
|
||||
: public Module
|
||||
{
|
||||
unsigned int rounds;
|
||||
#include "module.h"
|
||||
#include "modules/encryption.h"
|
||||
|
||||
Anope::string Salt()
|
||||
class BCryptContext final
|
||||
: public Encryption::Context
|
||||
{
|
||||
private:
|
||||
Anope::string buffer;
|
||||
|
||||
Anope::string GenerateSalt()
|
||||
{
|
||||
static std::random_device device;
|
||||
static std::mt19937 engine(device());
|
||||
static std::uniform_int_distribution<int> dist(CHAR_MIN, CHAR_MAX);
|
||||
char entropy[16];
|
||||
for (auto &chr : entropy)
|
||||
chr = static_cast<char>(rand() % 0xFF);
|
||||
for (size_t i = 0; i < sizeof(entropy); ++i)
|
||||
entropy[i] = static_cast<char>(dist(engine));
|
||||
|
||||
char salt[32];
|
||||
if (!_crypt_gensalt_blowfish_rn("$2a$", rounds, entropy, sizeof(entropy), salt, sizeof(salt)))
|
||||
return "";
|
||||
{
|
||||
Log(LOG_DEBUG) << "Unable to generate a salt for Bcrypt: " << strerror(errno);
|
||||
return {};
|
||||
}
|
||||
return salt;
|
||||
}
|
||||
|
||||
static Anope::string Generate(const Anope::string &data, const Anope::string &salt)
|
||||
public:
|
||||
static unsigned long rounds;
|
||||
|
||||
static Anope::string Hash(const Anope::string &data, const Anope::string &salt)
|
||||
{
|
||||
char hash[64];
|
||||
_crypt_blowfish_rn(data.c_str(), salt.c_str(), hash, sizeof(hash));
|
||||
if (!_crypt_blowfish_rn(data.c_str(), salt.c_str(), hash, sizeof(hash)))
|
||||
{
|
||||
Log(LOG_DEBUG) << "Unable to generate a hash for Bcrypt: " << strerror(errno);
|
||||
return {};
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
bool Compare(const Anope::string &string, const Anope::string &hash)
|
||||
void Update(const unsigned char *data, size_t len) override
|
||||
{
|
||||
Anope::string ret = Generate(string, hash);
|
||||
if (ret.empty())
|
||||
return false;
|
||||
|
||||
return (ret == hash);
|
||||
buffer.append(reinterpret_cast<const char *>(data), len);
|
||||
}
|
||||
|
||||
public:
|
||||
EBCRYPT(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, ENCRYPTION | VENDOR),
|
||||
rounds(10)
|
||||
Anope::string Finalize() override
|
||||
{
|
||||
// Test a pre-calculated hash
|
||||
bool test = Compare("Test!", "$2a$10$x9AQFAQScY0v9KF2suqkEOepsHFrG.CXHbIXI.1F28SfSUb56A/7K");
|
||||
auto salt = GenerateSalt();
|
||||
if (salt.empty())
|
||||
return {};
|
||||
return Hash(this->buffer, salt);
|
||||
}
|
||||
};
|
||||
|
||||
Anope::string salt;
|
||||
Anope::string hash;
|
||||
// Make sure it's working
|
||||
if (!test || (salt = Salt()).empty() || (hash = Generate("Test!", salt)).empty() || !Compare("Test!", hash))
|
||||
throw ModuleException("BCrypt could not load!");
|
||||
unsigned long BCryptContext::rounds = 10;
|
||||
|
||||
class BCryptProvider final
|
||||
: public Encryption::Provider
|
||||
{
|
||||
public:
|
||||
BCryptProvider(Module *creator)
|
||||
: Encryption::Provider(creator, "bcrypt", 0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
bool Compare(const Anope::string &hash, const Anope::string &plain) override
|
||||
{
|
||||
auto newhash = BCryptContext::Hash(plain, hash);
|
||||
return !newhash.empty() && hash.equals_cs(newhash);
|
||||
}
|
||||
|
||||
std::unique_ptr<Encryption::Context> CreateContext() override
|
||||
{
|
||||
return std::make_unique<BCryptContext>();
|
||||
}
|
||||
|
||||
Anope::string ToPrintable(const Anope::string &hash) override
|
||||
{
|
||||
// The crypt_blowfish library does not expose a raw form.
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class EBCrypt final
|
||||
: public Module
|
||||
{
|
||||
private:
|
||||
BCryptProvider bcryptprovider;
|
||||
|
||||
public:
|
||||
EBCrypt(const Anope::string &modname, const Anope::string &creator)
|
||||
: Module(modname, creator, ENCRYPTION | VENDOR)
|
||||
, bcryptprovider(this)
|
||||
{
|
||||
bcryptprovider.Check({
|
||||
{ "$2a$10$c9lUAuJmTYXEfNuLOiyIp.lZTMM.Rw5qsSAyZhvGT9EC3JevkUuOu", "" },
|
||||
{ "$2a$10$YV4jDSGs0ZtQbpL6IHtNO.lt5Q.uzghIohCcnERQVBGyw7QJMfyhe", "The quick brown fox jumps over the lazy dog" },
|
||||
});
|
||||
}
|
||||
|
||||
EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) override
|
||||
{
|
||||
dest = "bcrypt:" + Generate(src, Salt());
|
||||
dest = "bcrypt:" + bcryptprovider.Encrypt(src);
|
||||
Log(LOG_DEBUG_2) << "(enc_bcrypt) hashed password from [" << src << "] to [" << dest << "]";
|
||||
return EVENT_ALLOW;
|
||||
}
|
||||
|
||||
void OnCheckAuthentication(User *, IdentifyRequest *req) override
|
||||
{
|
||||
const NickAlias *na = NickAlias::Find(req->GetAccount());
|
||||
if (na == NULL)
|
||||
const auto *na = NickAlias::Find(req->GetAccount());
|
||||
if (!na)
|
||||
return;
|
||||
NickCore *nc = na->nc;
|
||||
|
||||
size_t pos = nc->pass.find(':');
|
||||
NickCore *nc = na->nc;
|
||||
auto pos = nc->pass.find(':');
|
||||
if (pos == Anope::string::npos)
|
||||
return;
|
||||
|
||||
Anope::string hash_method(nc->pass.begin(), nc->pass.begin() + pos);
|
||||
if (hash_method != "bcrypt")
|
||||
if (!hash_method.equals_cs("bcrypt"))
|
||||
return;
|
||||
|
||||
if (Compare(req->GetPassword(), nc->pass.substr(7)))
|
||||
Anope::string hash_value(nc->pass.begin() + pos + 1, nc->pass.end());
|
||||
if (bcryptprovider.Compare(hash_value, req->GetPassword()))
|
||||
{
|
||||
/* if we are NOT the first module in the list,
|
||||
* we want to re-encrypt the pass with the new encryption
|
||||
*/
|
||||
unsigned long rounds = 0;
|
||||
|
||||
unsigned int hashrounds = 0;
|
||||
try
|
||||
{
|
||||
size_t roundspos = nc->pass.find('$', 11);
|
||||
if (roundspos == Anope::string::npos)
|
||||
throw ConvertException("Could not find hashrounds");
|
||||
// Try to extract the rounds count to check if we need to
|
||||
// re-encrypt the password.
|
||||
pos = hash_value.find('$', 4);
|
||||
if (pos != Anope::string::npos)
|
||||
rounds = Anope::Convert<unsigned long>(hash_value.substr(4, pos - 4), 0);
|
||||
|
||||
hashrounds = convertTo<unsigned int>(nc->pass.substr(11, roundspos - 11));
|
||||
}
|
||||
catch (const ConvertException &)
|
||||
{
|
||||
Log(this) << "Could not get the round size of a hash. This is probably a bug. Hash: " << nc->pass;
|
||||
}
|
||||
if (!rounds)
|
||||
Log(LOG_DEBUG) << "Unable to determine the rounds of a bcrypt hash: " << hash_value;
|
||||
|
||||
if (ModuleManager::FindFirstOf(ENCRYPTION) != this || (hashrounds && hashrounds != rounds))
|
||||
// If we are NOT the first encryption module or the Bcrypt rounds
|
||||
// are different we want to re-encrypt the password with the primary
|
||||
// encryption method.
|
||||
if (ModuleManager::FindFirstOf(ENCRYPTION) != this || (rounds && rounds != BCryptContext::rounds))
|
||||
Anope::Encrypt(req->GetPassword(), nc->pass);
|
||||
req->Success(this);
|
||||
}
|
||||
@@ -109,28 +163,20 @@ public:
|
||||
|
||||
void OnReload(Configuration::Conf *conf) override
|
||||
{
|
||||
Configuration::Block *block = conf->GetModule(this);
|
||||
rounds = block->Get<unsigned int>("rounds", "10");
|
||||
auto *block = conf->GetModule(this);
|
||||
|
||||
if (rounds == 0)
|
||||
auto rounds = block->Get<unsigned long>("rounds", "10");
|
||||
if (rounds < 10 || rounds > 32)
|
||||
{
|
||||
rounds = 10;
|
||||
Log(this) << "Rounds can't be 0! Setting ignored.";
|
||||
}
|
||||
else if (rounds < 10)
|
||||
{
|
||||
Log(this) << "10 to 12 rounds is recommended.";
|
||||
}
|
||||
else if (rounds >= 32)
|
||||
{
|
||||
rounds = 10;
|
||||
Log(this) << "The maximum number of rounds supported is 31. Ignoring setting and using 10.";
|
||||
}
|
||||
else if (rounds >= 14)
|
||||
{
|
||||
Log(this) << "Are you sure you want to use " << stringify(rounds) << " in your bcrypt settings? This is very CPU intensive! Recommended rounds is 10-12.";
|
||||
Log(this) << "Bcrypt rounds MUST be between 10 and 32 inclusive; using 10 instead of " << rounds << '.';
|
||||
BCryptContext::rounds = 10;
|
||||
return;
|
||||
}
|
||||
|
||||
if (rounds > 14)
|
||||
Log(this) << "Bcrypt rounds higher than 14 are very CPU intensive; are you sure you want to use " << rounds << '?';
|
||||
BCryptContext::rounds = rounds;
|
||||
}
|
||||
};
|
||||
|
||||
MODULE_INIT(EBCRYPT)
|
||||
MODULE_INIT(EBCrypt)
|
||||
|
||||
+29
-337
@@ -5,7 +5,7 @@
|
||||
* Contact us at team@anope.org
|
||||
*
|
||||
* Taken from IRC Services and is copyright (c) 1996-2002 Andrew Church.
|
||||
* E-mail: <achurch@achurch.org>
|
||||
* Email: <achurch@achurch.org>
|
||||
* Parts written by Andrew Kempe and others.
|
||||
* This program is free but copyrighted software; see the file COPYING for
|
||||
* details.
|
||||
@@ -14,381 +14,73 @@
|
||||
#include "module.h"
|
||||
#include "modules/encryption.h"
|
||||
|
||||
/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
|
||||
rights reserved.
|
||||
|
||||
License to copy and use this software is granted provided that it
|
||||
is identified as the "RSA Data Security, Inc. MD5 Message-Digest
|
||||
Algorithm" in all material mentioning or referencing this software
|
||||
or this function.
|
||||
|
||||
License is also granted to make and use derivative works provided
|
||||
that such works are identified as "derived from the RSA Data
|
||||
Security, Inc. MD5 Message-Digest Algorithm" in all material
|
||||
mentioning or referencing the derived work.
|
||||
|
||||
RSA Data Security, Inc. makes no representations concerning either
|
||||
the merchantability of this software or the suitability of this
|
||||
software for any particular purpose. It is provided "as is"
|
||||
without express or implied warranty of any kind.
|
||||
|
||||
These notices must be retained in any copies of any part of this
|
||||
documentation and/or software.
|
||||
*/
|
||||
|
||||
static unsigned char PADDING[64] = {
|
||||
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
/* F, G, H and I are basic MD5 functions.
|
||||
*/
|
||||
inline static unsigned F(unsigned x, unsigned y, unsigned z) { return (x & y) | (~x & z); }
|
||||
inline static unsigned G(unsigned x, unsigned y, unsigned z) { return (x & z) | (y & ~z); }
|
||||
inline static unsigned H(unsigned x, unsigned y, unsigned z) { return x ^ y ^ z; }
|
||||
inline static unsigned I(unsigned x, unsigned y, unsigned z) { return y ^ (x | ~z); }
|
||||
|
||||
/* ROTATE_LEFT rotates x left n bits.
|
||||
*/
|
||||
inline static unsigned ROTATE_LEFT(unsigned x, unsigned n) { return (x << n) | (x >> (32 - n)); }
|
||||
|
||||
/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
|
||||
* Rotation is separate from addition to prevent recomputation.
|
||||
*/
|
||||
inline static void FF(unsigned &a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned s, unsigned ac)
|
||||
{
|
||||
a += F(b, c, d) + x + ac;
|
||||
a = ROTATE_LEFT(a, s);
|
||||
a += b;
|
||||
}
|
||||
inline static void GG(unsigned &a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned s, unsigned ac)
|
||||
{
|
||||
a += G(b, c, d) + x + ac;
|
||||
a = ROTATE_LEFT(a, s);
|
||||
a += b;
|
||||
}
|
||||
inline static void HH(unsigned &a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned s, unsigned ac)
|
||||
{
|
||||
a += H(b, c, d) + x + ac;
|
||||
a = ROTATE_LEFT(a, s);
|
||||
a += b;
|
||||
}
|
||||
inline static void II(unsigned &a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned s, unsigned ac)
|
||||
{
|
||||
a += I(b, c, d) + x + ac;
|
||||
a = ROTATE_LEFT(a, s);
|
||||
a += b;
|
||||
}
|
||||
|
||||
static const uint32_t md5_iv[4] =
|
||||
{
|
||||
0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476
|
||||
};
|
||||
#include "md5/md5.c"
|
||||
|
||||
class MD5Context final
|
||||
: public Encryption::Context
|
||||
{
|
||||
unsigned state[4]; /* state (ABCD) */
|
||||
unsigned count[2]; /* number of bits, modulo 2^64 (lsb first) */
|
||||
unsigned char buffer[64]; /* input buffer */
|
||||
unsigned char digest[16]; /* final digest */
|
||||
|
||||
/* Constants for MD5Transform routine.
|
||||
*/
|
||||
enum
|
||||
{
|
||||
S11 = 7,
|
||||
S12 = 12,
|
||||
S13 = 17,
|
||||
S14 = 22,
|
||||
S21 = 5,
|
||||
S22 = 9,
|
||||
S23 = 14,
|
||||
S24 = 20,
|
||||
S31 = 4,
|
||||
S32 = 11,
|
||||
S33 = 16,
|
||||
S34 = 23,
|
||||
S41 = 6,
|
||||
S42 = 10,
|
||||
S43 = 15,
|
||||
S44 = 21
|
||||
};
|
||||
|
||||
/* MD5 basic transformation. Transforms state based on block.
|
||||
*/
|
||||
void Transform(const unsigned char block[64])
|
||||
{
|
||||
unsigned a = state[0], b = state[1], c = state[2], d = state[3], x[16];
|
||||
|
||||
Decode(x, block, 64);
|
||||
|
||||
/* Round 1 */
|
||||
FF(a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */
|
||||
FF(d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */
|
||||
FF(c, d, a, b, x[2], S13, 0x242070db); /* 3 */
|
||||
FF(b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */
|
||||
FF(a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */
|
||||
FF(d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */
|
||||
FF(c, d, a, b, x[6], S13, 0xa8304613); /* 7 */
|
||||
FF(b, c, d, a, x[7], S14, 0xfd469501); /* 8 */
|
||||
FF(a, b, c, d, x[8], S11, 0x698098d8); /* 9 */
|
||||
FF(d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */
|
||||
FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
|
||||
FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
|
||||
FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
|
||||
FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
|
||||
FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
|
||||
FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
|
||||
|
||||
/* Round 2 */
|
||||
GG(a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */
|
||||
GG(d, a, b, c, x[6], S22, 0xc040b340); /* 18 */
|
||||
GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
|
||||
GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */
|
||||
GG(a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */
|
||||
GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */
|
||||
GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
|
||||
GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */
|
||||
GG(a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */
|
||||
GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
|
||||
GG(c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */
|
||||
GG(b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */
|
||||
GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
|
||||
GG(d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */
|
||||
GG(c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */
|
||||
GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
|
||||
|
||||
/* Round 3 */
|
||||
HH(a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */
|
||||
HH(d, a, b, c, x[8], S32, 0x8771f681); /* 34 */
|
||||
HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
|
||||
HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
|
||||
HH(a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */
|
||||
HH(d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */
|
||||
HH(c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */
|
||||
HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
|
||||
HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
|
||||
HH(d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */
|
||||
HH(c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */
|
||||
HH(b, c, d, a, x[6], S34, 0x4881d05); /* 44 */
|
||||
HH(a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */
|
||||
HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
|
||||
HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
|
||||
HH(b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */
|
||||
|
||||
/* Round 4 */
|
||||
II(a, b, c, d, x[0], S41, 0xf4292244); /* 49 */
|
||||
II(d, a, b, c, x[7], S42, 0x432aff97); /* 50 */
|
||||
II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
|
||||
II(b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */
|
||||
II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
|
||||
II(d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */
|
||||
II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
|
||||
II(b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */
|
||||
II(a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */
|
||||
II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
|
||||
II(c, d, a, b, x[6], S43, 0xa3014314); /* 59 */
|
||||
II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
|
||||
II(a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */
|
||||
II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
|
||||
II(c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */
|
||||
II(b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */
|
||||
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
|
||||
/* Zeroize sensitive information. */
|
||||
memset(x, 0, sizeof(x));
|
||||
}
|
||||
|
||||
/* Encodes input (unsigned) into output (unsigned char). Assumes len is
|
||||
* a multiple of 4.
|
||||
*/
|
||||
static void Encode(unsigned char *output, unsigned *input, unsigned len)
|
||||
{
|
||||
for (unsigned i = 0, j = 0; j < len; ++i, j += 4)
|
||||
{
|
||||
output[j] = static_cast<unsigned char>(input[i] & 0xff);
|
||||
output[j + 1] = static_cast<unsigned char>((input[i] >> 8) & 0xff);
|
||||
output[j + 2] = static_cast<unsigned char>((input[i] >> 16) & 0xff);
|
||||
output[j + 3] = static_cast<unsigned char>((input[i] >> 24) & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
/* Decodes input (unsigned char) into output (unsigned). Assumes len is
|
||||
* a multiple of 4.
|
||||
*/
|
||||
static void Decode(unsigned *output, const unsigned char *input, unsigned len)
|
||||
{
|
||||
for (unsigned i = 0, j = 0; j < len; ++i, j += 4)
|
||||
output[i] = static_cast<unsigned>(input[j]) | (static_cast<unsigned>(input[j + 1]) << 8) | (static_cast<unsigned>(input[j + 2]) << 16) | (static_cast<unsigned>(input[j + 3]) << 24);
|
||||
}
|
||||
private:
|
||||
MD5_CTX context;
|
||||
|
||||
public:
|
||||
MD5Context(Encryption::IV *iv = NULL)
|
||||
MD5Context()
|
||||
{
|
||||
if (iv != NULL)
|
||||
{
|
||||
if (iv->second != 4)
|
||||
throw CoreException("Invalid IV size");
|
||||
/* Load magic initialization constants. */
|
||||
for (int i = 0; i < 4; ++i)
|
||||
this->state[i] = iv->first[i];
|
||||
}
|
||||
else
|
||||
for (int i = 0; i < 4; ++i)
|
||||
this->state[i] = md5_iv[i];
|
||||
|
||||
this->count[0] = this->count[1] = 0;
|
||||
memset(this->buffer, 0, sizeof(this->buffer));
|
||||
MD5_Init(&context);
|
||||
}
|
||||
|
||||
/* MD5 block update operation. Continues an MD5 message-digest
|
||||
* operation, processing another message block, and updating the
|
||||
* context.
|
||||
*/
|
||||
void Update(const unsigned char *input, size_t len) override
|
||||
{
|
||||
unsigned i, index, partLen;
|
||||
|
||||
/* Compute number of bytes mod 64 */
|
||||
index = (this->count[0] >> 3) & 0x3F;
|
||||
|
||||
/* Update number of bits */
|
||||
if ((this->count[0] += len << 3) < (len << 3))
|
||||
++this->count[1];
|
||||
this->count[1] += len >> 29;
|
||||
|
||||
partLen = 64 - index;
|
||||
|
||||
/* Transform as many times as possible. */
|
||||
if (len >= partLen)
|
||||
{
|
||||
memcpy(&this->buffer[index], input, partLen);
|
||||
this->Transform(this->buffer);
|
||||
|
||||
for (i = partLen; i + 63 < len; i += 64)
|
||||
this->Transform(&input[i]);
|
||||
|
||||
index = 0;
|
||||
}
|
||||
else
|
||||
i = 0;
|
||||
|
||||
/* Buffer remaining input */
|
||||
memcpy(&this->buffer[index], &input[i], len - i);
|
||||
MD5_Update(&context, input, len);
|
||||
}
|
||||
|
||||
/* MD5 finalization. Ends an MD5 message-digest opera
|
||||
* the message digest and zeroizing the context.
|
||||
*/
|
||||
void Finalize() override
|
||||
Anope::string Finalize() override
|
||||
{
|
||||
unsigned char bits[8];
|
||||
unsigned index, padLen;
|
||||
|
||||
/* Save number of bits */
|
||||
this->Encode(bits, this->count, 8);
|
||||
|
||||
/* Pad out to 56 mod 64. */
|
||||
index = (this->count[0] >> 3) & 0x3f;
|
||||
padLen = index < 56 ? 56 - index : 120 - index;
|
||||
this->Update(PADDING, padLen);
|
||||
|
||||
/* Append length (before padding) */
|
||||
this->Update(bits, 8);
|
||||
/* Store state in digest */
|
||||
this->Encode(digest, this->state, 16);
|
||||
|
||||
/* Zeroize sensitive information. */
|
||||
memset(this->state, 0, sizeof(this->state));
|
||||
memset(this->count, 0, sizeof(this->count));
|
||||
memset(this->buffer, 0, sizeof(this->buffer));
|
||||
}
|
||||
|
||||
Encryption::Hash GetFinalizedHash() override
|
||||
{
|
||||
Encryption::Hash hash;
|
||||
hash.first = this->digest;
|
||||
hash.second = sizeof(this->digest);
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
class MD5Provider final
|
||||
: public Encryption::Provider
|
||||
{
|
||||
public:
|
||||
MD5Provider(Module *creator) : Encryption::Provider(creator, "md5") { }
|
||||
|
||||
Encryption::Context *CreateContext(Encryption::IV *iv) override
|
||||
{
|
||||
return new MD5Context(iv);
|
||||
}
|
||||
|
||||
Encryption::IV GetDefaultIV() override
|
||||
{
|
||||
Encryption::IV iv;
|
||||
iv.first = md5_iv;
|
||||
iv.second = sizeof(md5_iv) / sizeof(uint32_t);
|
||||
return iv;
|
||||
unsigned char digest[16];
|
||||
MD5_Final(digest, &context);
|
||||
return Anope::string(reinterpret_cast<const char *>(&digest), sizeof(digest));
|
||||
}
|
||||
};
|
||||
|
||||
class EMD5 final
|
||||
: public Module
|
||||
{
|
||||
MD5Provider md5provider;
|
||||
private:
|
||||
Encryption::SimpleProvider<MD5Context> md5provider;
|
||||
|
||||
public:
|
||||
EMD5(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, ENCRYPTION | VENDOR),
|
||||
md5provider(this)
|
||||
EMD5(const Anope::string &modname, const Anope::string &creator)
|
||||
: Module(modname, creator, ENCRYPTION | VENDOR)
|
||||
, md5provider(this, "md5", 16, 64)
|
||||
{
|
||||
if (ModuleManager::FindFirstOf(ENCRYPTION) == this)
|
||||
throw ModuleException("enc_md5 is deprecated and can not be used as a primary encryption method");
|
||||
}
|
||||
|
||||
EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) override
|
||||
{
|
||||
MD5Context context;
|
||||
|
||||
context.Update(reinterpret_cast<const unsigned char *>(src.c_str()), src.length());
|
||||
context.Finalize();
|
||||
|
||||
Encryption::Hash hash = context.GetFinalizedHash();
|
||||
|
||||
Anope::string buf = "md5:" + Anope::Hex(reinterpret_cast<const char *>(hash.first), hash.second);
|
||||
|
||||
Log(LOG_DEBUG_2) << "(enc_md5) hashed password from [" << src << "] to [" << buf << "]";
|
||||
dest = buf;
|
||||
return EVENT_ALLOW;
|
||||
md5provider.Check({
|
||||
{ "d41d8cd98f00b204e9800998ecf8427e", "" },
|
||||
{ "9e107d9d372bb6826bd81d3542a419d6", "The quick brown fox jumps over the lazy dog" },
|
||||
});
|
||||
}
|
||||
|
||||
void OnCheckAuthentication(User *, IdentifyRequest *req) override
|
||||
{
|
||||
const NickAlias *na = NickAlias::Find(req->GetAccount());
|
||||
if (na == NULL)
|
||||
const auto *na = NickAlias::Find(req->GetAccount());
|
||||
if (!na)
|
||||
return;
|
||||
NickCore *nc = na->nc;
|
||||
|
||||
size_t pos = nc->pass.find(':');
|
||||
NickCore *nc = na->nc;
|
||||
auto 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("md5"))
|
||||
return;
|
||||
|
||||
Anope::string buf;
|
||||
this->OnEncrypt(req->GetPassword(), buf);
|
||||
if (nc->pass.equals_cs(buf))
|
||||
auto enc = "md5:" + Anope::Hex(md5provider.Encrypt(req->GetPassword()));
|
||||
if (nc->pass.equals_cs(enc))
|
||||
{
|
||||
/* if we are NOT the first module in the list,
|
||||
* we want to re-encrypt the pass with the new encryption
|
||||
*/
|
||||
// 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);
|
||||
|
||||
@@ -13,44 +13,35 @@ class ENone final
|
||||
: public Module
|
||||
{
|
||||
public:
|
||||
ENone(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, ENCRYPTION | VENDOR)
|
||||
ENone(const Anope::string &modname, const Anope::string &creator)
|
||||
: Module(modname, creator, ENCRYPTION | VENDOR)
|
||||
{
|
||||
if (ModuleManager::FindFirstOf(ENCRYPTION) == this)
|
||||
throw ModuleException("enc_none is deprecated and can not be used as a primary encryption method");
|
||||
}
|
||||
|
||||
EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) override
|
||||
{
|
||||
Anope::string buf = "plain:";
|
||||
Anope::string cpass;
|
||||
Anope::B64Encode(src, cpass);
|
||||
buf += cpass;
|
||||
Log(LOG_DEBUG_2) << "(enc_none) hashed password from [" << src << "] to [" << buf << "]";
|
||||
dest = buf;
|
||||
return EVENT_ALLOW;
|
||||
}
|
||||
|
||||
void OnCheckAuthentication(User *, IdentifyRequest *req) override
|
||||
{
|
||||
const NickAlias *na = NickAlias::Find(req->GetAccount());
|
||||
if (na == NULL)
|
||||
const auto *na = NickAlias::Find(req->GetAccount());
|
||||
if (!na)
|
||||
return;
|
||||
NickCore *nc = na->nc;
|
||||
|
||||
size_t pos = nc->pass.find(':');
|
||||
NickCore *nc = na->nc;
|
||||
auto 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("plain"))
|
||||
return;
|
||||
|
||||
Anope::string buf;
|
||||
this->OnEncrypt(req->GetPassword(), buf);
|
||||
if (nc->pass.equals_cs(buf))
|
||||
Anope::string b64pass;
|
||||
Anope::B64Encode(req->GetPassword(), b64pass);
|
||||
auto enc = "plain:" + b64pass;
|
||||
if (nc->pass.equals_cs(enc))
|
||||
{
|
||||
/* if we are NOT the first module in the list,
|
||||
* we want to re-encrypt the pass with the new encryption
|
||||
*/
|
||||
// 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);
|
||||
|
||||
@@ -12,39 +12,41 @@
|
||||
#include "module.h"
|
||||
#include "modules/encryption.h"
|
||||
|
||||
static ServiceReference<Encryption::Provider> md5("Encryption::Provider", "md5");
|
||||
|
||||
class OldMD5Provider final
|
||||
: public Encryption::Provider
|
||||
{
|
||||
public:
|
||||
OldMD5Provider(Module *creator) : Encryption::Provider(creator, "oldmd5") { }
|
||||
|
||||
Encryption::Context *CreateContext(Encryption::IV *iv) override
|
||||
{
|
||||
if (md5)
|
||||
return md5->CreateContext(iv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Encryption::IV GetDefaultIV() override
|
||||
{
|
||||
if (md5)
|
||||
return md5->GetDefaultIV();
|
||||
return Encryption::IV(static_cast<const uint32_t *>(NULL), 0);
|
||||
}
|
||||
};
|
||||
|
||||
class EOld final
|
||||
: public Module
|
||||
{
|
||||
OldMD5Provider oldmd5provider;
|
||||
private:
|
||||
ServiceReference<Encryption::Provider> md5;
|
||||
|
||||
inline static char XTOI(char c) { return c > 9 ? c - 'A' + 10 : c - '0'; }
|
||||
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),
|
||||
oldmd5provider(this)
|
||||
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");
|
||||
@@ -52,58 +54,28 @@ public:
|
||||
ModuleManager::LoadModule("enc_md5", User::Find(creator, true));
|
||||
if (!md5)
|
||||
throw ModuleException("Unable to find md5 reference");
|
||||
|
||||
}
|
||||
|
||||
EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) override
|
||||
{
|
||||
if (!md5)
|
||||
return EVENT_CONTINUE;
|
||||
|
||||
Encryption::Context *context = md5->CreateContext();
|
||||
context->Update(reinterpret_cast<const unsigned char *>(src.c_str()), src.length());
|
||||
context->Finalize();
|
||||
|
||||
Encryption::Hash hash = context->GetFinalizedHash();
|
||||
|
||||
char digest[32], digest2[16];
|
||||
memset(digest, 0, sizeof(digest));
|
||||
if (hash.second > sizeof(digest))
|
||||
throw CoreException("Hash too large");
|
||||
memcpy(digest, hash.first, hash.second);
|
||||
|
||||
for (int i = 0; i < 32; i += 2)
|
||||
digest2[i / 2] = XTOI(digest[i]) << 4 | XTOI(digest[i + 1]);
|
||||
|
||||
Anope::string buf = "oldmd5:" + Anope::Hex(digest2, sizeof(digest2));
|
||||
|
||||
Log(LOG_DEBUG_2) << "(enc_old) hashed password from [" << src << "] to [" << buf << "]";
|
||||
dest = buf;
|
||||
delete context;
|
||||
return EVENT_ALLOW;
|
||||
}
|
||||
|
||||
void OnCheckAuthentication(User *, IdentifyRequest *req) override
|
||||
{
|
||||
const NickAlias *na = NickAlias::Find(req->GetAccount());
|
||||
if (na == NULL)
|
||||
const auto *na = NickAlias::Find(req->GetAccount());
|
||||
if (!na)
|
||||
return;
|
||||
NickCore *nc = na->nc;
|
||||
|
||||
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;
|
||||
|
||||
Anope::string buf;
|
||||
this->OnEncrypt(req->GetPassword(), buf);
|
||||
if (nc->pass.equals_cs(buf))
|
||||
auto enc = EncryptInternal(req->GetPassword());
|
||||
if (!enc.empty() && nc->pass.equals_cs(enc))
|
||||
{
|
||||
/* if we are NOT the first module in the list,
|
||||
* we want to re-encrypt the pass with the new encryption
|
||||
*/
|
||||
// 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);
|
||||
|
||||
@@ -60,7 +60,6 @@ class SHA1Context final
|
||||
uint32_t state[5];
|
||||
uint32_t count[2];
|
||||
unsigned char buffer[64];
|
||||
unsigned char digest[20];
|
||||
|
||||
void Transform(const unsigned char buf[64])
|
||||
{
|
||||
@@ -108,22 +107,13 @@ class SHA1Context final
|
||||
}
|
||||
|
||||
public:
|
||||
SHA1Context(Encryption::IV *iv = NULL)
|
||||
SHA1Context()
|
||||
{
|
||||
if (iv != NULL)
|
||||
{
|
||||
if (iv->second != 5)
|
||||
throw CoreException("Invalid IV size");
|
||||
for (int i = 0; i < 5; ++i)
|
||||
this->state[i] = iv->first[i];
|
||||
}
|
||||
else
|
||||
for (int i = 0; i < 5; ++i)
|
||||
this->state[i] = sha1_iv[i];
|
||||
for (int i = 0; i < 5; ++i)
|
||||
this->state[i] = sha1_iv[i];
|
||||
|
||||
this->count[0] = this->count[1] = 0;
|
||||
memset(this->buffer, 0, sizeof(this->buffer));
|
||||
memset(this->digest, 0, sizeof(this->digest));
|
||||
}
|
||||
|
||||
void Update(const unsigned char *data, size_t len) override
|
||||
@@ -147,7 +137,7 @@ public:
|
||||
memcpy(&this->buffer[j], &data[i], len - i);
|
||||
}
|
||||
|
||||
void Finalize() override
|
||||
Anope::string Finalize() override
|
||||
{
|
||||
uint32_t i;
|
||||
unsigned char finalcount[8];
|
||||
@@ -158,8 +148,10 @@ public:
|
||||
while ((this->count[0] & 504) != 448)
|
||||
this->Update(reinterpret_cast<const unsigned char *>("\0"), 1);
|
||||
this->Update(finalcount, 8); /* Should cause a SHA1Transform() */
|
||||
unsigned char digest[20];
|
||||
memset(digest, 0, sizeof(digest));
|
||||
for (i = 0; i < 20; ++i)
|
||||
this->digest[i] = static_cast<unsigned char>((this->state[i>>2] >> ((3 - (i & 3)) * 8)) & 255);
|
||||
digest[i] = static_cast<unsigned char>((this->state[i>>2] >> ((3 - (i & 3)) * 8)) & 255);
|
||||
|
||||
/* Wipe variables */
|
||||
memset(this->buffer, 0, sizeof(this->buffer));
|
||||
@@ -168,84 +160,51 @@ public:
|
||||
memset(&finalcount, 0, sizeof(finalcount));
|
||||
|
||||
this->Transform(this->buffer);
|
||||
}
|
||||
|
||||
Encryption::Hash GetFinalizedHash() override
|
||||
{
|
||||
Encryption::Hash hash;
|
||||
hash.first = this->digest;
|
||||
hash.second = sizeof(this->digest);
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
class SHA1Provider final
|
||||
: public Encryption::Provider
|
||||
{
|
||||
public:
|
||||
SHA1Provider(Module *creator) : Encryption::Provider(creator, "sha1") { }
|
||||
|
||||
Encryption::Context *CreateContext(Encryption::IV *iv) override
|
||||
{
|
||||
return new SHA1Context(iv);
|
||||
}
|
||||
|
||||
Encryption::IV GetDefaultIV() override
|
||||
{
|
||||
Encryption::IV iv;
|
||||
iv.first = sha1_iv;
|
||||
iv.second = sizeof(sha1_iv) / sizeof(uint32_t);
|
||||
return iv;
|
||||
return Anope::string(reinterpret_cast<const char *>(&digest), sizeof(digest));
|
||||
}
|
||||
};
|
||||
|
||||
class ESHA1 final
|
||||
: public Module
|
||||
{
|
||||
SHA1Provider sha1provider;
|
||||
private:
|
||||
Encryption::SimpleProvider<SHA1Context> sha1provider;
|
||||
|
||||
public:
|
||||
ESHA1(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, ENCRYPTION | VENDOR),
|
||||
sha1provider(this)
|
||||
ESHA1(const Anope::string &modname, const Anope::string &creator)
|
||||
: Module(modname, creator, ENCRYPTION | VENDOR)
|
||||
, sha1provider(this, "sha1", 20, 64)
|
||||
{
|
||||
if (ModuleManager::FindFirstOf(ENCRYPTION) == this)
|
||||
throw ModuleException("enc_sha1 is deprecated and can not be used as a primary encryption method");
|
||||
}
|
||||
|
||||
EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) override
|
||||
{
|
||||
SHA1Context context;
|
||||
|
||||
context.Update(reinterpret_cast<const unsigned char *>(src.c_str()), src.length());
|
||||
context.Finalize();
|
||||
|
||||
Encryption::Hash hash = context.GetFinalizedHash();
|
||||
|
||||
Anope::string buf = "sha1:" + Anope::Hex(reinterpret_cast<const char *>(hash.first), hash.second);
|
||||
|
||||
Log(LOG_DEBUG_2) << "(enc_sha1) hashed password from [" << src << "] to [" << buf << "]";
|
||||
dest = buf;
|
||||
return EVENT_ALLOW;
|
||||
sha1provider.Check({
|
||||
{ "da39a3ee5e6b4b0d3255bfef95601890afd80709", "" },
|
||||
{ "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12", "The quick brown fox jumps over the lazy dog" },
|
||||
});
|
||||
}
|
||||
|
||||
void OnCheckAuthentication(User *, IdentifyRequest *req) override
|
||||
{
|
||||
const NickAlias *na = NickAlias::Find(req->GetAccount());
|
||||
if (na == NULL)
|
||||
const auto *na = NickAlias::Find(req->GetAccount());
|
||||
if (!na)
|
||||
return;
|
||||
NickCore *nc = na->nc;
|
||||
|
||||
size_t pos = nc->pass.find(':');
|
||||
NickCore *nc = na->nc;
|
||||
auto 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("sha1"))
|
||||
return;
|
||||
|
||||
Anope::string buf;
|
||||
this->OnEncrypt(req->GetPassword(), buf);
|
||||
if (nc->pass.equals_cs(buf))
|
||||
auto enc = "sha1:" + Anope::Hex(sha1provider.Encrypt(req->GetPassword()));
|
||||
if (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);
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
/* Module for providing SHA-2 hashing
|
||||
*
|
||||
* (C) 2003-2024 Anope Team
|
||||
* Contact us at team@anope.org
|
||||
*
|
||||
* This program is free but copyrighted software; see the file COPYING for
|
||||
* details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <climits>
|
||||
#include <random>
|
||||
|
||||
#include "sha2/sha2.c"
|
||||
|
||||
#include "module.h"
|
||||
#include "modules/encryption.h"
|
||||
|
||||
template <typename SHAContext,
|
||||
void (* SHAInit)(SHAContext *),
|
||||
void (* SHAUpdate)(SHAContext *, const unsigned char *, unsigned int),
|
||||
void (* SHAFinal)(SHAContext *, unsigned char *)>
|
||||
class SHA2Context final
|
||||
: public Encryption::Context
|
||||
{
|
||||
private:
|
||||
SHAContext context;
|
||||
const size_t digest_size;
|
||||
|
||||
public:
|
||||
SHA2Context(size_t ds)
|
||||
: digest_size(ds)
|
||||
{
|
||||
SHAInit(&context);
|
||||
}
|
||||
|
||||
void Update(const unsigned char *data, size_t len) override
|
||||
{
|
||||
SHAUpdate(&context, data, len);
|
||||
}
|
||||
|
||||
Anope::string Finalize() override
|
||||
{
|
||||
std::vector<unsigned char> digest(digest_size);
|
||||
SHAFinal(&context, digest.data());
|
||||
return Anope::string(reinterpret_cast<const char *>(digest.data()), digest.size());
|
||||
}
|
||||
};
|
||||
|
||||
template <typename SHAContext,
|
||||
void (* SHAInit)(SHAContext *),
|
||||
void (* SHAUpdate)(SHAContext *, const unsigned char *, unsigned int),
|
||||
void (* SHAFinal)(SHAContext *, unsigned char *)>
|
||||
class SHA2Provider final
|
||||
: public Encryption::Provider
|
||||
{
|
||||
public:
|
||||
SHA2Provider(Module *creator, const Anope::string &algorithm, size_t bs, size_t ds)
|
||||
: Encryption::Provider(creator, algorithm, bs, ds)
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<Encryption::Context> CreateContext() override
|
||||
{
|
||||
return std::make_unique<SHA2Context<SHAContext, SHAInit, SHAUpdate, SHAFinal>>(this->digest_size);
|
||||
}
|
||||
};
|
||||
|
||||
class ESHA2 final
|
||||
: public Module
|
||||
{
|
||||
private:
|
||||
Encryption::Provider *defaultprovider = nullptr;
|
||||
SHA2Provider<sha224_ctx, sha224_init, sha224_update, sha224_final> sha224provider;
|
||||
SHA2Provider<sha256_ctx, sha256_init, sha256_update, sha256_final> sha256provider;
|
||||
SHA2Provider<sha384_ctx, sha384_init, sha384_update, sha384_final> sha384provider;
|
||||
SHA2Provider<sha512_ctx, sha512_init, sha512_update, sha512_final> sha512provider;
|
||||
|
||||
Anope::string GenerateKey(size_t keylen)
|
||||
{
|
||||
static std::random_device device;
|
||||
static std::mt19937 engine(device());
|
||||
static std::uniform_int_distribution<int> dist(CHAR_MIN, CHAR_MAX);
|
||||
Anope::string keybuf(keylen, ' ');
|
||||
for (size_t i = 0; i < keylen; ++i)
|
||||
keybuf[i] = static_cast<char>(dist(engine));
|
||||
return keybuf;
|
||||
}
|
||||
|
||||
Encryption::Provider *GetAlgorithm(const Anope::string &algorithm)
|
||||
{
|
||||
if (algorithm == "sha224")
|
||||
return &sha224provider;
|
||||
if (algorithm == "sha256")
|
||||
return &sha256provider;
|
||||
if (algorithm == "sha384")
|
||||
return &sha384provider;
|
||||
if (algorithm == "sha512")
|
||||
return &sha512provider;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
ESHA2(const Anope::string &modname, const Anope::string &creator)
|
||||
: Module(modname, creator, ENCRYPTION | VENDOR)
|
||||
, sha224provider(this, "sha224", SHA224_BLOCK_SIZE, SHA224_DIGEST_SIZE)
|
||||
, sha256provider(this, "sha256", SHA256_BLOCK_SIZE, SHA256_DIGEST_SIZE)
|
||||
, sha384provider(this, "sha384", SHA384_BLOCK_SIZE, SHA384_DIGEST_SIZE)
|
||||
, sha512provider(this, "sha512", SHA512_BLOCK_SIZE, SHA512_DIGEST_SIZE)
|
||||
{
|
||||
sha224provider.Check({
|
||||
{ "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", "" },
|
||||
{ "730e109bd7a8a32b1cb9d9a09aa2325d2430587ddbc0c38bad911525", "The quick brown fox jumps over the lazy dog" },
|
||||
});
|
||||
sha256provider.Check({
|
||||
{ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "" },
|
||||
{ "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592", "The quick brown fox jumps over the lazy dog" },
|
||||
});
|
||||
sha384provider.Check({
|
||||
{ "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b", "" },
|
||||
{ "ca737f1014a48f4c0b6dd43cb177b0afd9e5169367544c494011e3317dbf9a509cb1e5dc1e85a941bbee3d7f2afbc9b1", "The quick brown fox jumps over the lazy dog" },
|
||||
});
|
||||
sha512provider.Check({
|
||||
{ "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", "" },
|
||||
{ "07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6", "The quick brown fox jumps over the lazy dog" },
|
||||
});
|
||||
}
|
||||
|
||||
void OnReload(Configuration::Conf *conf) override
|
||||
{
|
||||
this->defaultprovider = GetAlgorithm(Config->GetModule(this)->Get<const Anope::string>("algorithm", "sha256"));
|
||||
}
|
||||
|
||||
EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) override
|
||||
{
|
||||
if (!defaultprovider)
|
||||
return EVENT_CONTINUE;
|
||||
|
||||
auto key = GenerateKey(defaultprovider->digest_size);
|
||||
auto hmac = defaultprovider->HMAC(key, src);
|
||||
auto enc = "hmac-" + defaultprovider->name + ":" + Anope::Hex(hmac) + ":" + Anope::Hex(key);
|
||||
Log(LOG_DEBUG_2) << "(enc_sha2) hashed password from [" << src << "] to [" << enc << "]";
|
||||
dest = enc;
|
||||
return EVENT_ALLOW;
|
||||
}
|
||||
|
||||
void OnCheckAuthentication(User *, IdentifyRequest *req) override
|
||||
{
|
||||
const auto *na = NickAlias::Find(req->GetAccount());
|
||||
if (!na)
|
||||
return;
|
||||
|
||||
NickCore *nc = na->nc;
|
||||
auto apos = nc->pass.find(':');
|
||||
if (apos == Anope::string::npos)
|
||||
return;
|
||||
|
||||
Anope::string hash_method(nc->pass.begin(), nc->pass.begin() + apos);
|
||||
bool is_hmac = !hash_method.compare(0, 5, "hmac-", 5);
|
||||
if (!is_hmac && hash_method.compare(0, 4, "raw-", 4))
|
||||
return; // Not a SHA-2 password.
|
||||
|
||||
auto provider = GetAlgorithm(hash_method.substr(is_hmac ? 5 : 4));
|
||||
if (!provider)
|
||||
return; // Not a hash for this module.
|
||||
|
||||
auto valid = false;
|
||||
if (is_hmac)
|
||||
{
|
||||
auto bpos = nc->pass.find(':', apos + 1);
|
||||
if (bpos == Anope::string::npos)
|
||||
return; // No HMAC key.
|
||||
|
||||
Anope::string pass_hex(nc->pass.begin() + apos + 1, nc->pass.begin() + bpos);
|
||||
Anope::string key_hex(nc->pass.begin() + bpos + 1, nc->pass.end());
|
||||
Anope::string key;
|
||||
Anope::Unhex(key_hex, key);
|
||||
|
||||
auto enc = Anope::Hex(provider->HMAC(key, req->GetPassword()));
|
||||
valid = pass_hex.equals_cs(enc);
|
||||
}
|
||||
else
|
||||
{
|
||||
Anope::string pass_hex(nc->pass.begin() + apos + 1, nc->pass.end());
|
||||
valid = provider->Compare(pass_hex, req->GetPassword());
|
||||
}
|
||||
|
||||
if (valid)
|
||||
{
|
||||
// If we are NOT the first encryption module, the password is a raw
|
||||
// hash, or the algorithm is different we want to re-encrypt the
|
||||
// password with the primary encryption method.
|
||||
if (ModuleManager::FindFirstOf(ENCRYPTION) != this || !is_hmac || provider != defaultprovider)
|
||||
Anope::Encrypt(req->GetPassword(), nc->pass);
|
||||
req->Success(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MODULE_INIT(ESHA2)
|
||||
@@ -1,247 +1,21 @@
|
||||
/* This module generates and compares password hashes using SHA256 algorithms.
|
||||
*
|
||||
* If an intruder gets access to your system or uses a brute force attack,
|
||||
* salt will not provide much value.
|
||||
* IMPORTANT: DATA HASHES CANNOT BE "DECRYPTED" BACK TO PLAIN TEXT.
|
||||
*
|
||||
* Modified for Anope.
|
||||
* (C) 2003-2024 Anope Team
|
||||
* Contact us at team@anope.org
|
||||
*
|
||||
* Taken from InspIRCd (https://www.inspircd.org/),
|
||||
* see https://wiki.inspircd.org/Credits
|
||||
* This program is free but copyrighted software; see the file COPYING for
|
||||
* details.
|
||||
*
|
||||
* This program is free but copyrighted software; see
|
||||
* the file COPYING for details.
|
||||
*/
|
||||
|
||||
/* FIPS 180-2 SHA-224/256/384/512 implementation
|
||||
* Last update: 05/23/2005
|
||||
* Issue date: 04/30/2005
|
||||
*
|
||||
* Copyright (C) 2005 Olivier Gay <olivier.gay@a3.epfl.ch>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the project nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
#include "sha2/sha2.c"
|
||||
|
||||
#include "module.h"
|
||||
#include "modules/encryption.h"
|
||||
|
||||
static const unsigned SHA256_DIGEST_SIZE = 256 / 8;
|
||||
static const unsigned SHA256_BLOCK_SIZE = 512 / 8;
|
||||
|
||||
inline static uint32_t SHFR(uint32_t x, uint32_t n) { return x >> n; }
|
||||
inline static uint32_t ROTR(uint32_t x, uint32_t n) { return (x >> n) | (x << ((sizeof(x) << 3) - n)); }
|
||||
inline static uint32_t CH(uint32_t x, uint32_t y, uint32_t z) { return (x & y) ^ (~x & z); }
|
||||
inline static uint32_t MAJ(uint32_t x, uint32_t y, uint32_t z) { return (x & y) ^ (x & z) ^ (y & z); }
|
||||
|
||||
inline static uint32_t SHA256_F1(uint32_t x) { return ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22); }
|
||||
inline static uint32_t SHA256_F2(uint32_t x) { return ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25); }
|
||||
inline static uint32_t SHA256_F3(uint32_t x) { return ROTR(x, 7) ^ ROTR(x, 18) ^ SHFR(x, 3); }
|
||||
inline static uint32_t SHA256_F4(uint32_t x) { return ROTR(x, 17) ^ ROTR(x, 19) ^ SHFR(x, 10); }
|
||||
|
||||
inline static void UNPACK32(unsigned x, unsigned char *str)
|
||||
{
|
||||
str[3] = static_cast<uint8_t>(x);
|
||||
str[2] = static_cast<uint8_t>(x >> 8);
|
||||
str[1] = static_cast<uint8_t>(x >> 16);
|
||||
str[0] = static_cast<uint8_t>(x >> 24);
|
||||
}
|
||||
|
||||
inline static void PACK32(unsigned char *str, uint32_t &x)
|
||||
{
|
||||
x = static_cast<uint32_t>(str[3]) | static_cast<uint32_t>(str[2]) << 8 | static_cast<uint32_t>(str[1]) << 16 | static_cast<uint32_t>(str[0]) << 24;
|
||||
}
|
||||
|
||||
/* Macros used for loops unrolling */
|
||||
|
||||
inline static void SHA256_SCR(uint32_t w[64], int i)
|
||||
{
|
||||
w[i] = SHA256_F4(w[i - 2]) + w[i - 7] + SHA256_F3(w[i - 15]) + w[i - 16];
|
||||
}
|
||||
|
||||
static const uint32_t sha256_h0[8] =
|
||||
{
|
||||
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
|
||||
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
|
||||
};
|
||||
|
||||
static const uint32_t sha256_k[64] =
|
||||
{
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
|
||||
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
||||
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
|
||||
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
|
||||
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
|
||||
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
|
||||
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
|
||||
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
||||
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
||||
};
|
||||
|
||||
/** An sha256 context
|
||||
*/
|
||||
class SHA256Context final
|
||||
: public Encryption::Context
|
||||
{
|
||||
void Transform(unsigned char *message, unsigned block_nb)
|
||||
{
|
||||
uint32_t w[64], wv[8];
|
||||
unsigned char *sub_block;
|
||||
for (unsigned i = 1; i <= block_nb; ++i)
|
||||
{
|
||||
int j;
|
||||
sub_block = message + ((i - 1) << 6);
|
||||
|
||||
for (j = 0; j < 16; ++j)
|
||||
PACK32(&sub_block[j << 2], w[j]);
|
||||
for (j = 16; j < 64; ++j)
|
||||
SHA256_SCR(w, j);
|
||||
for (j = 0; j < 8; ++j)
|
||||
wv[j] = this->h[j];
|
||||
for (j = 0; j < 64; ++j)
|
||||
{
|
||||
uint32_t t1 = wv[7] + SHA256_F2(wv[4]) + CH(wv[4], wv[5], wv[6]) + sha256_k[j] + w[j];
|
||||
uint32_t t2 = SHA256_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]);
|
||||
wv[7] = wv[6];
|
||||
wv[6] = wv[5];
|
||||
wv[5] = wv[4];
|
||||
wv[4] = wv[3] + t1;
|
||||
wv[3] = wv[2];
|
||||
wv[2] = wv[1];
|
||||
wv[1] = wv[0];
|
||||
wv[0] = t1 + t2;
|
||||
}
|
||||
for (j = 0; j < 8; ++j)
|
||||
this->h[j] += wv[j];
|
||||
}
|
||||
}
|
||||
|
||||
unsigned tot_len;
|
||||
unsigned len;
|
||||
unsigned char block[2 * SHA256_BLOCK_SIZE];
|
||||
uint32_t h[8];
|
||||
unsigned char digest[SHA256_DIGEST_SIZE];
|
||||
|
||||
public:
|
||||
SHA256Context(Encryption::IV *iv)
|
||||
{
|
||||
if (iv != NULL)
|
||||
{
|
||||
if (iv->second != 8)
|
||||
throw CoreException("Invalid IV size");
|
||||
for (int i = 0; i < 8; ++i)
|
||||
this->h[i] = iv->first[i];
|
||||
}
|
||||
else
|
||||
for (int i = 0; i < 8; ++i)
|
||||
this->h[i] = sha256_h0[i];
|
||||
|
||||
this->tot_len = 0;
|
||||
this->len = 0;
|
||||
memset(this->block, 0, sizeof(this->block));
|
||||
memset(this->digest, 0, sizeof(this->digest));
|
||||
}
|
||||
|
||||
void Update(const unsigned char *message, size_t mlen) override
|
||||
{
|
||||
unsigned tmp_len = SHA256_BLOCK_SIZE - this->len, rem_len = mlen < tmp_len ? mlen : tmp_len;
|
||||
|
||||
memcpy(&this->block[this->len], message, rem_len);
|
||||
if (this->len + mlen < SHA256_BLOCK_SIZE)
|
||||
{
|
||||
this->len += mlen;
|
||||
return;
|
||||
}
|
||||
unsigned new_len = mlen - rem_len, block_nb = new_len / SHA256_BLOCK_SIZE;
|
||||
unsigned char *shifted_message = new unsigned char[mlen - rem_len];
|
||||
memcpy(shifted_message, message + rem_len, mlen - rem_len);
|
||||
this->Transform(this->block, 1);
|
||||
this->Transform(shifted_message, block_nb);
|
||||
rem_len = new_len % SHA256_BLOCK_SIZE;
|
||||
memcpy(this->block, &shifted_message[block_nb << 6], rem_len);
|
||||
delete [] shifted_message;
|
||||
this->len = rem_len;
|
||||
this->tot_len += (block_nb + 1) << 6;
|
||||
}
|
||||
|
||||
void Finalize() override
|
||||
{
|
||||
unsigned block_nb = 1 + ((SHA256_BLOCK_SIZE - 9) < (this->len % SHA256_BLOCK_SIZE));
|
||||
unsigned len_b = (this->tot_len + this->len) << 3;
|
||||
unsigned pm_len = block_nb << 6;
|
||||
memset(this->block + this->len, 0, pm_len - this->len);
|
||||
this->block[this->len] = 0x80;
|
||||
UNPACK32(len_b, this->block + pm_len - 4);
|
||||
this->Transform(this->block, block_nb);
|
||||
for (int i = 0 ; i < 8; ++i)
|
||||
UNPACK32(this->h[i], &this->digest[i << 2]);
|
||||
}
|
||||
|
||||
Encryption::Hash GetFinalizedHash() override
|
||||
{
|
||||
Encryption::Hash hash;
|
||||
hash.first = this->digest;
|
||||
hash.second = SHA256_DIGEST_SIZE;
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
class SHA256Provider final
|
||||
: public Encryption::Provider
|
||||
{
|
||||
public:
|
||||
SHA256Provider(Module *creator) : Encryption::Provider(creator, "sha256") { }
|
||||
|
||||
Encryption::Context *CreateContext(Encryption::IV *iv) override
|
||||
{
|
||||
return new SHA256Context(iv);
|
||||
}
|
||||
|
||||
Encryption::IV GetDefaultIV() override
|
||||
{
|
||||
Encryption::IV iv;
|
||||
iv.first = sha256_h0;
|
||||
iv.second = sizeof(sha256_h0) / sizeof(uint32_t);
|
||||
return iv;
|
||||
}
|
||||
};
|
||||
|
||||
class ESHA256 final
|
||||
: public Module
|
||||
{
|
||||
SHA256Provider sha256provider;
|
||||
|
||||
private:
|
||||
unsigned iv[8];
|
||||
bool use_iv;
|
||||
|
||||
@@ -249,7 +23,7 @@ class ESHA256 final
|
||||
void NewRandomIV()
|
||||
{
|
||||
for (auto &ivsegment : iv)
|
||||
ivsegment = static_cast<uint32_t>(rand());
|
||||
ivsegment = static_cast<uint32_t>(Anope::RandomNumber());
|
||||
}
|
||||
|
||||
/* returns the IV as base64-encrypted string */
|
||||
@@ -271,62 +45,59 @@ class ESHA256 final
|
||||
char buf2[33];
|
||||
Anope::Unhex(buf, buf2, sizeof(buf2));
|
||||
for (int i = 0 ; i < 8; ++i)
|
||||
PACK32(reinterpret_cast<unsigned char *>(&buf2[i << 2]), iv[i]);
|
||||
PACK32(reinterpret_cast<unsigned char *>(&buf2[i << 2]), &iv[i]);
|
||||
}
|
||||
|
||||
public:
|
||||
ESHA256(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, ENCRYPTION | VENDOR),
|
||||
sha256provider(this)
|
||||
{
|
||||
|
||||
|
||||
use_iv = false;
|
||||
}
|
||||
|
||||
EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) override
|
||||
Anope::string EncryptInternal(const Anope::string &src)
|
||||
{
|
||||
if (!use_iv)
|
||||
NewRandomIV();
|
||||
else
|
||||
use_iv = false;
|
||||
|
||||
Encryption::IV initialization(this->iv, 8);
|
||||
SHA256Context ctx(&initialization);
|
||||
ctx.Update(reinterpret_cast<const unsigned char *>(src.c_str()), src.length());
|
||||
ctx.Finalize();
|
||||
sha256_ctx ctx;
|
||||
sha256_init(&ctx);
|
||||
for (size_t i = 0; i < 8; ++i)
|
||||
ctx.h[i] = iv[i];
|
||||
sha256_update(&ctx, reinterpret_cast<const unsigned char *>(src.data()), src.length());
|
||||
unsigned char digest[SHA256_DIGEST_SIZE];
|
||||
sha256_final(&ctx, digest);
|
||||
Anope::string hash(reinterpret_cast<const char *>(&digest), sizeof(digest));
|
||||
|
||||
Encryption::Hash hash = ctx.GetFinalizedHash();
|
||||
return "sha256:" + Anope::Hex(hash) + ":" + GetIVString();
|
||||
}
|
||||
|
||||
std::stringstream buf;
|
||||
buf << "sha256:" << Anope::Hex(reinterpret_cast<const char *>(hash.first), hash.second) << ":" << GetIVString();
|
||||
Log(LOG_DEBUG_2) << "(enc_sha256) hashed password from [" << src << "] to [" << buf.str() << " ]";
|
||||
dest = buf.str();
|
||||
return EVENT_ALLOW;
|
||||
public:
|
||||
ESHA256(const Anope::string &modname, const Anope::string &creator)
|
||||
: Module(modname, creator, 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");
|
||||
}
|
||||
|
||||
void OnCheckAuthentication(User *, IdentifyRequest *req) override
|
||||
{
|
||||
const NickAlias *na = NickAlias::Find(req->GetAccount());
|
||||
if (na == NULL)
|
||||
const auto *na = NickAlias::Find(req->GetAccount());
|
||||
if (!na)
|
||||
return;
|
||||
NickCore *nc = na->nc;
|
||||
|
||||
size_t pos = nc->pass.find(':');
|
||||
NickCore *nc = na->nc;
|
||||
auto 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("sha256"))
|
||||
return;
|
||||
|
||||
GetIVFromPass(nc->pass);
|
||||
use_iv = true;
|
||||
Anope::string buf;
|
||||
this->OnEncrypt(req->GetPassword(), buf);
|
||||
if (nc->pass.equals_cs(buf))
|
||||
auto enc = EncryptInternal(req->GetPassword());
|
||||
if (nc->pass.equals_cs(enc))
|
||||
{
|
||||
/* if we are NOT the first module in the list,
|
||||
* we want to re-encrypt the pass with the new encryption
|
||||
*/
|
||||
// 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);
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
/* Module for providing Argon2 hashing
|
||||
*
|
||||
* (C) 2003-2024 Anope Team
|
||||
* Contact us at team@anope.org
|
||||
*
|
||||
* This program is free but copyrighted software; see the file COPYING for
|
||||
* details.
|
||||
*
|
||||
*/
|
||||
|
||||
/* RequiredLibraries: argon2 */
|
||||
/* RequiredWindowsLibraries: argon2 */
|
||||
|
||||
#include <climits>
|
||||
#include <random>
|
||||
|
||||
#include <argon2.h>
|
||||
|
||||
#include "module.h"
|
||||
#include "modules/encryption.h"
|
||||
|
||||
class Argon2Context final
|
||||
: public Encryption::Context
|
||||
{
|
||||
private:
|
||||
Anope::string buffer;
|
||||
argon2_type type;
|
||||
|
||||
Anope::string GenerateSalt()
|
||||
{
|
||||
static std::random_device device;
|
||||
static std::mt19937 engine(device());
|
||||
static std::uniform_int_distribution<int> dist(CHAR_MIN, CHAR_MAX);
|
||||
Anope::string saltbuf(this->salt_length, ' ');
|
||||
for (size_t i = 0; i < this->salt_length; ++i)
|
||||
saltbuf[i] = static_cast<char>(dist(engine));
|
||||
return saltbuf;
|
||||
}
|
||||
|
||||
public:
|
||||
static uint32_t memory_cost;
|
||||
static uint32_t time_cost;
|
||||
static uint32_t parallelism;
|
||||
static uint32_t hash_length;
|
||||
static uint32_t salt_length;
|
||||
|
||||
Argon2Context(argon2_type at)
|
||||
: type(at)
|
||||
{
|
||||
}
|
||||
|
||||
void Update(const unsigned char *data, size_t len) override
|
||||
{
|
||||
buffer.append(reinterpret_cast<const char *>(data), len);
|
||||
}
|
||||
|
||||
Anope::string Finalize() override
|
||||
{
|
||||
auto salt = GenerateSalt();
|
||||
|
||||
// Calculate the size of and allocate the output buffer.
|
||||
auto length = argon2_encodedlen(this->time_cost, this->memory_cost, this->parallelism,
|
||||
this->salt_length, this->hash_length, this->type);
|
||||
|
||||
std::vector<char> digest(length);
|
||||
auto result = argon2_hash(this->time_cost, this->memory_cost, this->parallelism,
|
||||
buffer.c_str(), buffer.length(), salt.c_str(), salt.length(), nullptr,
|
||||
this->hash_length, digest.data(), digest.size(), this->type,
|
||||
ARGON2_VERSION_NUMBER);
|
||||
|
||||
if (result == ARGON2_OK)
|
||||
return Anope::string(digest.data(), digest.size());
|
||||
|
||||
Log(LOG_DEBUG_2) << "Argon2 error: " << argon2_error_message(result);
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
uint32_t Argon2Context::memory_cost;
|
||||
uint32_t Argon2Context::time_cost;
|
||||
uint32_t Argon2Context::parallelism;
|
||||
uint32_t Argon2Context::hash_length;
|
||||
uint32_t Argon2Context::salt_length;
|
||||
|
||||
class Argon2Provider final
|
||||
: public Encryption::Provider
|
||||
{
|
||||
private:
|
||||
argon2_type type;
|
||||
|
||||
public:
|
||||
Argon2Provider(Module *creator, argon2_type at)
|
||||
: Encryption::Provider(creator, argon2_type2string(at, 0), 0, 0)
|
||||
, type(at)
|
||||
{
|
||||
}
|
||||
|
||||
bool Compare(const Anope::string &hash, const Anope::string &plain) override
|
||||
{
|
||||
return argon2_verify(hash.c_str(), plain.c_str(), plain.length(), this->type) == ARGON2_OK;
|
||||
}
|
||||
|
||||
std::unique_ptr<Encryption::Context> CreateContext() override
|
||||
{
|
||||
return std::make_unique<Argon2Context>(this->type);
|
||||
}
|
||||
|
||||
Anope::string ToPrintable(const Anope::string &hash) override
|
||||
{
|
||||
// We have no way to make this printable without the creating context
|
||||
// so we always return the printed form.
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
class EArgon2 final
|
||||
: public Module
|
||||
{
|
||||
private:
|
||||
Encryption::Provider *defaultprovider = nullptr;
|
||||
Argon2Provider argon2dprovider;
|
||||
Argon2Provider argon2iprovider;
|
||||
Argon2Provider argon2idprovider;
|
||||
|
||||
Encryption::Provider *GetAlgorithm(const Anope::string &algorithm)
|
||||
{
|
||||
if (algorithm == "argon2d")
|
||||
return &argon2dprovider;
|
||||
if (algorithm == "argon2i")
|
||||
return &argon2iprovider;
|
||||
if (algorithm == "argon2id")
|
||||
return &argon2idprovider;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
EArgon2(const Anope::string &modname, const Anope::string &creator)
|
||||
: Module(modname, creator, ENCRYPTION | VENDOR)
|
||||
, argon2dprovider(this, Argon2_d)
|
||||
, argon2iprovider(this, Argon2_i)
|
||||
, argon2idprovider(this, Argon2_id)
|
||||
|
||||
{
|
||||
}
|
||||
|
||||
void OnReload(Configuration::Conf *conf) override
|
||||
{
|
||||
const auto *block = Config->GetModule(this);
|
||||
this->defaultprovider = GetAlgorithm(block->Get<const Anope::string>("algorithm", "argon2id"));
|
||||
Argon2Context::memory_cost = block->Get<uint32_t>("memory_cost", "131072");
|
||||
Argon2Context::time_cost = block->Get<uint32_t>("time_cost", "3");
|
||||
Argon2Context::parallelism = block->Get<uint32_t>("parallelism", "1");
|
||||
Argon2Context::hash_length = block->Get<uint32_t>("hash_length", "32");
|
||||
Argon2Context::salt_length = block->Get<uint32_t>("salt_length", "32");
|
||||
}
|
||||
|
||||
EventReturn OnEncrypt(const Anope::string &src, Anope::string &dest) override
|
||||
{
|
||||
if (!defaultprovider)
|
||||
return EVENT_CONTINUE;
|
||||
|
||||
auto hash = defaultprovider->Encrypt(src);
|
||||
auto enc = defaultprovider->name + ":" + hash;
|
||||
Log(LOG_DEBUG_2) << "(enc_argon2) hashed password from [" << src << "] to [" << enc << "]";
|
||||
dest = enc;
|
||||
return EVENT_ALLOW;
|
||||
|
||||
}
|
||||
|
||||
void OnCheckAuthentication(User *, IdentifyRequest *req) override
|
||||
{
|
||||
const auto *na = NickAlias::Find(req->GetAccount());
|
||||
if (!na)
|
||||
return;
|
||||
|
||||
NickCore *nc = na->nc;
|
||||
auto pos = nc->pass.find(':');
|
||||
if (pos == Anope::string::npos)
|
||||
return;
|
||||
|
||||
Anope::string hash_method(nc->pass.begin(), nc->pass.begin() + pos);
|
||||
auto provider = GetAlgorithm(hash_method);
|
||||
if (!provider)
|
||||
return; // Not a hash for this module.
|
||||
|
||||
Anope::string hash_value(nc->pass.begin() + pos + 1, nc->pass.end());
|
||||
if (provider->Compare(hash_value, req->GetPassword()))
|
||||
{
|
||||
// If we are NOT the first encryption module or the algorithm is
|
||||
// different we want to re-encrypt the password with the primary
|
||||
// encryption method.
|
||||
if (ModuleManager::FindFirstOf(ENCRYPTION) != this || provider != defaultprovider)
|
||||
Anope::Encrypt(req->GetPassword(), nc->pass);
|
||||
req->Success(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MODULE_INIT(EArgon2)
|
||||
@@ -0,0 +1,53 @@
|
||||
/* Module for providing POSIX crypt() hashing
|
||||
*
|
||||
* (C) 2003-2024 Anope Team
|
||||
* Contact us at team@anope.org
|
||||
*
|
||||
* This program is free but copyrighted software; see the file COPYING for
|
||||
* details.
|
||||
*
|
||||
*/
|
||||
|
||||
/* RequiredLibraries: crypt */
|
||||
|
||||
#include "module.h"
|
||||
|
||||
class EPOSIX final
|
||||
: public Module
|
||||
{
|
||||
public:
|
||||
EPOSIX(const Anope::string &modname, const Anope::string &creator)
|
||||
: Module(modname, creator, ENCRYPTION | VENDOR)
|
||||
{
|
||||
if (ModuleManager::FindFirstOf(ENCRYPTION) == this)
|
||||
throw ModuleException("enc_posix can not be used as a primary encryption method");
|
||||
}
|
||||
|
||||
void OnCheckAuthentication(User *, IdentifyRequest *req) override
|
||||
{
|
||||
const auto *na = NickAlias::Find(req->GetAccount());
|
||||
if (!na)
|
||||
return;
|
||||
|
||||
NickCore *nc = na->nc;
|
||||
auto 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("posix"))
|
||||
return;
|
||||
|
||||
Anope::string pass_hash(nc->pass.begin() + pos + 1, nc->pass.end());
|
||||
if (pass_hash.equals_cs(crypt(req->GetPassword().c_str(), pass_hash.c_str())))
|
||||
{
|
||||
// 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(EPOSIX)
|
||||
@@ -452,7 +452,7 @@ Query MySQLService::BuildInsert(const Anope::string &table, unsigned int id, Dat
|
||||
|
||||
for (const auto &[field, _] : data.data)
|
||||
query_text += ",`" + field + "`";
|
||||
query_text += ") VALUES (" + stringify(id);
|
||||
query_text += ") VALUES (" + Anope::ToString(id);
|
||||
for (const auto &[field, _] : data.data)
|
||||
query_text += ",@" + field + "@";
|
||||
query_text += ") ON DUPLICATE KEY UPDATE ";
|
||||
@@ -536,7 +536,7 @@ Anope::string MySQLService::BuildQuery(const Query &q)
|
||||
|
||||
Anope::string MySQLService::FromUnixtime(time_t t)
|
||||
{
|
||||
return "FROM_UNIXTIME(" + stringify(t) + ")";
|
||||
return "FROM_UNIXTIME(" + Anope::ToString(t) + ")";
|
||||
}
|
||||
|
||||
void DispatcherThread::Run()
|
||||
|
||||
@@ -30,7 +30,7 @@ public:
|
||||
{
|
||||
PCRE2_UCHAR error[128];
|
||||
pcre2_get_error_message(errcode, error, sizeof error);
|
||||
throw RegexException("Error in regex " + expr + " at offset " + stringify(erroffset) + ": " + reinterpret_cast<const char*>(error));
|
||||
throw RegexException("Error in regex " + expr + " at offset " + Anope::ToString(erroffset) + ": " + reinterpret_cast<const char*>(error));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -118,8 +118,7 @@ public:
|
||||
|
||||
if (this->SQLiteServices.find(connname) == this->SQLiteServices.end())
|
||||
{
|
||||
Anope::string database = Anope::DataDir + "/" + block->Get<const Anope::string>("database", "anope");
|
||||
|
||||
auto database = Anope::ExpandData(block->Get<const Anope::string>("database", "anope"));
|
||||
try
|
||||
{
|
||||
auto *ss = new SQLiteService(this, connname, database);
|
||||
@@ -297,7 +296,7 @@ Query SQLiteService::BuildInsert(const Anope::string &table, unsigned int id, Da
|
||||
query_text.erase(query_text.length() - 1);
|
||||
query_text += ") VALUES (";
|
||||
if (id > 0)
|
||||
query_text += stringify(id) + ",";
|
||||
query_text += Anope::ToString(id) + ",";
|
||||
for (const auto &[field, _] : data.data)
|
||||
query_text += "@" + field + "@,";
|
||||
query_text.erase(query_text.length() - 1);
|
||||
@@ -339,7 +338,7 @@ Anope::string SQLiteService::BuildQuery(const Query &q)
|
||||
|
||||
Anope::string SQLiteService::FromUnixtime(time_t t)
|
||||
{
|
||||
return "datetime('" + stringify(t) + "', 'unixepoch')";
|
||||
return "datetime('" + Anope::ToString(t) + "', 'unixepoch')";
|
||||
}
|
||||
|
||||
MODULE_INIT(ModuleSQLite)
|
||||
|
||||
@@ -329,9 +329,9 @@ public:
|
||||
{
|
||||
Configuration::Block *config = conf->GetModule(this);
|
||||
|
||||
const Anope::string certfile = config->Get<const Anope::string>("cert", "data/fullchain.pem");
|
||||
const Anope::string keyfile = config->Get<const Anope::string>("key", "data/privkey.pem");
|
||||
const Anope::string dhfile = config->Get<const Anope::string>("dh", "data/dhparams.pem");
|
||||
const Anope::string certfile = Anope::ExpandConfig(config->Get<const Anope::string>("cert", "fullchain.pem"));
|
||||
const Anope::string keyfile = Anope::ExpandConfig(config->Get<const Anope::string>("key", "privkey.pem"));
|
||||
const Anope::string dhfile = Anope::ExpandConfig(config->Get<const Anope::string>("dh", "dhparams.pem"));
|
||||
|
||||
CheckFile(certfile);
|
||||
CheckFile(keyfile);
|
||||
|
||||
@@ -146,8 +146,8 @@ public:
|
||||
{
|
||||
Configuration::Block *config = conf->GetModule(this);
|
||||
|
||||
this->certfile = config->Get<const Anope::string>("cert", "data/fullchain.pem");
|
||||
this->keyfile = config->Get<const Anope::string>("key", "data/privkey.pem");
|
||||
this->certfile = Anope::ExpandConfig(config->Get<const Anope::string>("cert", "fullchain.pem"));
|
||||
this->keyfile = Anope::ExpandConfig(config->Get<const Anope::string>("key", "privkey.pem"));
|
||||
|
||||
if (Anope::IsFile(this->certfile.c_str()))
|
||||
{
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ public:
|
||||
|
||||
if (Anope::ReadOnly)
|
||||
{
|
||||
source.Reply(_("Sorry, bot option setting is temporarily disabled."));
|
||||
source.Reply(READ_ONLY_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,40 +14,75 @@
|
||||
class CommandGLGlobal final
|
||||
: public Command
|
||||
{
|
||||
ServiceReference<GlobalService> GService;
|
||||
private:
|
||||
ServiceReference<GlobalService> global;
|
||||
|
||||
BotInfo *GetSender(CommandSource &source)
|
||||
{
|
||||
Reference<BotInfo> sender;
|
||||
if (global)
|
||||
sender = global->GetDefaultSender();
|
||||
if (!sender)
|
||||
sender = source.service;
|
||||
return sender;
|
||||
}
|
||||
|
||||
public:
|
||||
CommandGLGlobal(Module *creator) : Command(creator, "global/global", 1, 1), GService("GlobalService", "Global")
|
||||
CommandGLGlobal(Module *creator)
|
||||
: Command(creator, "global/global", 0)
|
||||
, global("GlobalService", "Global")
|
||||
{
|
||||
this->SetDesc(_("Send a message to all users"));
|
||||
this->SetSyntax(_("\037message\037"));
|
||||
this->SetSyntax(_("[\037message\037]"));
|
||||
}
|
||||
|
||||
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override
|
||||
{
|
||||
const Anope::string &msg = params[0];
|
||||
if (!global)
|
||||
{
|
||||
source.Reply(SERVICE_UNAVAILABLE, source.service->nick.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GService)
|
||||
source.Reply("No global reference, is global loaded?");
|
||||
auto queuesize = global->CountQueue(source.nc);
|
||||
if (!queuesize && params.empty())
|
||||
{
|
||||
source.Reply(GLOBAL_NO_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (queuesize && !params.empty())
|
||||
{
|
||||
source.Reply(GLOBAL_QUEUE_CONFLICT);
|
||||
return;
|
||||
}
|
||||
|
||||
if (params.empty())
|
||||
{
|
||||
// We are sending the message queue.
|
||||
global->SendQueue(source, GetSender(source));
|
||||
}
|
||||
else
|
||||
{
|
||||
Log(LOG_ADMIN, source, this);
|
||||
GService->SendGlobal(NULL, source.GetNick(), msg);
|
||||
// We are sending a single message.
|
||||
global->SendSingle(params[0], &source, GetSender(source));
|
||||
queuesize = 1;
|
||||
}
|
||||
|
||||
Log(LOG_ADMIN, source, this) << "to send " << queuesize << " messages to all users";
|
||||
}
|
||||
|
||||
bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
|
||||
{
|
||||
Reference<BotInfo> sender;
|
||||
if (GService)
|
||||
sender = GService->GetDefaultSender();
|
||||
if (!sender)
|
||||
sender = source.service;
|
||||
|
||||
this->SendSyntax(source);
|
||||
source.Reply(" ");
|
||||
source.Reply(_("Allows Administrators to send messages to all users on the\n"
|
||||
"network. The message will be sent from the nick \002%s\002."), sender->nick.c_str());
|
||||
source.Reply(_(
|
||||
"Allows sending messages to all users on the network. The message will be sent\n"
|
||||
"from \002%s\002.\n"
|
||||
"\n"
|
||||
"You can either send a message by specifying it as a parameter or provide no\n"
|
||||
"parameters to send a previously queued message.\n"
|
||||
), GetSender(source)->nick.c_str());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -55,11 +90,13 @@ public:
|
||||
class GLGlobal final
|
||||
: public Module
|
||||
{
|
||||
private:
|
||||
CommandGLGlobal commandglglobal;
|
||||
|
||||
public:
|
||||
GLGlobal(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
|
||||
commandglglobal(this)
|
||||
GLGlobal(const Anope::string &modname, const Anope::string &creator)
|
||||
: Module(modname, creator, VENDOR)
|
||||
, commandglglobal(this)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
/* Global core functions
|
||||
*
|
||||
* (C) 2003-2024 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"
|
||||
|
||||
#define QUEUE_EMPTY _("You have no messages queued.")
|
||||
|
||||
class QueueDelCallback final
|
||||
: public NumberList
|
||||
{
|
||||
private:
|
||||
unsigned deleted = 0;
|
||||
ServiceReference<GlobalService> &global;
|
||||
CommandSource &source;
|
||||
|
||||
public:
|
||||
QueueDelCallback(CommandSource &src, ServiceReference<GlobalService>& gl, const Anope::string &list)
|
||||
: NumberList(list, true)
|
||||
, global(gl)
|
||||
, source(src)
|
||||
{
|
||||
}
|
||||
|
||||
~QueueDelCallback() override
|
||||
{
|
||||
switch (deleted)
|
||||
{
|
||||
case 0:
|
||||
source.Reply(_("No matching entries in your message queue."));
|
||||
break;
|
||||
case 1:
|
||||
source.Reply(_("Deleted one entry from your message queue."));
|
||||
break;
|
||||
default:
|
||||
source.Reply(_("Deleted %u entries from your message queue."), deleted);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void HandleNumber(unsigned number) override
|
||||
{
|
||||
if (!number || number > global->CountQueue(source.nc))
|
||||
return;
|
||||
|
||||
if (global->Unqueue(source.nc, number - 1))
|
||||
deleted++;
|
||||
}
|
||||
};
|
||||
|
||||
class CommandGLQueue final
|
||||
: public Command
|
||||
{
|
||||
private:
|
||||
ServiceReference<GlobalService> global;
|
||||
|
||||
void DoAdd(CommandSource &source, const Anope::string &message)
|
||||
{
|
||||
if (message.empty())
|
||||
{
|
||||
this->OnSyntaxError(source, "ADD");
|
||||
return;
|
||||
}
|
||||
|
||||
auto maxqueue = Config->GetModule(this->module)->Get<size_t>("maxqueue", "10");
|
||||
if (global->CountQueue(source.nc) >= maxqueue)
|
||||
{
|
||||
source.Reply(_("You can not queue any more messages."));
|
||||
return;
|
||||
}
|
||||
|
||||
global->Queue(source.nc, message);
|
||||
source.Reply(_("Your message has been queued."));
|
||||
Log(LOG_ADMIN, source, this) << "to queue: " << message;
|
||||
}
|
||||
|
||||
void DoClear(CommandSource &source)
|
||||
{
|
||||
if (!global->CountQueue(source.nc))
|
||||
{
|
||||
source.Reply(_("You do not have any queued messages."));
|
||||
return;
|
||||
}
|
||||
|
||||
global->ClearQueue(source.nc);
|
||||
source.Reply(_("Your message queue has been cleared."));
|
||||
Log(LOG_ADMIN, source, this);
|
||||
}
|
||||
|
||||
void DoDel(CommandSource &source, const Anope::string &what)
|
||||
{
|
||||
if (what.empty())
|
||||
{
|
||||
this->OnSyntaxError(source, "DEL");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!global->CountQueue(source.nc))
|
||||
{
|
||||
source.Reply(QUEUE_EMPTY);
|
||||
return;
|
||||
}
|
||||
|
||||
QueueDelCallback(source, global, what).Process();
|
||||
}
|
||||
|
||||
void DoList(CommandSource &source)
|
||||
{
|
||||
const auto *q = global->GetQueue(source.nc);
|
||||
if (!q || q->empty())
|
||||
{
|
||||
source.Reply(QUEUE_EMPTY);
|
||||
return;
|
||||
}
|
||||
|
||||
ListFormatter list(source.nc);
|
||||
list.AddColumn(_("Number")).AddColumn(_("Message"));
|
||||
for (size_t i = 0; i < q->size(); ++i)
|
||||
{
|
||||
ListFormatter::ListEntry entry;
|
||||
entry["Number"] = Anope::ToString(i + 1);
|
||||
entry["Message"] = (*q)[i];
|
||||
list.AddEntry(entry);
|
||||
}
|
||||
|
||||
std::vector<Anope::string> replies;
|
||||
list.Process(replies);
|
||||
for (const auto &reply : replies)
|
||||
source.Reply(reply);
|
||||
}
|
||||
|
||||
public:
|
||||
CommandGLQueue(Module *creator)
|
||||
: Command(creator, "global/queue", 1, 2)
|
||||
, global("GlobalService", "Global")
|
||||
{
|
||||
this->SetDesc(_("Manages your pending message queue."));
|
||||
this->SetSyntax(_("ADD \037message\037"));
|
||||
this->SetSyntax(_("DEL \037entry-num\037"));
|
||||
this->SetSyntax("LIST");
|
||||
this->SetSyntax("CLEAR");
|
||||
}
|
||||
|
||||
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override
|
||||
{
|
||||
if (!global)
|
||||
{
|
||||
source.Reply(SERVICE_UNAVAILABLE, source.service->nick.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
const auto &cmd = params[0];
|
||||
const auto &what = params.size() > 1 ? params[1] : "";
|
||||
if (cmd.equals_ci("ADD"))
|
||||
this->DoAdd(source, what);
|
||||
else if (cmd.equals_ci("CLEAR"))
|
||||
this->DoClear(source);
|
||||
else if (cmd.equals_ci("DEL"))
|
||||
this->DoDel(source, what);
|
||||
else if (cmd.equals_ci("LIST"))
|
||||
this->DoList(source);
|
||||
else
|
||||
this->OnSyntaxError(source, "");
|
||||
}
|
||||
|
||||
bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
|
||||
{
|
||||
this->SendSyntax(source);
|
||||
source.Reply("");
|
||||
source.Reply(_(
|
||||
"Allows queueing messages to send to users on the network.\n"
|
||||
"\n"
|
||||
"The \002QUEUE ADD\002 command adds the given message to the message queue."
|
||||
"\n"
|
||||
"The \002QUEUE CLEAR\002 command clears the message queue."
|
||||
"\n"
|
||||
"The \002QUEUE DEL\002 command removes the specified message from the message queue. The\n"
|
||||
"message number can be obtained from the output of the \002QUEUE LIST\002 command."
|
||||
"\n"
|
||||
"The \002QUEUE LIST\002 command lists all messages that are currently in the message queue."
|
||||
));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class GLQueue final
|
||||
: public Module
|
||||
{
|
||||
private:
|
||||
CommandGLQueue commandglqueue;
|
||||
|
||||
public:
|
||||
GLQueue(const Anope::string &modname, const Anope::string &creator)
|
||||
: Module(modname, creator, VENDOR)
|
||||
, commandglqueue(this)
|
||||
{
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
MODULE_INIT(GLQueue)
|
||||
@@ -0,0 +1,112 @@
|
||||
/* Global core functions
|
||||
*
|
||||
* (C) 2003-2024 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 CommandGLServer final
|
||||
: public Command
|
||||
{
|
||||
private:
|
||||
ServiceReference<GlobalService> global;
|
||||
|
||||
BotInfo *GetSender(CommandSource &source)
|
||||
{
|
||||
Reference<BotInfo> sender;
|
||||
if (global)
|
||||
sender = global->GetDefaultSender();
|
||||
if (!sender)
|
||||
sender = source.service;
|
||||
return sender;
|
||||
}
|
||||
|
||||
public:
|
||||
CommandGLServer(Module *creator)
|
||||
: Command(creator, "global/server", 1)
|
||||
, global("GlobalService", "Global")
|
||||
{
|
||||
this->SetDesc(_("Send a message to all users on a server"));
|
||||
this->SetSyntax(_("\037server\037 [\037message\037]"));
|
||||
}
|
||||
|
||||
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override
|
||||
{
|
||||
if (!global)
|
||||
{
|
||||
source.Reply(SERVICE_UNAVAILABLE, source.service->nick.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
auto *server = Server::Find(params[0]);
|
||||
if (!server || server == Me || server->IsJuped())
|
||||
{
|
||||
source.Reply(_("Server \002%s\002 is not linked to the network."), params[0].c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
auto queuesize = global->CountQueue(source.nc);
|
||||
if (!queuesize && params.size() < 2)
|
||||
{
|
||||
source.Reply(GLOBAL_NO_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (queuesize && params.size() > 1)
|
||||
{
|
||||
source.Reply(GLOBAL_QUEUE_CONFLICT);
|
||||
return;
|
||||
}
|
||||
|
||||
if (params.empty())
|
||||
{
|
||||
// We are sending the message queue.
|
||||
global->SendQueue(source, GetSender(source), server);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We are sending a single message.
|
||||
global->SendSingle(params[1], &source, GetSender(source), server);
|
||||
queuesize = 1;
|
||||
}
|
||||
|
||||
Log(LOG_ADMIN, source, this) << "to send " << queuesize << " messages to users on " << server->GetName();
|
||||
}
|
||||
|
||||
bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
|
||||
{
|
||||
this->SendSyntax(source);
|
||||
source.Reply(" ");
|
||||
source.Reply(_(
|
||||
"Allows sending messages to all users on a server. The message will be sent\n"
|
||||
"from \002%s\002.\n"
|
||||
"\n"
|
||||
"You can either send a message by specifying it as a parameter or provide no\n"
|
||||
"parameters to send a previously queued message.\n"
|
||||
), GetSender(source)->nick.c_str());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class GLServer final
|
||||
: public Module
|
||||
{
|
||||
private:
|
||||
CommandGLServer commandglserver;
|
||||
|
||||
public:
|
||||
GLServer(const Anope::string &modname, const Anope::string &creator)
|
||||
: Module(modname, creator, VENDOR)
|
||||
, commandglserver(this)
|
||||
{
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
MODULE_INIT(GLServer)
|
||||
+113
-39
@@ -15,88 +15,162 @@ class GlobalCore final
|
||||
: public Module
|
||||
, public GlobalService
|
||||
{
|
||||
Reference<BotInfo> Global;
|
||||
private:
|
||||
Reference<BotInfo> global;
|
||||
PrimitiveExtensibleItem<std::vector<Anope::string>> queue;
|
||||
|
||||
void ServerGlobal(BotInfo *sender, Server *s, const Anope::string &message)
|
||||
void ServerGlobal(BotInfo *sender, Server *server, bool children, const Anope::string &message)
|
||||
{
|
||||
if (s != Me && !s->IsJuped())
|
||||
s->Notice(sender, message);
|
||||
for (auto *link : s->GetLinks())
|
||||
this->ServerGlobal(sender, link, message);
|
||||
if (server != Me && !server->IsJuped())
|
||||
server->Notice(sender, message);
|
||||
|
||||
if (children)
|
||||
{
|
||||
for (auto *link : server->GetLinks())
|
||||
this->ServerGlobal(sender, link, true, message);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
GlobalCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PSEUDOCLIENT | VENDOR),
|
||||
GlobalService(this)
|
||||
GlobalCore(const Anope::string &modname, const Anope::string &creator)
|
||||
: Module(modname, creator, PSEUDOCLIENT | VENDOR)
|
||||
, GlobalService(this)
|
||||
, queue(this, "global-queue")
|
||||
{
|
||||
}
|
||||
|
||||
Reference<BotInfo> GetDefaultSender() override
|
||||
void ClearQueue(NickCore *nc) override
|
||||
{
|
||||
return Global;
|
||||
queue.Unset(nc);
|
||||
}
|
||||
|
||||
void SendGlobal(BotInfo *sender, const Anope::string &source, const Anope::string &message) override
|
||||
Reference<BotInfo> GetDefaultSender() const override
|
||||
{
|
||||
if (Me->GetLinks().empty())
|
||||
return;
|
||||
if (!sender)
|
||||
sender = Global;
|
||||
if (!sender)
|
||||
return;
|
||||
return global;
|
||||
}
|
||||
|
||||
Anope::string rmessage;
|
||||
const std::vector<Anope::string> *GetQueue(NickCore* nc) const override
|
||||
{
|
||||
return queue.Get(nc);
|
||||
}
|
||||
|
||||
if (!source.empty() && !Config->GetModule("global")->Get<bool>("anonymousglobal"))
|
||||
rmessage = "[" + source + "] " + message;
|
||||
else
|
||||
rmessage = message;
|
||||
|
||||
this->ServerGlobal(sender, Servers::GetUplink(), rmessage);
|
||||
size_t Queue(NickCore *nc, const Anope::string &message) override
|
||||
{
|
||||
auto *q = queue.Require(nc);
|
||||
q->push_back(message);
|
||||
return q->size();
|
||||
}
|
||||
|
||||
void OnReload(Configuration::Conf *conf) override
|
||||
{
|
||||
const Anope::string &glnick = conf->GetModule(this)->Get<const Anope::string>("client");
|
||||
|
||||
const auto glnick = conf->GetModule(this)->Get<const Anope::string>("client");
|
||||
if (glnick.empty())
|
||||
throw ConfigException(Module::name + ": <client> must be defined");
|
||||
|
||||
BotInfo *bi = BotInfo::Find(glnick, true);
|
||||
auto *bi = BotInfo::Find(glnick, true);
|
||||
if (!bi)
|
||||
throw ConfigException(Module::name + ": no bot named " + glnick);
|
||||
|
||||
Global = bi;
|
||||
global = bi;
|
||||
}
|
||||
|
||||
void OnRestart() override
|
||||
{
|
||||
const Anope::string &gl = Config->GetModule(this)->Get<const Anope::string>("globaloncycledown");
|
||||
if (!gl.empty())
|
||||
this->SendGlobal(Global, "", gl);
|
||||
const auto msg = Config->GetModule(this)->Get<const Anope::string>("globaloncycledown");
|
||||
if (!msg.empty())
|
||||
this->SendSingle(msg, nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
void OnShutdown() override
|
||||
{
|
||||
const Anope::string &gl = Config->GetModule(this)->Get<const Anope::string>("globaloncycledown");
|
||||
if (!gl.empty())
|
||||
this->SendGlobal(Global, "", gl);
|
||||
const auto msg = Config->GetModule(this)->Get<const Anope::string>("globaloncycledown");
|
||||
if (!msg.empty())
|
||||
this->SendSingle(msg, nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
void OnNewServer(Server *s) override
|
||||
{
|
||||
const Anope::string &gl = Config->GetModule(this)->Get<const Anope::string>("globaloncycleup");
|
||||
if (!gl.empty() && !Me->IsSynced())
|
||||
s->Notice(Global, gl);
|
||||
const auto msg = Config->GetModule(this)->Get<const Anope::string>("globaloncycleup");
|
||||
if (!msg.empty() && !Me->IsSynced())
|
||||
s->Notice(global, msg);
|
||||
}
|
||||
|
||||
EventReturn OnPreHelp(CommandSource &source, const std::vector<Anope::string> ¶ms) override
|
||||
{
|
||||
if (!params.empty() || source.c || source.service != *Global)
|
||||
if (!params.empty() || source.c || source.service != *global)
|
||||
return EVENT_CONTINUE;
|
||||
source.Reply(_("%s commands:"), Global->nick.c_str());
|
||||
|
||||
source.Reply(_("%s commands:"), global->nick.c_str());
|
||||
return EVENT_CONTINUE;
|
||||
}
|
||||
|
||||
bool SendQueue(CommandSource &source, BotInfo *sender, Server *server) override
|
||||
{
|
||||
// We MUST have an account.
|
||||
if (!source.nc)
|
||||
return false;
|
||||
|
||||
// We MUST have a message queue.
|
||||
auto *q = queue.Get(source.nc);
|
||||
if (!q || q->empty())
|
||||
return false;
|
||||
|
||||
auto success = true;
|
||||
for (const auto &message : *q)
|
||||
{
|
||||
if (!SendSingle(message, &source, sender, server))
|
||||
{
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
queue.Unset(source.nc);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool SendSingle(const Anope::string &message, CommandSource *source, BotInfo* sender, Server* server) override
|
||||
{
|
||||
// We MUST have a sender.
|
||||
if (sender)
|
||||
sender = global;
|
||||
if (!sender)
|
||||
return false;
|
||||
|
||||
if (!server)
|
||||
server = Servers::GetUplink();
|
||||
|
||||
Anope::string line;
|
||||
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());
|
||||
}
|
||||
else
|
||||
{
|
||||
// A source isn't available or they're anonymous.
|
||||
line = message.empty() ? " " : message;
|
||||
}
|
||||
|
||||
if (server)
|
||||
this->ServerGlobal(sender, Servers::GetUplink(), true, line);
|
||||
else
|
||||
this->ServerGlobal(sender, server, false, line);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Unqueue(NickCore *nc, size_t idx) override
|
||||
{
|
||||
auto *q = queue.Get(nc);
|
||||
if (!q || idx > q->size())
|
||||
return false;
|
||||
|
||||
q->erase(q->begin() + idx);
|
||||
if (q->empty())
|
||||
queue.Unset(nc);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
MODULE_INIT(GlobalCore)
|
||||
|
||||
@@ -42,37 +42,35 @@ public:
|
||||
return;
|
||||
|
||||
const NickAlias *na = NickAlias::Find(u->nick);
|
||||
if (!na || na->nc != u->Account() || !na->HasVhost())
|
||||
if (!na || na->nc != u->Account() || !na->HasVHost())
|
||||
na = NickAlias::Find(u->Account()->display);
|
||||
if (!na || !na->HasVhost())
|
||||
if (!na || !na->HasVHost())
|
||||
return;
|
||||
|
||||
if (u->vhost.empty() || !u->vhost.equals_cs(na->GetVhostHost()) || (!na->GetVhostIdent().empty() && !u->GetVIdent().equals_cs(na->GetVhostIdent())))
|
||||
if (u->vhost.empty() || !u->vhost.equals_cs(na->GetVHostHost()) || (!na->GetVHostIdent().empty() && !u->GetVIdent().equals_cs(na->GetVHostIdent())))
|
||||
{
|
||||
IRCD->SendVhost(u, na->GetVhostIdent(), na->GetVhostHost());
|
||||
IRCD->SendVHost(u, na->GetVHostIdent(), na->GetVHostHost());
|
||||
|
||||
u->vhost = na->GetVhostHost();
|
||||
u->vhost = na->GetVHostHost();
|
||||
u->UpdateHost();
|
||||
|
||||
if (IRCD->CanSetVIdent && !na->GetVhostIdent().empty())
|
||||
u->SetVIdent(na->GetVhostIdent());
|
||||
if (IRCD->CanSetVIdent && !na->GetVHostIdent().empty())
|
||||
u->SetVIdent(na->GetVHostIdent());
|
||||
|
||||
if (HostServ)
|
||||
{
|
||||
if (!na->GetVhostIdent().empty())
|
||||
u->SendMessage(HostServ, _("Your vhost of \002%s\002@\002%s\002 is now activated."), na->GetVhostIdent().c_str(), na->GetVhostHost().c_str());
|
||||
else
|
||||
u->SendMessage(HostServ, _("Your vhost of \002%s\002 is now activated."), na->GetVhostHost().c_str());
|
||||
u->SendMessage(HostServ, _("Your vhost of \002%s\002 is now activated."),
|
||||
na->GetVHostMask().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnNickDrop(CommandSource &source, NickAlias *na) override
|
||||
{
|
||||
if (na->HasVhost())
|
||||
if (na->HasVHost())
|
||||
{
|
||||
FOREACH_MOD(OnDeleteVhost, (na));
|
||||
na->RemoveVhost();
|
||||
FOREACH_MOD(OnDeleteVHost, (na));
|
||||
na->RemoveVHost();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +87,7 @@ public:
|
||||
return EVENT_CONTINUE;
|
||||
}
|
||||
|
||||
void OnSetVhost(NickAlias *na) override
|
||||
void OnSetVHost(NickAlias *na) override
|
||||
{
|
||||
if (Config->GetModule(this)->Get<bool>("activate_on_set"))
|
||||
{
|
||||
@@ -97,33 +95,31 @@ public:
|
||||
|
||||
if (u && u->Account() == na->nc)
|
||||
{
|
||||
IRCD->SendVhost(u, na->GetVhostIdent(), na->GetVhostHost());
|
||||
IRCD->SendVHost(u, na->GetVHostIdent(), na->GetVHostHost());
|
||||
|
||||
u->vhost = na->GetVhostHost();
|
||||
u->vhost = na->GetVHostHost();
|
||||
u->UpdateHost();
|
||||
|
||||
if (IRCD->CanSetVIdent && !na->GetVhostIdent().empty())
|
||||
u->SetVIdent(na->GetVhostIdent());
|
||||
if (IRCD->CanSetVIdent && !na->GetVHostIdent().empty())
|
||||
u->SetVIdent(na->GetVHostIdent());
|
||||
|
||||
if (HostServ)
|
||||
{
|
||||
if (!na->GetVhostIdent().empty())
|
||||
u->SendMessage(HostServ, _("Your vhost of \002%s\002@\002%s\002 is now activated."), na->GetVhostIdent().c_str(), na->GetVhostHost().c_str());
|
||||
else
|
||||
u->SendMessage(HostServ, _("Your vhost of \002%s\002 is now activated."), na->GetVhostHost().c_str());
|
||||
u->SendMessage(HostServ, _("Your vhost of \002%s\002 is now activated."),
|
||||
na->GetVHostMask().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnDeleteVhost(NickAlias *na) override
|
||||
void OnDeleteVHost(NickAlias *na) override
|
||||
{
|
||||
if (Config->GetModule(this)->Get<bool>("activate_on_set"))
|
||||
{
|
||||
User *u = User::Find(na->nick);
|
||||
|
||||
if (u && u->Account() == na->nc)
|
||||
IRCD->SendVhostDel(u);
|
||||
IRCD->SendVHostDel(u);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -34,9 +34,9 @@ public:
|
||||
if (na)
|
||||
{
|
||||
Log(LOG_ADMIN, source, this) << "for user " << na->nick;
|
||||
FOREACH_MOD(OnDeleteVhost, (na));
|
||||
na->RemoveVhost();
|
||||
source.Reply(_("Vhost for \002%s\002 removed."), nick.c_str());
|
||||
FOREACH_MOD(OnDeleteVHost, (na));
|
||||
na->RemoveVHost();
|
||||
source.Reply(_("VHost for \002%s\002 removed."), nick.c_str());
|
||||
}
|
||||
else
|
||||
source.Reply(NICK_X_NOT_REGISTERED, nick.c_str());
|
||||
@@ -74,15 +74,15 @@ public:
|
||||
NickAlias *na = NickAlias::Find(nick);
|
||||
if (na)
|
||||
{
|
||||
FOREACH_MOD(OnDeleteVhost, (na));
|
||||
FOREACH_MOD(OnDeleteVHost, (na));
|
||||
const NickCore *nc = na->nc;
|
||||
for (auto *alias : *nc->aliases)
|
||||
{
|
||||
na = alias;
|
||||
na->RemoveVhost();
|
||||
na->RemoveVHost();
|
||||
}
|
||||
Log(LOG_ADMIN, source, this) << "for all nicks in group " << nc->display;
|
||||
source.Reply(_("vhosts for group \002%s\002 have been removed."), nc->display.c_str());
|
||||
source.Reply(_("VHosts for group \002%s\002 have been removed."), nc->display.c_str());
|
||||
}
|
||||
else
|
||||
source.Reply(NICK_X_NOT_REGISTERED, nick.c_str());
|
||||
|
||||
@@ -22,16 +22,16 @@ public:
|
||||
if (setting)
|
||||
return;
|
||||
|
||||
if (!na || !na->HasVhost())
|
||||
if (!na || !na->HasVHost())
|
||||
return;
|
||||
|
||||
setting = true;
|
||||
for (auto *nick : *na->nc->aliases)
|
||||
{
|
||||
if (nick)
|
||||
if (nick && nick != na)
|
||||
{
|
||||
nick->SetVhost(na->GetVhostIdent(), na->GetVhostHost(), na->GetVhostCreator());
|
||||
FOREACH_MOD(OnSetVhost, (nick));
|
||||
nick->SetVHost(na->GetVHostIdent(), na->GetVHostHost(), na->GetVHostCreator());
|
||||
FOREACH_MOD(OnSetVHost, (nick));
|
||||
}
|
||||
}
|
||||
setting = false;
|
||||
@@ -51,13 +51,11 @@ public:
|
||||
}
|
||||
|
||||
NickAlias *na = NickAlias::Find(source.GetNick());
|
||||
if (na && source.GetAccount() == na->nc && na->HasVhost())
|
||||
if (na && source.GetAccount() == na->nc && na->HasVHost())
|
||||
{
|
||||
this->Sync(na);
|
||||
if (!na->GetVhostIdent().empty())
|
||||
source.Reply(_("All vhosts in the group \002%s\002 have been set to \002%s\002@\002%s\002."), source.nc->display.c_str(), na->GetVhostIdent().c_str(), na->GetVhostHost().c_str());
|
||||
else
|
||||
source.Reply(_("All vhosts in the group \002%s\002 have been set to \002%s\002."), source.nc->display.c_str(), na->GetVhostHost().c_str());
|
||||
source.Reply(_("All vhosts in the group \002%s\002 have been set to \002%s\002."),
|
||||
source.nc->display.c_str(), na->GetVHostMask().c_str());
|
||||
}
|
||||
else
|
||||
source.Reply(HOST_NOT_ASSIGNED);
|
||||
@@ -91,7 +89,7 @@ public:
|
||||
throw ModuleException("Your IRCd does not support vhosts");
|
||||
}
|
||||
|
||||
void OnSetVhost(NickAlias *na) override
|
||||
void OnSetVHost(NickAlias *na) override
|
||||
{
|
||||
if (!synconset)
|
||||
return;
|
||||
|
||||
@@ -45,39 +45,33 @@ public:
|
||||
source.Reply(LIST_INCORRECT_RANGE);
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
from = convertTo<int>(key.substr(1, tmp - 1));
|
||||
to = convertTo<int>(key.substr(tmp + 1));
|
||||
}
|
||||
catch (const ConvertException &) { }
|
||||
|
||||
from = Anope::Convert<int>(key.substr(1, tmp - 1), 0);
|
||||
to = Anope::Convert<int>(key.substr(tmp + 1), 0);
|
||||
}
|
||||
}
|
||||
|
||||
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.AddColumn(_("Number")).AddColumn(_("Nick")).AddColumn(_("VHost")).AddColumn(_("Creator")).AddColumn(_("Created"));
|
||||
|
||||
for (const auto &[_, na] : *NickAliasList)
|
||||
{
|
||||
if (!na->HasVhost())
|
||||
if (!na->HasVHost())
|
||||
continue;
|
||||
|
||||
if (!key.empty() && key[0] != '#')
|
||||
{
|
||||
if ((Anope::Match(na->nick, key) || Anope::Match(na->GetVhostHost(), key)) && display_counter < listmax)
|
||||
if ((Anope::Match(na->nick, key) || Anope::Match(na->GetVHostHost(), key)) && display_counter < listmax)
|
||||
{
|
||||
++display_counter;
|
||||
|
||||
ListFormatter::ListEntry entry;
|
||||
entry["Number"] = stringify(display_counter);
|
||||
entry["Number"] = Anope::ToString(display_counter);
|
||||
entry["Nick"] = na->nick;
|
||||
if (!na->GetVhostIdent().empty())
|
||||
entry["Vhost"] = na->GetVhostIdent() + "@" + na->GetVhostHost();
|
||||
else
|
||||
entry["Vhost"] = na->GetVhostHost();
|
||||
entry["Creator"] = na->GetVhostCreator();
|
||||
entry["Created"] = Anope::strftime(na->GetVhostCreated(), NULL, true);
|
||||
entry["VHost"] = na->GetVHostMask();
|
||||
entry["Creator"] = na->GetVHostCreator();
|
||||
entry["Created"] = Anope::strftime(na->GetVHostCreated(), NULL, true);
|
||||
list.AddEntry(entry);
|
||||
}
|
||||
}
|
||||
@@ -91,14 +85,11 @@ public:
|
||||
{
|
||||
++display_counter;
|
||||
ListFormatter::ListEntry entry;
|
||||
entry["Number"] = stringify(display_counter);
|
||||
entry["Number"] = Anope::ToString(display_counter);
|
||||
entry["Nick"] = na->nick;
|
||||
if (!na->GetVhostIdent().empty())
|
||||
entry["Vhost"] = na->GetVhostIdent() + "@" + na->GetVhostHost();
|
||||
else
|
||||
entry["Vhost"] = na->GetVhostHost();
|
||||
entry["Creator"] = na->GetVhostCreator();
|
||||
entry["Created"] = Anope::strftime(na->GetVhostCreated(), NULL, true);
|
||||
entry["VHost"] = na->GetVHostMask();
|
||||
entry["Creator"] = na->GetVHostCreator();
|
||||
entry["Created"] = Anope::strftime(na->GetVHostCreated(), NULL, true);
|
||||
list.AddEntry(entry);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,15 +26,15 @@ public:
|
||||
User *u = source.GetUser();
|
||||
|
||||
const NickAlias *na = NickAlias::Find(u->nick);
|
||||
if (!na || na->nc != u->Account() || !na->HasVhost())
|
||||
if (!na || na->nc != u->Account() || !na->HasVHost())
|
||||
na = NickAlias::Find(u->Account()->display);
|
||||
|
||||
if (!na || !na->HasVhost())
|
||||
if (!na || !na->HasVHost())
|
||||
source.Reply(HOST_NOT_ASSIGNED);
|
||||
else
|
||||
{
|
||||
u->vhost.clear();
|
||||
IRCD->SendVhostDel(u);
|
||||
IRCD->SendVHostDel(u);
|
||||
u->UpdateHost();
|
||||
Log(LOG_COMMAND, source, this) << "to disable their vhost";
|
||||
source.Reply(_("Your vhost was removed and the normal cloaking restored."));
|
||||
|
||||
@@ -28,19 +28,16 @@ public:
|
||||
|
||||
User *u = source.GetUser();
|
||||
const NickAlias *na = NickAlias::Find(u->nick);
|
||||
if (!na || na->nc != u->Account() || !na->HasVhost())
|
||||
if (!na || na->nc != u->Account() || !na->HasVHost())
|
||||
na = NickAlias::Find(u->Account()->display);
|
||||
if (na && u->Account() == na->nc && na->HasVhost())
|
||||
if (na && u->Account() == na->nc && na->HasVHost())
|
||||
{
|
||||
if (!na->GetVhostIdent().empty())
|
||||
source.Reply(_("Your vhost of \002%s\002@\002%s\002 is now activated."), na->GetVhostIdent().c_str(), na->GetVhostHost().c_str());
|
||||
else
|
||||
source.Reply(_("Your vhost of \002%s\002 is now activated."), na->GetVhostHost().c_str());
|
||||
Log(LOG_COMMAND, source, this) << "to enable their vhost of " << (!na->GetVhostIdent().empty() ? na->GetVhostIdent() + "@" : "") << na->GetVhostHost();
|
||||
IRCD->SendVhost(u, na->GetVhostIdent(), na->GetVhostHost());
|
||||
u->vhost = na->GetVhostHost();
|
||||
if (IRCD->CanSetVIdent && !na->GetVhostIdent().empty())
|
||||
u->SetVIdent(na->GetVhostIdent());
|
||||
source.Reply(_("Your vhost of \002%s\002 is now activated."), na->GetVHostMask().c_str());
|
||||
Log(LOG_COMMAND, source, this) << "to enable their vhost of " << na->GetVHostMask();
|
||||
IRCD->SendVHost(u, na->GetVHostIdent(), na->GetVHostHost());
|
||||
u->vhost = na->GetVHostHost();
|
||||
if (IRCD->CanSetVIdent && !na->GetVHostIdent().empty())
|
||||
u->SetVIdent(na->GetVHostIdent());
|
||||
u->UpdateHost();
|
||||
}
|
||||
else
|
||||
|
||||
@@ -15,20 +15,20 @@
|
||||
*/
|
||||
|
||||
#include "module.h"
|
||||
#include "modules/hs_request.h"
|
||||
|
||||
static ServiceReference<MemoServService> memoserv("MemoServService", "MemoServ");
|
||||
|
||||
static void req_send_memos(Module *me, CommandSource &source, const Anope::string &vIdent, const Anope::string &vHost);
|
||||
static void req_send_memos(Module *me, CommandSource &source, const Anope::string &vident, const Anope::string &vhost);
|
||||
|
||||
struct HostRequest final
|
||||
: Serializable
|
||||
struct HostRequestImpl final
|
||||
: HostRequest
|
||||
, Serializable
|
||||
{
|
||||
Anope::string nick;
|
||||
Anope::string ident;
|
||||
Anope::string host;
|
||||
time_t time;
|
||||
|
||||
HostRequest(Extensible *) : Serializable("HostRequest") { }
|
||||
HostRequestImpl(Extensible *)
|
||||
: Serializable("HostRequest")
|
||||
{
|
||||
}
|
||||
|
||||
void Serialize(Serialize::Data &data) const override
|
||||
{
|
||||
@@ -47,11 +47,11 @@ struct HostRequest final
|
||||
if (na == NULL)
|
||||
return NULL;
|
||||
|
||||
HostRequest *req;
|
||||
HostRequestImpl *req;
|
||||
if (obj)
|
||||
req = anope_dynamic_static_cast<HostRequest *>(obj);
|
||||
req = anope_dynamic_static_cast<HostRequestImpl *>(obj);
|
||||
else
|
||||
req = na->Extend<HostRequest>("hostrequest");
|
||||
req = na->Extend<HostRequestImpl>("hostrequest");
|
||||
if (req)
|
||||
{
|
||||
req->nick = na->nick;
|
||||
@@ -75,7 +75,7 @@ class CommandHSRequest final
|
||||
public:
|
||||
CommandHSRequest(Module *creator) : Command(creator, "hostserv/request", 1, 1)
|
||||
{
|
||||
this->SetDesc(_("Request a vHost for your nick"));
|
||||
this->SetDesc(_("Request a vhost for your nick"));
|
||||
this->SetSyntax(_("vhost"));
|
||||
}
|
||||
|
||||
@@ -122,9 +122,9 @@ public:
|
||||
|
||||
if (!user.empty())
|
||||
{
|
||||
if (user.length() > IRCD->GetMaxUser())
|
||||
if (user.length() > IRCD->MaxUser)
|
||||
{
|
||||
source.Reply(HOST_SET_IDENTTOOLONG, IRCD->GetMaxUser());
|
||||
source.Reply(HOST_SET_IDENTTOOLONG, IRCD->MaxUser);
|
||||
return;
|
||||
}
|
||||
else if (!IRCD->CanSetVIdent)
|
||||
@@ -142,9 +142,9 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
if (host.length() > IRCD->GetMaxHost())
|
||||
if (host.length() > IRCD->MaxHost)
|
||||
{
|
||||
source.Reply(HOST_SET_TOOLONG, IRCD->GetMaxHost());
|
||||
source.Reply(HOST_SET_TOOLONG, IRCD->MaxHost);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -157,19 +157,19 @@ public:
|
||||
time_t send_delay = Config->GetModule("memoserv")->Get<time_t>("senddelay");
|
||||
if (Config->GetModule(this->owner)->Get<bool>("memooper") && send_delay > 0 && u && u->lastmemosend + send_delay > Anope::CurTime)
|
||||
{
|
||||
source.Reply(_("Please wait %lu seconds before requesting a new vHost."), (unsigned long)send_delay);
|
||||
source.Reply(_("Please wait %lu seconds before requesting a new vhost."), (unsigned long)send_delay);
|
||||
u->lastmemosend = Anope::CurTime;
|
||||
return;
|
||||
}
|
||||
|
||||
HostRequest req(na);
|
||||
HostRequestImpl req(na);
|
||||
req.nick = source.GetNick();
|
||||
req.ident = user;
|
||||
req.host = host;
|
||||
req.time = Anope::CurTime;
|
||||
na->Extend<HostRequest>("hostrequest", req);
|
||||
na->Extend<HostRequestImpl>("hostrequest", req);
|
||||
|
||||
source.Reply(_("Your vHost has been requested."));
|
||||
source.Reply(_("Your vhost has been requested."));
|
||||
req_send_memos(owner, source, user, host);
|
||||
Log(LOG_COMMAND, source, this) << "to request new vhost " << (!user.empty() ? user + "@" : "") << host;
|
||||
}
|
||||
@@ -178,7 +178,7 @@ public:
|
||||
{
|
||||
this->SendSyntax(source);
|
||||
source.Reply(" ");
|
||||
source.Reply(_("Request the given vHost to be activated for your nick by the\n"
|
||||
source.Reply(_("Request the given vhost to be activated for your nick by the\n"
|
||||
"network administrators. Please be patient while your request\n"
|
||||
"is being considered."));
|
||||
return true;
|
||||
@@ -191,7 +191,7 @@ class CommandHSActivate final
|
||||
public:
|
||||
CommandHSActivate(Module *creator) : Command(creator, "hostserv/activate", 1, 1)
|
||||
{
|
||||
this->SetDesc(_("Approve the requested vHost of a user"));
|
||||
this->SetDesc(_("Approve the requested vhost of a user"));
|
||||
this->SetSyntax(_("\037nick\037"));
|
||||
}
|
||||
|
||||
@@ -206,18 +206,18 @@ public:
|
||||
const Anope::string &nick = params[0];
|
||||
|
||||
NickAlias *na = NickAlias::Find(nick);
|
||||
HostRequest *req = na ? na->GetExt<HostRequest>("hostrequest") : NULL;
|
||||
HostRequestImpl *req = na ? na->GetExt<HostRequestImpl>("hostrequest") : NULL;
|
||||
if (req)
|
||||
{
|
||||
na->SetVhost(req->ident, req->host, source.GetNick(), req->time);
|
||||
FOREACH_MOD(OnSetVhost, (na));
|
||||
na->SetVHost(req->ident, req->host, source.GetNick(), req->time);
|
||||
FOREACH_MOD(OnSetVHost, (na));
|
||||
|
||||
if (Config->GetModule(this->owner)->Get<bool>("memouser") && memoserv)
|
||||
memoserv->Send(source.service->nick, na->nick, _("[auto memo] Your requested vHost has been approved."), true);
|
||||
memoserv->Send(source.service->nick, na->nick, _("[auto memo] Your requested vhost has been approved."), true);
|
||||
|
||||
source.Reply(_("vHost for %s has been activated."), na->nick.c_str());
|
||||
source.Reply(_("VHost for %s has been activated."), na->nick.c_str());
|
||||
Log(LOG_COMMAND, source, this) << "for " << na->nick << " for vhost " << (!req->ident.empty() ? req->ident + "@" : "") << req->host;
|
||||
na->Shrink<HostRequest>("hostrequest");
|
||||
na->Shrink<HostRequestImpl>("hostrequest");
|
||||
}
|
||||
else
|
||||
source.Reply(_("No request for nick %s found."), nick.c_str());
|
||||
@@ -227,7 +227,7 @@ public:
|
||||
{
|
||||
this->SendSyntax(source);
|
||||
source.Reply(" ");
|
||||
source.Reply(_("Activate the requested vHost for the given nick."));
|
||||
source.Reply(_("Activate the requested vhost for the given nick."));
|
||||
if (Config->GetModule(this->owner)->Get<bool>("memouser"))
|
||||
source.Reply(_("A memo informing the user will also be sent."));
|
||||
|
||||
@@ -241,7 +241,7 @@ class CommandHSReject final
|
||||
public:
|
||||
CommandHSReject(Module *creator) : Command(creator, "hostserv/reject", 1, 2)
|
||||
{
|
||||
this->SetDesc(_("Reject the requested vHost of a user"));
|
||||
this->SetDesc(_("Reject the requested vhost of a user"));
|
||||
this->SetSyntax(_("\037nick\037 [\037reason\037]"));
|
||||
}
|
||||
|
||||
@@ -257,23 +257,23 @@ public:
|
||||
const Anope::string &reason = params.size() > 1 ? params[1] : "";
|
||||
|
||||
NickAlias *na = NickAlias::Find(nick);
|
||||
HostRequest *req = na ? na->GetExt<HostRequest>("hostrequest") : NULL;
|
||||
HostRequestImpl *req = na ? na->GetExt<HostRequestImpl>("hostrequest") : NULL;
|
||||
if (req)
|
||||
{
|
||||
na->Shrink<HostRequest>("hostrequest");
|
||||
na->Shrink<HostRequestImpl>("hostrequest");
|
||||
|
||||
if (Config->GetModule(this->owner)->Get<bool>("memouser") && memoserv)
|
||||
{
|
||||
Anope::string message;
|
||||
if (!reason.empty())
|
||||
message = Anope::printf(_("[auto memo] Your requested vHost has been rejected. Reason: %s"), reason.c_str());
|
||||
message = Anope::printf(_("[auto memo] Your requested vhost has been rejected. Reason: %s"), reason.c_str());
|
||||
else
|
||||
message = _("[auto memo] Your requested vHost has been rejected.");
|
||||
message = _("[auto memo] Your requested vhost has been rejected.");
|
||||
|
||||
memoserv->Send(source.service->nick, nick, Language::Translate(source.GetAccount(), message.c_str()), true);
|
||||
}
|
||||
|
||||
source.Reply(_("vHost for %s has been rejected."), nick.c_str());
|
||||
source.Reply(_("VHost for %s has been rejected."), nick.c_str());
|
||||
Log(LOG_COMMAND, source, this) << "to reject vhost for " << nick << " (" << (!reason.empty() ? reason : "no reason") << ")";
|
||||
}
|
||||
else
|
||||
@@ -284,7 +284,7 @@ public:
|
||||
{
|
||||
this->SendSyntax(source);
|
||||
source.Reply(" ");
|
||||
source.Reply(_("Reject the requested vHost for the given nick."));
|
||||
source.Reply(_("Reject the requested vhost for the given nick."));
|
||||
if (Config->GetModule(this->owner)->Get<bool>("memouser"))
|
||||
source.Reply(_("A memo informing the user will also be sent, which includes the reason for the rejection if supplied."));
|
||||
|
||||
@@ -307,11 +307,11 @@ public:
|
||||
unsigned display_counter = 0, listmax = Config->GetModule(this->owner)->Get<unsigned>("listmax");
|
||||
ListFormatter list(source.GetAccount());
|
||||
|
||||
list.AddColumn(_("Number")).AddColumn(_("Nick")).AddColumn(_("Vhost")).AddColumn(_("Created"));
|
||||
list.AddColumn(_("Number")).AddColumn(_("Nick")).AddColumn(_("VHost")).AddColumn(_("Created"));
|
||||
|
||||
for (const auto &[nick, na] : *NickAliasList)
|
||||
{
|
||||
HostRequest *hr = na->GetExt<HostRequest>("hostrequest");
|
||||
HostRequestImpl *hr = na->GetExt<HostRequestImpl>("hostrequest");
|
||||
if (!hr)
|
||||
continue;
|
||||
|
||||
@@ -320,12 +320,12 @@ public:
|
||||
++display_counter;
|
||||
|
||||
ListFormatter::ListEntry entry;
|
||||
entry["Number"] = stringify(display_counter);
|
||||
entry["Number"] = Anope::ToString(display_counter);
|
||||
entry["Nick"] = nick;
|
||||
if (!hr->ident.empty())
|
||||
entry["Vhost"] = hr->ident + "@" + hr->host;
|
||||
entry["VHost"] = hr->ident + "@" + hr->host;
|
||||
else
|
||||
entry["Vhost"] = hr->host;
|
||||
entry["VHost"] = hr->host;
|
||||
entry["Created"] = Anope::strftime(hr->time, NULL, true);
|
||||
list.AddEntry(entry);
|
||||
}
|
||||
@@ -358,26 +358,26 @@ class HSRequest final
|
||||
CommandHSActivate commandhsactive;
|
||||
CommandHSReject commandhsreject;
|
||||
CommandHSWaiting commandhswaiting;
|
||||
ExtensibleItem<HostRequest> hostrequest;
|
||||
ExtensibleItem<HostRequestImpl> hostrequest;
|
||||
Serialize::Type request_type;
|
||||
|
||||
public:
|
||||
HSRequest(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
|
||||
commandhsrequest(this), commandhsactive(this),
|
||||
commandhsreject(this), commandhswaiting(this), hostrequest(this, "hostrequest"), request_type("HostRequest", HostRequest::Unserialize)
|
||||
commandhsreject(this), commandhswaiting(this), hostrequest(this, "hostrequest"), request_type("HostRequest", HostRequestImpl::Unserialize)
|
||||
{
|
||||
if (!IRCD || !IRCD->CanSetVHost)
|
||||
throw ModuleException("Your IRCd does not support vhosts");
|
||||
}
|
||||
};
|
||||
|
||||
static void req_send_memos(Module *me, CommandSource &source, const Anope::string &vIdent, const Anope::string &vHost)
|
||||
static void req_send_memos(Module *me, CommandSource &source, const Anope::string &vident, const Anope::string &vhost)
|
||||
{
|
||||
Anope::string host;
|
||||
if (!vIdent.empty())
|
||||
host = vIdent + "@" + vHost;
|
||||
if (!vident.empty())
|
||||
host = vident + "@" + vhost;
|
||||
else
|
||||
host = vHost;
|
||||
host = vhost;
|
||||
|
||||
if (Config->GetModule(me)->Get<bool>("memooper") && memoserv)
|
||||
{
|
||||
@@ -387,7 +387,7 @@ static void req_send_memos(Module *me, CommandSource &source, const Anope::strin
|
||||
if (!na)
|
||||
continue;
|
||||
|
||||
Anope::string message = Anope::printf(_("[auto memo] vHost \002%s\002 has been requested by %s."), host.c_str(), source.GetNick().c_str());
|
||||
Anope::string message = Anope::printf(_("[auto memo] 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);
|
||||
}
|
||||
|
||||
+15
-21
@@ -71,9 +71,9 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
if (host.length() > IRCD->GetMaxHost())
|
||||
if (host.length() > IRCD->MaxHost)
|
||||
{
|
||||
source.Reply(HOST_SET_TOOLONG, IRCD->GetMaxHost());
|
||||
source.Reply(HOST_SET_TOOLONG, IRCD->MaxHost);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -85,12 +85,9 @@ public:
|
||||
|
||||
Log(LOG_ADMIN, source, this) << "to set the vhost of " << na->nick << " to " << (!user.empty() ? user + "@" : "") << host;
|
||||
|
||||
na->SetVhost(user, host, source.GetNick());
|
||||
FOREACH_MOD(OnSetVhost, (na));
|
||||
if (!user.empty())
|
||||
source.Reply(_("VHost for \002%s\002 set to \002%s\002@\002%s\002."), nick.c_str(), user.c_str(), host.c_str());
|
||||
else
|
||||
source.Reply(_("VHost for \002%s\002 set to \002%s\002."), nick.c_str(), host.c_str());
|
||||
na->SetVHost(user, host, source.GetNick());
|
||||
FOREACH_MOD(OnSetVHost, (na));
|
||||
source.Reply(_("VHost for \002%s\002 set to \002%s\002."), nick.c_str(), na->GetVHostMask().c_str());
|
||||
}
|
||||
|
||||
bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
|
||||
@@ -98,7 +95,7 @@ public:
|
||||
this->SendSyntax(source);
|
||||
source.Reply(" ");
|
||||
source.Reply(_("Sets the vhost for the given nick to that of the given\n"
|
||||
"hostmask. If your IRCD supports vIdents, then using\n"
|
||||
"hostmask. If your IRCD supports vidents, then using\n"
|
||||
"SET <nick> <ident>@<hostmask> set idents for users as\n"
|
||||
"well as vhosts."));
|
||||
return true;
|
||||
@@ -110,13 +107,13 @@ class CommandHSSetAll final
|
||||
{
|
||||
static void Sync(const NickAlias *na)
|
||||
{
|
||||
if (!na || !na->HasVhost())
|
||||
if (!na || !na->HasVHost())
|
||||
return;
|
||||
|
||||
for (auto *nick : *na->nc->aliases)
|
||||
{
|
||||
if (nick)
|
||||
nick->SetVhost(na->GetVhostIdent(), na->GetVhostHost(), na->GetVhostCreator());
|
||||
if (nick && nick != na)
|
||||
nick->SetVHost(na->GetVHostIdent(), na->GetVHostHost(), na->GetVHostCreator());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,9 +174,9 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
if (host.length() > IRCD->GetMaxHost())
|
||||
if (host.length() > IRCD->MaxHost)
|
||||
{
|
||||
source.Reply(HOST_SET_TOOLONG, IRCD->GetMaxHost());
|
||||
source.Reply(HOST_SET_TOOLONG, IRCD->MaxHost);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -191,13 +188,10 @@ public:
|
||||
|
||||
Log(LOG_ADMIN, source, this) << "to set the vhost of " << na->nick << " to " << (!user.empty() ? user + "@" : "") << host;
|
||||
|
||||
na->SetVhost(user, host, source.GetNick());
|
||||
na->SetVHost(user, host, source.GetNick());
|
||||
this->Sync(na);
|
||||
FOREACH_MOD(OnSetVhost, (na));
|
||||
if (!user.empty())
|
||||
source.Reply(_("VHost for group \002%s\002 set to \002%s\002@\002%s\002."), nick.c_str(), user.c_str(), host.c_str());
|
||||
else
|
||||
source.Reply(_("VHost for group \002%s\002 set to \002%s\002."), nick.c_str(), host.c_str());
|
||||
FOREACH_MOD(OnSetVHost, (na));
|
||||
source.Reply(_("VHost for group \002%s\002 set to \002%s\002."), nick.c_str(), na->GetVHostMask().c_str());
|
||||
}
|
||||
|
||||
bool OnHelp(CommandSource &source, const Anope::string &subcommand) override
|
||||
@@ -205,7 +199,7 @@ public:
|
||||
this->SendSyntax(source);
|
||||
source.Reply(" ");
|
||||
source.Reply(_("Sets the vhost for all nicks in the same group as that\n"
|
||||
"of the given nick. If your IRCD supports vIdents, then\n"
|
||||
"of the given nick. If your IRCD supports vidents, then\n"
|
||||
"using SETALL <nick> <ident>@<hostmask> will set idents\n"
|
||||
"for users as well as vhosts.\n"
|
||||
"* NOTE, this will not update the vhost for any nicks\n"
|
||||
|
||||
+3
-6
@@ -216,11 +216,8 @@ public:
|
||||
}
|
||||
else if (buf.find_ci("Content-Length: ") == 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
this->content_length = convertTo<unsigned>(buf.substr(16));
|
||||
}
|
||||
catch (const ConvertException &ex) { }
|
||||
if (auto len = Anope::TryConvert<unsigned>(buf.substr(16)))
|
||||
this->content_length = len.value();
|
||||
}
|
||||
else if (buf.find(':') != Anope::string::npos)
|
||||
{
|
||||
@@ -252,7 +249,7 @@ public:
|
||||
this->WriteClient("Content-Type: text/html");
|
||||
else
|
||||
this->WriteClient("Content-Type: " + msg->content_type);
|
||||
this->WriteClient("Content-Length: " + stringify(msg->length));
|
||||
this->WriteClient("Content-Length: " + Anope::ToString(msg->length));
|
||||
|
||||
for (const auto &cookie : msg->cookies)
|
||||
{
|
||||
|
||||
@@ -229,8 +229,10 @@ public:
|
||||
this->disable_email_reason = conf->Get<const Anope::string>("disable_email_reason");
|
||||
|
||||
if (!email_attribute.empty())
|
||||
{
|
||||
/* Don't complain to users about how they need to update their email, we will do it for them */
|
||||
config->GetModule("nickserv")->Set("forceemail", "false");
|
||||
config->GetModule("nickserv")->Set("forceemail", "no");
|
||||
}
|
||||
}
|
||||
|
||||
EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector<Anope::string> ¶ms) override
|
||||
@@ -303,6 +305,15 @@ public:
|
||||
Anope::string new_dn = username_attribute + "=" + na->nick + "," + basedn;
|
||||
this->ldap->Add(&this->orinterface, new_dn, attributes);
|
||||
}
|
||||
|
||||
void OnPreNickExpire(NickAlias *na, bool &expire) override
|
||||
{
|
||||
// We can't let nicks expire if they still have a group or
|
||||
// there will be a zombie account left over that can't be
|
||||
// authenticated to.
|
||||
if (na->nick == na->nc->display && na->nc->aliases->size() > 1)
|
||||
expire = false;
|
||||
}
|
||||
};
|
||||
|
||||
MODULE_INIT(ModuleLDAPAuthentication)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user